Fix #2809: architect owns slice composition; risk_analyst becomes dual-role plan reviewer - #2821
Conversation
…l-role plan reviewer Restructures the plan-phase BRC graph so slice composition is a single role's job (architect), risk_analyst joins reviewer_plan as a critical reviewer, and reviewer_plan can hard-NACK oversized slices on a new `slice_size` rubric. Graph (`get_default_plan_graph`): - Add `risk_analyst -> architect` (CRITICAL) - Add `risk_analyst -> task_planner` (CRITICAL) - risk_analyst becomes dual-role via the existing `is_producer and is_reviewer` derivation, picking up the CONSENSUS_PROPOSE-augmented producer waits and the Dual-Role Execution Order banner for free (mirrors tester from #2749). Prompts (`_build_phase_prompt` + `_build_agent_prompt`): - architect: new "Slice composition authority (#2809)" section; emits `architect-slices.yaml` scaffold alongside the existing analysis JSON; file restrictions extended to permit it. - task_planner: drops "Slice shape is your call" and "Slice-sizing guidance (soft, advisory only) ... never NACKs on size"; now copies the architect scaffold verbatim and raises slicing problems as NACK pressure on architect rather than silently re-shaping. - risk_analyst: declared dual-role with a documented verdict shape; reviewer-role text explains when to NACK on the risk lens vs ACK with forward-looking register entries. - single-agent plan prompt: slice-sizing wording aligned to the new hard-NACK contract (the planner is the architect in that mode, so "Slice shape is your call" stays). Reviewer: - `_get_plan_review_criteria` gains §11 Slice Sizing (hard NACK, judgment-based) with the four canonical sub-division triggers and worked NACK examples. - `_build_reviewer_preparation(reviewer_plan, plan)` replaces the "advisory only - never NACK" guidance with the new judgment- based NACK directive that points at §11. Tests: - `test_plan_graph_has_correct_edges` now asserts the two new CRITICAL edges and the dual-role flag on risk_analyst. - `TestPlanPhaseArchitectOwnsComposition` covers the new prompt surfaces: architect authority, scaffold path + schema, file restrictions, task_planner verbatim consumption, NACK-pressure escape valve, risk_analyst dual-role + verdict shape, §11 rubric, and reviewer_plan prep wording. - `test_dual_role_banner_present_for_risk_analyst` pins the BRC preamble derivation. - Updated `TestPlannerPromptTreatsRefinerSlicingAsAdvisory` so the concurrent path asserts the architect-owns-composition contract instead of the old "Slice shape is your call" framing. Closes #2809.
…ns-slice-composition
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Review of #2821 — architect ownership of slice composition + risk_analyst dual-role.
Verdict
Core change is correct. The graph edge additions auto-derive risk_analyst as dual-role through the existing is_producer and is_reviewer predicate; the dual-role banner and wait-loop augmentations fire from that derivation without further wiring. Approval matrix (seed_auto_ack_for_empty_pure_producers skips dual-role) and check_confirm_guard (applies both producer and reviewer guards) both handle the new dual-role agent correctly. §11 rubric is well-scoped — judgment-based with concrete NACK patterns and an architect-routed re-propose path. Tests cover the new graph edges, the dual-role banner, the architect scaffold contract, the §11 rubric presence, and the reviewer-prep wording inversion.
Three non-blocking issues below.
Findings
1. Documentation drift in docs/architecture/slice-dag.md (non-blocking)
docs/architecture/slice-dag.md:625-654 still describes slice-sizing as advisory-only, anchored to HITL decision-6 opt-2:
Slice-sizing guidance (advisory only, never enforced; HITL decision-6 opt-2)
Slice-sizing advisory (advisory only, NEVER NACK) ... promoting the advisory to a hard NACK requires a fresh HITL revision.
This PR is exactly that promotion. The doc now contradicts the contract: §11 in _get_plan_review_criteria() makes slice-sizing a hard NACK rubric, the reviewer-prep wording in _build_reviewer_preparation instructs reviewer_plan to NACK on slice sizing, and the architect prompt asserts sole authority over slice composition. The architecture doc should be updated in the same PR — agents reading slice-dag.md to understand the contract will get the wrong story.
Suggested edits:
- §"Planner & plan-reviewer prompt updates" → reframe item 1 under planner as "architect owns slice composition, task_planner copies the scaffold verbatim"; remove "advisory only, never enforced" framing.
- §"Plan reviewer" item 2 → replace the "NEVER NACK" advisory text with the new §11 hard-NACK rubric pointer (cite #2809 alongside the existing HITL decision-6 reference; note the inversion).
2. Documentation drift in docs/reference/agent-roles.md (non-blocking)
docs/reference/agent-roles.md:28-29 doesn't reflect risk_analyst's new dual-role status:
- Line 28:
risk_analystrow only mentions Analysis category — it's now also a REVIEW-category agent in plan phase (mirrors howtesteris documented as dual-role in implement). - Line 29:
reviewer_plan"Depends On" lists onlytask_planner, risk_analyst— should also includearchitect(architect is now a reviewed producer per the new CRITICAL edges). - Line 111-120:
risk_analystsection under "Plan Phase" describes only its producer role; add a "Reviewer role" subsection paralleling how the architect/task_planner descriptions handle the new contract.
3. _synthesize_plan_draft fallback ignores the new scaffold (non-blocking)
orchestrator/routes/pipelines.py:18851-18854 — the fallback synthesizer's agent_files list still only enumerates:
agent_files = [
("architect-output.json", "Architecture Analysis"),
("risk_analyst-output.json", "Risk Assessment"),
]This path fires only when task_planner fails to write {id}-plan.md directly (the comment at 18820-18824 makes this explicit), so it's an edge-case recovery — but in that path the architect's architect-slices.yaml scaffold (which is now the binding source of slice DAG shape) is silently dropped from the synthesized draft. Either:
- Append
("architect-slices.yaml", "Slice Scaffold")toagent_files(note: it's YAML not JSON; the existingjson.loadsbranch will fall through to raw text, which is acceptable since the file is small and human-readable), or - Document that the fallback is best-effort and the scaffold is recoverable from
agent-outputs/directly.
The synthesized draft is read by _populate_contract_from_plan() for the HITL gate, so losing the slice DAG in the fallback path means the operator sees an incomplete plan in a recovery scenario.
What I verified end-to-end
risk_analystresolves to dual-role via existingis_dual_rolederivation inconcurrent_executor.pyonce both new CRITICAL edges are present._brc_preconfirm_wait_lineand_brc_stay_alive_wait_lineaugmentations fire on dual-role producers; no additional wiring needed.- Approval matrix skips seeding auto-ACK for dual-role producers (
seed_auto_ack_for_empty_pure_producers) — risk_analyst will not get a phantom ACK from itself. check_confirm_guardapplies producer + reviewer guards in sequence for dual-role agents.- Gateway file restrictions:
ARCHITECT_ROLE.allowed_writeincludes.egg-state/agent-outputs/, which covers the newarchitect-slices.yamlwrite path. - Test coverage matches the contract:
test_dual_role_banner_present_for_risk_analyst,TestPlanPhaseArchitectOwnsComposition(10 tests), and the updatedtest_plan_graph_has_correct_edges.
— Authored by egg
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
- docs/architecture/slice-dag.md: replace 'advisory only / NEVER NACK' framing with the new architect-owns-composition contract and §11 hard-NACK rubric pointer (#2809 inverts HITL decision-6 opt-2). Add architect bullet for the binding architect-slices.yaml scaffold. - docs/reference/agent-roles.md: mark risk_analyst as dual-role in the plan phase (Analysis + reviewer of architect/task_planner); add architect to reviewer_plan's Depends On; document architect's slice composition authority + architect-slices.yaml output; add Reviewer role subsection under risk_analyst paralleling the implement-phase tester dual-role pattern. - orchestrator/routes/pipelines.py: include architect-slices.yaml in _synthesize_plan_draft's agent_files list so the binding scaffold is captured by the fallback recovery synthesizer (path fires only when task_planner fails to write the plan draft directly). YAML falls through json.loads to raw text, which is acceptable per the reviewer's observation. - orchestrator/tests/test_pipeline_prompts.py: add test_includes_architect_slices_yaml_when_present pinning the scaffold capture behaviour.
|
Addressed all three non-blocking findings in this PR — no follow-up issues opened (none required HITL input). Per-item disposition1. Documentation drift in
|
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
No agent-mode design concerns.
The structured outputs in this PR (architect-slices.yaml scaffold, risk_analyst verdict JSON) are legitimate inter-agent contracts with real downstream consumers — task_planner copies the scaffold verbatim, _synthesize_plan_draft reads it as a HITL fallback, and the upstream producer acts on blocking_concerns/feedback. The BRC consensus protocol mandates ACK/NACK structure regardless. This is not "structured output for humans."
Other checks: no large diffs/file contents pre-fetched into prompts; the new slice-sizing rubric is explicitly judgment-based (no fixed LOC budget, four heuristic triggers) rather than lockstep procedure; the "copy verbatim" directive to task_planner is a clean separation of concerns with a documented escape valve (NACK pressure on architect) rather than silent re-shaping; no LLM API calls, no hardcoded model identifiers, no prompt-level security boundaries.
— Authored by egg
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Re-review of #2821 at e75ce78e767bdbc0d9e43fb39cb468b7403c5f94 (delta from prior review at 8c150e93bd2a74ac9a28058d0a9556cfb96a10ce).
Verdict
All three non-blocking findings from the prior review are properly addressed. No new issues found.
Per-finding verification
1. docs/architecture/slice-dag.md documentation drift — ✅ Fixed
docs/architecture/slice-dag.md:625-677 is now consistent with the new contract:
- New Architect bullet declares sole authority for slice composition (#2809) and the binding
architect-slices.yamlscaffold output. - Planner bullets reframe item 1 around verbatim consumption of the architect scaffold, with the "raise NACK pressure on architect" escape valve replacing local re-shaping. Items 2 (Forest constraint) and 3 (Auto-serialization) correctly attribute origination to the architect and explicitly state the planner preserves the field from the scaffold.
- Plan reviewer item 1 (Forest-violation NACK) now correctly NACKs the architect rather than the planner, with a clear "slice scaffold ownership moved to architect in #2809" inline note.
- Plan reviewer item 2 now describes the §11 hard-NACK rubric, cites #2809 alongside HITL decision-6 with the "inverts" framing, documents the seam-naming requirement, points at
_get_plan_review_criteria()§11, and preserves the refiner/operator override path for deliberate large slices.
I grepped docs/ for stale framing — advisory only, NEVER NACK, Slice shape is your call, soft target, never enforced, Slice-sizing advisory, Slice-sizing guidance — and the only remaining HITL decision-6 opt-2 references are the two intentional "inverting" mentions in slice-dag.md:626,664. No drift left in docs/.
2. docs/reference/agent-roles.md documentation drift — ✅ Fixed
docs/reference/agent-roles.md:28-29:
- Line 28:
risk_analyst's Category now readsAnalysis (dual-role: also reviews architect + task_planner). - Line 29:
reviewer_plan's Depends On now listsarchitect, task_planner, risk_analyst(architect added).
agent-roles.md:86-129:
architectsection (line 86) documents sole authority for slice composition,architect-slices.yamlin Outputs, and adds aReviewed by:line namingreviewer_plan(CRITICAL, structural) andrisk_analyst(CRITICAL, risk).task_plannersection (line 102) reframes purpose around enumerating tasks inside the architect's scaffold and adds the scaffold to prompt context.risk_analystsection (line 116) is split into Producer role + Reviewer role subsections; the Reviewer role text correctly cites the Dual-Role Execution Order banner andCONSENSUS_PROPOSE-augmented producer waits, matching thetester(#2749) dual-role pattern. Verifiedtester → coderis the analogous edge inorchestrator/review_graph.py:312.
3. _synthesize_plan_draft fallback ignores the scaffold — ✅ Fixed
orchestrator/routes/pipelines.py:18851-18855 — ("architect-slices.yaml", "Slice Scaffold") is appended to agent_files between architect-output and risk_analyst-output, which is a sensible ordering (slice scaffold sits next to its author's analysis JSON).
YAML fall-through behavior is correct: json.loads(raw_yaml) raises JSONDecodeError for the standard scaffold shape (multiline list under slices:, unquoted keys), and the except json.JSONDecodeError: content = raw branch (line 18872-18874) preserves the raw YAML as the section body. This matches the reviewer's prior observation that the YAML fall-through is acceptable for this small, human-readable file.
The new test_includes_architect_slices_yaml_when_present (orchestrator/tests/test_pipeline_prompts.py:2656-2702) exercises the production code path directly (no mocks of the function under test), uses a representative scaffold YAML structure (slices:, id, name, goal, parent_slice_id), and pins:
- the new
## Slice Scaffoldheading is emitted, - raw YAML body content survives the fallback (
Bootstrap,parent_slice_id: null), - the existing architect-output and risk_analyst-output sections are still included alongside it.
Test name matches assertions (no name-vs-behavior contradiction). Fixture is hand-built but the production code path under test is _synthesize_plan_draft itself, which is what we want to verify.
What I re-verified end-to-end
risk_analystcontinues to resolve to dual-role viais_producer and is_reviewerderivation inconcurrent_executor.py— no change in this delta.- §11 rubric in
_get_plan_review_criteria()and the reviewer-prep wording in_build_reviewer_preparationare unchanged from the previous review; the doc updates now match the code. - Architect prompt at
orchestrator/routes/pipelines.py:14347-14395is internally consistent with the new doc framing (scaffold output, file restrictions, verbatim consumption contract). _synthesize_plan_draftis a recovery-only path (fires when task_planner fails to write the plan draft directly per the docstring atpipelines.py:18820-18824); the synthesized output is best-effort and feeds the HITL gate, not the structured slice populator. Including the raw scaffold YAML is an improvement over the prior behavior where the operator saw only architecture analysis + risk assessment.
— Authored by egg
|
egg review completed. View run logs 5 previous review(s) hidden. |
…ropped slice keys (#2872) * Fix #2870: emit canonical 'dependencies' in slice scaffold; warn on unknown slice keys The plan→implement contract populator silently dropped the slice dependency chain whenever the architect scaffold expressed ordering via ``parent_slice_id``. ``plan_parser`` reads slice edges only from ``dependencies`` (or the ``depends_on`` alias, #2743) and never ``parent_slice_id``, so multi-slice linear chains parsed to all-roots and ran concurrently — guaranteeing integration-branch conflicts for overlapping slices. Root cause: #2779 (2026-05-22) settled ``dependencies`` as the canonical single-parent key and forbade ``parent_slice_id`` in ``yaml-tasks.schema.json`` (``additionalProperties: false``). #2821 (2026-05-27) then introduced the architect slice scaffold and told the architect + task_planner prompts to emit/preserve ``parent_slice_id`` — a key the parser, schema, and even the task_planner's own worked example do not use. This is that prompt-side drift. Fix (two layers): - Align the prompts back to the canonical vocabulary: the architect scaffold and task_planner copy instructions now emit ``dependencies: slice-<N>`` (omit for roots), matching the schema, the parser, and the task_planner Slice-DAG worked example. No schema or parser vocabulary change — ``parent_slice_id`` stays out, as #2779 intended. - Make the next drift loud, not silent: ``plan_parser`` now emits a ParseWarning when a slice carries a key outside the set it consumes. The schema already encodes this rule but is only enforced in tests, never at parse/populate time — so an unrecognized key (e.g. a future stray ``parent_slice_id``) would otherwise vanish with its data. Tests: parser warns on ``parent_slice_id`` while still documenting the drop; no false-positive on the full known-key set; prompt tests assert the scaffold emits ``dependencies: slice-1`` and no ``parent_slice_id``. * Address review: update stale parent_slice_id docs; generalize warn msg Replace the remaining `parent_slice_id` scaffold-vocabulary references in docs (slice-dag.md, agent-roles.md) with the canonical `dependencies` key this PR settles on, so they no longer contradict the fix or risk re-introducing #2870. The runtime `iter_ready()` tuple reference is left as-is (it is the DAG field, not the scaffold key). Generalize the unknown-slice-key ParseWarning message so it points at the actual stray key(s) instead of hardcoding `parent_slice_id` as the example, which was misleading when a different unknown key triggered it. * ci: re-trigger Test workflow after transient runner disk exhaustion The prior Integration Tests run failed during k3s image import with 'no space left on device' (3/3 import attempts) — a runner infra flake, not a code failure. The same branch passed the Test workflow on the prior commit, and this PR touches no integration-test infrastructure. --------- Co-authored-by: egg-reviewer[bot] <261018737+egg-reviewer[bot]@users.noreply.github.com> Co-authored-by: james-in-a-box[bot] <246424927+james-in-a-box[bot]@users.noreply.github.com>
…ropped slice keys (#2872) * Fix #2870: emit canonical 'dependencies' in slice scaffold; warn on unknown slice keys The plan→implement contract populator silently dropped the slice dependency chain whenever the architect scaffold expressed ordering via ``parent_slice_id``. ``plan_parser`` reads slice edges only from ``dependencies`` (or the ``depends_on`` alias, #2743) and never ``parent_slice_id``, so multi-slice linear chains parsed to all-roots and ran concurrently — guaranteeing integration-branch conflicts for overlapping slices. Root cause: #2779 (2026-05-22) settled ``dependencies`` as the canonical single-parent key and forbade ``parent_slice_id`` in ``yaml-tasks.schema.json`` (``additionalProperties: false``). #2821 (2026-05-27) then introduced the architect slice scaffold and told the architect + task_planner prompts to emit/preserve ``parent_slice_id`` — a key the parser, schema, and even the task_planner's own worked example do not use. This is that prompt-side drift. Fix (two layers): - Align the prompts back to the canonical vocabulary: the architect scaffold and task_planner copy instructions now emit ``dependencies: slice-<N>`` (omit for roots), matching the schema, the parser, and the task_planner Slice-DAG worked example. No schema or parser vocabulary change — ``parent_slice_id`` stays out, as #2779 intended. - Make the next drift loud, not silent: ``plan_parser`` now emits a ParseWarning when a slice carries a key outside the set it consumes. The schema already encodes this rule but is only enforced in tests, never at parse/populate time — so an unrecognized key (e.g. a future stray ``parent_slice_id``) would otherwise vanish with its data. Tests: parser warns on ``parent_slice_id`` while still documenting the drop; no false-positive on the full known-key set; prompt tests assert the scaffold emits ``dependencies: slice-1`` and no ``parent_slice_id``. * Address review: update stale parent_slice_id docs; generalize warn msg Replace the remaining `parent_slice_id` scaffold-vocabulary references in docs (slice-dag.md, agent-roles.md) with the canonical `dependencies` key this PR settles on, so they no longer contradict the fix or risk re-introducing #2870. The runtime `iter_ready()` tuple reference is left as-is (it is the DAG field, not the scaffold key). Generalize the unknown-slice-key ParseWarning message so it points at the actual stray key(s) instead of hardcoding `parent_slice_id` as the example, which was misleading when a different unknown key triggered it. * ci: re-trigger Test workflow after transient runner disk exhaustion The prior Integration Tests run failed during k3s image import with 'no space left on device' (3/3 import attempts) — a runner infra flake, not a code failure. The same branch passed the Test workflow on the prior commit, and this PR touches no integration-test infrastructure. --------- Co-authored-by: egg-reviewer[bot] <261018737+egg-reviewer[bot]@users.noreply.github.com> Co-authored-by: james-in-a-box[bot] <246424927+james-in-a-box[bot]@users.noreply.github.com>
Summary
Restructures the plan-phase BRC graph per #2809: composition
becomes a single role's job (architect),
risk_analystjoinsreviewer_planas a critical reviewer, andreviewer_plancannow hard-NACK oversized slices on a new judgment-based
slice_sizerubric.Review graph (
get_default_plan_graph)risk_analyst → architectCRITICAL edgerisk_analyst → task_plannerCRITICAL edgerisk_analystbecomes dual-role automatically via the existingis_producer and is_reviewerderivation, picking up theCONSENSUS_PROPOSE-augmented producer waits and the Dual-RoleExecution Order banner for free (mirrors the implement-phase
testerpattern from #2749)Prompts
section; emits
architect-slices.yamlscaffold alongside theexisting analysis JSON; file restrictions extended
"soft, advisory only ... never NACKs on size" guidance; now
copies the architect scaffold verbatim and raises slicing
problems as NACK pressure on architect rather than silently
re-shaping
shape; reviewer-role text explains when to NACK on the risk
lens vs ACK with forward-looking register entries
the new hard-NACK contract (the planner is the architect in
that mode, so "Slice shape is your call" stays)
Reviewer rubric
_get_plan_review_criteriagains §11 Slice Sizing (hard NACK,judgment-based) with the four canonical sub-division triggers
and worked NACK examples
_build_reviewer_preparation(reviewer_plan, plan)replaces the"advisory only — never NACK" guidance with the new judgment-
based NACK directive that points at §11
Tests
test_plan_graph_has_correct_edgesasserts the two newCRITICAL edges and the dual-role flag on
risk_analystTestPlanPhaseArchitectOwnsCompositioncovers: architectauthority, scaffold path + schema, file restrictions,
task_planner verbatim consumption, NACK-pressure escape valve,
risk_analyst dual-role + verdict shape, §11 rubric, and
reviewer_plan prep wording
test_dual_role_banner_present_for_risk_analystpins the BRCpreamble derivation
TestPlannerPromptTreatsRefinerSlicingAsAdvisorysothe concurrent path asserts the architect-owns-composition
contract instead of the old "Slice shape is your call" framing
Test plan
make lint(ruff + ruff format + extras) passesmake test-allsuiteverify the architect now NACK-rejects the original
multi-category bundling instead of letting it through
Closes #2809.