From e5f705cde973d529d36fd67cfa3537b6acaca0a2 Mon Sep 17 00:00:00 2001 From: tobrun Date: Mon, 5 Oct 2026 15:06:23 +0200 Subject: [PATCH 1/5] feat(scope): lint echo mismatches name the expected echo Co-Authored-By: Claude Sonnet 5.5 --- dev/evals/tests/test_lint_spec.py | 79 +++++++++++++++++++++++ dev/evals/tests/test_memory.py | 1 + dev/skills/scope/scripts/lint-spec.py | 5 +- factory/phases/scope/scripts/lint-spec.py | 5 +- 4 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 dev/evals/tests/test_lint_spec.py diff --git a/dev/evals/tests/test_lint_spec.py b/dev/evals/tests/test_lint_spec.py new file mode 100644 index 0000000..e8e285e --- /dev/null +++ b/dev/evals/tests/test_lint_spec.py @@ -0,0 +1,79 @@ +"""Unit tests for lint-spec.py's echo check: a mismatched echo names what it should be. + +Run from the repo root: python3 -m unittest discover -s dev/evals/tests -p 'test_lint_spec.py' -t . +Each test writes a small spec to a scratch directory and runs the script as a +subprocess, so it proves the CLI contract the scope skill loops against. +""" + +from __future__ import annotations + +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[3] +SCRIPT = REPO_ROOT / "dev" / "skills" / "scope" / "scripts" / "lint-spec.py" + +CHOSEN = """D-storage: Where do files live? + ✓ S3 bucket - durable across redeploys + ✗ local disk - lost on redeploy +""" +OPEN = """D-storage: Where do files live? [open] + ? S3 bucket - durable across redeploys + ⚑ ask: expected retention? +""" +NOT_DOING = """D-storage: Where do files live? + ✗ S3 bucket - no upload feature yet + ⊘ not doing - nobody asked for uploads ? verify: support tickets; reopen on the first request +""" + + +def spec(decision: str, echo: str) -> str: + return f"""# Title + +## Research + +{decision} +## Scope + +Files go where D-storage ({echo}) says. +""" + + +class LintSpecEchoTest(unittest.TestCase): + def setUp(self) -> None: + self.tmp = tempfile.TemporaryDirectory() + self.addCleanup(self.tmp.cleanup) + + def lint(self, decision: str, echo: str) -> subprocess.CompletedProcess: + path = Path(self.tmp.name) / "spec.md" + path.write_text(spec(decision, echo), encoding="utf-8") + return subprocess.run( + [sys.executable, str(SCRIPT), str(path)], capture_output=True, text=True, check=False + ) + + def echo_errors(self, result: subprocess.CompletedProcess) -> str: + return "\n".join(line for line in result.stdout.splitlines() if "echo" in line) + + def test_echo_copied_from_the_chosen_line_passes(self) -> None: + self.assertEqual(self.echo_errors(self.lint(CHOSEN, "✓ S3")), "") + + def test_echo_not_in_the_chosen_line_names_the_chosen_text(self) -> None: + result = self.lint(CHOSEN, "✓ cloud") + self.assertIn("does not match its resolution", self.echo_errors(result)) + self.assertIn("s3 bucket", self.echo_errors(result)) + + def test_chosen_echo_on_an_open_decision_expects_open(self) -> None: + self.assertIn("expected (open)", self.echo_errors(self.lint(OPEN, "✓ S3"))) + + def test_chosen_echo_on_a_not_doing_decision_expects_not_doing(self) -> None: + self.assertIn("expected (⊘ not doing)", self.echo_errors(self.lint(NOT_DOING, "✓ S3"))) + + def test_bare_check_mark_on_a_chosen_decision_passes(self) -> None: + self.assertEqual(self.echo_errors(self.lint(CHOSEN, "✓")), "") + + +if __name__ == "__main__": + unittest.main() diff --git a/dev/evals/tests/test_memory.py b/dev/evals/tests/test_memory.py index 031ee0c..9d42682 100644 --- a/dev/evals/tests/test_memory.py +++ b/dev/evals/tests/test_memory.py @@ -60,6 +60,7 @@ class Sandbox: """A scratch repo with a transcript where skill-metrics.py will find it.""" def __init__(self, tmp: Path, rows: list[dict] = TRANSCRIPT): + tmp = tmp.resolve() # git reports the real path; macOS temp dirs sit behind a symlink self.repo = tmp / f"repo-{os.getpid()}-{id(self)}" self.repo.mkdir() subprocess.run(["git", "init", "-q"], cwd=self.repo, check=True) diff --git a/dev/skills/scope/scripts/lint-spec.py b/dev/skills/scope/scripts/lint-spec.py index 2cb73b3..bdbb3bf 100755 --- a/dev/skills/scope/scripts/lint-spec.py +++ b/dev/skills/scope/scripts/lint-spec.py @@ -120,17 +120,20 @@ def check_echoes( problem(number, f"{slug} is echoed but never argued in the research section") continue resolution = echo.strip().lower() + hint = f"({CHOSEN} )" if decision.get("not_doing"): expected = resolution.startswith(NOT_DOING) + hint = f"({NOT_DOING} not doing)" elif decision["open"]: expected = resolution == "open" + hint = "(open)" elif resolution.startswith(CHOSEN): shorthand = resolution[1:].strip() expected = not shorthand or shorthand in decision.get("choice", "") else: expected = False if not expected: - problem(number, f"{slug} echo '({echo})' does not match its resolution") + problem(number, f"{slug} echo '({echo})' does not match its resolution; expected {hint}") if in_change_plan and (decision["open"] or decision["flagged"]): problem(number, f"the change plan links {slug}, which is still open or flagged") diff --git a/factory/phases/scope/scripts/lint-spec.py b/factory/phases/scope/scripts/lint-spec.py index 2cb73b3..bdbb3bf 100755 --- a/factory/phases/scope/scripts/lint-spec.py +++ b/factory/phases/scope/scripts/lint-spec.py @@ -120,17 +120,20 @@ def check_echoes( problem(number, f"{slug} is echoed but never argued in the research section") continue resolution = echo.strip().lower() + hint = f"({CHOSEN} )" if decision.get("not_doing"): expected = resolution.startswith(NOT_DOING) + hint = f"({NOT_DOING} not doing)" elif decision["open"]: expected = resolution == "open" + hint = "(open)" elif resolution.startswith(CHOSEN): shorthand = resolution[1:].strip() expected = not shorthand or shorthand in decision.get("choice", "") else: expected = False if not expected: - problem(number, f"{slug} echo '({echo})' does not match its resolution") + problem(number, f"{slug} echo '({echo})' does not match its resolution; expected {hint}") if in_change_plan and (decision["open"] or decision["flagged"]): problem(number, f"the change plan links {slug}, which is still open or flagged") From 80db90a62d5a320ce518120a3d5d622e8cff06a1 Mon Sep 17 00:00:00 2001 From: tobrun Date: Mon, 5 Oct 2026 15:07:39 +0200 Subject: [PATCH 2/5] feat(scope): tell authors to lead chosen lines with the echo label Co-Authored-By: Claude Sonnet 5.5 --- dev/evals/scope.json | 8 ++++++++ dev/skills/scope/SKILL.md | 2 +- factory/phases/scope/SKILL.md | 2 +- plugins/dev/skills/scope/SKILL.md | 2 +- plugins/dev/skills/scope/scripts/lint-spec.py | 5 ++++- plugins/factory/phases/scope/SKILL.md | 2 +- plugins/factory/phases/scope/scripts/lint-spec.py | 5 ++++- 7 files changed, 20 insertions(+), 6 deletions(-) diff --git a/dev/evals/scope.json b/dev/evals/scope.json index 683ab55..88f9fc9 100644 --- a/dev/evals/scope.json +++ b/dev/evals/scope.json @@ -86,6 +86,14 @@ "Answer 4: it is handed only the original request and the relevant code, and is told to stay out of .dev/, where the catalog is being written", "Answer 5: a spec.md that lint-spec.py does not pass, holding everything established before the session died; it is a draft that scope picks up where it stopped, and no other skill builds on it" ] + }, + { + "id": "scope-echo-matches-chosen", + "prompt": "Answer briefly, from the skill text and the references it links only. A decision's chosen line reads `✓ S3 bucket in us-east-1 - durable across redeploys`. (1) Write the echo a later section should use for it. (2) You were about to write `(✓ cloud storage)` instead. Will lint-spec.py accept that, and what does the skill tell you to do when writing the chosen line so the echo passes the first time?", + "assertions": [ + "Answer 1: `D-slug (✓ S3 bucket)` or a shorter prefix such as `(✓ S3)` - text taken from the chosen line before its ` - `, not from the because clause", + "Answer 2: no, lint-spec.py rejects it because the echo is not found in the chosen line's text before ` - `; lead the chosen line with the short label that will be echoed, and copy the echo from it" + ] } ] } diff --git a/dev/skills/scope/SKILL.md b/dev/skills/scope/SKILL.md index caff8ae..b264371 100644 --- a/dev/skills/scope/SKILL.md +++ b/dev/skills/scope/SKILL.md @@ -84,7 +84,7 @@ D-response-caching: Should responses be cached? ⊘ not doing - no measured latency problem ? verify: p95 from prod metrics; reopen if p95 exceeds 500ms ``` -When a later section references a decision, echo the resolution in parentheses - `D-file-storage (✓ S3)`, `(open)`, `(⊘ not doing)` - so the reader only jumps back for the why. +When a later section references a decision, echo the resolution in parentheses - `D-file-storage (✓ S3)`, `(open)`, `(⊘ not doing)` - so the reader only jumps back for the why. The lint requires the text after `✓` to be copied from the chosen line before its ` - `, so lead each chosen line with the short label you will echo. ## 4. Scope section diff --git a/factory/phases/scope/SKILL.md b/factory/phases/scope/SKILL.md index 523ea3a..ed1f7be 100644 --- a/factory/phases/scope/SKILL.md +++ b/factory/phases/scope/SKILL.md @@ -79,7 +79,7 @@ D-response-caching: Should responses be cached? ⊘ not doing - no measured latency problem ? verify: p95 from prod metrics; reopen if p95 exceeds 500ms ``` -When a later section references a decision, echo the resolution in parentheses - `D-file-storage (✓ S3)`, `(open)`, `(⊘ not doing)` - so the reader only jumps back for the why. +When a later section references a decision, echo the resolution in parentheses - `D-file-storage (✓ S3)`, `(open)`, `(⊘ not doing)` - so the reader only jumps back for the why. The lint requires the text after `✓` to be copied from the chosen line before its ` - `, so lead each chosen line with the short label you will echo. ## 4. Scope section diff --git a/plugins/dev/skills/scope/SKILL.md b/plugins/dev/skills/scope/SKILL.md index c32c477..82fb715 100644 --- a/plugins/dev/skills/scope/SKILL.md +++ b/plugins/dev/skills/scope/SKILL.md @@ -83,7 +83,7 @@ D-response-caching: Should responses be cached? ⊘ not doing - no measured latency problem ? verify: p95 from prod metrics; reopen if p95 exceeds 500ms ``` -When a later section references a decision, echo the resolution in parentheses - `D-file-storage (✓ S3)`, `(open)`, `(⊘ not doing)` - so the reader only jumps back for the why. +When a later section references a decision, echo the resolution in parentheses - `D-file-storage (✓ S3)`, `(open)`, `(⊘ not doing)` - so the reader only jumps back for the why. The lint requires the text after `✓` to be copied from the chosen line before its ` - `, so lead each chosen line with the short label you will echo. ## 4. Scope section diff --git a/plugins/dev/skills/scope/scripts/lint-spec.py b/plugins/dev/skills/scope/scripts/lint-spec.py index 2cb73b3..bdbb3bf 100755 --- a/plugins/dev/skills/scope/scripts/lint-spec.py +++ b/plugins/dev/skills/scope/scripts/lint-spec.py @@ -120,17 +120,20 @@ def check_echoes( problem(number, f"{slug} is echoed but never argued in the research section") continue resolution = echo.strip().lower() + hint = f"({CHOSEN} )" if decision.get("not_doing"): expected = resolution.startswith(NOT_DOING) + hint = f"({NOT_DOING} not doing)" elif decision["open"]: expected = resolution == "open" + hint = "(open)" elif resolution.startswith(CHOSEN): shorthand = resolution[1:].strip() expected = not shorthand or shorthand in decision.get("choice", "") else: expected = False if not expected: - problem(number, f"{slug} echo '({echo})' does not match its resolution") + problem(number, f"{slug} echo '({echo})' does not match its resolution; expected {hint}") if in_change_plan and (decision["open"] or decision["flagged"]): problem(number, f"the change plan links {slug}, which is still open or flagged") diff --git a/plugins/factory/phases/scope/SKILL.md b/plugins/factory/phases/scope/SKILL.md index 3a3a3ee..5dd6c6c 100644 --- a/plugins/factory/phases/scope/SKILL.md +++ b/plugins/factory/phases/scope/SKILL.md @@ -78,7 +78,7 @@ D-response-caching: Should responses be cached? ⊘ not doing - no measured latency problem ? verify: p95 from prod metrics; reopen if p95 exceeds 500ms ``` -When a later section references a decision, echo the resolution in parentheses - `D-file-storage (✓ S3)`, `(open)`, `(⊘ not doing)` - so the reader only jumps back for the why. +When a later section references a decision, echo the resolution in parentheses - `D-file-storage (✓ S3)`, `(open)`, `(⊘ not doing)` - so the reader only jumps back for the why. The lint requires the text after `✓` to be copied from the chosen line before its ` - `, so lead each chosen line with the short label you will echo. ## 4. Scope section diff --git a/plugins/factory/phases/scope/scripts/lint-spec.py b/plugins/factory/phases/scope/scripts/lint-spec.py index 2cb73b3..bdbb3bf 100755 --- a/plugins/factory/phases/scope/scripts/lint-spec.py +++ b/plugins/factory/phases/scope/scripts/lint-spec.py @@ -120,17 +120,20 @@ def check_echoes( problem(number, f"{slug} is echoed but never argued in the research section") continue resolution = echo.strip().lower() + hint = f"({CHOSEN} )" if decision.get("not_doing"): expected = resolution.startswith(NOT_DOING) + hint = f"({NOT_DOING} not doing)" elif decision["open"]: expected = resolution == "open" + hint = "(open)" elif resolution.startswith(CHOSEN): shorthand = resolution[1:].strip() expected = not shorthand or shorthand in decision.get("choice", "") else: expected = False if not expected: - problem(number, f"{slug} echo '({echo})' does not match its resolution") + problem(number, f"{slug} echo '({echo})' does not match its resolution; expected {hint}") if in_change_plan and (decision["open"] or decision["flagged"]): problem(number, f"the change plan links {slug}, which is still open or flagged") From 5ba01f786df73b7bcff1bca81ca19f889aeda6ef Mon Sep 17 00:00:00 2001 From: tobrun Date: Mon, 5 Oct 2026 15:18:16 +0200 Subject: [PATCH 3/5] test(scope): cover the echo kinds and the change-plan link check What: four more lint-spec tests for check_echoes. Why: mutation testing showed the wrong-kind echo and the change-plan link branches unproven. --- dev/evals/tests/test_lint_spec.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/dev/evals/tests/test_lint_spec.py b/dev/evals/tests/test_lint_spec.py index e8e285e..5caa7cd 100644 --- a/dev/evals/tests/test_lint_spec.py +++ b/dev/evals/tests/test_lint_spec.py @@ -74,6 +74,33 @@ def test_chosen_echo_on_a_not_doing_decision_expects_not_doing(self) -> None: def test_bare_check_mark_on_a_chosen_decision_passes(self) -> None: self.assertEqual(self.echo_errors(self.lint(CHOSEN, "✓")), "") + def test_open_echo_on_a_chosen_decision_expects_the_chosen_text(self) -> None: + result = self.lint(CHOSEN, "open") + self.assertIn("does not match its resolution", self.echo_errors(result)) + self.assertIn("s3 bucket", self.echo_errors(result)) + + def test_change_plan_linking_an_open_decision_is_flagged(self) -> None: + path = Path(self.tmp.name) / "spec.md" + path.write_text( + spec(OPEN, "open") + "\n## Change plan\n\n1. Store files\n a. `src/store.py` - write files - decisions: D-storage (open)\n", + encoding="utf-8", + ) + result = subprocess.run([sys.executable, str(SCRIPT), str(path)], capture_output=True, text=True, check=False) + self.assertIn("the change plan links D-storage, which is still open or flagged", result.stdout) + + def test_scope_echo_of_an_open_decision_is_not_a_change_plan_link(self) -> None: + self.assertNotIn("change plan links", self.lint(OPEN, "open").stdout) + + def test_change_plan_linking_an_open_unflagged_decision_is_flagged(self) -> None: + path = Path(self.tmp.name) / "spec.md" + open_only = OPEN.replace(" ⚑ ask: expected retention?\n", "") + path.write_text( + spec(open_only, "open") + "\n## Change plan\n\n1. Store files\n a. `src/store.py` - write files - decisions: D-storage (open)\n", + encoding="utf-8", + ) + result = subprocess.run([sys.executable, str(SCRIPT), str(path)], capture_output=True, text=True, check=False) + self.assertIn("the change plan links D-storage", result.stdout) + if __name__ == "__main__": unittest.main() From f322b275ca791b1f8b5d037da024e6f92bfa4ca9 Mon Sep 17 00:00:00 2001 From: tobrun Date: Tue, 6 Oct 2026 11:10:41 +0200 Subject: [PATCH 4/5] feat(scope): recommend build by default, scope-review only for large changes scope's closing hand-off named scope-review first, which read as a required step before build. build never needed a review, so the hand-off now recommends build and mentions scope-review only when the change is large or complex, leaving the choice to the user. The README describes scope-review as optional to match. --- dev/README.md | 3 ++- dev/skills/scope/SKILL.md | 3 ++- plugins/dev/skills/scope/SKILL.md | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/dev/README.md b/dev/README.md index 0661e5b..b361f8f 100644 --- a/dev/README.md +++ b/dev/README.md @@ -2,7 +2,7 @@ Development workflow skills for Claude Code, Codex, opencode, and Pi, built around two ideas: layered tests are the enforceable spec for behavior, and every phase produces something a human actually reviews as HTML, not markdown scrolling. -The skills chain loosely rather than as a rigid pipeline: `/scope` interviews for the real problem, argues every design decision against alternatives, and writes a self-contained spec whose change plan carries layer-tagged test scenarios; `/scope-review` puts the settled spec through a fresh-context, adversarially verified agent panel that checks the plan against the actual repo and refines the spec in place, looping without a human and closing with a short interview for the few findings only the user can decide, so a finished run hands `build` a spec ready to implement; `/build` executes the spec's change sets across unit/integration/e2e, proving every scenario with a real test at its tagged layer, in parallel waves where file lists allow, keeping a running implementation-notes log; `/ship` runs a deterministic quality gauntlet - the repo's own static analysis, security scan, dead code, duplication, dependency rules, coverage-weighted complexity, flakiness, mutation testing - looping fix agents until the checkers pass, then verifies the result with a fan-out review panel that checks spec conformance, e2e coverage, and logged deviations; `/commit` groups pending changes into granular commits with structured what/why messages; `/reflect` consolidates the journal every run leaves into cited claims about the skills themselves and hands the most recurrent one to `scope` as a brief; `/to-pitch` and `/to-quiz` turn finished work into a buy-in doc or a comprehension check. +The skills chain loosely rather than as a rigid pipeline: `/scope` interviews for the real problem, argues every design decision against alternatives, and writes a self-contained spec whose change plan carries layer-tagged test scenarios; `/scope-review` is an optional step for a large or complex change, never a gate in front of `build`: it puts the settled spec through a fresh-context, adversarially verified agent panel that checks the plan against the actual repo and refines the spec in place, looping without a human and closing with a short interview for the few findings only the user can decide, so a finished run hands `build` a spec ready to implement; `/build` executes the spec's change sets across unit/integration/e2e, proving every scenario with a real test at its tagged layer, in parallel waves where file lists allow, keeping a running implementation-notes log; `/ship` runs a deterministic quality gauntlet - the repo's own static analysis, security scan, dead code, duplication, dependency rules, coverage-weighted complexity, flakiness, mutation testing - looping fix agents until the checkers pass, then verifies the result with a fan-out review panel that checks spec conformance, e2e coverage, and logged deviations; `/commit` groups pending changes into granular commits with structured what/why messages; `/reflect` consolidates the journal every run leaves into cited claims about the skills themselves and hands the most recurrent one to `scope` as a brief; `/to-pitch` and `/to-quiz` turn finished work into a buy-in doc or a comprehension check. The durable context is deliberately small: the code, its tests, the active spec under `.dev/{plan-name}/`, and three repo-tracked registries the skills maintain in the consuming project - `docs/decisions.md` (design decisions with their argued alternatives, read only after a review forms its findings), `docs/contracts.md` (boundary guarantees, read as premises before a review walks the diff), and `docs/dependencies.md` (machine-checkable module dependency rules, enforced by `ship`). The files under `.dev/{plan-name}/` are written as a run goes, not when a stage closes: the spec opens during the interview, a report opens before its panel returns, the implementation notes gain an entry per change set and per fixup, and the PR body fills check by check, so a run can be followed from its files and a dead session loses only what was in flight. Alongside them, `docs/architecture.md` is a plain high-level overview of the system - components, flows, boundaries, entry points - captured in full the first time a skill needs it and finds it absent, then kept current by build and commit whenever the structure changes, with a small checker that catches stale paths and files no component covers. @@ -21,6 +21,7 @@ Promotes durable decisions to `docs/decisions.md` and cross-boundary invariants ### scope-review +Optional: `build` runs on any spec `scope` finished, and `scope` advises this review only when the change is large or complex. Reviews a settled spec with agents that did not write it and refines it in place, before build starts - the cheapest review in the chain, since a defect caught here costs a spec edit instead of a re-implementation, and the loop makes those edits itself. Gates on `scope`'s own `lint-spec.py` first (a mechanically unsettled spec is sent back, not reviewed), then loops up to two rounds of panel, verification, and refinement: four lenses in parallel - feasibility (does the plan survive contact with the repo: named files, assumed hooks, claimed prior art, stated premises about current behavior), completeness (failure paths, second-order work, and affected call sites the spec never mentions), consistency (change sets versus their linked decisions and the project's ledgers, semantically), and testability (will the `tests:` lines produce real proof at their tagged layers) - with every BLOCK and CONCERN adversarially verified on ship's transport and aggregation machinery before a refine agent may touch the spec. Refinements follow a strict authority order (recorded user intent beats the repo's reality beats settled decisions beats spec prose) and edit `spec.md` only; anything that would flip a decision, change user-visible scope, or add a dependency is never auto-applied - the run ends by asking the user those as decisions, one at a time with alternatives and tradeoffs, and applies the answers to the spec before finishing. diff --git a/dev/skills/scope/SKILL.md b/dev/skills/scope/SKILL.md index b264371..8026c40 100644 --- a/dev/skills/scope/SKILL.md +++ b/dev/skills/scope/SKILL.md @@ -150,7 +150,8 @@ Audit the run itself. Three checks, each reported as a typed line - silence read - **Catalog gaps** - `none | gap`. What did the reviewer or blind-spot pass find that phase 2 should have caught? A missed *category* is a proposed edit to the phase 2 list - propose it to the user, never apply it silently. Then print the measured run metrics with `python3 {scope-skill-root}/../../scripts/skill-metrics.py end scope --count decisions=N --count change_sets=N --count scenarios=N`, plus any `--friction` lines per [../../references/run-journal.md](../../references/run-journal.md), pasting its table verbatim. -Then recommend next steps, never launching them: `scope-review` first when the change is large or risky or build will run in a different session - it reviews the spec with a fresh-context panel and refines it in place before any code exists - and `build` to implement. +Then recommend the next step, never launching it: `build`, which implements the spec as written and needs no review first. +Only when the change is large or complex, add that `scope-review` is worth running before `build` - it reviews the spec with a fresh-context panel and refines it in place before any code exists - and leave that choice to the user. ## Jira sync diff --git a/plugins/dev/skills/scope/SKILL.md b/plugins/dev/skills/scope/SKILL.md index 82fb715..b75d6b5 100644 --- a/plugins/dev/skills/scope/SKILL.md +++ b/plugins/dev/skills/scope/SKILL.md @@ -149,7 +149,8 @@ Audit the run itself. Three checks, each reported as a typed line - silence read - **Catalog gaps** - `none | gap`. What did the reviewer or blind-spot pass find that phase 2 should have caught? A missed *category* is a proposed edit to the phase 2 list - propose it to the user, never apply it silently. Then print the measured run metrics with `python3 {scope-skill-root}/../../scripts/skill-metrics.py end scope --count decisions=N --count change_sets=N --count scenarios=N`, plus any `--friction` lines per [../../references/run-journal.md](../../references/run-journal.md), pasting its table verbatim. -Then recommend next steps, never launching them: `scope-review` first when the change is large or risky or build will run in a different session - it reviews the spec with a fresh-context panel and refines it in place before any code exists - and `build` to implement. +Then recommend the next step, never launching it: `build`, which implements the spec as written and needs no review first. +Only when the change is large or complex, add that `scope-review` is worth running before `build` - it reviews the spec with a fresh-context panel and refines it in place before any code exists - and leave that choice to the user. ## Jira sync From a8b1e38d5e81fbcbee0d80838534b9b02a7356d4 Mon Sep 17 00:00:00 2001 From: tobrun Date: Tue, 6 Oct 2026 12:17:42 +0200 Subject: [PATCH 5/5] refactor: remove the Jira sync from the dev and factory workflows The skills carried an opt-in Jira mirror through acli: Epic and Task creation in scope, transitions in build, and an Epic key prefix on ship's branch and PR title. The workflow no longer assumes any issue tracker, so the sections, the jira.md reference, its evals and stub fixture, and the docs that described it are removed. build keeps its work-branch rule under its own heading, and .dev/config.json stays for the run journal opt-out. --- dev/README.md | 37 +----- dev/evals/build.json | 19 --- dev/evals/fixtures/acli | 39 ------ dev/evals/scope.json | 9 -- dev/references/jira.md | 115 ------------------ dev/references/plan-layout.md | 2 +- dev/skills/build/SKILL.md | 12 +- dev/skills/scope/SKILL.md | 7 +- dev/skills/ship/references/pull-request.md | 6 +- docs/architecture.md | 1 - docs/decisions.md | 5 +- factory/phases/build/SKILL.md | 12 +- factory/phases/scope/SKILL.md | 7 +- .../phases/ship/references/pull-request.md | 6 +- factory/references/jira.md | 115 ------------------ factory/references/plan-layout.md | 2 +- plugins/dev/references/jira.md | 115 ------------------ plugins/dev/references/plan-layout.md | 2 +- plugins/dev/skills/build/SKILL.md | 12 +- plugins/dev/skills/scope/SKILL.md | 7 +- .../skills/ship/references/pull-request.md | 6 +- plugins/factory/phases/build/SKILL.md | 12 +- plugins/factory/phases/scope/SKILL.md | 7 +- .../phases/ship/references/pull-request.md | 6 +- plugins/factory/references/jira.md | 115 ------------------ plugins/factory/references/plan-layout.md | 2 +- scripts/validate.sh | 2 +- 27 files changed, 31 insertions(+), 649 deletions(-) delete mode 100755 dev/evals/fixtures/acli delete mode 100644 dev/references/jira.md delete mode 100644 factory/references/jira.md delete mode 100644 plugins/dev/references/jira.md delete mode 100644 plugins/factory/references/jira.md diff --git a/dev/README.md b/dev/README.md index b361f8f..3f9882d 100644 --- a/dev/README.md +++ b/dev/README.md @@ -85,41 +85,6 @@ Every run appends a row to `.dev/metrics.jsonl` in the consuming repository, and The same call appends an entry to the cross-repository run journal under `~/.dev-workflow/memory/journal/` (or `$DEV_MEMORY_DIR`): the friction signals measured from the transcript (interrupts, denied and failed tool calls, the user's own turns, and how often each deterministic checker ran and failed), each with its transcript line, plus at most three `--friction` lines in which the skill names where the run fought its own instructions. That journal is what `reflect` consolidates. It stays on the machine; set `DEV_MEMORY_DIR=off`, or `"memory": {"enabled": false}` in `.dev/config.json`, to keep a repository out of it. -## Jira integration - -The spec-driven workflow can mirror its local state to Jira through the -Atlassian CLI (`acli`). Install and authenticate `acli` before enabling it. -Create `.dev/config.json` in the consuming repository: - -```json -{ - "jira": { - "enabled": true, - "site": "acme.atlassian.net", - "project": "PROJ" - } -} -``` - -`site` is optional and `project` is required when enabled. An absent config -file or `jira.enabled: false` keeps the workflow pure-local with no Jira -calls or questions. - -The hierarchy is Initiative > Epic > Task. `scope` asks you to choose -an existing open Initiative, creates one Epic under it once the change plan is -final, stores the Epic key in the spec, and creates one Jira Task per change -set, storing each key under its change set and closing old issues when a -change set is superseded. `build` moves the Epic to In Progress at start, -moves each change-set issue through In Progress and Done as the orchestrator -dispatches and commits it. `ship` then pushes the keyed branch and opens a PR -whose title starts with the Epic key. - -One-time Jira administration is required. Install the GitHub for Jira -integration, then add an automation rule: "when a linked pull request is -merged, transition the Epic to Done". The Epic key in the branch name and PR -title is what lets Jira link the pull request and trigger that rule. The -skills do not poll for merges. - ## Claude Code installation ```bash @@ -152,7 +117,7 @@ done ln -sfn ~/ws/workflow/dev/references ~/.config/opencode/references ``` -The last symlink keeps the shared references (`jira.md`, `decision-ledger.md`, `contracts.md`, `architecture.md`) reachable through the `../../references/` links inside the skills. +The last symlink keeps the shared references (`decision-ledger.md`, `contracts.md`, `architecture.md`) reachable through the `../../references/` links inside the skills. Symlinks mean a `git pull` updates the skills in place; restart opencode afterwards, since skills load at startup. opencode has no `disable-model-invocation` field (it is ignored harmlessly); skills load through a model-invoked `skill` tool. diff --git a/dev/evals/build.json b/dev/evals/build.json index 78b8117..3b24a62 100644 --- a/dev/evals/build.json +++ b/dev/evals/build.json @@ -101,25 +101,6 @@ "No required command whose inputs the change reaches is deferred because it is slow", "The closing message points at ship, whose PR's CI runs the deferred commands" ] - }, - { - "id": "jira-disabled", - "prompt": "Implement this spec.", - "fixture": "a git repo with a keyed .dev/{plan-name}/spec.md, no .dev/config.json, and no acli executable on PATH", - "assertions": [ - "The transcript contains no Jira, acli, Initiative, Epic, or issue-tracker mention", - "After the e2e report the user is asked whether to push and open the PR, and no push or PR command occurs before a yes" - ] - }, - { - "id": "jira-enabled", - "prompt": "Implement this spec.", - "fixture": "a git repo with .dev/config.json enabling Jira for PROJ, spec.md with Jira: PROJ-101 under its title and Jira: PROJ-201 / Jira: PROJ-202 under change sets 1 and 2, and the stub acli on PATH", - "assertions": [ - "The stub log transitions the Epic PROJ-101 to In Progress before change-set transitions", - "For each change set, the stub log shows In Progress at dispatch before Done after that change set's verification and commit", - "The final PR question is asked after the e2e report, and the proposed branch name and PR title start with the Epic key" - ] } ] } diff --git a/dev/evals/fixtures/acli b/dev/evals/fixtures/acli deleted file mode 100755 index 9434155..0000000 --- a/dev/evals/fixtures/acli +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -log_file="${ACLI_LOG:-./acli-invocations.log}" -printf '%s\n' "$*" >> "$log_file" - -case " $* " in - *" workitem search "*) - printf '%s\n' '{"issues":[{"key":"PROJ-10","summary":"Initiative one","status":"Open"}]}' - ;; - *" workitem create "*) - if [[ " $* " == *" --type Epic "* ]]; then - printf '%s\n' '{"key":"PROJ-101","fields":{"issuetype":{"name":"Epic"}}}' - else - task_count=$(grep ' workitem create ' "$log_file" 2>/dev/null | grep -vc ' --type Epic ' || true) - key="PROJ-2$(printf '%02d' "$task_count")" - printf '{"key":"%s","fields":{"issuetype":{"name":"Task"}}}\n' "$key" - fi - ;; - *" transition-list "*) - printf '%s\n' '{"key":"PROJ-201","transitions":[{"id":"11","name":"In Progress"},{"id":"31","name":"Done"}]}' - ;; - *" transition "*) - printf '%s\n' '{"key":"PROJ-201","status":"Done"}' - ;; - *" comment "*) - printf '%s\n' '{"key":"PROJ-201","ok":true}' - ;; - *" workitem view "*) - printf '%s\n' '{"key":"PROJ-201","fields":{"summary":"stub issue"}}' - ;; - *" auth status "*) - printf '%s\n' '{"authenticated":true}' - ;; - *) - printf 'stub acli: unsupported invocation: %s\n' "$*" >&2 - exit 1 - ;; -esac diff --git a/dev/evals/scope.json b/dev/evals/scope.json index 88f9fc9..2d38538 100644 --- a/dev/evals/scope.json +++ b/dev/evals/scope.json @@ -31,15 +31,6 @@ "The retrospective reports the three typed lines: contamination, sizing, catalog gaps" ] }, - { - "id": "jira-disabled", - "prompt": "We want to add rate limiting to our public API to stop abuse. Spec this change.", - "fixture": "a repo with src/api/server.js and no .dev/config.json, with no acli executable on PATH", - "assertions": [ - "The transcript contains no Jira, acli, Initiative, Epic, or issue-tracker mention", - "No acli invocation occurs and the spec is written using the normal local workflow" - ] - }, { "id": "remediation-from-review", "prompt": "Take the accepted findings from the last review and turn them into new change sets.", diff --git a/dev/references/jira.md b/dev/references/jira.md deleted file mode 100644 index 9ccda17..0000000 --- a/dev/references/jira.md +++ /dev/null @@ -1,115 +0,0 @@ -# Jira mirror reference - -This reference is used only when the consuming repository opts into Jira in -`.dev/config.json`. The `.dev/` files remain the source of truth; Jira mirrors -the records identified by the keys persisted in those files. - -## Configuration and activation - -Read `.dev/config.json` before doing any Jira work. The supported shape is: - -```json -{ - "jira": { - "enabled": true, - "site": "acme.atlassian.net", - "project": "PROJ" - } -} -``` - -`jira.project` is required when enabled. `jira.site` is optional and should be -passed to `acli` when present. If the file is absent, malformed, Jira is -missing, or `jira.enabled` is false, the workflow is pure-local. In the -disabled case do not mention Jira, ask a Jira question, or invoke `acli`. - -When enabled, check that `acli` is installed and authenticated before the first -operation. A useful check is: - -```text -acli jira auth status [--site SITE] -``` - -If the check fails, stop and ask the user to install or authenticate `acli`, or -disable Jira sync, per the failure protocol below. - -## Command shapes - -The exact flags may vary slightly with the installed `acli` release. Preserve -the intent and verify every created issue with `workitem view`. Treat a -non-zero exit, invalid JSON, missing key, or ambiguous result as a failure. - -List open Initiatives for the configured project: - -```text -acli jira workitem search --project PROJ --type Initiative --status open --json [--site SITE] -``` - -Ask the user to choose one returned Initiative. If none is returned, stop with -a clear message that an existing open Initiative is required; never create one. - -Create the spec Epic linked to the chosen Initiative, then verify it: - -```text -acli jira workitem create --project PROJ --type Epic --summary SUMMARY --parent INITIATIVE-1 --json [--site SITE] -acli jira workitem view EPIC-1 --json [--site SITE] -``` - -Create a task issue for a change set under the Epic, then verify it: - -```text -acli jira workitem create --project PROJ --type Task --summary SUMMARY --parent EPIC-1 --json [--site SITE] -acli jira workitem view TASK-1 --json [--site SITE] -``` - -Discover the project's available transitions once per issue type per run, from -a representative issue, and reuse the names; re-discover only after a failed or -ambiguous transition. Match the displayed names exactly and require one -unambiguous transition for the requested state: - -```text -acli jira workitem transition-list --issue EPIC-1 --json [--site SITE] -acli jira workitem transition --issue EPIC-1 --transition "In Progress" --json [--site SITE] -``` - -Use the same discovery and transition sequence for task issues. - -When a change set is superseded by a spec revision, transition the old issue -to its available closed state and add the required comment: - -```text -acli jira workitem transition-list --issue TASK-1 --json [--site SITE] -acli jira workitem transition --issue TASK-1 --transition "Done" --json [--site SITE] -acli jira workitem comment --issue TASK-1 --body "superseded by change set 3" --json [--site SITE] -``` - -## Persistence and timing - -After the Epic is created, write `Jira: PROJ-1` directly under the spec title -in `spec.md`. After each task issue is created, write its key directly under -the matching change set in the spec's change plan. - -`scope` lists Initiatives during its interview and creates the Epic only -after the change plan is final. It then creates one Task per change set; a -re-run creates Tasks only for change sets that don't carry a key yet. On -supersede, close and comment the old issue before creating and persisting the -replacement issue. - -build transitions the persisted Epic to In Progress at the start. The -orchestrator transitions each persisted change-set issue to In Progress -immediately when its wave is dispatched, and to Done only after the change set -is verified and its commit lands. - -## Failure protocol - -Jira is first-class when enabled. Any failed auth check, command, JSON parse, -verification, missing key, missing Initiative, unknown transition, or -ambiguous transition stops the skill immediately. Explain the failed -operation and ask the user how to proceed. Never silently skip Jira, continue -with divergent local-only state, invent an issue key, or mark a transition -successful without verification. - -The Epic is not polled or closed by the skills. When build creates the work -branch and when ship opens the PR, include the Epic key in the branch name and -at the start of the PR title. A documented Jira automation rule closes the Epic -after the linked PR is merged. diff --git a/dev/references/plan-layout.md b/dev/references/plan-layout.md index 549b2bf..09a9500 100644 --- a/dev/references/plan-layout.md +++ b/dev/references/plan-layout.md @@ -12,7 +12,7 @@ This reference owns the layout, the locating convention, and the diff scope; ski | `implementation-notes.md` | `build` (append-only) | `ship`, `to-pitch`, `to-quiz` | | `review_N.md` | `ship` (next free index, opened when the panel is selected) | `scope` (remediation), re-reviews | | `pr.md` | `ship` (opened at the start of a run that reaches phase 3, overwritten per run) | the PR tool via `--body-file`; re-runs | -| `.dev/config.json` | the user | any skill with Jira behavior | +| `.dev/config.json` | the user | `skill-metrics.py` (run journal opt-out, per [run-journal.md](run-journal.md)) | Each producing skill also renders an HTML companion under `/tmp/{project-slug}/reports/` per [reporting.md](reporting.md), named by that skill. One writer per file; every other skill only reads. diff --git a/dev/skills/build/SKILL.md b/dev/skills/build/SKILL.md index 5755731..759d046 100644 --- a/dev/skills/build/SKILL.md +++ b/dev/skills/build/SKILL.md @@ -24,17 +24,9 @@ Read [references/layers.md](references/layers.md), [references/tests.md](referen 7. Run the repository's required pull-request commands, plus every Validation command the wave gates left for the end, at the branch's impact per [../../references/ci-parity.md](../../references/ci-parity.md) - what lies outside it is deferred to the PR's CI, which `ship` follows to green - starting them in the background as soon as the e2e loop is green and rendering the e2e report while they run - the two share nothing. A known-red CI scenario is not an acceptable deviation. 8. After the e2e report and CI-parity gate, close per "Closing message". Build never pushes or opens a PR; that is `ship`'s phase 3. -## Jira sync +## Work branch -Read `.dev/config.json`; when `jira.enabled` is true, follow -[../../references/jira.md](../../references/jira.md) from before the first `acli` call - it owns the -command shapes, the transition timing, and the failure protocol. The -orchestrator alone invokes `acli`; subagent prompts and the `parallel.md` -contract do not change. - -With an absent or disabled config, perform no Jira behavior or mention. -If still on the default branch, create the work branch before the first commit, -named per jira.md when Jira is enabled; never push it and never open a PR - +If still on the default branch, create the work branch `{plan-name}` before the first commit; never push it and never open a PR - `ship` pushes and opens the PR with evidence after its gauntlet and review. ## Closing message diff --git a/dev/skills/scope/SKILL.md b/dev/skills/scope/SKILL.md index 8026c40..1a05f9b 100644 --- a/dev/skills/scope/SKILL.md +++ b/dev/skills/scope/SKILL.md @@ -132,7 +132,7 @@ Once clean it prints the build waves the file lists allow and the files that mak ## 7. Visualize Full-size changes only. Map the settled spec onto `SPEC_DATA` per [references/data-schema.md](references/data-schema.md) and render [templates/spec.html](templates/spec.html) to `/tmp/{project-slug}/reports/{plan-name}-spec.html`, opening and publishing per [../../references/reporting.md](../../references/reporting.md). -Once the spec is settled, this phase, the phase 8 promotion, and the Jira sync have no ordering between them - overlap them rather than running a march. +Once the spec is settled, this phase and the phase 8 promotion have no ordering between them - overlap them rather than running a march. ## 8. Promote to the ledger @@ -153,11 +153,6 @@ Then print the measured run metrics with `python3 {scope-skill-root}/../../scrip Then recommend the next step, never launching it: `build`, which implements the spec as written and needs no review first. Only when the change is large or complex, add that `scope-review` is worth running before `build` - it reviews the spec with a fresh-context panel and refines it in place before any code exists - and leave that choice to the user. -## Jira sync - -Read `.dev/config.json`; when `jira.enabled` is true, follow [../../references/jira.md](../../references/jira.md) from before the first `acli` call - it owns the command shapes, the Initiative/Epic/Task timing, and the failure protocol. -With an absent or disabled config, no Jira behavior or mention. - ## Starting from review findings When the plan directory ([../../references/plan-layout.md](../../references/plan-layout.md)) holds `review_N.md` or `spec-review_N.md` files, the highest-numbered review's accepted Blockers and Concerns are the interview's opening agenda: each becomes an open decision, and rejected or deferred findings land as `⊘` lines so they are visibly not dropped. diff --git a/dev/skills/ship/references/pull-request.md b/dev/skills/ship/references/pull-request.md index a7feec1..9684083 100644 --- a/dev/skills/ship/references/pull-request.md +++ b/dev/skills/ship/references/pull-request.md @@ -13,7 +13,7 @@ Never force-push, never rebase, and never touch a branch other than the work bra 1. Fixes the gauntlet left uncommitted are committed now, one commit per tool, in `commit`'s `type(scope): subject` plus What/Why shape. Name files explicitly; never `git add -A`, never a `Co-Authored-By` line. -2. On the default branch, create the work branch first: `{plan-name}`, or `{EPIC-KEY}-{plan-name}` when [../../../references/jira.md](../../../references/jira.md) is enabled. +2. On the default branch, create the work branch first: `{plan-name}`. 3. `git push -u {remote} {branch}`; a rejected push is reported and stops the phase. ## Evidence @@ -49,7 +49,7 @@ Only the Evidence section is left for this phase; a part the run has not reached ## Summary {What changed and why, 2-4 sentences from the spec's research and scope sections.} -Plan `.dev/{plan-name}` | Review {N}: {verdict} | Jira {EPIC-KEY when enabled} +Plan `.dev/{plan-name}` | Review {N}: {verdict} ## Evidence @@ -69,7 +69,7 @@ CI parity ({impact verdict and packages}): {each reproducible required command, {Each surviving human call from the gauntlet, each real blocker with its two-round history, and each concern from the review, one line each, or "none".} ``` -Title: `{EPIC-KEY} ` prefix when Jira is enabled, then the change in imperative mood, under 70 characters. +Title: the change in imperative mood, under 70 characters. ## Create or update diff --git a/docs/architecture.md b/docs/architecture.md index 6833c40..c7ccc33 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -54,7 +54,6 @@ Captured: 2026-09-17 (full, scope) - Updated: 2026-10-05 (runs journal their fri | -------- | ---- | -------- | ----- | | Claude Code, Codex, opencode, Pi | host | the skills | each reads the skills in its own format; the generated tree under `plugins/` serves Codex | | git and GitHub | external | the ship skill | branch state and `gh pr` calls made during a ship run | -| Jira | external HTTP | dev skills | through `acli`, only when .dev/config.json enables it | | workflow memory | local store | skill-metrics.py, claims.py | ~/.dev-workflow/memory/ or $DEV_MEMORY_DIR; quotes from transcripts stay on the machine | | consuming repository | store | the skills | `.dev/` plan files, `docs/` ledgers, the .factory/ config and injected phase copies, and the root AGENTS.md bootstrap writes | diff --git a/docs/decisions.md b/docs/decisions.md index b726bcf..9ea0d3c 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -95,7 +95,7 @@ D-invoker-invariant: The repo says skills never invoke each other; does the orch ✓ the orchestrator is the one sanctioned invoker - it launches phase skills by path, and phase skills still never invoke each other or the next phase; `docs/architecture.md` and `CLAUDE.md` state the exception (auto-applied at Confidence: 85%) ✗ keep the invariant literal and make the human launch each phase - that is the dev workflow, not a factory D-unattended-wording-check: How is "no human after scope" enforced in the copies? (2026-09-20, factory-plugin/spec.md) scan root superseded by D-unattended-proof (2026-09-20, config-driven-factory/spec.md), which narrows the guarantee to factory-owned bodies - ✓ a validate.sh check (revived old F03) - it fails any line under `factory/skills/{scope-review,build,ship,run}` (SKILL.md and their references) and `factory/references/factory-run.md` that routes a decision to a person, with `` blocks exempt; the regex self-tests against four sample phrases and a failed self-test fails validation, as the removed F03 already did - P-deterministic-guards-over-prose (auto-applied at Confidence: 85%) ⚠ the other copied references (`ci-parity.md`, `contracts.md`, `jira.md`) keep their dev wording about people because phases read them for notation, not for who decides + ✓ a validate.sh check (revived old F03) - it fails any line under `factory/skills/{scope-review,build,ship,run}` (SKILL.md and their references) and `factory/references/factory-run.md` that routes a decision to a person, with `` blocks exempt; the regex self-tests against four sample phrases and a failed self-test fails validation, as the removed F03 already did - P-deterministic-guards-over-prose (auto-applied at Confidence: 85%) ⚠ the other copied references (`ci-parity.md`, `contracts.md`) keep their dev wording about people because phases read them for notation, not for who decides ✗ prose in the preamble only - a rule in prose softens as the context grows (CLAUDE.md); the check's exit code does not D-protocol-check: How is the result protocol kept present in every phase copy? (2026-09-20, factory-plugin/spec.md) ✓ a validate.sh check (revived old F02) requires each phase SKILL.md to name `factory-run.json` and the result path pattern `results/{phase}-{attempt}.json`, and the `run` SKILL.md to name every phase - a copy that drifts off the protocol fails validation @@ -131,9 +131,6 @@ D-nested-validate: How does the harness run `validate.sh` without recursing into ✓ `check_factory_script` discovers the tests under `factory/evals/tests/` with `test_factory_checks.py` excluded by name, with the reason at the call site - that one file copies the tree and runs the copy's `scripts/validate.sh`, whose ROOT is `dirname $0`, so without the exclusion the gate re-enters itself once per level and never terminates (a minimal reproduction recursed seven levels, one full tree copy each); with it, the copy's gate runs `test_run_state.py` only and the nesting stops at one level ⚠ a second harness-shaped test would have to be excluded too, so the exclusion is one line naming the files that drive `validate.sh` ✗ a guard environment variable the harness exports so the nested `check_factory_script` skips - it also makes change set 5's "an unmutated copy passes all three checks" unassertable, because the third check never runs in the copy ✗ the harness runs a stub instead of the real `scripts/validate.sh` - the thing under test is shell in `validate.sh`, and a stub would only prove the stub -D-jira-source: Can a run start from a Jira ticket? (2026-09-20, factory-plugin/spec.md) - ✗ an `acli` fetch of the ticket into `request.md` - the old `--jira` mode needed runner plumbing, and the dev Jira sync in `.dev/config.json` still applies inside phases - ⊘ not doing - text and file cover the first slice; reopen when a run starts from a ticket more often than from text D-verification: How is the factory itself verified? (2026-09-20, factory-plugin/spec.md) ✓ three levels - validate.sh structural checks and unit tests for the one script, both free; one real end-to-end run per host on a fixture repository, paid and manual, with its procedure and results recorded under `factory/evals/`; races with a user editing during a run and host-side subagent crashes are not testable and are recorded as such; six stated behaviors are knowingly unproven because no scenario, script, or check covers them: the one-writer-per-file invariant, the three-attempt cap, the two-repairs-per-attempt cap, the no-subagent-tool preflight, the `.dev/`-ignored preflight with its `.gitignore` repair (the fixture's `setup.sh` writes the entry, so no free scenario ever reaches the missing-entry branch), and the third-failure report (auto-applied at Confidence: 80%) ⚠ those six stay orchestrator prose by choice: counting them or checking them in validate.sh would put a deterministic gate above the model's judgment, which D-completion-authority rejects as what produced the oscillating build attempts and the parks on codes nobody could act on (user (2026-09-20)); this is the one place the spec sets P-deterministic-guards-over-prose aside, and deliberately: the principle buys a guard where a guard may hold, and a count that refuses a fourth attempt is exactly the gate above the model's judgment this design removes, while the guards the principle does buy here (the two validate.sh checks, `run-state.py`) all sit below it; the run state still records attempts and repairs, so the accepted cost is only that a miscount silently burns a fourth attempt, and runaway attempts are the pattern that cost 18 hours in the old runner ✗ an offline benchmark with stub hosts - the old bench never ran in real mode and proved nothing about parking diff --git a/factory/phases/build/SKILL.md b/factory/phases/build/SKILL.md index 0d89af5..2043917 100644 --- a/factory/phases/build/SKILL.md +++ b/factory/phases/build/SKILL.md @@ -24,17 +24,9 @@ Read [references/layers.md](references/layers.md), [references/tests.md](referen 7. Run the repository's required pull-request commands, plus every Validation command the wave gates left for the end, at the branch's impact per [../../references/ci-parity.md](../../references/ci-parity.md) - what lies outside it is deferred to the PR's CI, which `ship` follows to green - starting them in the background as soon as the e2e loop is green and rendering the e2e report while they run - the two share nothing. A known-red CI scenario is not an acceptable deviation. 8. After the e2e report and CI-parity gate, close per "Closing message". Build never pushes or opens a PR; that is `ship`'s phase 3. -## Jira sync +## Work branch -Read `.dev/config.json`; when `jira.enabled` is true, follow -[../../references/jira.md](../../references/jira.md) from before the first `acli` call - it owns the -command shapes, the transition timing, and the failure protocol. The -orchestrator alone invokes `acli`; subagent prompts and the `parallel.md` -contract do not change. - -With an absent or disabled config, perform no Jira behavior or mention. -If still on the default branch, create the work branch before the first commit, -named per jira.md when Jira is enabled; never push it and never open a PR - +If still on the default branch, create the work branch `{plan-name}` before the first commit; never push it and never open a PR - `ship` pushes and opens the PR with evidence after its gauntlet and review. ## Closing message diff --git a/factory/phases/scope/SKILL.md b/factory/phases/scope/SKILL.md index ed1f7be..97f1479 100644 --- a/factory/phases/scope/SKILL.md +++ b/factory/phases/scope/SKILL.md @@ -127,7 +127,7 @@ Once clean it prints the build waves the file lists allow and the files that mak ## 7. Visualize Full-size changes only. Map the settled spec onto `SPEC_DATA` per [references/data-schema.md](references/data-schema.md) and render [templates/spec.html](templates/spec.html) to `/tmp/{project-slug}/reports/{plan-name}-spec.html`, opening and publishing per [../../references/reporting.md](../../references/reporting.md). -Once the spec is settled, this phase, the phase 8 promotion, and the Jira sync have no ordering between them - overlap them rather than running a march. +Once the spec is settled, this phase and the phase 8 promotion have no ordering between them - overlap them rather than running a march. ## 8. Promote to the ledger @@ -150,11 +150,6 @@ Then print the measured run metrics with `python3 {scope-skill-root}/../../scrip Read `request.md` in the plan directory as the request; the `run` skill already created the directory and named the plan, so use it rather than naming one. This phase keeps its interview - it is the one interactive phase and is exempt from `check_factory_unattended`'s scan - and still ends with one explicit go question. Read `factory-run.json` and the result envelope in [../../references/factory-run.md](../../references/factory-run.md), then write `.dev/{plan}/results/{phase}-{attempt}.json` as the last action. Never name or launch the next phase. -## Jira sync - -Read `.dev/config.json`; when `jira.enabled` is true, follow [../../references/jira.md](../../references/jira.md) from before the first `acli` call - it owns the command shapes, the Initiative/Epic/Task timing, and the failure protocol. -With an absent or disabled config, no Jira behavior or mention. - ## Starting from review findings When the plan directory ([../../references/plan-layout.md](../../references/plan-layout.md)) holds `review_N.md` or `spec-review_N.md` files, the highest-numbered review's accepted Blockers and Concerns are the interview's opening agenda: each becomes an open decision, and rejected or deferred findings land as `⊘` lines so they are visibly not dropped. diff --git a/factory/phases/ship/references/pull-request.md b/factory/phases/ship/references/pull-request.md index a65127b..ae1dba5 100644 --- a/factory/phases/ship/references/pull-request.md +++ b/factory/phases/ship/references/pull-request.md @@ -13,7 +13,7 @@ Never force-push, never rebase, and never touch a branch other than the work bra 1. Fixes the gauntlet left uncommitted are committed now, one commit per tool, in `commit`'s `type(scope): subject` plus What/Why shape. Name files explicitly; never `git add -A`, never a `Co-Authored-By` line. -2. On the default branch, create the work branch first: `{plan-name}`, or `{EPIC-KEY}-{plan-name}` when [../../../references/jira.md](../../../references/jira.md) is enabled. +2. On the default branch, create the work branch first: `{plan-name}`. 3. `git push -u {remote} {branch}`; a rejected push is reported and stops the phase. ## Evidence @@ -43,7 +43,7 @@ Write the body to `.dev/{plan-name}/pr.md` and hand it to the PR tool with `--bo ## Summary {What changed and why, 2-4 sentences from the spec's research and scope sections.} -Plan `.dev/{plan-name}` | Review {N}: {verdict} | Jira {EPIC-KEY when enabled} +Plan `.dev/{plan-name}` | Review {N}: {verdict} ## Evidence @@ -63,7 +63,7 @@ CI parity ({impact verdict and packages}): {each reproducible required command, {Each surviving recorded decision from the gauntlet, each real blocker with its two-round history, and each concern from the review, one line each, or "none".} ``` -Title: `{EPIC-KEY} ` prefix when Jira is enabled, then the change in imperative mood, under 70 characters. +Title: the change in imperative mood, under 70 characters. ## Create or update diff --git a/factory/references/jira.md b/factory/references/jira.md deleted file mode 100644 index 9ccda17..0000000 --- a/factory/references/jira.md +++ /dev/null @@ -1,115 +0,0 @@ -# Jira mirror reference - -This reference is used only when the consuming repository opts into Jira in -`.dev/config.json`. The `.dev/` files remain the source of truth; Jira mirrors -the records identified by the keys persisted in those files. - -## Configuration and activation - -Read `.dev/config.json` before doing any Jira work. The supported shape is: - -```json -{ - "jira": { - "enabled": true, - "site": "acme.atlassian.net", - "project": "PROJ" - } -} -``` - -`jira.project` is required when enabled. `jira.site` is optional and should be -passed to `acli` when present. If the file is absent, malformed, Jira is -missing, or `jira.enabled` is false, the workflow is pure-local. In the -disabled case do not mention Jira, ask a Jira question, or invoke `acli`. - -When enabled, check that `acli` is installed and authenticated before the first -operation. A useful check is: - -```text -acli jira auth status [--site SITE] -``` - -If the check fails, stop and ask the user to install or authenticate `acli`, or -disable Jira sync, per the failure protocol below. - -## Command shapes - -The exact flags may vary slightly with the installed `acli` release. Preserve -the intent and verify every created issue with `workitem view`. Treat a -non-zero exit, invalid JSON, missing key, or ambiguous result as a failure. - -List open Initiatives for the configured project: - -```text -acli jira workitem search --project PROJ --type Initiative --status open --json [--site SITE] -``` - -Ask the user to choose one returned Initiative. If none is returned, stop with -a clear message that an existing open Initiative is required; never create one. - -Create the spec Epic linked to the chosen Initiative, then verify it: - -```text -acli jira workitem create --project PROJ --type Epic --summary SUMMARY --parent INITIATIVE-1 --json [--site SITE] -acli jira workitem view EPIC-1 --json [--site SITE] -``` - -Create a task issue for a change set under the Epic, then verify it: - -```text -acli jira workitem create --project PROJ --type Task --summary SUMMARY --parent EPIC-1 --json [--site SITE] -acli jira workitem view TASK-1 --json [--site SITE] -``` - -Discover the project's available transitions once per issue type per run, from -a representative issue, and reuse the names; re-discover only after a failed or -ambiguous transition. Match the displayed names exactly and require one -unambiguous transition for the requested state: - -```text -acli jira workitem transition-list --issue EPIC-1 --json [--site SITE] -acli jira workitem transition --issue EPIC-1 --transition "In Progress" --json [--site SITE] -``` - -Use the same discovery and transition sequence for task issues. - -When a change set is superseded by a spec revision, transition the old issue -to its available closed state and add the required comment: - -```text -acli jira workitem transition-list --issue TASK-1 --json [--site SITE] -acli jira workitem transition --issue TASK-1 --transition "Done" --json [--site SITE] -acli jira workitem comment --issue TASK-1 --body "superseded by change set 3" --json [--site SITE] -``` - -## Persistence and timing - -After the Epic is created, write `Jira: PROJ-1` directly under the spec title -in `spec.md`. After each task issue is created, write its key directly under -the matching change set in the spec's change plan. - -`scope` lists Initiatives during its interview and creates the Epic only -after the change plan is final. It then creates one Task per change set; a -re-run creates Tasks only for change sets that don't carry a key yet. On -supersede, close and comment the old issue before creating and persisting the -replacement issue. - -build transitions the persisted Epic to In Progress at the start. The -orchestrator transitions each persisted change-set issue to In Progress -immediately when its wave is dispatched, and to Done only after the change set -is verified and its commit lands. - -## Failure protocol - -Jira is first-class when enabled. Any failed auth check, command, JSON parse, -verification, missing key, missing Initiative, unknown transition, or -ambiguous transition stops the skill immediately. Explain the failed -operation and ask the user how to proceed. Never silently skip Jira, continue -with divergent local-only state, invent an issue key, or mark a transition -successful without verification. - -The Epic is not polled or closed by the skills. When build creates the work -branch and when ship opens the PR, include the Epic key in the branch name and -at the start of the PR title. A documented Jira automation rule closes the Epic -after the linked PR is merged. diff --git a/factory/references/plan-layout.md b/factory/references/plan-layout.md index f1bb69d..94e6c80 100644 --- a/factory/references/plan-layout.md +++ b/factory/references/plan-layout.md @@ -15,7 +15,7 @@ This reference owns the layout, the locating convention, and the diff scope; ski | `pr.md` | `ship` (phase 3, overwritten per run) | the PR tool via `--body-file`; re-runs | | `factory-run.json` | the `run` skill only | the `run` skill's judgment loop | | `results/{phase}-{attempt}.json` | the phase skill, as its last action | the `run` skill's judgment loop | -| `.dev/config.json` | the user | any skill with Jira behavior | +| `.dev/config.json` | the user | `skill-metrics.py` (run journal opt-out, per [run-journal.md](run-journal.md)) | Each producing skill also renders an HTML companion under `/tmp/{project-slug}/reports/` per [reporting.md](reporting.md), named by that skill. One writer per file; every other skill only reads. diff --git a/plugins/dev/references/jira.md b/plugins/dev/references/jira.md deleted file mode 100644 index 9ccda17..0000000 --- a/plugins/dev/references/jira.md +++ /dev/null @@ -1,115 +0,0 @@ -# Jira mirror reference - -This reference is used only when the consuming repository opts into Jira in -`.dev/config.json`. The `.dev/` files remain the source of truth; Jira mirrors -the records identified by the keys persisted in those files. - -## Configuration and activation - -Read `.dev/config.json` before doing any Jira work. The supported shape is: - -```json -{ - "jira": { - "enabled": true, - "site": "acme.atlassian.net", - "project": "PROJ" - } -} -``` - -`jira.project` is required when enabled. `jira.site` is optional and should be -passed to `acli` when present. If the file is absent, malformed, Jira is -missing, or `jira.enabled` is false, the workflow is pure-local. In the -disabled case do not mention Jira, ask a Jira question, or invoke `acli`. - -When enabled, check that `acli` is installed and authenticated before the first -operation. A useful check is: - -```text -acli jira auth status [--site SITE] -``` - -If the check fails, stop and ask the user to install or authenticate `acli`, or -disable Jira sync, per the failure protocol below. - -## Command shapes - -The exact flags may vary slightly with the installed `acli` release. Preserve -the intent and verify every created issue with `workitem view`. Treat a -non-zero exit, invalid JSON, missing key, or ambiguous result as a failure. - -List open Initiatives for the configured project: - -```text -acli jira workitem search --project PROJ --type Initiative --status open --json [--site SITE] -``` - -Ask the user to choose one returned Initiative. If none is returned, stop with -a clear message that an existing open Initiative is required; never create one. - -Create the spec Epic linked to the chosen Initiative, then verify it: - -```text -acli jira workitem create --project PROJ --type Epic --summary SUMMARY --parent INITIATIVE-1 --json [--site SITE] -acli jira workitem view EPIC-1 --json [--site SITE] -``` - -Create a task issue for a change set under the Epic, then verify it: - -```text -acli jira workitem create --project PROJ --type Task --summary SUMMARY --parent EPIC-1 --json [--site SITE] -acli jira workitem view TASK-1 --json [--site SITE] -``` - -Discover the project's available transitions once per issue type per run, from -a representative issue, and reuse the names; re-discover only after a failed or -ambiguous transition. Match the displayed names exactly and require one -unambiguous transition for the requested state: - -```text -acli jira workitem transition-list --issue EPIC-1 --json [--site SITE] -acli jira workitem transition --issue EPIC-1 --transition "In Progress" --json [--site SITE] -``` - -Use the same discovery and transition sequence for task issues. - -When a change set is superseded by a spec revision, transition the old issue -to its available closed state and add the required comment: - -```text -acli jira workitem transition-list --issue TASK-1 --json [--site SITE] -acli jira workitem transition --issue TASK-1 --transition "Done" --json [--site SITE] -acli jira workitem comment --issue TASK-1 --body "superseded by change set 3" --json [--site SITE] -``` - -## Persistence and timing - -After the Epic is created, write `Jira: PROJ-1` directly under the spec title -in `spec.md`. After each task issue is created, write its key directly under -the matching change set in the spec's change plan. - -`scope` lists Initiatives during its interview and creates the Epic only -after the change plan is final. It then creates one Task per change set; a -re-run creates Tasks only for change sets that don't carry a key yet. On -supersede, close and comment the old issue before creating and persisting the -replacement issue. - -build transitions the persisted Epic to In Progress at the start. The -orchestrator transitions each persisted change-set issue to In Progress -immediately when its wave is dispatched, and to Done only after the change set -is verified and its commit lands. - -## Failure protocol - -Jira is first-class when enabled. Any failed auth check, command, JSON parse, -verification, missing key, missing Initiative, unknown transition, or -ambiguous transition stops the skill immediately. Explain the failed -operation and ask the user how to proceed. Never silently skip Jira, continue -with divergent local-only state, invent an issue key, or mark a transition -successful without verification. - -The Epic is not polled or closed by the skills. When build creates the work -branch and when ship opens the PR, include the Epic key in the branch name and -at the start of the PR title. A documented Jira automation rule closes the Epic -after the linked PR is merged. diff --git a/plugins/dev/references/plan-layout.md b/plugins/dev/references/plan-layout.md index 549b2bf..09a9500 100644 --- a/plugins/dev/references/plan-layout.md +++ b/plugins/dev/references/plan-layout.md @@ -12,7 +12,7 @@ This reference owns the layout, the locating convention, and the diff scope; ski | `implementation-notes.md` | `build` (append-only) | `ship`, `to-pitch`, `to-quiz` | | `review_N.md` | `ship` (next free index, opened when the panel is selected) | `scope` (remediation), re-reviews | | `pr.md` | `ship` (opened at the start of a run that reaches phase 3, overwritten per run) | the PR tool via `--body-file`; re-runs | -| `.dev/config.json` | the user | any skill with Jira behavior | +| `.dev/config.json` | the user | `skill-metrics.py` (run journal opt-out, per [run-journal.md](run-journal.md)) | Each producing skill also renders an HTML companion under `/tmp/{project-slug}/reports/` per [reporting.md](reporting.md), named by that skill. One writer per file; every other skill only reads. diff --git a/plugins/dev/skills/build/SKILL.md b/plugins/dev/skills/build/SKILL.md index 1653089..d165491 100644 --- a/plugins/dev/skills/build/SKILL.md +++ b/plugins/dev/skills/build/SKILL.md @@ -23,17 +23,9 @@ Read [references/layers.md](references/layers.md), [references/tests.md](referen 7. Run the repository's required pull-request commands, plus every Validation command the wave gates left for the end, at the branch's impact per [../../references/ci-parity.md](../../references/ci-parity.md) - what lies outside it is deferred to the PR's CI, which `ship` follows to green - starting them in the background as soon as the e2e loop is green and rendering the e2e report while they run - the two share nothing. A known-red CI scenario is not an acceptable deviation. 8. After the e2e report and CI-parity gate, close per "Closing message". Build never pushes or opens a PR; that is `ship`'s phase 3. -## Jira sync +## Work branch -Read `.dev/config.json`; when `jira.enabled` is true, follow -[../../references/jira.md](../../references/jira.md) from before the first `acli` call - it owns the -command shapes, the transition timing, and the failure protocol. The -orchestrator alone invokes `acli`; subagent prompts and the `parallel.md` -contract do not change. - -With an absent or disabled config, perform no Jira behavior or mention. -If still on the default branch, create the work branch before the first commit, -named per jira.md when Jira is enabled; never push it and never open a PR - +If still on the default branch, create the work branch `{plan-name}` before the first commit; never push it and never open a PR - `ship` pushes and opens the PR with evidence after its gauntlet and review. ## Closing message diff --git a/plugins/dev/skills/scope/SKILL.md b/plugins/dev/skills/scope/SKILL.md index b75d6b5..a443533 100644 --- a/plugins/dev/skills/scope/SKILL.md +++ b/plugins/dev/skills/scope/SKILL.md @@ -131,7 +131,7 @@ Once clean it prints the build waves the file lists allow and the files that mak ## 7. Visualize Full-size changes only. Map the settled spec onto `SPEC_DATA` per [references/data-schema.md](references/data-schema.md) and render [templates/spec.html](templates/spec.html) to `/tmp/{project-slug}/reports/{plan-name}-spec.html`, opening and publishing per [../../references/reporting.md](../../references/reporting.md). -Once the spec is settled, this phase, the phase 8 promotion, and the Jira sync have no ordering between them - overlap them rather than running a march. +Once the spec is settled, this phase and the phase 8 promotion have no ordering between them - overlap them rather than running a march. ## 8. Promote to the ledger @@ -152,11 +152,6 @@ Then print the measured run metrics with `python3 {scope-skill-root}/../../scrip Then recommend the next step, never launching it: `build`, which implements the spec as written and needs no review first. Only when the change is large or complex, add that `scope-review` is worth running before `build` - it reviews the spec with a fresh-context panel and refines it in place before any code exists - and leave that choice to the user. -## Jira sync - -Read `.dev/config.json`; when `jira.enabled` is true, follow [../../references/jira.md](../../references/jira.md) from before the first `acli` call - it owns the command shapes, the Initiative/Epic/Task timing, and the failure protocol. -With an absent or disabled config, no Jira behavior or mention. - ## Starting from review findings When the plan directory ([../../references/plan-layout.md](../../references/plan-layout.md)) holds `review_N.md` or `spec-review_N.md` files, the highest-numbered review's accepted Blockers and Concerns are the interview's opening agenda: each becomes an open decision, and rejected or deferred findings land as `⊘` lines so they are visibly not dropped. diff --git a/plugins/dev/skills/ship/references/pull-request.md b/plugins/dev/skills/ship/references/pull-request.md index a7feec1..9684083 100644 --- a/plugins/dev/skills/ship/references/pull-request.md +++ b/plugins/dev/skills/ship/references/pull-request.md @@ -13,7 +13,7 @@ Never force-push, never rebase, and never touch a branch other than the work bra 1. Fixes the gauntlet left uncommitted are committed now, one commit per tool, in `commit`'s `type(scope): subject` plus What/Why shape. Name files explicitly; never `git add -A`, never a `Co-Authored-By` line. -2. On the default branch, create the work branch first: `{plan-name}`, or `{EPIC-KEY}-{plan-name}` when [../../../references/jira.md](../../../references/jira.md) is enabled. +2. On the default branch, create the work branch first: `{plan-name}`. 3. `git push -u {remote} {branch}`; a rejected push is reported and stops the phase. ## Evidence @@ -49,7 +49,7 @@ Only the Evidence section is left for this phase; a part the run has not reached ## Summary {What changed and why, 2-4 sentences from the spec's research and scope sections.} -Plan `.dev/{plan-name}` | Review {N}: {verdict} | Jira {EPIC-KEY when enabled} +Plan `.dev/{plan-name}` | Review {N}: {verdict} ## Evidence @@ -69,7 +69,7 @@ CI parity ({impact verdict and packages}): {each reproducible required command, {Each surviving human call from the gauntlet, each real blocker with its two-round history, and each concern from the review, one line each, or "none".} ``` -Title: `{EPIC-KEY} ` prefix when Jira is enabled, then the change in imperative mood, under 70 characters. +Title: the change in imperative mood, under 70 characters. ## Create or update diff --git a/plugins/factory/phases/build/SKILL.md b/plugins/factory/phases/build/SKILL.md index 080ae0e..bc4c4d8 100644 --- a/plugins/factory/phases/build/SKILL.md +++ b/plugins/factory/phases/build/SKILL.md @@ -23,17 +23,9 @@ Read [references/layers.md](references/layers.md), [references/tests.md](referen 7. Run the repository's required pull-request commands, plus every Validation command the wave gates left for the end, at the branch's impact per [../../references/ci-parity.md](../../references/ci-parity.md) - what lies outside it is deferred to the PR's CI, which `ship` follows to green - starting them in the background as soon as the e2e loop is green and rendering the e2e report while they run - the two share nothing. A known-red CI scenario is not an acceptable deviation. 8. After the e2e report and CI-parity gate, close per "Closing message". Build never pushes or opens a PR; that is `ship`'s phase 3. -## Jira sync +## Work branch -Read `.dev/config.json`; when `jira.enabled` is true, follow -[../../references/jira.md](../../references/jira.md) from before the first `acli` call - it owns the -command shapes, the transition timing, and the failure protocol. The -orchestrator alone invokes `acli`; subagent prompts and the `parallel.md` -contract do not change. - -With an absent or disabled config, perform no Jira behavior or mention. -If still on the default branch, create the work branch before the first commit, -named per jira.md when Jira is enabled; never push it and never open a PR - +If still on the default branch, create the work branch `{plan-name}` before the first commit; never push it and never open a PR - `ship` pushes and opens the PR with evidence after its gauntlet and review. ## Closing message diff --git a/plugins/factory/phases/scope/SKILL.md b/plugins/factory/phases/scope/SKILL.md index 5dd6c6c..f10a199 100644 --- a/plugins/factory/phases/scope/SKILL.md +++ b/plugins/factory/phases/scope/SKILL.md @@ -126,7 +126,7 @@ Once clean it prints the build waves the file lists allow and the files that mak ## 7. Visualize Full-size changes only. Map the settled spec onto `SPEC_DATA` per [references/data-schema.md](references/data-schema.md) and render [templates/spec.html](templates/spec.html) to `/tmp/{project-slug}/reports/{plan-name}-spec.html`, opening and publishing per [../../references/reporting.md](../../references/reporting.md). -Once the spec is settled, this phase, the phase 8 promotion, and the Jira sync have no ordering between them - overlap them rather than running a march. +Once the spec is settled, this phase and the phase 8 promotion have no ordering between them - overlap them rather than running a march. ## 8. Promote to the ledger @@ -149,11 +149,6 @@ Then print the measured run metrics with `python3 {scope-skill-root}/../../scrip Read `request.md` in the plan directory as the request; the `run` skill already created the directory and named the plan, so use it rather than naming one. This phase keeps its interview - it is the one interactive phase and is exempt from `check_factory_unattended`'s scan - and still ends with one explicit go question. Read `factory-run.json` and the result envelope in [../../references/factory-run.md](../../references/factory-run.md), then write `.dev/{plan}/results/{phase}-{attempt}.json` as the last action. Never name or launch the next phase. -## Jira sync - -Read `.dev/config.json`; when `jira.enabled` is true, follow [../../references/jira.md](../../references/jira.md) from before the first `acli` call - it owns the command shapes, the Initiative/Epic/Task timing, and the failure protocol. -With an absent or disabled config, no Jira behavior or mention. - ## Starting from review findings When the plan directory ([../../references/plan-layout.md](../../references/plan-layout.md)) holds `review_N.md` or `spec-review_N.md` files, the highest-numbered review's accepted Blockers and Concerns are the interview's opening agenda: each becomes an open decision, and rejected or deferred findings land as `⊘` lines so they are visibly not dropped. diff --git a/plugins/factory/phases/ship/references/pull-request.md b/plugins/factory/phases/ship/references/pull-request.md index a65127b..ae1dba5 100644 --- a/plugins/factory/phases/ship/references/pull-request.md +++ b/plugins/factory/phases/ship/references/pull-request.md @@ -13,7 +13,7 @@ Never force-push, never rebase, and never touch a branch other than the work bra 1. Fixes the gauntlet left uncommitted are committed now, one commit per tool, in `commit`'s `type(scope): subject` plus What/Why shape. Name files explicitly; never `git add -A`, never a `Co-Authored-By` line. -2. On the default branch, create the work branch first: `{plan-name}`, or `{EPIC-KEY}-{plan-name}` when [../../../references/jira.md](../../../references/jira.md) is enabled. +2. On the default branch, create the work branch first: `{plan-name}`. 3. `git push -u {remote} {branch}`; a rejected push is reported and stops the phase. ## Evidence @@ -43,7 +43,7 @@ Write the body to `.dev/{plan-name}/pr.md` and hand it to the PR tool with `--bo ## Summary {What changed and why, 2-4 sentences from the spec's research and scope sections.} -Plan `.dev/{plan-name}` | Review {N}: {verdict} | Jira {EPIC-KEY when enabled} +Plan `.dev/{plan-name}` | Review {N}: {verdict} ## Evidence @@ -63,7 +63,7 @@ CI parity ({impact verdict and packages}): {each reproducible required command, {Each surviving recorded decision from the gauntlet, each real blocker with its two-round history, and each concern from the review, one line each, or "none".} ``` -Title: `{EPIC-KEY} ` prefix when Jira is enabled, then the change in imperative mood, under 70 characters. +Title: the change in imperative mood, under 70 characters. ## Create or update diff --git a/plugins/factory/references/jira.md b/plugins/factory/references/jira.md deleted file mode 100644 index 9ccda17..0000000 --- a/plugins/factory/references/jira.md +++ /dev/null @@ -1,115 +0,0 @@ -# Jira mirror reference - -This reference is used only when the consuming repository opts into Jira in -`.dev/config.json`. The `.dev/` files remain the source of truth; Jira mirrors -the records identified by the keys persisted in those files. - -## Configuration and activation - -Read `.dev/config.json` before doing any Jira work. The supported shape is: - -```json -{ - "jira": { - "enabled": true, - "site": "acme.atlassian.net", - "project": "PROJ" - } -} -``` - -`jira.project` is required when enabled. `jira.site` is optional and should be -passed to `acli` when present. If the file is absent, malformed, Jira is -missing, or `jira.enabled` is false, the workflow is pure-local. In the -disabled case do not mention Jira, ask a Jira question, or invoke `acli`. - -When enabled, check that `acli` is installed and authenticated before the first -operation. A useful check is: - -```text -acli jira auth status [--site SITE] -``` - -If the check fails, stop and ask the user to install or authenticate `acli`, or -disable Jira sync, per the failure protocol below. - -## Command shapes - -The exact flags may vary slightly with the installed `acli` release. Preserve -the intent and verify every created issue with `workitem view`. Treat a -non-zero exit, invalid JSON, missing key, or ambiguous result as a failure. - -List open Initiatives for the configured project: - -```text -acli jira workitem search --project PROJ --type Initiative --status open --json [--site SITE] -``` - -Ask the user to choose one returned Initiative. If none is returned, stop with -a clear message that an existing open Initiative is required; never create one. - -Create the spec Epic linked to the chosen Initiative, then verify it: - -```text -acli jira workitem create --project PROJ --type Epic --summary SUMMARY --parent INITIATIVE-1 --json [--site SITE] -acli jira workitem view EPIC-1 --json [--site SITE] -``` - -Create a task issue for a change set under the Epic, then verify it: - -```text -acli jira workitem create --project PROJ --type Task --summary SUMMARY --parent EPIC-1 --json [--site SITE] -acli jira workitem view TASK-1 --json [--site SITE] -``` - -Discover the project's available transitions once per issue type per run, from -a representative issue, and reuse the names; re-discover only after a failed or -ambiguous transition. Match the displayed names exactly and require one -unambiguous transition for the requested state: - -```text -acli jira workitem transition-list --issue EPIC-1 --json [--site SITE] -acli jira workitem transition --issue EPIC-1 --transition "In Progress" --json [--site SITE] -``` - -Use the same discovery and transition sequence for task issues. - -When a change set is superseded by a spec revision, transition the old issue -to its available closed state and add the required comment: - -```text -acli jira workitem transition-list --issue TASK-1 --json [--site SITE] -acli jira workitem transition --issue TASK-1 --transition "Done" --json [--site SITE] -acli jira workitem comment --issue TASK-1 --body "superseded by change set 3" --json [--site SITE] -``` - -## Persistence and timing - -After the Epic is created, write `Jira: PROJ-1` directly under the spec title -in `spec.md`. After each task issue is created, write its key directly under -the matching change set in the spec's change plan. - -`scope` lists Initiatives during its interview and creates the Epic only -after the change plan is final. It then creates one Task per change set; a -re-run creates Tasks only for change sets that don't carry a key yet. On -supersede, close and comment the old issue before creating and persisting the -replacement issue. - -build transitions the persisted Epic to In Progress at the start. The -orchestrator transitions each persisted change-set issue to In Progress -immediately when its wave is dispatched, and to Done only after the change set -is verified and its commit lands. - -## Failure protocol - -Jira is first-class when enabled. Any failed auth check, command, JSON parse, -verification, missing key, missing Initiative, unknown transition, or -ambiguous transition stops the skill immediately. Explain the failed -operation and ask the user how to proceed. Never silently skip Jira, continue -with divergent local-only state, invent an issue key, or mark a transition -successful without verification. - -The Epic is not polled or closed by the skills. When build creates the work -branch and when ship opens the PR, include the Epic key in the branch name and -at the start of the PR title. A documented Jira automation rule closes the Epic -after the linked PR is merged. diff --git a/plugins/factory/references/plan-layout.md b/plugins/factory/references/plan-layout.md index f1bb69d..94e6c80 100644 --- a/plugins/factory/references/plan-layout.md +++ b/plugins/factory/references/plan-layout.md @@ -15,7 +15,7 @@ This reference owns the layout, the locating convention, and the diff scope; ski | `pr.md` | `ship` (phase 3, overwritten per run) | the PR tool via `--body-file`; re-runs | | `factory-run.json` | the `run` skill only | the `run` skill's judgment loop | | `results/{phase}-{attempt}.json` | the phase skill, as its last action | the `run` skill's judgment loop | -| `.dev/config.json` | the user | any skill with Jira behavior | +| `.dev/config.json` | the user | `skill-metrics.py` (run journal opt-out, per [run-journal.md](run-journal.md)) | Each producing skill also renders an HTML companion under `/tmp/{project-slug}/reports/` per [reporting.md](reporting.md), named by that skill. One writer per file; every other skill only reads. diff --git a/scripts/validate.sh b/scripts/validate.sh index 8d7484c..620fc1b 100755 --- a/scripts/validate.sh +++ b/scripts/validate.sh @@ -633,7 +633,7 @@ check_pi() { # # Scoped to factory/phases/* minus the built-in pipeline's interactive phases, # plus factory/skills/run (SKILL.md and their references) and -# factory/references/factory-run.md only - scope keeps its interview, and ci-parity.md, contracts.md, and jira.md keep their dev +# factory/references/factory-run.md only - scope keeps its interview, and ci-parity.md and contracts.md keep their dev # wording because they are dev-shared references whose human-call branches are # overridden for a factory run by factory-run.md's unattended policy. #