Skip to content

launch_wave: write the wave to a file and print a listing that fits in one tool result #247

Open

Description

@astefanutti

Jira: AISDLC-453 (under AISDLC-187, pipeline hardening). This issue is item 1 of that story; item 2 (a longer wait-for-wave block) waits on the CI runner's Bash budget and is not part of this issue.

Observed

Every production batch of 13 items or more since 2026-10-02 makes the orchestrator improvise: the launch_wave result does not fit in one tool result, so the model greps its own transcript files for KEY= / DATA_FILE= / PROMPT_PATH=, and on 2026-10-14 it listed artifacts/rfe-reviews/ and the staging directory by hand while a wave was pending. Batches of 9 or fewer never do this.

Run Items Improvised commands Compactions
2026-10-02 03:06 24 12 2
2026-10-14 03:06 22 11 1
2026-10-16 03:07 19 3 0
2026-10-13 15:11 15 3 0
2026-10-14 17:27 13 22 0
every run of 9 or fewer 0 0

Why it happens

scripts/pipeline_state.py next-action prints the whole wave as YAML (the launch_wave branch: output = {"action": "launch_wave", ..., "agents": agents} dumped with _BlockDumper). Every agent entry carries its full vars text: the type's launch block (type_registry.launch_vars(<type>, "auto-fix"), 65 lines / about 2.4 KB for rfe, 69 lines for initiative) followed by the agent's own two or three lines, rendered by _render_vars. ASSESS launches two agents per item (scorer and feasibility), so a 25-item wave prints about 50 copies of the same launch block, roughly 125 KB. The harness truncates a result that large to a file, and the model goes looking for the values in it.

What to do

  1. Write the wave to a file and print a listing that fits. In the launch_wave branch of next-action:
    • write the complete agent list, one block per agent (subagent_type if present, prompt_file, the full vars), to tmp/pipeline-launch-wave.yaml (same naming family as tmp/pipeline-wave-ids.txt and tmp/pipeline-wave-launch.txt; overwrite per wave);
    • print action: launch_wave, phase, message, agents_file: tmp/pipeline-launch-wave.yaml, agent_count: N, then common_vars (the launch block, once, {ID} left unsubstituted) and agents: with each entry's subagent_type, prompt_file and only its per-agent lines (the lines _render_vars adds after the launch block, with {ID} substituted). The listing for a 50-agent wave must stay well under 30,000 characters; pin an upper bound in a test.
  2. Read-back through the state helper. Add python3 scripts/state.py read-launch tmp/pipeline-launch-wave.yaml [--agent N] (prints the whole file, or one agent's full block) in scripts/state.py, next to read / read-ids. It exists so the model never reads the transcript: after a compaction it re-reads the file.
  3. Say so where the model reads it. .claude/skills/rfe-auto-fix/SKILL.md, the **launch_wave**: step and the "Example launch_wave output" section: the prompt is common_vars with {ID} replaced by the agent's KEY value, then the agent's own lines, then "\n\nRead <prompt_file> and follow all instructions exactly."; a lost or compacted listing is recovered with the read-launch command, not by searching files. Keep the DISPATCH_LOOP banner in scripts/pipeline_state.py (cmd_dispatch_context, printed after a compaction) in step with the skill text. rfe-review does not use launch_wave and is untouched.

Constraints

  • The production command stream is pinned: tests/test_pr1_transparent_edits.py and tests/test_typed_prompts.py::test_every_legacy_script_invocation_survives. Never edit the frozen corpus under tests/fixtures/legacy-prompts/; a changed or new script invocation in a skill body is recorded with _allow_command(...) in tests/test_typed_prompts.py, the way fix(#241): clear a feasibility flag a passing revision addressed #242 did for check_revised.py.
  • rfe-auto-fix/SKILL.md body (everything after the frontmatter) must stay under 16,000 characters (skillsaw's 4,000-token budget, strict in CI; it is at about 7,200 today).
  • No new literal key-prefix predicates in scripts/ (scripts/lint_prefix_predicates.py).
  • tests/test_pipeline_state.py asserts launch_wave results in many places (around lines 2381–2649 and 3846–4279): keep action, phase, message as they are; extend the assertions to the file and the compact listing for both types.
  • Verification: python3 -m pytest -q tests/test_pipeline_state.py tests/test_pr1_transparent_edits.py tests/test_typed_prompts.py tests/test_state.py and python3 scripts/lint_prefix_predicates.py. Do not run the whole suite or make lint.

Done when

  • A 25-item ASSESS wave (50 agents) produces one launch_wave result under the bound, the file holds every agent's full block, and state.py read-launch returns it, for rfe and initiative (tests).
  • The skill text and the dispatch banner describe the file form and the read-back command, and no other wording in the production command stream changed.

Activity

  1. fullsend-ai-coder commented on Oct 22, 2026

    @fullsend-ai-coder
    Contributor

    🤖 Code · ❌ Terminated · Started 3:49 PM UTC · Ended 4:00 PM UTC

    Commit: 36ef1a0 · View workflow run →

  2. fullsend-ai-triage commented on Oct 22, 2026

    @fullsend-ai-triage

    🤖 Finished Triage · ✅ Success · Started 3:50 PM UTC · Completed 3:51 PM UTC

    Commit: 36ef1a0 · View workflow run →

    Runtime: claude · Model: sonnet → claude-sonnet-5-5 · Effort: high · Cost: $0.12

  3. fullsend-ai-triage commented on Oct 22, 2026

    @fullsend-ai-triage

    Triage notes for this change.

    Scope check: the spec's file targets all exist in this repository (scripts/pipeline_state.py, scripts/state.py, the rfe-auto-fix skill, and the named test files). No open or merged PR currently addresses this, and there are no blocking prerequisites.

    Related work: the barrier-discipline issue (#250) is explicitly sequenced after this one, because its contract text names the read-launch command introduced here. This issue does not depend on it.

    Severity: medium. It only affects batches of 13 or more items, but the improvised commands during a pending wave risk duplicate launches.

    Watch-outs for the implementer: the _render_vars output has to be split into the common launch block and the per-agent lines without changing the final prompt text the agents receive, because the production command stream is pinned by tests. The --agent N indexing should be documented (0-based or 1-based) in both state.py usage text and the skill.

    Proposed test case:

    for type in (rfe, initiative):
      state = ASSESS phase, 25 IDs  (2 agents per ID -> 50 agents)
      out = run next-action
      assert action/phase/message unchanged
      assert len(out) < 30000
      assert agent_count == 50 and agents_file == tmp/pipeline-launch-wave.yaml
      assert common_vars appears once, with {ID} unsubstituted
      file = parse tmp/pipeline-launch-wave.yaml
      assert len(file.agents) == 50 and each has full vars (launch block + own lines)
      assert `state.py read-launch <file>` == file contents
      assert `state.py read-launch <file> --agent N` == agent N's block only
      next wave overwrites the file
    

    Next steps:

    • /fs-code — agent creates a PR to implement this issue
    • /fs-code <your instruction> — agent implements with your specific guidance
  4. fullsend-ai-coder commented on Oct 22, 2026

    @fullsend-ai-coder
    Contributor

    🤖 Finished Code · ✅ Success · Started 3:49 PM UTC · Completed 4:00 PM UTC

    Commit: 36ef1a0 · View workflow run →

    Runtime: claude · Model: opus → claude-opus-5-5 · Effort: high · Cost: $2.45

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions