fix: unbreak main CI — skills HIVE_HOME refactor + run_parallel_workers task text (#7149)
* fix(skills): restore module-level path constants for HIVE_HOME refactor
ae2aa30e replaced module-level USER_SKILLS_DIR / INSTALL_NOTICE_SENTINEL
in installer.py and _NOTICE_SENTINEL_PATH / _TRUSTED_REPOS_PATH in
trust.py with lazy helper functions, but left callers and tests still
referencing the original symbols. CI fails with ImportError /
AttributeError.
Restore them as module-level constants computed from HIVE_HOME so the
desktop-shell override still works, callers in cli.py keep importing
the same names, and existing test monkeypatches stay valid.
Refs #7148
* fix(colony): preserve task text in run_parallel_workers spawn data
run_parallel_workers stamps __template_task_id into spec['data'] before
calling spawn_batch. Once that mutation makes spec['data'] non-empty,
colony_runtime.spawn()'s ``input_data or {"task": task}`` fallback no
longer fires and the task description disappears from the worker's
first user message. Workers loop on empty responses and never emit
SUBAGENT_REPORT.
Hoist the ``setdefault("task")`` step out of the template-publish try
block so task text survives even if the template store fails
non-fatally. Inner loop only stamps __template_task_id.
Refs #7148
This commit is contained in:
@@ -15,26 +15,17 @@ import subprocess
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from framework.config import HIVE_HOME
|
||||
from framework.skills.parser import ParsedSkill
|
||||
from framework.skills.skill_errors import SkillError, SkillErrorCode
|
||||
|
||||
# Default install destination for user-scope skills.
|
||||
# Anchored on HIVE_HOME so the desktop shell can override the install
|
||||
# root via $HIVE_HOME without patching every call site.
|
||||
USER_SKILLS_DIR = HIVE_HOME / "skills"
|
||||
|
||||
# Default install destination for user-scope skills + sentinel file for
|
||||
# the one-time security notice on first install (NFR-5). Computed via
|
||||
# helpers so HIVE_HOME (set by the desktop shell to a per-user dir)
|
||||
# is honoured. ``framework.config.HIVE_HOME`` is module-global and
|
||||
# resolved at first import — so a single call here is enough; we don't
|
||||
# need to re-resolve on every access.
|
||||
def _user_skills_dir() -> Path:
|
||||
from framework.config import HIVE_HOME
|
||||
|
||||
return HIVE_HOME / "skills"
|
||||
|
||||
|
||||
def _install_notice_sentinel() -> Path:
|
||||
from framework.config import HIVE_HOME
|
||||
|
||||
return HIVE_HOME / ".install_notice_shown"
|
||||
# Sentinel file for the one-time security notice on first install (NFR-5).
|
||||
INSTALL_NOTICE_SENTINEL = HIVE_HOME / ".install_notice_shown"
|
||||
|
||||
|
||||
_INSTALL_NOTICE = """\
|
||||
@@ -60,13 +51,12 @@ def maybe_show_install_notice() -> None:
|
||||
Touches a sentinel file in $HIVE_HOME after showing the notice so it is
|
||||
only displayed once across all future installs.
|
||||
"""
|
||||
sentinel = _install_notice_sentinel()
|
||||
if sentinel.exists():
|
||||
if INSTALL_NOTICE_SENTINEL.exists():
|
||||
return
|
||||
print(_INSTALL_NOTICE, flush=True)
|
||||
try:
|
||||
sentinel.parent.mkdir(parents=True, exist_ok=True)
|
||||
sentinel.touch()
|
||||
INSTALL_NOTICE_SENTINEL.parent.mkdir(parents=True, exist_ok=True)
|
||||
INSTALL_NOTICE_SENTINEL.touch()
|
||||
except OSError:
|
||||
pass # If we can't write the sentinel, just show the notice every time
|
||||
|
||||
@@ -107,7 +97,7 @@ def install_from_git(
|
||||
fix="Install git (https://git-scm.com/) and retry.",
|
||||
)
|
||||
|
||||
dest = (target_dir or _user_skills_dir()) / skill_name
|
||||
dest = (target_dir or USER_SKILLS_DIR) / skill_name
|
||||
if dest.exists():
|
||||
raise SkillError(
|
||||
code=SkillErrorCode.SKILL_ACTIVATION_FAILED,
|
||||
@@ -208,7 +198,7 @@ def remove_skill(name: str, skills_dir: Path | None = None) -> bool:
|
||||
Raises:
|
||||
SkillError: If the directory exists but cannot be removed.
|
||||
"""
|
||||
target = (skills_dir or _user_skills_dir()) / name
|
||||
target = (skills_dir or USER_SKILLS_DIR) / name
|
||||
if not target.exists():
|
||||
return False
|
||||
try:
|
||||
|
||||
@@ -20,6 +20,7 @@ from enum import StrEnum
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from framework.config import HIVE_HOME
|
||||
from framework.skills.parser import ParsedSkill
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -30,17 +31,11 @@ _ENV_TRUST_ALL = "HIVE_TRUST_PROJECT_SKILLS"
|
||||
# Env var for comma-separated own-remote glob patterns (e.g. "github.com/myorg/*").
|
||||
_ENV_OWN_REMOTES = "HIVE_OWN_REMOTES"
|
||||
|
||||
# Persisted store of trusted git remotes (one-shot consent per repo).
|
||||
_TRUSTED_REPOS_PATH = HIVE_HOME / "trusted_repos.json"
|
||||
|
||||
def _trusted_repos_path() -> Path:
|
||||
from framework.config import HIVE_HOME
|
||||
|
||||
return HIVE_HOME / "trusted_repos.json"
|
||||
|
||||
|
||||
def _notice_sentinel_path() -> Path:
|
||||
from framework.config import HIVE_HOME
|
||||
|
||||
return HIVE_HOME / ".skill_trust_notice_shown"
|
||||
# Sentinel for the one-time security notice (NFR-5).
|
||||
_NOTICE_SENTINEL_PATH = HIVE_HOME / ".skill_trust_notice_shown"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -59,7 +54,7 @@ class TrustedRepoStore:
|
||||
"""Persists permanently-trusted repo keys to ~/.hive/trusted_repos.json."""
|
||||
|
||||
def __init__(self, path: Path | None = None) -> None:
|
||||
self._path = path or _trusted_repos_path()
|
||||
self._path = path or _TRUSTED_REPOS_PATH
|
||||
self._entries: dict[str, TrustedRepoEntry] = {}
|
||||
self._loaded = False
|
||||
|
||||
@@ -426,7 +421,7 @@ class TrustGate:
|
||||
|
||||
def _maybe_show_security_notice(self, Colors) -> None: # noqa: N803
|
||||
"""Show the one-time security notice if not already shown (NFR-5)."""
|
||||
sentinel = _notice_sentinel_path()
|
||||
sentinel = _NOTICE_SENTINEL_PATH
|
||||
if sentinel.exists():
|
||||
return
|
||||
self._print("")
|
||||
|
||||
@@ -1326,6 +1326,16 @@ def register_queen_lifecycle_tools(
|
||||
# the entries' template ids can be threaded into the spawn data
|
||||
# (workers' ctx.picked_up_from references them). This mirrors the
|
||||
# plan §5d "auto-populated by run_parallel_workers" behavior.
|
||||
# Preserve the task text in spec["data"] before any template-store
|
||||
# mutation. Once spec["data"] is non-empty, spawn()'s
|
||||
# ``input_data or {"task": task}`` fallback no longer fires, so the
|
||||
# task description would otherwise vanish from the worker's first
|
||||
# user message. Hoisted out of the try below so a non-fatal template
|
||||
# failure cannot drop task text from the spawn payload.
|
||||
for spec in normalised:
|
||||
spec["data"] = dict(spec.get("data") or {})
|
||||
spec["data"].setdefault("task", spec["task"])
|
||||
|
||||
_template_ids: list[int | None] = [None] * len(normalised)
|
||||
try:
|
||||
from framework.tasks import TaskListRole, get_task_store
|
||||
@@ -1343,7 +1353,6 @@ def register_queen_lifecycle_tools(
|
||||
_template_ids[i] = rec.id
|
||||
# Thread the template id into the worker's spawn data so
|
||||
# ColonyRuntime.spawn populates ctx.picked_up_from correctly.
|
||||
spec["data"] = dict(spec.get("data") or {})
|
||||
spec["data"]["__template_task_id"] = rec.id
|
||||
except Exception:
|
||||
logger.warning(
|
||||
|
||||
Reference in New Issue
Block a user