Skip to content

[issue-3077][slice-2/6] Artifact spec module: single declarative... - #3141

Merged
jwbron merged 6 commits into
mainfrom
egg/issue-3077/slice-2
Jun 12, 2026
Merged

[issue-3077][slice-2/6] Artifact spec module: single declarative...#3141
jwbron merged 6 commits into
mainfrom
egg/issue-3077/slice-2

Conversation

@james-in-a-box

Copy link
Copy Markdown
Contributor

shared/egg_contracts/artifact_spec.py with frozen ArtifactSpec rows and resolution helpers, plus MANDATORY in-slice consistency tests pinning every existing path hardcoding to the spec. No consumer behavior change yet.

Base PR: #3139

What's in this PR

Commits (3):

.egg-state/brc-history/3077-implement-slice-2.json | 7082 ++++++++++++++++++++++++++++++++++++++++
 .egg-state/brc-history/3077-implement-slice-2.md   | 6149 ++++++++++++++++++++++++++++++++++
 shared/egg_contracts/artifact_spec.py              |  247 ++
 shared/egg_contracts/tests/test_artifact_spec.py   |  669 ++++
 4 files changed, 14147 insertions(+)

This slice

Artifact spec module: single declarative registry of coordination artifacts

Files affected:

  • shared/egg_contracts/artifact_spec.py
  • shared/egg_contracts/tests/test_artifact_spec.py
Tasks (2) + acceptance criteria
  • task-2-1: Create shared/egg_contracts/artifact_spec.py (NEW): frozen ArtifactSpec dataclass rows — name, path_template keyed on {identifier}, phase, producer_role, consumer_roles — for analysis-draft (refine), plan-draft (plan), architect-output, architect-slices, and risk-analyst-output (plan), matching the paths currently encoded by _get_draft_path (orchestrator/routes/signals.py:1162-1166) and the agent-output conventions in .egg-state/agent-outputs/. Helpers: resolve_artifact_path(name, identifier), specs_for(phase, producer_role), spec_by_name(name). Pure Python, no orchestrator/gateway imports, no new config format (Genericize the SDLC pipeline into a declarative, config-driven workflow (custom phases + agents + per-phase slicing) #3017 consumes this later, decoupled).
    • Acceptance criteria: - resolve_artifact_path("plan-draft", "3077") yields .egg-state/drafts/3077-plan.md; analysis-draft yields the -analysis.md path; both identifier shapes (issue number, pipeline id) resolve. - specs_for("plan", "task_planner") returns exactly the plan-draft row; spec_by_name round-trips every row. - Rows are immutable (frozen) and each names one producer_role and at least one consumer role. - Module imports cleanly with no orchestrator/gateway deps.
  • task-2-2: Mandatory consistency suite in shared/egg_contracts/tests/test_artifact_spec.py (NEW): (a) every spec path (both identifier shapes) is admitted by the matching gateway/phase_filter.py phase gate AND the shared/egg_restrictions/phase_patterns.py mirror; (b) _get_draft_path output equals spec resolution for refine and plan across both identifier shapes; (c) the prompt f-string literals in orchestrator/routes/pipelines.py that name draft paths match spec resolution. Plus resolution/reverse-lookup round-trips for every registered row. These tests are the refine-risk-1 mitigation: any future drift in any replica fails CI instead of reproducing feat(orchestrator): validate producer's phase draft at the canonical path on propose (deterministic refine/plan gate input) #3016.
    • Acceptance criteria: - Consistency assertions (a), (b), (c) all present and green. - A deliberate template mutation (parametrized) demonstrably fails the gate-admission assertion. - Every spec row is exercised by a resolve/round-trip case.

Stack

egg and others added 3 commits June 11, 2026 21:57
…2 TASK-2-1)

Add `shared/egg_contracts/artifact_spec.py`: a frozen `ArtifactSpec`
dataclass plus a tuple registry covering the five refine/plan
coordination artifacts (analysis-draft, plan-draft, architect-output,
architect-slices, risk-analyst-output) with `resolve_artifact_path`,
`specs_for`, `spec_by_name`, and `all_specs` helpers.

Path templates mirror today's conventions
(`orchestrator.routes.pipelines._get_draft_path` + the
`.egg-state/agent-outputs/` filenames) so slice-3 can subsume
`_validate_producer_draft_present` and `_get_draft_path` without a
behavioural change.  Pure Python; no orchestrator or gateway imports.
Add `shared/egg_contracts/tests/test_artifact_spec.py` — the
refine-risk-1 mitigation from #3077: every path replica that admits or
constructs a coordination artifact must stay in lockstep with the new
`egg_contracts.artifact_spec` registry, or CI fails here.

Coverage matches the `task-2-2` contract:
- (a) every spec path (both identifier shapes) is admitted by
      `gateway.phase_filter.PhaseFilter.check_phase_file_restrictions`
      AND `egg_restrictions.phase_patterns.phase_file_verdict`;
- (b) `_get_draft_path(refine|plan, …)` byte-equals
      `resolve_artifact_path("analysis-draft"|"plan-draft", …)` for both
      identifier shapes (`int` issue number / `str` pipeline id);
- (c) every f-string draft-path literal in
      `orchestrator/routes/pipelines.py` corresponds to a registered
      spec row, and every registered `agent-outputs/` row appears as a
      literal — drift either direction fails CI.
- Plus resolve/round-trip/`spec_by_name` cases for every registered row,
  registry-shape assertions (frozen rows, single producer, non-empty
  consumers, `{identifier}` placeholder), unknown-name `raises`, and
  the no-orchestrator/gateway-import purity check from `task-2-1`.

`TestSpecMutationFailsGate` shows the (a) admission assertions are not
trivially green: a `wrong_directory` mutation of each spec template is
demonstrably rejected by both gate replicas, and a `wrong_token`
mutation flips the verdict for drafts. Without this, the assertion
suite could silently pass against a permissive default.

All 34 cases collect and pass locally on top of coder's task-2-1
(commit 15258a4) with `PYTHONPATH=shared:gateway:orchestrator`.
@james-in-a-box

This comment has been minimized.

@james-in-a-box

Copy link
Copy Markdown
Contributor Author
Autofix tracking
{"Lint/Python": 1}

@james-in-a-box

This comment has been minimized.

@james-in-a-box james-in-a-box Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Contract Verification — PR #3141 (issue-3077 slice-2)

All slice-2 acceptance criteria verified against the implementation; all 34 tests in the new consistency suite pass locally.

Task-2-1: shared/egg_contracts/artifact_spec.py

AC Evidence
resolve_artifact_path("plan-draft", "3077").egg-state/drafts/3077-plan.md; analysis-draft-analysis.md; both identifier shapes resolve artifact_spec.py:126,138 + test_known_concrete_paths + _IDENTIFIERS parametrization (int=3077, str="issue-1557-v2")
specs_for("plan", "task_planner") returns exactly the plan-draft row; spec_by_name round-trips every row test_specs_for_plan_draft_is_singleton + test_spec_by_name_round_trips
Rows immutable (frozen), one producer + ≥1 consumer @dataclass(frozen=True, slots=True) (artifact_spec.py:41) + __post_init__ invariants (:75-92) + test_every_row_is_frozen + test_every_row_has_single_producer_and_nonempty_consumers
Module imports cleanly with no orchestrator/gateway deps artifact_spec.py imports only __future__, collections.abc.Mapping, dataclasses — verified by TestSpecModuleIsPure.test_spec_module_imports_are_pure

All five registered rows (analysis-draft, plan-draft, architect-output, architect-slices, risk-analyst-output) match the path conventions encoded by orchestrator/routes/pipelines.py::_get_draft_path (pipelines.py:5250-5263) and the architect / risk-analyst prompt f-strings (pipelines.py:14021, 14081, 14127, 14407). Note the deliberate mismatch between artifact name (hyphenated risk-analyst-output) and on-disk filename (underscored risk_analyst-output.json) is called out in the module docstring (artifact_spec.py:117-120) — that mirrors today's prompt prose, which Consistency (c) pins.

Task-2-2: shared/egg_contracts/tests/test_artifact_spec.py

AC Evidence
Consistency (a): every spec path admitted by gateway/phase_filter.py AND shared/egg_restrictions/phase_patterns.py TestConsistencyA_PhaseFilterAdmits + TestConsistencyA_PhasePatternsAdmits, parametrized on both identifier shapes
Consistency (b): _get_draft_path equals spec resolution for refine+plan, both shapes TestConsistencyB_GetDraftPathEquality.test_get_draft_path_equals_spec_resolution — 4 parametrized cases (int/str × refine/plan)
Consistency (c): prompt f-string literals match spec resolution TestConsistencyC_PromptFStringLiterals checks both directions — test_every_agent_output_spec_appears_as_a_literal (spec → literal) and test_each_literal_resolves_via_some_spec (literal → spec)
Deliberate template mutation (parametrized) fails gate admission TestSpecMutationFailsGate with @pytest.mark.parametrize("mutation", ["wrong_directory"]) + dedicated test_wrong_token_is_rejected_by_gateway_for_drafts — mutation regex (_mutate_template) asserts non-no-op before checking gate rejection, so the ratchet cannot be silently weakened
Every spec row exercised by a resolve/round-trip case all_specs fixture unions every row via specs_for; test_spec_by_name_round_trips, test_resolve_artifact_path_matches_template, registry-shape tests all iterate the full union; test_expected_rows_are_registered pins the names against silent drops

The mutation tests are the load-bearing piece per refine-risk-1: they prove the admission assertions aren't trivially green against a permissive default. The assert mutated_template != spec.path_template guard inside test_wrong_directory_is_rejected_by_gateway is a nice belt-and-braces against a future no-op mutation slipping through.

Local run: pytest shared/egg_contracts/tests/test_artifact_spec.py -v34 passed.

Top-level acceptance_criteria

The contract's top-level acceptance_criteria array is empty ([]); all AC live at the task level on task-2-1 / task-2-2. There are no ac-N identifiers for egg-contract verify-criterion. The task-level AC are verified above. (The orchestrator is also currently unreachable from this sandbox, so even a top-level criterion couldn't be marked via the CLI right now — noted for transparency.)

Out-of-scope diff: orchestrator/tests/test_consensus_wrapper.py

The PR also reformats slice-1's test_consensus_wrapper.py (16 line changes). All edits are cosmetic — function signature compressed to one line, multi-line string concatenations collapsed, single→double quote normalization on a docstring snippet. No semantic change. The fourth commit message ("Fix checks: apply automated formatting fixes") identifies this as collateral from an autoformatter. This is outside slice-2's declared files_affected (which is shared/egg_contracts/artifact_spec.py for task-2-1 and shared/egg_contracts/tests/test_artifact_spec.py for task-2-2), but as a pure formatting normalization on a sibling-slice test file it's not a contract violation worth blocking on.

BRC history

.egg-state/brc-history/3077-implement-slice-2.{json,md} are auto-persisted by egg-orchestrator (the third commit) — expected automation, no review concern.

Verdict

All task-2-1 and task-2-2 acceptance criteria are objectively met. The implementation matches the contract description, the helpers behave as specified, and the mandatory consistency suite covers the refine-risk-1 mitigation as designed.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@egg-reviewer egg-reviewer Bot left a comment

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.

Review

LGTM. This is a clean, well-scoped slice that adds a frozen artifact registry plus a comprehensive consistency suite pinning it against every existing path-knowledge replica. The spec accurately mirrors today's production paths, both identifier shapes work, and the mutation tests prove the gate-admission assertions are non-tautological. I verified the consistency-A checks manually against the live PhaseFilter and phase_file_verdict for all 5 spec rows × both identifier shapes — every path is admitted; every wrong_directory and (drafts-only) wrong_token mutation is rejected. CI is green.

orchestrator/tests/test_consensus_wrapper.py changes are pure formatting (long-line collapses, quote swaps) — no behavior change.

Non-blocking nits

1. Dead docstring reference (shared/egg_contracts/tests/test_artifact_spec.py:113). The docstring of _candidate_producer_roles_for_phase claims the safety net is test_specs_for_round_trips_every_row:

the failure surfaces via :func:`test_specs_for_round_trips_every_row`,
which compares the union of ``specs_for(...)`` against
:func:`spec_by_name` on every registered row.

No such test exists in this file. The actual safety net is test_expected_rows_are_registered. Either drop the reference or rename the existing test to match.

2. Fixture vs. module helper drift risk (test_artifact_spec.py:88-105). The all_specs fixture re-derives the registry by iterating specs_for(phase, role) over a hardcoded role roster (_candidate_producer_roles_for_phase). The module already exposes all_specs() (the function, not the fixture) for exactly this purpose.

The hand-rolled iteration creates a quiet drift trap: if a future row adds a producer role outside the hardcoded roster (e.g., a new refine-phase producer), the fixture silently skips that row, and every consistency-A / consistency-B / mutation test that consumes the fixture skips it too. The test_expected_rows_are_registered pin doesn't rescue this — it asserts expected ⊆ registered (where registered is the fixture's view), so a new row that's not in expected and not in the role roster lands in neither set and never gets exercised.

Fix: have the fixture return artifact_spec.all_specs() directly. The "test specs_for too" angle can be a separate small test against specs_for("plan", "task_planner") (which you already have at line 234).

3. is OR == is redundant (test_artifact_spec.py:198).

assert spec_by_name(spec.name) is spec or spec_by_name(spec.name) == spec

_BY_NAME stores the same instance so is is always True. The or == branch is dead. Pick one (is is the right one — frozen dataclasses with structural eq could pass == even for a re-built spec, defeating the round-trip intent).

4. Purity check has a blind spot (test_artifact_spec.py:589-608). TestSpecModuleIsPure walks vars(mod), which catches imported callables but misses pure side-effect statements like import orchestrator.routes.pipelines # for side effects — those land in sys.modules without binding a name. Today the module is clean so this is theoretical; a sys.modules-based check after importlib.reload(artifact_spec) would be more robust, but it's not worth the bookkeeping for a regression that nobody's about to introduce.

5. Fixture duplication / shadowing. TestConsistencyA_PhaseFilterAdmits.phase_filter (class fixture) and module-level _gateway_phase_filter both produce PhaseFilter(). Consolidate. The class fixture also shadows the imported phase_filter module name — confusing on read; pick a different fixture name.

6. risk-analyst-output name/path inconsistency (artifact_spec.py:170-178). The artifact name uses a hyphen (risk-analyst-output) but the filename uses underscore (risk_analyst-output.json). The comment at line 105-109 acknowledges this is intentional (the filename matches the role name risk_analyst, the artifact name follows the hyphenated convention). It's defensible but ergonomically rough — any future spec consumer that does string-munging to derive paths from names will trip on this. Worth flagging on the slice-3 PR to make sure the resolver always goes through spec_by_name(...).resolve_path(...) and never reconstructs the path from the name.

7. consumer_roles are not yet cross-checked against actual readers. The lists are documentation-only at this slice. That's fine for slice-2, but it means a wrong consumer list ships unchallenged. When slice-3 lands the propose-time validator and slice-4 wires the gateway read endpoint, please add a consistency test (or have the consumer audit happen as part of the slice-3 review).

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

…us_wrapper.py

The conflict was a dead local variable assignment (repo, _ = ...) that
slice-1 removed via review feedback in PR #3140. Taking slice-1's
version since the variables were unused.
@james-in-a-box

Copy link
Copy Markdown
Contributor Author

Conflict Resolution Summary

Resolved merge conflict with egg/issue-3077/slice-1:

File Category Resolution
orchestrator/tests/test_consensus_wrapper.py Semantic (dead code removal) Took slice-1's version, dropping the unused repo, _ = ... line

Details

The single conflict was at TestSyncOutcomesAndBanner._run_harness:

  • HEAD (this PR) carried repo, _ = (None, None) if not (tmp_path / "repo").exists() else (tmp_path / "repo", None) — collapsed to one line by automated ruff format in 8d47446.
  • slice-1 removed the line entirely in PR [issue-3077][slice-1/6] R1 non-silent sync: surface... #3140 ("Address review feedback: banner wording, doc drift, test cleanups") because repo and _ were never used in the function body — _build_harness is called with str(tmp_path / "repo") directly.

Adopted slice-1's deletion since the variables were dead and slice-1's change is strictly a cleanup that landed via reviewer feedback. Local ruff check and ruff format --check pass on the resolved file.

No conflicts in the slice-2 contribution (shared/egg_contracts/artifact_spec.py or its test suite) — those files only exist on this PR.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

@egg-reviewer egg-reviewer Bot left a comment

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.

Re-review

LGTM — no slice-2 code change since my prior approval.

Delta inspection

Between my last review at 8d474463a and the current HEAD eb148cb0, the only PR-authored commit is the merge of origin/egg/issue-3077/slice-1. git diff 8d474463a..eb148cb0 -- shared/egg_contracts/ is empty — artifact_spec.py and test_artifact_spec.py are byte-identical to the version I previously approved.

The merge brought in slice-1's review-feedback commits:

File Change Origin
docs/architecture/coordination-state.md New "Slice landings" status table slice-1 review feedback (PR #3140)
orchestrator/consensus_wrapper.py Banner wording: "use the git show commands below" → "use the rendered git log / git show fallback commands in this prompt instead" slice-1 review feedback
orchestrator/routes/event_prompt.py Empty-delta caution points at the re-review git log rather than absent git show slice-1 review feedback
orchestrator/tests/test_compose_event_prompt.py Caution test asserts git log (matches the rendered command in the re-review path) slice-1 review feedback
orchestrator/tests/test_consensus_wrapper.py Banner test now pins both SYNC_FAILURE_BANNERS+= append sites (unresolvable + merge-failed) slice-1 review feedback
shared/prompts/REVIEWER-SYNC.md Delta-command row rewritten to describe the git log <sha>..<end_ref> re-review command + slice-2-4 forward references slice-1 review feedback

These are all slice-1's PR (#3140) territory and were reviewed there; they are out of scope for slice-2 review.

Conflict resolution

The single semantic conflict at TestSyncOutcomesAndBanner._run_harness (orchestrator/tests/test_consensus_wrapper.py:1747) was resolved by adopting slice-1's deletion of repo, _ = (None, None) if not (tmp_path / "repo").exists() else (tmp_path / "repo", None). I confirmed the variables are dead — _build_harness is called with str(tmp_path / "repo") directly, and repo is never referenced in the function body. The resolved file is byte-identical to slice-1's version on those lines. Correct call.

CI

All real check suites green (Unit Tests, Integration Tests, Security Scan, lint, Docker). The single egg-reviewer-contract-verification failure is a workflow-internal issue (the status comment links to an Actions run that errored before posting a verification verdict), not a regression in the diff.

Prior non-blocking suggestions

My earlier <!-- has-suggestions --> nits on the slice-2 test file (dead test_specs_for_round_trips_every_row docstring reference; all_specs fixture re-deriving from a hardcoded role roster instead of returning artifact_spec.all_specs(); redundant is OR ==; consolidation of duplicate phase_filter fixtures) still stand — none of them were addressed in this revision, and that's fine: they're non-blocking and can ride a slice-3 follow-up. Flagging here just so they aren't lost in the re-review noise.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

Apply egg-reviewer's non-blocking nits on shared/egg_contracts/tests/
test_artifact_spec.py:

* Replace the bespoke `all_specs` fixture that re-derived rows via
  `specs_for(phase, role)` over a hardcoded role roster with a direct
  call to `artifact_spec.all_specs()` (aliased on import as
  `registered_specs`). Future rows whose producer role lies outside the
  refine + plan roster slice-2 originally covered are now exercised
  automatically. Removes the now-dead `_candidate_producer_roles_for_
  phase` helper and the docstring reference to a non-existent
  `test_specs_for_round_trips_every_row` test.

* Drop the redundant `or ==` branch in
  `TestResolutionRoundTrip.test_spec_by_name_round_trips` — `_BY_NAME`
  stores the same instance, so `is` is the load-bearing check (and the
  one that fails on a rebuilt-but-equal spec, which is the intended
  failure mode for a registry round-trip).

* Consolidate the duplicate `PhaseFilter()` fixtures
  (`TestConsistencyA_PhaseFilterAdmits.phase_filter` class fixture and
  the module-level `_gateway_phase_filter`) onto a single module-scoped
  fixture, hoisted near the top alongside `all_specs`. Removes the
  shadowing of the imported `phase_filter` module name in the class.

No behaviour change. All 34 tests in the consistency suite still pass
locally.
@james-in-a-box

Copy link
Copy Markdown
Contributor Author

Review feedback disposition

Thanks for the thorough re-review. Per-item disposition for the 7 non-blocking nits from egg-reviewer's approve-with-suggestions:

# Item Disposition
1 Dead test_specs_for_round_trips_every_row docstring reference at test_artifact_spec.py:113 fixed-in-PR (commit 62b8929) — removed as part of #2, since the helper that carried the docstring is gone
2 all_specs fixture re-derives rows via hardcoded role roster instead of artifact_spec.all_specs() (drift trap) fixed-in-PR (commit 62b8929) — fixture now delegates to artifact_spec.all_specs() (aliased on import as registered_specs to dodge the name collision with the fixture). _candidate_producer_roles_for_phase removed
3 Redundant is OR == at test_artifact_spec.py:198 fixed-in-PR (commit 62b8929) — picked is (the load-bearing branch) and dropped or ==
4 TestSpecModuleIsPure blind to import x # side effects statements disagree (no technical impact in this PR) — the reviewer themselves wrote "Today the module is clean so this is theoretical … it's not worth the bookkeeping for a regression that nobody's about to introduce." The spec module today does not have any side-effect imports; the vars(mod) walk catches every binding form actually present. Adding an importlib.reload + sys.modules diff would only catch a regression that doesn't exist yet, and the contract calls for that mitigation to land alongside any actual side-effect import — not pre-emptively
5 Duplicate PhaseFilter() fixtures + phase_filter name shadowing in TestConsistencyA_PhaseFilterAdmits fixed-in-PR (commit 62b8929) — consolidated onto the module-level _gateway_phase_filter fixture (now hoisted next to all_specs for readability since two classes consume it). The class fixture that shadowed the imported phase_filter module name is gone
6 risk-analyst-output artifact-name hyphen vs risk_analyst-output.json filename underscore disagree (slice-3 concern, intentional design) — the reviewer explicitly framed this as "Worth flagging on the slice-3 PR to make sure the resolver always goes through spec_by_name(...).resolve_path(...) and never reconstructs the path from the name", i.e. a slice-3 review-time check rather than a slice-2 fix. The mismatch is intentional (mirrors today's prompt prose pinned by Consistency-c) and is already called out in the module docstring at artifact_spec.py:117-120. Will carry into slice-3 review
7 consumer_roles not cross-checked against actual readers disagree (explicit deferral request) — the reviewer wrote: "When slice-3 lands the propose-time validator and slice-4 wires the gateway read endpoint, please add a consistency test (or have the consumer audit happen as part of the slice-3 review)." That is an explicit defer-to-future-slice ask. No follow-up issue needed since the request lives in the slice-3 / slice-4 review checklist

Verification

  • PYTHONPATH=shared:gateway:orchestrator pytest shared/egg_contracts/tests/test_artifact_spec.py34 passed post-fix.
  • ruff check + ruff format --check clean on both artifact_spec.py and test_artifact_spec.py.
  • make lint mypy errors at shared/egg_agent/client.py:305,395,396 are pre-existing (unrelated to this PR — that file is not in the slice-2 diff).

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

@james-in-a-box james-in-a-box Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Contract Re-verification — PR #3141 (slice-2)

Incremental review of changes since 8d474463aa51ac2d1ca111b84d56f363115e4e7d. Two new commits on the PR branch (excluding egg/issue-3077/slice-1 merge-in):

  • eb148cb — Merge origin/egg/issue-3077/slice-1 (conflict resolution in orchestrator/tests/test_consensus_wrapper.py: dead (repo, _ = …) assignment dropped per slice-1's review feedback). Slice-1 territory; no slice-2 contract surface touched.
  • 62b8929 — Test-only cleanups in shared/egg_contracts/tests/test_artifact_spec.py addressing prior non-blocking nits.

Delta scope

Production code (shared/egg_contracts/artifact_spec.py) is unchanged since last review. Only the consistency test suite (shared/egg_contracts/tests/test_artifact_spec.py) was edited. Three refactors:

  1. all_specs fixture now delegates to artifact_spec.all_specs() (aliased registered_specs) instead of re-deriving via specs_for(phase, role) over a hardcoded ("refiner",) / ("task_planner", "architect", "risk_analyst") roster. Removes the _candidate_producer_roles_for_phase helper and the stale docstring reference. Strictly broader coverage — a future spec row whose producer sits outside the refine+plan roster will now be auto-exercised by every consistency / mutation test, which is exactly the refine-risk-1 ratchet posture the contract calls for.
  2. TestResolutionRoundTrip.test_spec_by_name_round_trips drops the or == branch. Correct: _BY_NAME stores the same instance built at module import, so is is the load-bearing check; a structurally-equal rebuild would == but not is, defeating the round-trip's "registry is source of truth" intent.
  3. Duplicate PhaseFilter() fixtures consolidated onto a single module-scoped _gateway_phase_filter hoisted to the top, removing the class-scoped phase_filter that was also shadowing the imported module name.

Contract compliance

Contract task-2-2 acceptance criteria — re-confirmed:

  • (a) Consistency assertions (a), (b), (c) all present and green:
    • TestConsistencyA_PhaseFilterAdmits (gateway) + TestConsistencyA_PhasePatternsAdmits (mirror) — both identifier shapes parametrized
    • TestConsistencyB_GetDraftPathEquality — refine + plan, both identifier shapes
    • TestConsistencyC_PromptFStringLiterals — forward + reverse direction against orchestrator/routes/pipelines.py
  • A deliberate template mutation demonstrably fails the gate-admission assertion: TestSpecMutationFailsGate (wrong_directory + wrong_token, both gate replicas).
  • Every spec row is exercised by a resolve/round-trip case (TestResolutionRoundTrip, TestRegistryShape).

Local run: PYTHONPATH=shared:gateway:orchestrator pytest shared/egg_contracts/tests/test_artifact_spec.py34 passed.

Contract task-2-1 acceptance criteria (production module unchanged since last review): still satisfied — registered rows for analysis-draft, plan-draft, architect-output, architect-slices, risk-analyst-output; helpers resolve_artifact_path, specs_for, spec_by_name, all_specs exposed; module is pure (the TestSpecModuleIsPure no-import assertion still applies).

Verdict

No regressions to previously verified contract surface. The test-suite refactor strictly improves the refine-risk-1 ratchet by removing the hardcoded role roster as a drift surface. Slice-2 contract compliance preserved.

Note: the contract's top-level acceptance_criteria field is [] for issue-3077 — there are no ac-N items to mark via egg-contract verify-criterion. Per-task acceptance criteria are inline prose and aren't separately addressable from the CLI.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@egg-reviewer egg-reviewer Bot left a comment

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.

Re-review (62b8929)

LGTM. The single new commit since my prior approval is a focused fixture-cleanup pass that addresses the four actionable nits from 8d474463a cleanly — not just superficially. The other three items were either declined or deferred to slice-3/4 per my own original framing; all defensible. CI is green (Unit Tests, Integration Tests, Security Scan, Lint, Docker all SUCCESS).

Delta scope

git log eb148cb0..62b8929 --not origin/egg/issue-3077/slice-1 is the lone commit. Diff is 174 lines, entirely within shared/egg_contracts/tests/test_artifact_spec.py. No production code change, no spec/registry change.

Verification of each addressed item

  1. Dead test_specs_for_round_trips_every_row docstring reference — Fixed by deletion of the entire _candidate_producer_roles_for_phase helper that carried it. grep test_specs_for_round_trips_every_row is now empty in the file. ✓

  2. all_specs fixture re-derives via hardcoded role roster (drift trap) — Fixed properly. The fixture now delegates to registered_specs() (alias for egg_contracts.artifact_spec.all_specs(), imported on line 66). A future row with a producer outside the refine + plan roster will now flow through every consistency / mutation test automatically. The aliasing approach (from egg_contracts.artifact_spec import all_specs as registered_specs) sidesteps the name collision with the pytest fixture cleanly; alternative would have been renaming the fixture itself but that's a wider blast radius (every test signature would change). The choice is justifiable. ✓

  3. Redundant is OR == — Fixed by picking is alone (line 192). The new docstring (lines 184-190) explicitly calls out why is is load-bearing — exactly the rationale I argued for. ✓

  4. TestSpecModuleIsPure blind spot — Declined. The reviewer's own prior framing was "it's not worth the bookkeeping for a regression that nobody's about to introduce." The producer's disposition mirrors that. Accepted.

  5. Duplicate PhaseFilter() fixtures + phase_filter shadowing — Fixed by removing the class-scoped TestConsistencyA_PhaseFilterAdmits.phase_filter fixture entirely. Both TestConsistencyA_PhaseFilterAdmits (line 256) and the two TestSpecMutationFailsGate tests (lines 492, 537) now consume the single module-scoped _gateway_phase_filter fixture, which has been hoisted up next to all_specs (lines 104-109). No more name shadowing of the imported phase_filter module. ✓

  6. risk-analyst-output name/path inconsistency — Deferred to slice-3 review. My prior comment explicitly framed it as "Worth flagging on the slice-3 PR." Accepted.

  7. consumer_roles not cross-checked against actual readers — Deferred to slice-3/4 review. My prior comment explicitly framed it as a slice-3/4 review-time check. Accepted.

Spot checks

  • _gateway_phase_filter (underscore prefix) is consumed as a pytest fixture parameter — pytest fixture resolution matches by name regardless of leading underscore, so injection still works. Consistent with the original pre-cleanup name.
  • The all_specs() module function returns _SPECS (an already-immutable tuple), so the fixture returns the same tuple identity across the module-scoped lifetime. No mutation risk.
  • TestRegistryShape.test_expected_rows_are_registered still pins the five expected names against {spec.name for spec in all_specs}, so a silent drop of an existing spec row still fails loudly even though the fixture is now registry-wide.
  • The deleted _candidate_producer_roles_for_phase had no other consumers (grepped clean).

Out-of-scope diff

None on this revision — the prior orchestrator/tests/test_consensus_wrapper.py cosmetic edits and slice-1 merge are unchanged from eb148cb0.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

Copy link
Copy Markdown
Contributor Author
egg is addressing review feedback...

@james-in-a-box

Copy link
Copy Markdown
Contributor Author

Review feedback disposition

Both reviews at the current HEAD (62b892969785a503d6f80e2a4079b541f9109108) are unqualified approves with no new actionable items, so there is nothing new to address in this round.

Reviewer Verdict Actionable items
egg-reviewer[bot] (approve, 62b8929) LGTM None — explicitly confirms the four actionable nits from the prior 8d474463a review are fixed correctly in 62b8929 (items 1–3 and 5 from my prior disposition), and accepts items 4, 6, 7 as "all defensible" per their own original framing
james-in-a-box[bot] (contract verification, 62b8929) Verified — approve None — re-confirms all task-2-1 / task-2-2 acceptance criteria are met; production module unchanged; 34 tests pass

The seven non-blocking nits from the earlier 8d474463a approve-with-suggestions are accounted for in my prior disposition comment — no follow-up issues filed (items 6 and 7 are explicit slice-3 / slice-3-4 review-time checks per the reviewer's own framing, not deferred work).

Verification

  • git rev-parse HEAD62b892969785a503d6f80e2a4079b541f9109108 (matches the SHA both reviewers approved)
  • Local ruff check shared/egg_contracts/ and ruff format --check shared/egg_contracts/ clean
  • No code change made or pushed in this round, as no new actionable feedback exists

— Authored by egg

@james-in-a-box

Copy link
Copy Markdown
Contributor Author

egg feedback addressed. View run logs

14 previous review(s) hidden.

@jwbron
jwbron changed the base branch from egg/issue-3077/slice-1 to main June 12, 2026 05:21
@jwbron
jwbron merged commit ccc01c4 into main Jun 12, 2026
36 of 38 checks passed
jwbron added a commit that referenced this pull request Jun 13, 2026
…3152)

* docs: mark #3077 slices 2-4 shipped in coordination-state doc

Slices 2 (#3141), 3 (#3142), and 4 (#3143) have all merged since the
coordination-state doc was last updated (it still read "as of slice-1").

Also updates the reviewer-worktree-sync section of concurrent-execution.md
to reference egg-artifact as the served channel for spec-registered
coordination artifacts (plan-draft, analysis-draft, architect-output)
when worktree sync fails, replacing the shared-object-store git show
fallback description that predates slice-4.

* docs: mark #3077 slices 5-6 shipped in coordination-state

All six slices of the #3077 epic have landed on main. Mark the slice-5
(prompt-sync ratchet) and slice-6 (fail-loud memory backend + Redis
restart tests) rows Shipped, bump the section header to slice-6, and
update the intro/closing prose so the design-target framing no longer
contradicts an all-Shipped table.

---------

Co-authored-by: jwbron <8340608+jwbron@users.noreply.github.com>
Co-authored-by: egg-reviewer[bot] <261018737+egg-reviewer[bot]@users.noreply.github.com>
jwbron added a commit that referenced this pull request Jun 13, 2026
Update docs for #3077 slice-2 (artifact_spec.py merged in #3141):
- STRUCTURE.md: add artifact_spec.py entry to egg_contracts listing
- coordination-state.md: mark slice-2 rows Shipped, bump heading to slice-2

Co-authored-by: jwbron <8340608+jwbron@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant