Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .agents/skills/crystallize-artifact/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ echo "$TICKET_ID" > runtime/harden/crystallize-$NAME/ticket_id.txt

## Step 3: Write the task file

The frontmatter carries `operation: crystallize`, the `artifact`, the worker
reporting fields (`lead_agent` / `finish_report_path` per
The frontmatter carries `operation: crystallize`, the `artifact`,
`finish_report_path` (the report destination the lead polls; see
`.agents/shared/references/worker-reporting.md`), and an optional
`source_artifacts_dir`. The body *describes* the work and -- for a skill
reconstructed from the transcript -- anchors the worker's search with verbatim
Expand All @@ -92,7 +92,6 @@ flow steps, or argparse surfaces -- those are the worker's decisions.
{
cat << FRONTMATTER_EOF
---
lead_agent: $MNGR_AGENT_NAME
finish_report_path: runtime/harden/crystallize-$NAME/reports/report.md
operation: crystallize
artifact: skill
Expand Down
1 change: 0 additions & 1 deletion .agents/skills/heal-artifact/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ transcript.
```bash
cat > runtime/harden/heal-$TARGET/task.md << TASK_EOF
---
lead_agent: $MNGR_AGENT_NAME
finish_report_path: runtime/harden/heal-$TARGET/reports/report.md
operation: heal
artifact: skill
Expand Down
7 changes: 3 additions & 4 deletions .agents/skills/launch-task/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,15 @@ tk close cod-step-XXXX "Briefed a sub-agent on the <task> and reviewed its resul

## 1. Write the task file

Write a clear task file with YAML frontmatter (so the worker can address
reports back to you) followed by the human-readable task description.
The frontmatter contains `lead_agent` and `finish_report_path`.
Write a clear task file with YAML frontmatter followed by the human-readable
task description. The frontmatter contains `finish_report_path` (where the
worker writes its report).

```bash
mkdir -p runtime/launch-task/$NAME
{
cat << FRONTMATTER_EOF
---
lead_agent: $MNGR_AGENT_NAME
finish_report_path: runtime/launch-task/$NAME/reports/report.md
---
FRONTMATTER_EOF
Expand Down
80 changes: 80 additions & 0 deletions .agents/skills/launch-task/scripts/create_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,79 @@ def _read_finish_report_path(task_file: Path) -> Path:
return Path(value)


def _set_frontmatter_field(text: str, key: str, value: str) -> str:
"""Return ``text`` with frontmatter ``key`` set to ``value``.

Replaces the existing ``key:`` line in place (preserving the rest of the
file verbatim) or, if absent, inserts it just after the opening ``---``.
Returns ``text`` unchanged when there is no frontmatter block.
"""
lines = text.split("\n")
if not lines or lines[0].strip() != "---":
return text
end = next((i for i in range(1, len(lines)) if lines[i].strip() == "---"), None)
if end is None:
return text
for i in range(1, end):
if lines[i].split(":", 1)[0].strip() == key:
lines[i] = f"{key}: {value}"
return "\n".join(lines)
lines.insert(1, f"{key}: {value}")
return "\n".join(lines)


def _ensure_lead_agent(task_file: Path) -> int | None:
"""Stamp the launching agent as the report recipient in the task file.

The agent running ``launch`` *is* the lead that polls for the worker's
report, so its own ``MNGR_AGENT_NAME`` is the authoritative ``lead_agent`` --
we fill it in (overwriting whatever the file holds) from the environment
rather than trusting the task file. That frees task-file authors from setting
the field at all and eliminates a silent-failure class: a literal,
unexpanded ``$MNGR_AGENT_NAME`` (or a stale/omitted value) used to leave the
worker with no valid address, so it could not rsync its report back and the
lead's poll waited forever.

When ``MNGR_AGENT_NAME`` is unset -- i.e. ``launch`` is running outside an
mngr agent, as in a manual invocation or a test -- the file's existing value
is used as a fallback; an unresolved value (missing, blank, or an unexpanded
``$...``) in that case is fatal (exit 2) rather than launching an
unaddressable worker.

Returns exit code ``2`` on unrecoverable misconfiguration; otherwise
``None``.
"""
try:
frontmatter, _body = _split_frontmatter(task_file.read_text(encoding="utf-8"))
except yaml.YAMLError:
return None # malformed YAML surfaces later via _read_source_artifacts_dir
Comment on lines +271 to +272

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, but why not let it blow up right away?

if frontmatter is None:
return None
current = frontmatter.get("lead_agent")
lead_name = os.environ.get("MNGR_AGENT_NAME")
if lead_name:
if current == lead_name:
return None
fixed = _set_frontmatter_field(task_file.read_text(encoding="utf-8"), "lead_agent", lead_name)
task_file.write_text(fixed, encoding="utf-8")
print(
f"create_worker: set lead_agent to {lead_name!r} (was {current!r})",
file=sys.stderr,
)
return None
# No launcher identity in the environment: fall back to the file's own value.
resolved = isinstance(current, str) and current.strip() and "$" not in current
if resolved:
return None
print(
"create_worker: lead_agent is unresolved "
f"({current!r}) and MNGR_AGENT_NAME is unset -- the worker would have no "
"address to send its report to.",
file=sys.stderr,
)
return 2


class Runner:
"""Indirection over ``subprocess.run`` so tests can intercept commands.

Expand Down Expand Up @@ -348,6 +421,13 @@ def launch(
)
return 2

# Stamp the lead agent (this launcher) into the task file before creating
# the worker, so the report has a valid return address and an unaddressable
# case fails fast rather than after provisioning.
lead_rc = _ensure_lead_agent(task_file)
if lead_rc is not None:
return lead_rc

runner.run(
[
"mngr",
Expand Down
136 changes: 136 additions & 0 deletions .agents/skills/launch-task/scripts/create_worker_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,142 @@ def test_malformed_frontmatter_does_not_abort_launch(tmp_path: Path) -> None:
]


def test_lead_agent_stamped_from_env_over_literal(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""A literal, unexpanded ``$MNGR_AGENT_NAME`` is replaced with the launching
agent's real name so the worker has a valid address to send its report to."""
runtime, task, _ = _make_layout(tmp_path)
task.write_text("---\nlead_agent: $MNGR_AGENT_NAME\n---\n\nbody\n")
monkeypatch.setenv("MNGR_AGENT_NAME", "real-lead")
runner = _RecordingRunner()

rc = create_worker_mod.launch(
name="demo-worker",
template="worker",
runtime_dir=runtime,
task_file=task,
workspace="ws-1",
runner=runner,
)

assert rc == 0
body = task.read_text()
assert "lead_agent: real-lead" in body
assert "$MNGR_AGENT_NAME" not in body
assert ["mngr", "create", "demo-worker", "-t", "worker", "--label", "workspace=ws-1"] in [
c.argv for c in runner.calls
]


def test_lead_agent_env_overrides_resolved_file_value(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""The launcher's own identity is authoritative: even a plain, resolved
file value is overwritten with MNGR_AGENT_NAME (the agent that polls for the
report). The file value is never trusted when the env names the launcher."""
runtime, task, _ = _make_layout(tmp_path) # lead_agent: lead
monkeypatch.setenv("MNGR_AGENT_NAME", "real-lead")
runner = _RecordingRunner()

rc = create_worker_mod.launch(
name="demo-worker",
template="worker",
runtime_dir=runtime,
task_file=task,
workspace="ws-1",
runner=runner,
)

assert rc == 0
assert "lead_agent: real-lead" in task.read_text()


def test_lead_agent_injected_when_field_absent(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""A task file that omits lead_agent entirely still gets it filled in from
the environment -- authors no longer need to set it."""
runtime, task, _ = _make_layout(tmp_path)
task.write_text("---\nfinish_report_path: r/report.md\n---\n\nbody\n")
monkeypatch.setenv("MNGR_AGENT_NAME", "real-lead")
runner = _RecordingRunner()

rc = create_worker_mod.launch(
name="demo-worker",
template="worker",
runtime_dir=runtime,
task_file=task,
workspace="ws-1",
runner=runner,
)

assert rc == 0
body = task.read_text()
assert "lead_agent: real-lead" in body
assert "finish_report_path: r/report.md" in body # sibling field preserved


def test_unresolved_lead_agent_without_env_is_fatal(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
"""If the launcher cannot name itself (no MNGR_AGENT_NAME) and the file value
is unresolved, fail before provisioning rather than launch an unaddressable
worker."""
runtime, task, _ = _make_layout(tmp_path)
task.write_text("---\nlead_agent: $MNGR_AGENT_NAME\n---\n\nbody\n")
monkeypatch.delenv("MNGR_AGENT_NAME", raising=False)
runner = _RecordingRunner()

rc = create_worker_mod.launch(
name="demo-worker",
template="worker",
runtime_dir=runtime,
task_file=task,
workspace="ws-1",
runner=runner,
)

assert rc == 2
assert runner.calls == [] # no worker created
assert "lead_agent is unresolved" in capsys.readouterr().err


def test_resolved_lead_agent_used_as_fallback_without_env(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Outside an mngr agent (no MNGR_AGENT_NAME), a plain author-set value is
accepted as a fallback so manual/test invocations still work."""
runtime, task, _ = _make_layout(tmp_path) # lead_agent: lead
monkeypatch.delenv("MNGR_AGENT_NAME", raising=False)
runner = _RecordingRunner()

rc = create_worker_mod.launch(
name="demo-worker",
template="worker",
runtime_dir=runtime,
task_file=task,
workspace="ws-1",
runner=runner,
)

assert rc == 0
assert "lead_agent: lead" in task.read_text()


def test_set_frontmatter_field_replaces_inserts_and_ignores_bodyless() -> None:
replaced = create_worker_mod._set_frontmatter_field(
"---\nlead_agent: old\nx: 1\n---\nbody\n", "lead_agent", "new"
)
assert "lead_agent: new" in replaced
assert "lead_agent: old" not in replaced
assert "x: 1" in replaced # sibling fields preserved
inserted = create_worker_mod._set_frontmatter_field("---\nx: 1\n---\nbody\n", "lead_agent", "new")
assert "lead_agent: new" in inserted
assert "x: 1" in inserted
assert create_worker_mod._set_frontmatter_field("just body", "lead_agent", "new") == "just body"


def test_runtime_dir_must_exist(
tmp_path: Path, capsys: pytest.CaptureFixture[str]
) -> None:
Expand Down
3 changes: 1 addition & 2 deletions .agents/skills/update-artifact/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,13 @@ git log -p "$COMMIT_RANGE" > runtime/harden/update-$TARGET/commit.diff
```

Write the task file. Frontmatter carries `operation: update`, the `artifact`,
and the worker reporting fields (per
and `finish_report_path` (the report destination the lead polls; see
`.agents/shared/references/worker-reporting.md`). The body carries the
`## Change origin` marker the worker dispatches on, plus origin-specific content:

```bash
cat > runtime/harden/update-$TARGET/task.md << TASK_EOF
---
lead_agent: $MNGR_AGENT_NAME
finish_report_path: runtime/harden/update-$TARGET/reports/report.md
operation: update
artifact: skill
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/update-system-interface/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ the worker, background-poll the report) with these system-interface specifics:
`update-$SLUG` / `mngr/update-$SLUG`; the runtime dir is
`runtime/harden/update-$SLUG/`.
- **Task-file frontmatter:** `operation: update`, `artifact: system-interface`,
plus the standard `lead_agent` / `finish_report_path`
plus the standard `finish_report_path`
(`runtime/harden/update-$SLUG/reports/report.md`). Per the system-interface
exception in `op-update.md`, there is **no `## Change origin` marker** -- the
body is a plain change brief, not an absorb/verify incident.
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/use-ai-integration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ uv run .agents/skills/launch-task/scripts/create_worker.py launch-sync \
It launches, waits for the worker's finish report in the foreground, writes a JSON
result (`timed_out`, `type`, `name`, `body`, `branch`, `raw_report`) to
`--result-json`, and destroys the worker (the `mngr/<name>` branch survives).
Write the task file first with `lead_agent` / `finish_report_path` frontmatter
Write the task file first with `finish_report_path` frontmatter
(see the `launch-task` skill). **User- or error-triggered, tightly scoped** -- a
broad unattended launch is how cost and time run away. What to do with the
returned branch (merge, review) is your concern.
Expand Down