Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7786556
Initialize SDLC contract for issue #1557
May 12, 2026
1712029
refine(#1557): analysis for Jira-epic SDLC pipeline support
May 12, 2026
1e75a1f
refine(#1557): address reviewer_refine non-blocking nudges
May 12, 2026
9a4079b
Persist agent statefile writes before refine sync
May 12, 2026
6ca279b
Persist statefiles after refine phase
May 12, 2026
f0f739f
Persist HITL resolution after refine phase gate
May 12, 2026
4809f57
risk-analyst(#1557): risk assessment for Jira-epic SDLC pipeline support
May 12, 2026
dac0bbe
plan(architect): #1557 architecture analysis for Slice 1 (A+B+C+D fre…
May 12, 2026
a703a06
plan(#1557): task plan for Jira-epic SDLC pipeline support
May 12, 2026
8f0d056
plan(#1557): split TASK-2-7 to honor coder/documenter file boundaries
May 12, 2026
2d27b38
plan(#1557): address reviewer_plan v1 NACK (3 blocking + non-blocking)
May 12, 2026
a840478
Persist agent statefile writes before plan sync
May 12, 2026
1a0f7b6
Persist statefiles after plan phase
May 12, 2026
018bfac
implement(#1557): documenter prompts for epic-mode + apply-phase
May 12, 2026
e639c35
implement(#1557): documenter v2 — address reviewer_code 3 blocking NACKs
May 12, 2026
0271282
recover(#1557-v2): restore plan + analysis drafts to integration branch
jwbron May 12, 2026
7a25d3d
recover(#1557-v2): restore plan + analysis drafts AND populated contr…
jwbron May 12, 2026
c5c0d97
recover(#1557-v2): advance contract.current_phase to implement
jwbron May 12, 2026
cbda1ff
Persist agent statefile writes before implement sync
May 12, 2026
5d0f5b9
Persist statefiles after implement phase
May 12, 2026
56109e4
Remove ephemeral agent-output handoff artifacts (#1731)
May 12, 2026
32ba7cb
slice-1 → /work for review (issue-1557-v2 slice-DAG triage) (#2679)
jwbron May 12, 2026
ff660f4
Fix checks: register APPLY phase / APPLIER role + fix gateway import …
jwbron May 12, 2026
2cada26
Fix checks: apply automated formatting fixes
May 12, 2026
96c3cb6
Fix mypy no-redef error for _PRIVATE_MODE_MARKER_ATTR in except block
james-in-a-box[bot] May 12, 2026
91abe64
Fix checks: apply automated formatting fixes
May 12, 2026
60ad22c
Merge branch 'main' into egg/issue-1557-v2/work
jwbron May 12, 2026
9f3029a
Fix mypy no-redef false positive in gateway.py try/except import
james-in-a-box[bot] May 12, 2026
4a86752
Merge remote-tracking branch 'origin/egg/issue-1557-v2/work' into egg…
james-in-a-box[bot] May 12, 2026
6a62df9
Fix ruff I001 import sort warning in try/except fallback import
james-in-a-box[bot] May 12, 2026
ea6adc0
Address review feedback on PR #2678
egg-reviewer[bot] May 12, 2026
efdde72
Address re-review feedback on PR #2678 (suggestions N1-N6)
egg-reviewer[bot] May 12, 2026
b3c8f28
Merge branch 'main' into egg/issue-1557-v2/work
jwbron May 13, 2026
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
336 changes: 0 additions & 336 deletions .egg-state/agent-outputs/2474-architect-output.json

This file was deleted.

362 changes: 0 additions & 362 deletions .egg-state/agent-outputs/2474-risk_analyst-output.json

This file was deleted.

51 changes: 51 additions & 0 deletions .egg-state/agent-outputs/coder-to-tester-1557-test-followups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Coder → Tester handoff: test follow-ons for issue #1557 (apply-phase scheduler + APPLIER + epic_link_field)

The coder commit `4cf20c886` (`implement(#1557): apply-phase scheduler + wontdo drain + test fixes`) introduces three production changes that have mechanical follow-on test deltas. Per the gateway's file-restriction policy
(`shared/egg_restrictions/patterns.py`), tests under `gateway/tests/`,
`orchestrator/tests/`, and `shared/tests/` are tester scope — the
coder role cannot push them. The patch below captures those deltas
verbatim; please apply them on the slice-2 integration branch and
re-ACK my proposal.

## Files affected

| Path | Why the test needs updating |
|---------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
| `gateway/tests/test_phase_transition.py` | `PHASE_TRANSITIONS[PLAN]` grew from `[IMPLEMENT]` to `[IMPLEMENT, APPLY]`; a new `APPLY → IMPLEMENT` edge needs coverage. |
| `gateway/tests/test_jira_routes.py` | NEW: task-1-6 acceptance — two tests verifying the ticket-create route propagates `JiraPolicy.epic_link_field()` verbatim to `JiraClient.create_issue` for both `parent` (default) and `customfield_10014`. |
| `orchestrator/tests/test_advance_phase_thread.py` | The auto-advance source-inspection block window was 3000 chars; the new applier-handoff + Won't-Do drain hooks push the `_spawn_pipeline_run_thread` call past that window. Widen to 5000. |
| `orchestrator/tests/test_models.py` | `AgentRole` count moved from 19 → 20 (APPLIER added); `PipelinePhase` declaration order now has APPLY between PLAN and IMPLEMENT. |
| `shared/tests/test_egg_restrictions.py` | Same registry-count bump on the `AGENT_PATTERNS` parity assertions. |

## How to apply

```bash
git apply .egg-state/agent-outputs/coder-to-tester-1557-test-followups.patch
git add gateway/tests/test_jira_routes.py \
gateway/tests/test_phase_transition.py \
orchestrator/tests/test_advance_phase_thread.py \
orchestrator/tests/test_models.py \
shared/tests/test_egg_restrictions.py
git commit -m 'test(#1557): follow-on assertions for APPLY phase + APPLIER role + epic_link_field'
```

The patch is mechanical — it only adjusts assertions that have hard-coded counts / ordering / window-sizes the coder's production change shifted. There are no behavioral test rewrites and no new fixture infrastructure.

## Validation that the patch passes locally

Each touched test file was run against the coder's production diff
before the test files were extracted from the commit:

- `gateway/tests/test_jira_routes.py` — 104 tests pass (including the
two new `test_epic_link_dispatches_via_{parent_field,customfield}`).
- `gateway/tests/test_phase_transition.py` — 29 tests pass (including
the new `test_apply_to_implement`).
- `orchestrator/tests/test_advance_phase_thread.py` — 15 tests pass.
- `orchestrator/tests/test_models.py` — 85 tests pass.
- `shared/tests/test_egg_restrictions.py` — 211 tests pass.

## Reviewer pointer

Pair with my proposal's `pre_merge_condition`: the human reviewer
(or you, with `mcp__brc__resolve_obligation`) closes the obligation
once this patch is on the integration branch.
219 changes: 219 additions & 0 deletions .egg-state/agent-outputs/coder-to-tester-1557-test-followups.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
diff --git a/gateway/tests/test_jira_routes.py b/gateway/tests/test_jira_routes.py
index ed7a96492..759a65377 100644
--- a/gateway/tests/test_jira_routes.py
+++ b/gateway/tests/test_jira_routes.py
@@ -979,6 +979,86 @@ class TestTicketCreate:
kwargs = fake_client.create_issue.call_args.kwargs
assert kwargs["description"] == adf

+ # -------------------------------------------------------------------
+ # Issue #1557 task-1-6 — per-project ``epic_link_field`` dispatch.
+ # -------------------------------------------------------------------
+ #
+ # The dispatch from the ``epicLink`` shorthand to either ``parent``
+ # (next-gen / company-managed projects, default) or
+ # ``customfield_10014`` (classic / team-managed projects) is wired
+ # at ``gateway/gateway.py:6097`` — the route reads
+ # ``JiraPolicy.epic_link_field`` and passes it to
+ # ``JiraClient.create_issue``. ``JiraClient.create_issue``'s wire
+ # translation is covered by
+ # ``gateway/tests/test_jira_client.py::TestCreateIssue::test_epic_
+ # link_with_{parent,customfield}_dispatch``. The tests below close
+ # the route-layer half: they assert the gateway route reads the
+ # policy and propagates the resolved field name verbatim to the
+ # JiraClient call. Together the two sides verify the operator-
+ # managed ``epic_link_field`` setting (refine decision-3) is
+ # exercised end-to-end before the epic pipeline relies on it for
+ # child-ticket creation.
+
+ def test_epic_link_dispatches_via_parent_field(
+ self, client, private_headers, allow_eng, captured_audit, monkeypatch
+ ):
+ """Default ``epic_link_field='parent'`` (next-gen / company-managed
+ sites) → the route hands ``epic_link_field='parent'`` to
+ ``JiraClient.create_issue``, which then writes
+ ``fields: {parent: {key: <KEY>}}`` on the Atlassian wire.
+ Verified at the JiraClient layer by
+ ``test_epic_link_with_parent_dispatch`` in test_jira_client.py."""
+ monkeypatch.setattr(gateway, "jira_epic_link_field", lambda: "parent")
+ fake_client = MagicMock()
+ fake_client.create_issue.return_value = (
+ 201,
+ {"id": "1", "key": "ENG-2", "self": "https://e.atlassian.net/rest/api/3/issue/1"},
+ False,
+ )
+ with patch.object(gateway, "get_jira_client", return_value=fake_client):
+ resp = client.post(
+ self.PATH,
+ headers=private_headers,
+ data=json.dumps({**self._valid_body(), "epicLink": "ENG-1"}),
+ content_type="application/json",
+ )
+ assert resp.status_code == 200, resp.data
+ kwargs = fake_client.create_issue.call_args.kwargs
+ # The route must forward both the requested epic link AND the
+ # operator-configured dispatch field — the JiraClient layer
+ # then translates ``epic_link_field='parent'`` into
+ # ``fields.parent: {key: <KEY>}`` (covered in test_jira_client.py).
+ assert kwargs["epic_link"] == "ENG-1"
+ assert kwargs["epic_link_field"] == "parent"
+
+ def test_epic_link_dispatches_via_customfield(
+ self, client, private_headers, allow_eng, captured_audit, monkeypatch
+ ):
+ """``epic_link_field='customfield_10014'`` (classic / team-managed
+ sites) → the route hands the customfield name to
+ ``JiraClient.create_issue``, which writes
+ ``fields: {customfield_10014: <KEY>}`` on the wire. Verified at
+ the JiraClient layer by ``test_epic_link_with_customfield_
+ dispatch`` in test_jira_client.py."""
+ monkeypatch.setattr(gateway, "jira_epic_link_field", lambda: "customfield_10014")
+ fake_client = MagicMock()
+ fake_client.create_issue.return_value = (
+ 201,
+ {"id": "1", "key": "ENG-2", "self": "https://e.atlassian.net/rest/api/3/issue/1"},
+ False,
+ )
+ with patch.object(gateway, "get_jira_client", return_value=fake_client):
+ resp = client.post(
+ self.PATH,
+ headers=private_headers,
+ data=json.dumps({**self._valid_body(), "epicLink": "ENG-1"}),
+ content_type="application/json",
+ )
+ assert resp.status_code == 200, resp.data
+ kwargs = fake_client.create_issue.call_args.kwargs
+ assert kwargs["epic_link"] == "ENG-1"
+ assert kwargs["epic_link_field"] == "customfield_10014"
+
def test_upstream_error_passes_through(
self, client, private_headers, allow_eng, captured_audit
):
diff --git a/gateway/tests/test_phase_transition.py b/gateway/tests/test_phase_transition.py
index f664696d9..a53adda6f 100644
--- a/gateway/tests/test_phase_transition.py
+++ b/gateway/tests/test_phase_transition.py
@@ -96,9 +96,34 @@ class TestValidTransitions:
assert len(VALID_TRANSITIONS[PipelinePhase.REFINE]) == 1

def test_plan_to_implement(self):
- """Plan can only transition to implement."""
+ """Plan can transition to implement (and to apply for epic pipelines).
+
+ Issue #1557: ``PLAN`` gained ``APPLY`` as a second valid
+ successor so epic-mode pipelines can route Jira mutations
+ through a dedicated APPLY phase between PLAN and IMPLEMENT.
+ Non-epic pipelines continue to use the IMPLEMENT edge —
+ ``IMPLEMENT`` is listed first so ``get_next_phase`` keeps the
+ pre-#1557 default."""
assert PipelinePhase.IMPLEMENT in VALID_TRANSITIONS[PipelinePhase.PLAN]
- assert len(VALID_TRANSITIONS[PipelinePhase.PLAN]) == 1
+ assert PipelinePhase.APPLY in VALID_TRANSITIONS[PipelinePhase.PLAN]
+ assert len(VALID_TRANSITIONS[PipelinePhase.PLAN]) == 2
+ # Default-first ordering invariant: epic-aware schedulers pick
+ # APPLY by name; non-epic flows that take ``next_phases[0]``
+ # must still see IMPLEMENT.
+ assert VALID_TRANSITIONS[PipelinePhase.PLAN][0] == PipelinePhase.IMPLEMENT
+
+ def test_apply_to_implement(self):
+ """Apply (Jira-epic phase) advances only to implement.
+
+ Issue #1557: the new ``APPLY`` phase is the second step in the
+ epic-mode pipeline (PLAN → APPLY → IMPLEMENT). The orchestrator-
+ side scheduler in ``orchestrator.routes.pipelines.
+ _next_phases_for_epic`` picks APPLY only when ``Pipeline.is_epic``
+ is true; this transition is what carries the pipeline back into
+ the standard IMPLEMENT phase once the applier has driven all Jira
+ mutations and BRC consensus has confirmed."""
+ assert PipelinePhase.IMPLEMENT in VALID_TRANSITIONS[PipelinePhase.APPLY]
+ assert len(VALID_TRANSITIONS[PipelinePhase.APPLY]) == 1

def test_implement_to_pr(self):
"""Implement can only transition to PR."""
diff --git a/orchestrator/tests/test_advance_phase_thread.py b/orchestrator/tests/test_advance_phase_thread.py
index 6a3de2df2..ff73bf07b 100644
--- a/orchestrator/tests/test_advance_phase_thread.py
+++ b/orchestrator/tests/test_advance_phase_thread.py
@@ -278,7 +278,10 @@ class TestAutoAdvanceRespawnsThread:
)
idx = source.index(self._BLOCK_MARKER)
# Take a generous window so the block including the return is included.
- return source[idx : idx + 3000]
+ # Widened from 3000 to 5000 in issue #1557 to absorb the epic-mode
+ # applier-handoff write + Won't-Do drain hook the auto-advance
+ # block now performs before respawning the next-phase thread.
+ return source[idx : idx + 5000]

def test_auto_advance_bumps_run_epoch(self):
block = self._auto_advance_block()
diff --git a/orchestrator/tests/test_models.py b/orchestrator/tests/test_models.py
index af463e384..d8bde8aee 100644
--- a/orchestrator/tests/test_models.py
+++ b/orchestrator/tests/test_models.py
@@ -819,6 +819,10 @@ class TestAgentRole:
assert AgentRole.CODER in roles
assert AgentRole.TESTER in roles
assert AgentRole.DOCUMENTER in roles
+ # Issue #1557 — APPLIER joined the registry for Jira-epic
+ # SDLC support (drives gateway Jira mutations after HITL
+ # approval on epic-mode pipelines).
+ assert AgentRole.APPLIER in roles
assert AgentRole.ARCHITECT in roles
assert AgentRole.TASK_PLANNER in roles
assert AgentRole.RISK_ANALYST in roles
@@ -835,7 +839,7 @@ class TestAgentRole:
assert AgentRole.OVERSEER in roles
assert AgentRole.AUTOFIXER in roles
assert AgentRole.CONFLICT_RESOLVER in roles
- assert len(roles) == 19
+ assert len(roles) == 20


class TestBackwardCompatibility:
@@ -898,9 +902,18 @@ class TestPipelinePhase:
"""Tests for PipelinePhase enum."""

def test_phase_order(self):
- """Test phases are defined in SDLC order."""
+ """Test phases are defined in SDLC order.
+
+ Issue #1557 inserted ``APPLY`` between ``PLAN`` and ``IMPLEMENT``
+ — the new phase runs only on epic-mode pipelines (gated by
+ ``Pipeline.is_epic`` in the orchestrator-side scheduler) so the
+ enum declaration order reflects the SDLC reading order for an
+ epic pipeline; non-epic pipelines skip APPLY entirely via
+ ``orchestrator.routes.pipelines._next_phases_for_epic``.
+ """
phases = list(PipelinePhase)
assert phases[0] == PipelinePhase.REFINE
assert phases[1] == PipelinePhase.PLAN
- assert phases[2] == PipelinePhase.IMPLEMENT
- assert phases[3] == PipelinePhase.PR
+ assert phases[2] == PipelinePhase.APPLY
+ assert phases[3] == PipelinePhase.IMPLEMENT
+ assert phases[4] == PipelinePhase.PR
diff --git a/shared/tests/test_egg_restrictions.py b/shared/tests/test_egg_restrictions.py
index 23a7c7765..0097fd278 100644
--- a/shared/tests/test_egg_restrictions.py
+++ b/shared/tests/test_egg_restrictions.py
@@ -77,14 +77,18 @@ class TestAgentRole:


class TestAgentPatterns:
- def test_registry_has_all_19_roles(self):
- assert len(AGENT_PATTERNS) == 19
+ def test_registry_has_all_20_roles(self):
+ # Issue #1557 — APPLIER joined the registry (Jira-epic SDLC
+ # support); the count grew from 19 to 20.
+ assert len(AGENT_PATTERNS) == 20

def test_registry_keys_match_role_constants(self):
expected_roles = {
AgentRole.CODER,
AgentRole.TESTER,
AgentRole.DOCUMENTER,
+ # Issue #1557 — Jira-epic SDLC pipeline support.
+ AgentRole.APPLIER,
AgentRole.ARCHITECT,
AgentRole.TASK_PLANNER,
AgentRole.RISK_ANALYST,
Loading
Loading