From 7c4e500a35d48b5dba311094b4d34d8ca97f25a1 Mon Sep 17 00:00:00 2001 From: Scott Haug Date: Mon, 20 Jul 2026 02:45:07 -0700 Subject: [PATCH] feat: compose epic execution through implement-ticket ## Summary - Replace `implement-epic-sequence` with the canonical `implement-epic` skill - Delegate one-ticket execution and review mechanics to `implement-ticket` - Add focused graph, result, authority, closeout, and forward-evaluation contracts ## Why - Keep epic orchestration focused on live dependency selection and refresh - Establish one canonical owner for ticket implementation, review, merge, and cleanup - Preserve scope and authority boundaries while allowing future safe parallel execution --- CHANGELOG.md | 5 + README.md | 10 +- justfile | 3 + skills/implement-epic-sequence/SKILL.md | 306 ------------------ .../agents/openai.yaml | 4 - .../implement-epic-sequence/evals/cases.json | 150 --------- .../evals/results.json | 44 --- .../references/closeout-and-cleanup.md | 100 ------ .../references/github.md | 203 ------------ .../references/linear.md | 54 ---- .../references/review-and-merge-gates.md | 122 ------- .../tests/test_review_integration_contract.py | 120 ------- skills/implement-epic/SKILL.md | 182 +++++++++++ skills/implement-epic/agents/openai.yaml | 4 + skills/implement-epic/evals/cases.json | 78 +++++ skills/implement-epic/evals/results.json | 67 ++++ skills/implement-epic/references/closeout.md | 55 ++++ skills/implement-epic/references/github.md | 49 +++ skills/implement-epic/references/linear.md | 43 +++ .../tests/test_orchestration_contract.py | 164 ++++++++++ skills/implement-ticket/SKILL.md | 6 +- .../tests/test_implement_ticket_contract.py | 3 +- 22 files changed, 660 insertions(+), 1112 deletions(-) delete mode 100644 skills/implement-epic-sequence/SKILL.md delete mode 100644 skills/implement-epic-sequence/agents/openai.yaml delete mode 100644 skills/implement-epic-sequence/evals/cases.json delete mode 100644 skills/implement-epic-sequence/evals/results.json delete mode 100644 skills/implement-epic-sequence/references/closeout-and-cleanup.md delete mode 100644 skills/implement-epic-sequence/references/github.md delete mode 100644 skills/implement-epic-sequence/references/linear.md delete mode 100644 skills/implement-epic-sequence/references/review-and-merge-gates.md delete mode 100644 skills/implement-epic-sequence/scripts/tests/test_review_integration_contract.py create mode 100644 skills/implement-epic/SKILL.md create mode 100644 skills/implement-epic/agents/openai.yaml create mode 100644 skills/implement-epic/evals/cases.json create mode 100644 skills/implement-epic/evals/results.json create mode 100644 skills/implement-epic/references/closeout.md create mode 100644 skills/implement-epic/references/github.md create mode 100644 skills/implement-epic/references/linear.md create mode 100644 skills/implement-epic/scripts/tests/test_orchestration_contract.py diff --git a/CHANGELOG.md b/CHANGELOG.md index afc36b0..a8e914c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,14 @@ summary: Chronological history of repository and skill changes. # Changelog +## 2026-07-20 — Composed ticket and epic execution + +- feat: compose epic execution through implement-ticket + ## 2026-07-19 — Epic workflow and review contract cleanup - feat: add standalone ticket implementation skill + (7113afd5ab04d0200c2bfa6b5008d9fcd2b2f7f6) - feat: integrate repository-owned review into epic execution (28c3945b3db8f84a812cd2e498d54a6912bcd934) - feat: compose repository-owned code review diff --git a/README.md b/README.md index 259706c..349e177 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,12 @@ Current skills: - `skills/implement-ticket` — implement exactly one standalone ticket or named epic child through isolated execution, repository-owned review, PR gates, and authorized merge and cleanup; this is the canonical owner of generic - single-ticket execution rules, with duplication in `implement-epic-sequence` - temporary until its dependent orchestrator refactor + single-ticket execution rules consumed by `implement-epic` +- `skills/implement-epic` — traverse live GitHub or Linear epic graphs and + delegate each selected child to `implement-ticket`, then refresh graph state + and verify separately authorized epic closeout - `skills/review-code-change` — orchestrate the repository-owned review lenses into one evidence-bound, deduplicated verdict -- `skills/implement-epic-sequence` — execute GitHub or Linear epics through - dependency-aware implementation, repository-owned review, remote gates, merge, - cleanup, and closeout - `skills/prepare-changesets` — decompose a large, review-ready branch into a deterministic chain of smaller, reviewable changesets and GitHub PRs - `skills/review-correctness` — find material behavioral, security, @@ -44,6 +43,7 @@ Run skill-specific tests: just test-prepare-changesets just test-review-suite just test-implement-ticket +just test-implement-epic ``` Validate a review packet and result together: diff --git a/justfile b/justfile index a7568b3..bdb257d 100644 --- a/justfile +++ b/justfile @@ -31,6 +31,9 @@ test-review-suite: test-implement-ticket: python3 -m unittest discover -s {{skills_dir}}/implement-ticket/scripts/tests -p 'test_*.py' +test-implement-epic: + python3 -m unittest discover -s {{skills_dir}}/implement-epic/scripts/tests -p 'test_*.py' + test-prepare-changesets: python3 -m unittest discover -s {{skills_dir}}/prepare-changesets/scripts/tests -p 'test_*.py' diff --git a/skills/implement-epic-sequence/SKILL.md b/skills/implement-epic-sequence/SKILL.md deleted file mode 100644 index 012e528..0000000 --- a/skills/implement-epic-sequence/SKILL.md +++ /dev/null @@ -1,306 +0,0 @@ ---- -name: implement-epic-sequence -description: Execute one or more GitHub or Linear epics end to end through their live dependency graphs. Use when Codex should select unblocked PR-sized children, implement each in an isolated branch or worktree, run repository validation and bounded review loops, wait for current-head review and CI gates, merge and clean up safely, refresh the graph after every merge, and close epics only after resulting behavior and acceptance criteria are verified. ---- - -# Implement Epic Sequence - -Execute the live work graph, one independently mergeable child at a time. Treat -tracker state and repository evidence as the source of truth; do not turn an old -plan or conversation summary into execution state. - -## Load the relevant references - -- Resolve the issue tracker and the code/PR host independently. -- Read `references/github.md` whenever GitHub owns issue state or hosts the - repository and PRs. This includes Linear-owned epics implemented through - GitHub PRs. -- Read `references/linear.md` whenever Linear owns parent, child, dependency, or - status state. -- Always read `references/review-and-merge-gates.md` before publishing the first - PR in a run. -- Always read `references/closeout-and-cleanup.md` before merging the first PR - or closing an epic. - -For cross-system work, load every applicable adapter and record which system -owns issue status, dependency state, source code, PR state, checks, review, and -merge. Do not assume the issue tracker also hosts the PR. - -## Resolve the operating contract - -Before changing code, resolve: - -- repository and tracker; -- epic identifiers and any named child subset; -- base branch; -- completion policy: ready PRs only, merge after gates, or merge plus epic - closeout; -- execution mode: serial by default, parallel only when explicitly requested; -- required review mechanism; -- mutation authority for tracker edits, status or dependency changes, follow-up - creation, review replies and resolution, merge, parent closeout, remote-branch - deletion, and deployment; -- named architecture, design, contract, or rollout documents. - -For a merge-inclusive run, verify before implementation that -`review-code-change` is available and readable. It is the single local -adversarial-review dependency. If it is unavailable, stop with an explicit -missing-dependency result; do not substitute a third-party skill, a generic -reviewer, or an unreviewed merge path. - -Unless the user or repository contract states otherwise, apply this authority -matrix: - -- `ready PRs only` authorizes isolated implementation, commits, feature-branch - pushes, PR creation or updates, evidence-based review replies, and resolution - of fully addressed review threads; -- `merge after gates` additionally authorizes merging the in-scope PR and safely - deleting its verified merged feature branch; -- `merge plus epic closeout` additionally authorizes evidence comments and - manual status or close transitions for the in-scope children and parents; -- issue-body edits, dependency-graph mutations, and follow-up-ticket creation - require explicit ticket-management authority; and -- deployment, production mutation, and destructive data operations always - require explicit authority. - -Do not infer merge or issue-close authority from words such as `finish`, -`complete`, or `execute end to end`; treat them as persistence within the -selected completion policy. Merge PRs or close issues only when the user -explicitly selects a merge-inclusive policy or unambiguously asks for those -actions. When merge authority is unclear, stop at a ready PR and request it. -Never infer deployment or production mutation from merge authorization. - -If a required input cannot be discovered safely and materially changes the -outcome, stop and ask. Otherwise use repository conventions. - -## Establish source-of-truth precedence - -Use this order within the task: - -1. Current user instructions. -2. Live parent, child, dependency, and PR state. -3. Repository `AGENTS.md` and equivalent local instructions. -4. Named architecture, design, contract, and rollout documents. -5. Current code and tests. -6. Prior summaries or memory as orientation only. - -Stop on material conflicts between the live dependency graph, ticket body, and -repository contract. Do not choose the most convenient interpretation. - -## Run the epic loop - -Repeat the following procedure until the requested scope reaches its completion -policy or a genuine blocker requires user input. - -### 1. Refresh live state - -- Read every in-scope epic and its open children. -- Read native parent, blocking, and blocked-by relationships. -- Read the dispositions and delivered outcomes of closed blockers for candidate - children. -- Inspect existing branches and open or merged PRs for candidate children. -- Recompute ready work after every merge; never reuse yesterday's order. -- Separate the serial critical-path recommendation from parallel-ready work. - -When multiple children are ready, prefer the child that unlocks the most -downstream work without widening scope. Prefer contracts and additive -foundations before consumers and cutovers. - -### 2. Apply the readiness gate - -Select a child only when it: - -- is open and belongs to an in-scope epic; -- has no unresolved native blocker; -- has every required closed-blocker outcome verified in its authoritative - repository, artifact registry, tracker, or environment; for a cross-repository - or operational prerequisite, also verifies that the consumer uses the required - contract, version, configuration, approval, or environment state; -- is not already implemented, superseded, or represented by another PR; -- delivers one coherent outcome through one PR; -- states goals, non-goals, acceptance criteria, and verification; -- contains no unresolved product, data, authorization, migration, destructive, - or architecture decision; -- identifies existing behavior that must be preserved; -- can merge without exposing misleading or incomplete behavior. - -Treat a canceled or not-planned blocker with an unmet required outcome as -unresolved even when the tracker marks it closed. Do not absorb that missing -prerequisite into the selected child. Do not improvise missing requirements. -When ticket editing is authorized, make the ticket implementation-ready and -re-read it. Otherwise select another ready child or report the missing decision. - -### 3. Create isolated implementation state - -- Confirm the primary checkout, current worktree, and all registered worktrees. -- Fetch and prune the remote. -- Start a fresh feature branch and disposable worktree from the current remote - base unless the repository directs otherwise. -- Use one ticket per branch and one ticket-closing PR. -- Verify the new worktree is clean before editing. -- Install dependencies and start documented local services before diagnosing - missing-tool or missing-service failures as feature failures. - -Preserve unrelated worktrees, branches, and local changes. Never reuse a dirty -or ambiguous checkout merely to save setup time. - -### 4. Implement the ticket contract - -- Read nearby implementation and tests before editing. -- Keep the live child and named source documents as the scope boundary. -- Preserve explicit non-goals and existing behavior named by the ticket. -- Follow existing architecture and extension points. -- Add focused behavior tests with the implementation. -- Update contract or contributor documentation when executable behavior changes. -- Keep exported-symbol documentation aligned with repository rules. -- Avoid speculative backfills, compatibility layers, or abstractions for - nonexistent conditions. - -Apply incidental changes only for a demonstrated correctness, security, -acceptance, architecture, or validation need, or for an obvious low-cost -correctness or safety improvement with demonstrated current risk. Refactor to -reduce cognitive load only when the user explicitly requests it or when the -ticket's correctness cannot otherwise be made evident. Defer polish, broad -refactors, hypothetical hardening, and adjacent epic work. - -### 5. Validate in layers - -Discover validation from repository instructions and tooling. Run: - -1. focused tests for changed behavior; -2. relevant static checks; -3. the complete required repository gate; -4. integration tests with documented real dependencies; -5. build, packaging, or manual checks required by the ticket. - -Report exact outcomes. Distinguish bootstrap/environment failures from feature -failures. Do not claim completion when required validation is unavailable or -failing. - -### 6. Publish one focused PR - -- Confirm no existing PR already owns the ticket. -- Commit using repository conventions. -- Push only the feature branch. -- Summarize the whole branch, observable outcome, important non-goals, and - actual validation. -- Include the tracker-closing reference required by the repository. -- Open the PR as draft or ready according to the operating contract. - -Do not combine independently useful tickets in one PR, even when they touch the -same files. - -### 7. Run bounded review - -Follow `references/review-and-merge-gates.md`. - -- Implement and apply fixes only in the primary execution context. -- Commit and validate a clean candidate, then invoke `review-code-change` in a - fresh or minimally inherited read-only context with raw ticket, repository, - diff, validation, and worktree artifacts. -- Exclude the implementation transcript, intended solution, prior conclusions, - and suspected findings. -- Consume the suite's validated aggregate verdict, findings, dispositions, - limitations, and next action without reproducing its lens rubrics or ordering. -- Apply only blocking and strong-recommendation findings that are material, - tractable, and ticket-scoped. Preserve deferred findings without expanding the - PR. -- After fixes, run affected and required validation, commit, push, build a new - head-bound evidence packet, and invoke the suite according to its re-review - instructions. -- Use at most three full fix/re-review cycles by default. Keep the PR open and - report unresolved material findings after the final cycle. - -Treat a missing dependency, malformed result, `blocked` verdict, reviewer -mutation, or unavailable required evidence as a failed local review gate. The -review suite remains read-only; this workflow owns every code and GitHub -mutation. - -Create a follow-up ticket only when ticket management is authorized and the gap -is real, evidenced, and intentionally outside the current PR. Otherwise report a -pasteable follow-up proposal. - -### 8. Merge only through current-candidate gates - -Follow both bundled gate references. Merge only when: - -- every intended ticket change is committed and represented by the candidate - diff, with any unrelated artifacts classified, preserved, and proven - irrelevant; -- required local validation passed; -- required CI passed or the repository explicitly has no such checks; -- the repository-owned local review has a clean result bound to the current head - and an explicit disposition for any later base drift; -- every applicable required human and connector review has a current approving - or clean verdict under repository policy; -- no undispositioned actionable conversation comment, formal review, connector - feedback, or review thread remains; -- the PR still matches its ticket and base; -- no other PR superseded it. - -Treat every head-changing edit, push, rebase, conflict resolution, or -update-branch operation as invalidating head-bound evidence. When only the base -advances, inspect the current merge candidate and record why each gate was -retained or invalidated. Retain evidence only when the effective diff and -resulting tree are unchanged, no conflict or relevant overlap exists, and -repository policy permits retention. Otherwise rerun every affected gate. - -After merge, verify the remote state and base-branch result before cleanup. - -### 9. Clean up and refresh - -- Confirm merge or patch equivalence before deleting branch state. -- Confirm the disposable worktree is clean and contains no untracked or ignored - user files. -- Confirm the local branch has no commit absent from its pushed PR branch, the - pushed branch when it still exists has not advanced beyond the PR's recorded - merged head, and that merged-head result is fully represented on the verified - base by ancestry or patch equivalence. Never force cleanup past a failed - precondition. -- Remove only the merged feature branch and disposable worktree. -- Preserve unrelated worktrees and edits. -- Verify the ticket closed as intended. -- Re-read the live graph before selecting another child. - -## Close an epic conservatively - -Use `references/closeout-and-cleanup.md`. Close an epic only when: - -- every required child is closed; -- no unresolved blocker remains; -- every required PR is represented on the base branch; -- epic acceptance criteria are verified against resulting behavior; -- required documentation, compatibility, migration, rollout, and cleanup work is - complete; -- no child closed as not planned leaves an unsatisfied outcome; -- the mandatory late-feedback sweep for every merged PR is complete and every - finding has a recorded disposition; -- required clean-main and candidate checks passed. - -For a series, validate each epic separately, then validate any umbrella parent. -Keep the parent open when any required outcome remains unsatisfied. - -## Stop conditions - -Stop and request direction when: - -- graph and ticket scope conflict materially; -- implementation requires an unresolved product or architecture choice; -- a destructive migration, production cutover, or deployment lacks authority; -- credentials, external approval, or unavailable infrastructure are required; -- correctness would materially exceed ticket scope; -- review feedback requires redesigning the ticket; -- required validation remains unavailable after documented bootstrap attempts. - -Difficulty, a long test suite, or ordinary CI wait time are not blockers. - -## Final reporting - -For each child, report the ticket, PR, merge result, validation, review state, -and cleanup result. At epic completion, report: - -- closed and intentionally deferred children; -- final dependency-graph state; -- mainline and acceptance-criteria evidence; -- remaining parallel-ready work outside the requested scope; -- any limitation that prevented full closeout. diff --git a/skills/implement-epic-sequence/agents/openai.yaml b/skills/implement-epic-sequence/agents/openai.yaml deleted file mode 100644 index d6b41e4..0000000 --- a/skills/implement-epic-sequence/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Implement Epic Sequence" - short_description: "Execute epics through safe reviewed merges" - default_prompt: "Use $implement-epic-sequence to implement the remaining ready children of this epic through repository-owned review, merge, cleanup, and closeout." diff --git a/skills/implement-epic-sequence/evals/cases.json b/skills/implement-epic-sequence/evals/cases.json deleted file mode 100644 index a3433a7..0000000 --- a/skills/implement-epic-sequence/evals/cases.json +++ /dev/null @@ -1,150 +0,0 @@ -[ - { - "id": "correctness-fix-and-rereview", - "request": "Implement and merge the ready child after all required gates pass.", - "ticket": { - "goal": "Stop retry scheduling at the configured maximum.", - "acceptance_criteria": ["No retry is scheduled when retry_count equals max_retries."], - "non_goals": ["Redesign the queue."], - "preserved_behaviors": ["Retries below the maximum still schedule normally."] - }, - "candidate": { - "head_sha": "1111111111111111111111111111111111111111", - "comparison_base_sha": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "diff_summary": "The retry branch still schedules when retry_count equals max_retries.", - "validation": ["focused passed", "full passed"] - }, - "review_dependency_available": true, - "suite_result": { - "schema_version": "1.0", "lens": "aggregate", - "candidate": {"head_sha": "1111111111111111111111111111111111111111", "comparison_base_sha": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, - "verdict": "changes_required", - "findings": [{ - "id": "correctness.retry-bound", "lens": "correctness", "severity": "blocking", "confidence": "high", - "rule": "Retries must stop at max_retries.", - "evidence": [{"location": "worker retry branch", "detail": "Equality still reaches schedule_retry."}], - "concern": "The maximum does not stop retry scheduling.", "impact": "Work can exceed the configured retry budget.", - "proposed_change": "Return before scheduling when retry_count is at least max_retries.", "expected_effect": "Bound retries at the configured maximum." - }], - "blocking_reasons": [], - "next_action": "Apply the correctness fix, validate, commit and push a new head, then rerun correctness and affected downstream lenses." - }, - "followup_suite_result": { - "schema_version": "1.0", "lens": "aggregate", - "candidate": {"head_sha": "1212121212121212121212121212121212121212", "comparison_base_sha": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, - "verdict": "clean", "findings": [], "blocking_reasons": [], - "next_action": "No further local review action is required." - }, - "remote_policy": {"ci": "passed", "human": "not_required", "connector": "not_configured"} - }, - { - "id": "solution-redesign-early-exit", - "request": "Implement and merge the ready child after all required gates pass.", - "ticket": { - "goal": "Persist one local audit record.", - "acceptance_criteria": ["One local record is written."], - "non_goals": ["Support remote storage providers."], - "preserved_behaviors": ["Existing local writes remain atomic."] - }, - "candidate": { - "head_sha": "2222222222222222222222222222222222222222", - "comparison_base_sha": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "diff_summary": "Adds a provider registry, dispatch layer, and remote-provider configuration for one local write.", - "validation": ["focused passed", "full passed"] - }, - "review_dependency_available": true, - "suite_result": { - "schema_version": "1.0", "lens": "aggregate", - "candidate": {"head_sha": "2222222222222222222222222222222222222222", "comparison_base_sha": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}, - "verdict": "changes_required", - "findings": [{ - "id": "solution-simplicity.remove-provider-platform", "lens": "solution_simplicity", "severity": "blocking", "confidence": "high", - "rule": "Remote providers are outside scope.", - "evidence": [{"location": "candidate diff", "detail": "Provider configuration and dispatch exist for one local write."}], - "concern": "The implementation strategy exceeds the ticket.", "impact": "It adds unsupported ownership and failure modes.", - "proposed_change": "Replace the provider platform with one direct local write.", "expected_effect": "Remove provider configuration, dispatch, and remote failure modes." - }], - "blocking_reasons": [], - "next_action": "Redesign the implementation, capture a new head, and restart the full review sequence." - }, - "remote_policy": {"ci": "required", "human": "not_required", "connector": "not_configured"} - }, - { - "id": "code-simplicity-targeted-correctness", - "request": "Implement and merge the ready child after all required gates pass.", - "ticket": { - "goal": "Authorize two new admin actions.", - "acceptance_criteria": ["Only active admins may invoke either action."], - "non_goals": ["Create another authorization framework."], - "preserved_behaviors": ["The shared active-admin policy remains authoritative."] - }, - "candidate": { - "head_sha": "3333333333333333333333333333333333333333", - "comparison_base_sha": "cccccccccccccccccccccccccccccccccccccccc", - "diff_summary": "Both actions repeat the existing active-admin policy instead of calling its helper.", - "validation": ["focused passed", "full passed"] - }, - "review_dependency_available": true, - "suite_result": { - "schema_version": "1.0", "lens": "aggregate", - "candidate": {"head_sha": "3333333333333333333333333333333333333333", "comparison_base_sha": "cccccccccccccccccccccccccccccccccccccccc"}, - "verdict": "changes_required", - "findings": [{ - "id": "code-simplicity.reuse-admin-policy", "lens": "code_simplicity", "severity": "strong_recommendation", "confidence": "high", - "rule": "Reuse the repository authorization helper.", - "evidence": [{"location": "two new actions", "detail": "Both repeat require_active_admin logic."}], - "concern": "Authorization policy has three owners.", "impact": "The repeated checks can drift.", - "proposed_change": "Call require_active_admin from each action.", "expected_effect": "Restore one authorization-policy owner." - }], - "blocking_reasons": [], - "next_action": "Apply the code-simplicity fix on a new head, rerun code simplicity, then run targeted correctness." - }, - "remote_policy": {"ci": "required", "human": "not_required", "connector": "not_configured"} - }, - { - "id": "unrelated-base-drift-retained", - "request": "Merge the clean reviewed child when all current gates pass.", - "ticket": {"goal": "Add a parser boundary test.", "acceptance_criteria": ["The boundary is covered."], "non_goals": [], "preserved_behaviors": ["Parser behavior is unchanged."]}, - "candidate": { - "head_sha": "4444444444444444444444444444444444444444", - "comparison_base_sha": "dddddddddddddddddddddddddddddddddddddddd", - "diff_summary": "Adds one parser test.", "validation": ["focused passed", "full passed"] - }, - "review_dependency_available": true, - "suite_result": {"schema_version": "1.0", "lens": "aggregate", "candidate": {"head_sha": "4444444444444444444444444444444444444444", "comparison_base_sha": "dddddddddddddddddddddddddddddddddddddddd"}, "verdict": "clean", "findings": [], "blocking_reasons": []}, - "base_drift": {"new_base_sha": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", "effective_diff_changed": false, "resulting_tree_changed": false, "conflict": false, "relevant_overlap": false, "repository_requires_reset": false}, - "remote_policy": {"ci": "passed", "human": "not_required", "connector": "not_configured"} - }, - { - "id": "relevant-base-drift-restarts-gates", - "request": "Merge the clean reviewed child when all current gates pass.", - "ticket": {"goal": "Change retry classification.", "acceptance_criteria": ["Permanent errors do not retry."], "non_goals": [], "preserved_behaviors": ["Transient errors retry."]}, - "candidate": { - "head_sha": "5555555555555555555555555555555555555555", - "comparison_base_sha": "ffffffffffffffffffffffffffffffffffffffff", - "diff_summary": "Changes the retry classifier.", "validation": ["focused passed", "full passed"] - }, - "review_dependency_available": true, - "suite_result": {"schema_version": "1.0", "lens": "aggregate", "candidate": {"head_sha": "5555555555555555555555555555555555555555", "comparison_base_sha": "ffffffffffffffffffffffffffffffffffffffff"}, "verdict": "clean", "findings": [], "blocking_reasons": []}, - "base_drift": {"new_base_sha": "abababababababababababababababababababab", "effective_diff_changed": true, "resulting_tree_changed": true, "conflict": true, "relevant_overlap": true, "repository_requires_reset": false}, - "remote_policy": {"ci": "required", "human": "required", "connector": "required"} - }, - { - "id": "missing-review-dependency", - "request": "Implement and merge the ready child after all required gates pass.", - "ticket": {"goal": "Add one command.", "acceptance_criteria": ["The command runs."], "non_goals": [], "preserved_behaviors": []}, - "candidate": {"head_sha": "6666666666666666666666666666666666666666", "comparison_base_sha": "1212121212121212121212121212121212121212", "diff_summary": "Adds one command.", "validation": ["focused passed", "full passed"]}, - "review_dependency_available": false, - "suite_result": {"schema_version": "1.0", "lens": "aggregate", "candidate": {"head_sha": "6666666666666666666666666666666666666666", "comparison_base_sha": "1212121212121212121212121212121212121212"}, "verdict": "blocked", "findings": [], "blocking_reasons": ["Required review skill review-code-change is unavailable."], "next_action": "Make review-code-change available before continuing the merge-inclusive run."}, - "remote_policy": {"ci": "required", "human": "not_required", "connector": "not_configured"} - }, - { - "id": "clean-local-review-remote-policy-pending", - "request": "Merge the clean reviewed child when all current gates pass.", - "ticket": {"goal": "Expose one read-only status field.", "acceptance_criteria": ["The field is returned."], "non_goals": [], "preserved_behaviors": ["Existing fields are unchanged."]}, - "candidate": {"head_sha": "7777777777777777777777777777777777777777", "comparison_base_sha": "3434343434343434343434343434343434343434", "diff_summary": "Adds one read-only field and test.", "validation": ["focused passed", "full passed"]}, - "review_dependency_available": true, - "suite_result": {"schema_version": "1.0", "lens": "aggregate", "candidate": {"head_sha": "7777777777777777777777777777777777777777", "comparison_base_sha": "3434343434343434343434343434343434343434"}, "verdict": "clean", "findings": [], "blocking_reasons": []}, - "remote_policy": {"ci": "passed", "human": "required_pending", "connector": "required_pending", "unresolved_threads": 0} - } -] diff --git a/skills/implement-epic-sequence/evals/results.json b/skills/implement-epic-sequence/evals/results.json deleted file mode 100644 index 466f339..0000000 --- a/skills/implement-epic-sequence/evals/results.json +++ /dev/null @@ -1,44 +0,0 @@ -[ - { - "case_id": "correctness-fix-and-rereview", - "workflow_state": "ready_for_remote_gates", - "observed_actions": ["apply ticket-scoped correctness fix", "run affected and full validation", "commit and push new head", "build new evidence packet", "follow suite re-review instructions", "accept clean new-head aggregate"], - "review_cycles": 2 - }, - { - "case_id": "solution-redesign-early-exit", - "workflow_state": "redesign_required", - "observed_actions": ["stop current candidate review", "redesign in primary context", "validate and commit new head", "restart full review sequence"], - "review_cycles": 1 - }, - { - "case_id": "code-simplicity-targeted-correctness", - "workflow_state": "fix_and_targeted_rereview_required", - "observed_actions": ["apply ticket-scoped code simplification", "run affected and full validation", "commit and push new head", "build new evidence packet", "rerun code simplicity", "run targeted correctness"], - "review_cycles": 1 - }, - { - "case_id": "unrelated-base-drift-retained", - "workflow_state": "merged", - "observed_actions": ["accept clean local review for current head", "inspect effective candidate after base advance", "record unrelated base-drift retention", "retain head-bound validation and review evidence", "confirm remote gates passed", "merge candidate", "verify merge and mainline result before cleanup"], - "review_cycles": 1 - }, - { - "case_id": "relevant-base-drift-restarts-gates", - "workflow_state": "affected_gates_invalidated", - "observed_actions": ["inspect current merge candidate", "record relevant overlap and conflict", "resolve conflict on a new head", "rerun local validation", "rerun repository-owned review", "restart required CI human connector and thread gates"], - "review_cycles": 1 - }, - { - "case_id": "missing-review-dependency", - "workflow_state": "blocked", - "observed_actions": ["return explicit missing-dependency result", "do not substitute another reviewer", "do not merge"], - "review_cycles": 0 - }, - { - "case_id": "clean-local-review-remote-policy-pending", - "workflow_state": "waiting_for_remote_review", - "observed_actions": ["accept clean local aggregate", "keep human review gate pending", "keep connector review gate pending", "do not infer approval from CI or zero threads", "do not merge"], - "review_cycles": 1 - } -] diff --git a/skills/implement-epic-sequence/references/closeout-and-cleanup.md b/skills/implement-epic-sequence/references/closeout-and-cleanup.md deleted file mode 100644 index 76e4f67..0000000 --- a/skills/implement-epic-sequence/references/closeout-and-cleanup.md +++ /dev/null @@ -1,100 +0,0 @@ -# Closeout and cleanup - -Verify remote and mainline state before deleting anything or closing a parent. - -## Per-PR cleanup - -01. Confirm the PR is merged remotely. -02. Fetch and prune the remote. -03. Confirm the branch's result is represented on the base branch. -04. Use ancestry first; use patch equivalence after squash or rebase when - ancestry is unavailable. -05. Map the exact disposable worktree, local branch, upstream branch, PR branch, - and base branch. Do not rely on the current directory or a branch-name - guess. -06. Inspect tracked, staged, unstaged, untracked, and ignored state in that - exact worktree. Always inventory ignored files, even when ordinary status is - clean. Classify ignored and untracked paths as reproducible generated output - or non-reproducible/user-created data. Stop or preserve `.env` files, - credentials, local databases, and every other non-reproducible artifact. -07. Confirm the local branch has no commits absent from its pushed PR branch. - When the remote branch no longer exists, compare the local tip with the PR's - recorded merged head. -08. When the pushed PR branch still exists, confirm it did not advance beyond - the recorded merged head. Confirm the merged-head result is fully - represented on the verified base by ancestry or patch equivalence. Stop if - either branch check fails. -09. If the local branch is checked out, remove the clean disposable worktree or - detach it before deleting the branch. Never force worktree removal. -10. Delete only the verified local feature branch, then its remote feature - branch when it still exists and repository policy permits deletion. -11. Prune worktree metadata and verify that the old path, local branch, and - remote branch are gone. -12. Preserve unrelated worktrees, branches, ignored and untracked files, and - user edits. - -If another worktree owns the base branch, update or merge from that checkout or -use the remote API. Do not force branch ownership changes. If cleanup -preconditions fail, report the exact dirty, ignored, or untracked paths or -unique commits and leave the worktree and branches intact. - -## Mainline verification - -After every merge, verify: - -- the remote base advanced as expected; -- the merged behavior and tests exist on the base; -- the child ticket closed or transitioned correctly; -- no required check or review state changed after merge; -- the next dependency graph read uses current remote state. - -Separate environment failures from regressions when validating a clean mainline. -Bootstrap documented dependencies and services before modifying code. - -## Late-feedback sweep - -Before closing an epic, inspect every merged PR in scope again. Read ordinary -conversation comments and thread-aware review state, including connector -comments or threads posted after merge. - -Classify late findings with the same rules used during active review and record -the disposition of each one. When a late finding exposes a required correctness, -security, acceptance, architecture, or validation gap: - -- keep the epic open; -- create or use an in-scope follow-up ticket when ticket mutation is authorized; -- implement the fix from a fresh branch based on the current remote base; -- never reopen, reuse, or edit the merged feature branch as the implementation - path; and -- pass the normal validation, review, merge, and cleanup gates. - -Do not close the epic until every late comment and review thread has a recorded -disposition and every blocking finding is resolved on the base branch. - -## Epic closeout - -Before closing an epic: - -- read the live child and blocker graph; -- confirm every required child disposition; -- confirm every required PR is merged and represented on the base; -- verify epic acceptance criteria against resulting behavior; -- run required clean-main validation; -- complete the mandatory late-feedback sweep for every merged PR; -- perform any required candidate smoke check, migration, documentation, - compatibility, or cleanup work; -- confirm no not-planned child leaves an unsatisfied outcome. - -Close each epic separately. For a series, close the umbrella only after every -required epic passes the same check. A nearly complete graph is not complete. - -## Final report - -Record: - -- merged PRs and their tickets; -- validation and current-head review evidence; -- branch and worktree cleanup results; -- closed, deferred, canceled, or blocked children; -- remaining critical-path and parallel-ready work; -- the exact reason any epic remains open. diff --git a/skills/implement-epic-sequence/references/github.md b/skills/implement-epic-sequence/references/github.md deleted file mode 100644 index 2644da1..0000000 --- a/skills/implement-epic-sequence/references/github.md +++ /dev/null @@ -1,203 +0,0 @@ -# GitHub execution adapter - -Use GitHub independently as an issue tracker, a PR host, or both. Apply only the -sections owned by its role in the current run. - -## Contents - -- [Ownership boundary](#ownership-boundary) -- [PR-host preflight](#pr-host-preflight) -- [GitHub-tracker preflight](#github-tracker-preflight) -- [Selecting work when GitHub owns issue state](#selecting-work-when-github-owns-issue-state) -- [PR contract](#pr-contract) -- [Review state](#review-state) -- [Checks and merge](#checks-and-merge) -- [Epic closeout when GitHub owns issue state](#epic-closeout-when-github-owns-issue-state) - -## Ownership boundary - -- When GitHub owns issue state, use the GitHub-tracker preflight, selection, - issue-closing syntax, dependency refresh, and epic closeout sections. -- Whenever GitHub hosts PRs, use the PR-host preflight, PR contract, review, - checks, merge, and branch-handling sections. -- When Linear owns issue state and GitHub hosts PRs, route all parent, child, - blocker, status, and closeout decisions through `linear.md`. Do not inspect or - mutate same-numbered GitHub issues as substitutes. - -## PR-host preflight - -- Resolve the repository from the request or current checkout. -- Confirm GitHub authentication. -- Inspect open and merged PRs that reference the candidate tracker ticket. -- Confirm local branch and worktree topology before creating implementation - state. -- When connector review is required, record the connector identity, automatic or - request-driven mode, exact initiation action, per-push policy, expected - run-start evidence, and accepted clean signal. Derive this contract from - repository instructions, configuration, or verified recent PR behavior. - -Fail closed before connector polling when its identity, initiation contract, or -required current-candidate signal cannot be discovered. - -## GitHub-tracker preflight - -Run this section only when GitHub owns issue state. - -- Read the epic body, labels, state, and comments when they affect scope. -- Read native `parent`, `subIssues`, `blockedBy`, and `blocking` relationships - through GraphQL or an equivalent structured GitHub tool. - -Do not derive ownership or ordering from issue title, issue number, Markdown -task lists, or prose dependency notes when native relationships exist. - -## Selecting work when GitHub owns issue state - -Choose an open child whose native `blockedBy` set contains no open issue. Verify -every required closed-blocker outcome in its authoritative repository, artifact -registry, tracker, or environment. For same-repository code, verify the result -on the base. For cross-repository or operational prerequisites, also verify that -the consumer uses the required contract, version, configuration, approval, or -environment state. Treat a canceled or not-planned blocker with an unmet outcome -as unresolved. - -Check that no open or merged PR already represents the child. When duplicate -branches or PRs exist, compare their actual patches or resulting trees and -retain one canonical path. - -Read the graph again after every merge because closing one child can unblock -work in another epic. - -## PR contract - -- Use one tracker child per branch and PR. -- When GitHub owns issue state, use the repository's closing syntax, normally - `Fixes #`. -- When another tracker owns issue state, use its required reference and avoid - GitHub issue-closing syntax unless a real GitHub issue is intentionally in - scope. -- Describe the branch as a whole rather than the latest commit. -- Preserve explicit ticket non-goals in the PR when scope pressure is likely. -- Confirm the PR base and head match the intended worktree. - -Use file-based PR bodies when shell interpolation could alter Markdown. - -## Review state - -Capture the exact PR head and base SHAs before evaluating review state. Read -ordinary PR conversation, formal reviews, and inline review threads; use GraphQL -or an equivalent thread-aware API when flat PR output does not expose thread -resolution. - -For required human review, record the reviewer, review state, reviewed commit -OID, submission time, and base SHA observed when the review arrives. Require its -reviewed commit to equal the current head. After a base advance, apply the -generic base-drift gate: retain approval only when the effective candidate is -unchanged and repository policy permits it; otherwise request fresh approval. -When the host cannot bind a required fresh approval to an unchanged head plus a -new base, update the branch so the candidate has a new reviewable head. - -When the repository uses a connector bot, use the identity, initiation mode, and -clean signal recorded during PR-host preflight. Accept a clean result only when -all of these conditions hold: - -- the evidence comes from that configured connector identity; -- it is an explicit clean verdict represented by one of these forms: - - a formal review whose commit OID equals the captured SHA; - - a conversation comment that names the captured SHA; - - the configured clean reaction on a request or result that names the captured - SHA; or - - a configured PR-object thumbs-up from the connector bot that appeared as the - completion signal for the connector run on the captured SHA; -- the PR head still equals the captured SHA; and -- any base advance has passed the generic base-drift gate and the connector - policy permits retaining its current-head signal; and -- there are zero unresolved connector-authored review threads. - -For a PR-object thumbs-up, require repository evidence that this is the -configured completion signal, capture the reaction identity and timestamp or -equivalent run evidence, and prove it appeared after the connector request or -run began for the captured SHA. Never reuse a PR-object reaction that predates -the captured head or cannot be tied to its connector run. - -Do not infer current-head approval from timing, comment order, a generic bot -message, CI success, or a verdict on an earlier commit. A push, rebase, conflict -resolution, or update-branch operation changes the head and invalidates all -older current-head evidence. - -For every actionable conversation comment, formal review, and inline thread: - -1. Verify the claim against the current code and ticket. -2. Fix it or reject it with concrete evidence. -3. Run affected and required validation. -4. Push when the code changed. -5. Reply publicly on the originating surface when possible and record the item - identifier, disposition, and evidence. -6. Resolve an inline thread only after its disposition is complete. - -Require zero undispositioned actionable items across conversation comments, -formal reviews, connector feedback, and inline threads before merge. - -After a head change, capture the new head/base pair and wait for a fresh -connector verdict tied to that candidate. After base-only drift, apply the -generic drift gate and request a fresh connector verdict only when the effective -candidate changed, relevant overlap exists, or connector/repository policy -requires it. Trigger the configured connector when review is request-driven. -When review is automatic, record evidence that a run began for the captured -candidate after it was pushed or rebuilt. Do not accept a required fresh verdict -until its review request or automatic run is tied to that candidate. - -Resolve the connector polling window and interval from repository instructions. -When none is configured, state and use a 30-minute window with a 30-to-60-second -polling interval. Start the window when the review request is sent or, for an -automatic connector, when the head is pushed. During every poll, re-read the -head and base SHAs, run-start evidence, required checks, conversation comments, -formal reviews, reactions, and review threads. - -If no automatic run begins or no valid current-head signal arrives within the -window, stop and report the missing gate. Keep the PR and epic open; never infer -approval from green CI or zero unresolved threads. Re-read the conversation, -formal reviews, review threads, reactions, head and base SHAs, and checks -immediately before merge. - -Use at most three connector feedback passes per PR by default, including the -initial review and re-reviews after fixes. A review repeated only because the -candidate identity changed without producing feedback does not consume this -feedback budget. If the final allowed pass still reports a material in-scope -finding, keep the PR and epic open, report the remaining finding, and request -direction. Do not spend connector passes on feedback already classified as -incorrect, outside scope, polish, or hypothetical hardening. - -## Checks and merge - -- Read required GitHub Actions checks directly. -- Continue monitoring pending required checks; ordinary check execution is not a - blocker requiring user input. -- Inspect failed required checks and their logs. Distinguish an in-scope - regression from an infrastructure, dependency, or external-service failure. -- Fix only demonstrated in-scope failures, rerun affected and required local - validation, push the fix, and restart every current-head connector and check - gate. -- Report unavailable external checks without weakening or bypassing the gate. -- If the repository has no branch checks, state that explicitly and use the - documented local and review gates. -- Immediately before merge, re-read the base SHA. If it differs from the - comparison base, build or inspect an up-to-date merge candidate and apply the - generic base-drift gate. Record why each signal is retained or rerun every - affected local validation, CI, local review, human review, connector review, - and feedback disposition. When a review system cannot bind required fresh - evidence to an unchanged head plus a new base, update the branch so the new - candidate has a reviewable head SHA. -- Use the repository's approved merge method. -- If local worktree ownership prevents the CLI from switching to the base - branch, merge through GitHub's API and perform local cleanup separately. - -After merge, verify PR state, merge commit or squash result, and base-branch -content before deleting the branch. Verify issue transition or closure through -the adapter for the owning tracker; do not assume GitHub owns it. - -## Epic closeout when GitHub owns issue state - -Read back native relationships. Close the epic only when required sub-issues are -closed, no blocker remains, every required closed-blocker outcome is satisfied, -and epic-level behavior is verified. Do not infer completion solely from merged -PR count or a canceled/not-planned issue state. diff --git a/skills/implement-epic-sequence/references/linear.md b/skills/implement-epic-sequence/references/linear.md deleted file mode 100644 index 200f78b..0000000 --- a/skills/implement-epic-sequence/references/linear.md +++ /dev/null @@ -1,54 +0,0 @@ -# Linear execution adapter - -Use live Linear issue relationships and current repository evidence to execute -epics whose planning source lives in Linear. - -## Preflight - -- Read the parent or epic and its current project context. -- List current children and inspect explicit blocking relationships. -- Read linked design documents, comments, and prior implementation PRs when they - affect scope. -- Search for completed, canceled, duplicate, or superseding issues before - selecting work. -- Resolve the code repository and its local instructions independently from - Linear. - -Do not use list order, priority labels, or an old prompt as dependency order -when explicit relations are available. - -## Selecting work - -Select an open child with no unresolved blocker and one PR-sized outcome. Verify -every required outcome from a completed, canceled, or superseded blocker in its -authoritative repository, artifact registry, tracker, or environment. For -same-repository code, verify the result on the base. For cross-repository or -operational prerequisites, also verify that the consumer uses the required -contract, version, configuration, approval, or environment state. Treat a -canceled or not-planned blocker with an unmet outcome as unresolved. Confirm -that the child is not already represented by a merged or open PR. Re-read the -parent after every merge or status transition because sibling readiness may have -changed. - -If Linear's relationship model cannot express a required dependency, record the -limitation in the issue and execution report. Do not silently treat prose as -equivalent to a native blocker. - -## Scope and PR linkage - -- Treat the live Linear issue body as the child scope contract. -- Preserve exact product rules such as timing, timezone, idempotency, skip, - unavailable, and migration behavior. -- Use the repository's required Linear reference in the branch, commit, and PR - body. -- Keep one child per PR unless the user explicitly changes the decomposition. -- Update Linear status only when the corresponding workflow state has actually - been reached. - -## Closeout - -After merge, verify the code is represented on the base branch and update the -child according to the team's Linear workflow. Close the parent only after all -required children, required closed-blocker outcomes, and parent-level acceptance -criteria are satisfied. Preserve explicitly deferred or canceled scope in the -final report; do not count an unmet canceled outcome as complete. diff --git a/skills/implement-epic-sequence/references/review-and-merge-gates.md b/skills/implement-epic-sequence/references/review-and-merge-gates.md deleted file mode 100644 index 769c4a4..0000000 --- a/skills/implement-epic-sequence/references/review-and-merge-gates.md +++ /dev/null @@ -1,122 +0,0 @@ -# Review and merge gates - -Use these gates for every implementation PR. Repository instructions may add -stricter requirements but must not silently weaken them. - -## Contents - -- [Bounded review loop](#bounded-review-loop) -- [Revalidation](#revalidation) -- [Base-drift gate](#base-drift-gate) -- [Merge gate](#merge-gate) -- [Feedback that should not expand the PR](#feedback-that-should-not-expand-the-pr) - -## Bounded review loop - -Before a merge-inclusive run, require the repository-owned `review-code-change` -skill. Fail closed with an explicit missing-dependency result when it is not -available or readable. Do not substitute another review skill, generic -self-review, or an unreviewed merge path. - -After implementation and required local validation, invoke the suite in a fresh -or minimally inherited read-only context containing only raw task artifacts. -Exclude the implementation transcript, intended solution, prior conclusions, and -suspected findings. - -Before review, require every intended ticket change to be committed and the -implementation worktree to be clean. If unrelated user artifacts prevent a clean -state, classify and preserve them and prove they are irrelevant to the -candidate. - -Before delegation, capture HEAD, commit history, and tracked, untracked, and -ignored worktree state. After the reviewer returns, verify that all remain -unchanged. Treat any mutation as an integrity failure, inspect it, and preserve -user work rather than resetting or deleting it. - -Give the suite: - -- the live ticket and acceptance criteria; -- every named architecture, design, contract, migration, and rollout document; -- the exact captured head and comparison-base SHAs and their complete - `base...HEAD` diff, not only the latest commit; and -- exact focused and full validation evidence, including unavailable checks. - -Validate and consume the aggregate result according to the suite's shared -contract. Do not restate or override lens ordering, severity semantics, -deduplication, or correctness-versus-simplicity rules here. Apply only blocking -and strong-recommendation findings that are material, tractable, and -ticket-scoped. Preserve deferred findings without expanding the current PR. -Reply with evidence for findings that no longer apply. When authorized, create a -focused follow-up only for a real, evidenced gap outside active scope. - -After a material fix, run affected and required validation, commit and push the -new head, rebuild the packet, and invoke the suite according to its returned -re-review instructions. Use at most three full fix/re-review cycles by default. -A clean aggregate ends the local loop. If the final cycle still reports a -material finding, keep the PR open and report the unresolved evidence. Do not -spend cycles on deferred findings. - -## Revalidation - -After every fix cycle: - -- run affected focused tests; -- rerun the repository-required gate; -- commit every intended ticket change and confirm that no uncommitted ticket - work is absent from `base...HEAD`; -- push the updated head; -- capture the current head and comparison-base SHAs; -- re-read current-candidate review and check state. - -Do not carry older head-bound evidence across an edit, push, rebase, conflict -resolution, or update-branch operation that changes the head. A base advance -uses the separate drift gate below. - -## Base-drift gate - -Bind local review evidence to the captured head. Immediately before merge, -re-read the head and base and inspect the effective merge candidate when the -base advanced. - -Retain head-bound evidence across base-only drift only when the effective diff -and resulting tree are unchanged, no conflict exists, no relevant base code -overlaps the candidate, repository policy permits retention, and the reason is -recorded. Otherwise invalidate and rerun each affected local-validation, CI, -local-review, human-review, connector-review, and feedback-disposition gate. -Repository policy may require a complete reset even for unrelated drift. Any -rebase, merge, conflict resolution, or update that changes the head restarts -every head-bound gate. - -## Merge gate - -Require all applicable conditions: - -- every intended ticket change is committed and represented by the candidate - diff, with any unrelated artifacts classified, preserved, and proven - irrelevant; -- local focused and full validation passed; -- required remote checks passed; -- no undispositioned actionable conversation comment, formal review, connector - feedback, or review thread remains; -- the repository-owned local review has a clean aggregate bound to the current - head, with any later base drift explicitly retained or re-reviewed; -- every applicable required human and connector review is current under - repository policy; -- no merge conflict or superseding PR exists; -- the diff still satisfies one ticket and its non-goals; -- rollout or migration prerequisites required before merge are complete. - -CI success alone is not a clean review. A stale approval alone is not a clean -review. If the repository has no CI, record that fact and require its documented -local and review signals. - -## Feedback that should not expand the PR - -Keep these out unless the live ticket requires them: - -- speculative pre-release backfills; -- support for nonexistent legacy data; -- broad refactors unrelated to correctness; -- defensive abstraction without demonstrated duplication; -- product polish or future-hardening suggestions; -- changes owned by another epic. diff --git a/skills/implement-epic-sequence/scripts/tests/test_review_integration_contract.py b/skills/implement-epic-sequence/scripts/tests/test_review_integration_contract.py deleted file mode 100644 index ea0f56b..0000000 --- a/skills/implement-epic-sequence/scripts/tests/test_review_integration_contract.py +++ /dev/null @@ -1,120 +0,0 @@ -from __future__ import annotations - -import importlib.util -import json -import unittest -from pathlib import Path - -SKILL_ROOT = Path(__file__).resolve().parents[2] -REPOSITORY_ROOT = SKILL_ROOT.parents[1] -REVIEW_SUITE = REPOSITORY_ROOT / "review-suite" -SPEC = importlib.util.spec_from_file_location( - "review_contract_validator", REVIEW_SUITE / "scripts" / "validate.py" -) -assert SPEC and SPEC.loader -VALIDATOR = importlib.util.module_from_spec(SPEC) -SPEC.loader.exec_module(VALIDATOR) - - -def load(path: Path): - return json.loads(path.read_text()) - - -class ReviewIntegrationContractTests(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.skill = (SKILL_ROOT / "SKILL.md").read_text() - cls.gates = ( - SKILL_ROOT / "references" / "review-and-merge-gates.md" - ).read_text() - cls.github = (SKILL_ROOT / "references" / "github.md").read_text() - cls.cases = { - case["id"]: case for case in load(SKILL_ROOT / "evals" / "cases.json") - } - cls.results = { - item["case_id"]: item - for item in load(SKILL_ROOT / "evals" / "results.json") - } - - def test_review_suite_is_the_only_local_review_dependency(self): - combined = self.skill + self.gates - self.assertNotIn("code-review-pro", combined) - self.assertIn("single local\nadversarial-review dependency", self.skill) - self.assertIn("`review-code-change`", self.skill) - self.assertIn("explicit\nmissing-dependency result", self.skill) - self.assertIn("Do not substitute another review skill", self.gates) - - def test_epic_skill_owns_mechanics_not_lens_rubrics(self): - self.assertIn("primary execution context", self.skill) - self.assertIn("commit, push, build a new", self.skill) - self.assertIn("without reproducing its lens rubrics or ordering", self.skill) - self.assertNotIn("Review correctness, acceptance criteria", self.skill) - self.assertIn("thread remains", self.skill) - self.assertIn("current-head signal", self.github) - - def test_review_context_is_raw_fresh_and_read_only(self): - combined = self.skill + self.gates - self.assertIn("fresh or minimally inherited read-only context", combined) - self.assertIn("raw ticket, repository", combined) - self.assertIn("Exclude the implementation transcript", combined) - self.assertIn("worktree state", self.gates) - - def test_fix_scope_cycles_and_deferred_findings_are_bounded(self): - self.assertIn("blocking and strong-recommendation findings", self.skill) - self.assertIn("Preserve deferred findings", self.skill) - self.assertIn("at most three full fix/re-review cycles", self.skill) - self.assertIn("Keep the PR open", self.skill) - - def test_base_drift_is_risk_based_without_weakening_remote_gates(self): - combined = self.skill + self.gates + self.github - self.assertIn("effective diff", combined) - self.assertIn("resulting tree are unchanged", combined) - self.assertIn("no conflict or relevant overlap", combined) - self.assertIn("repository policy permits retention", combined) - self.assertIn("rerun every affected gate", combined) - self.assertIn("required human and connector review", self.skill) - self.assertIn("zero unresolved connector-authored review threads", self.github) - self.assertIn("CI success alone is not a clean review", self.gates) - - def test_forward_cases_cover_ticket_scenarios_and_suite_results_conform(self): - required = { - "correctness-fix-and-rereview", - "solution-redesign-early-exit", - "code-simplicity-targeted-correctness", - "unrelated-base-drift-retained", - "relevant-base-drift-restarts-gates", - "missing-review-dependency", - "clean-local-review-remote-policy-pending", - } - self.assertEqual(required, set(self.cases)) - self.assertEqual(required, set(self.results)) - for case_id, case in self.cases.items(): - with self.subTest(case=case_id): - self.assertEqual([], VALIDATOR.validate_result(case["suite_result"])) - if followup := case.get("followup_suite_result"): - self.assertEqual([], VALIDATOR.validate_result(followup)) - - def test_forward_results_preserve_required_boundaries(self): - self.assertEqual( - "blocked", self.results["missing-review-dependency"]["workflow_state"] - ) - self.assertEqual( - "merged", - self.results["unrelated-base-drift-retained"]["workflow_state"], - ) - self.assertIn( - "retain head-bound validation and review evidence", - self.results["unrelated-base-drift-retained"]["observed_actions"], - ) - self.assertEqual( - "affected_gates_invalidated", - self.results["relevant-base-drift-restarts-gates"]["workflow_state"], - ) - remote = self.results["clean-local-review-remote-policy-pending"] - self.assertIn("do not merge", remote["observed_actions"]) - targeted = self.results["code-simplicity-targeted-correctness"] - self.assertIn("run targeted correctness", targeted["observed_actions"]) - - -if __name__ == "__main__": - unittest.main() diff --git a/skills/implement-epic/SKILL.md b/skills/implement-epic/SKILL.md new file mode 100644 index 0000000..47eed53 --- /dev/null +++ b/skills/implement-epic/SKILL.md @@ -0,0 +1,182 @@ +--- +name: implement-epic +description: Implement one or more GitHub or Linear epics through their live dependency graphs by selecting ready PR-sized children and invoking the repository-owned implement-ticket skill exactly once per child. Use when an agent should sequence an entire epic, one named child, or a named subset; refresh graph state after merges; preserve authority and isolation boundaries; and verify epic-wide acceptance and closeout without duplicating single-ticket implementation, review, PR, merge, or cleanup mechanics. +--- + +# Implement Epic + +Orchestrate the live work graph. Delegate each selected child to +`implement-ticket`; never reproduce its one-ticket workflow. + +## Require the ticket skill + +Before an epic run, verify that `implement-ticket` is available, readable, and +supports `ready_pr`, `merged`, `blocked`, and `requires_epic` terminal results. +Return `blocked` before mutation when the dependency or result contract is +missing or untrustworthy. Do not substitute a generic implementation agent, +inline a copy of the ticket workflow, or weaken any gate. + +`implement-ticket` owns ticket readiness, isolated implementation, validation, +`review-code-change`, PR state, remote gates, merge, tracker transition, per-PR +cleanup, and terminal evidence. Do not invoke individual review lenses or +`review-code-change` directly from this skill. + +## Load graph and closeout references + +- Read [the GitHub graph adapter](references/github.md) whenever GitHub owns + parent, child, or dependency state. +- Read [the Linear graph adapter](references/linear.md) whenever Linear owns + parent, child, dependency, or status state. +- Always read [epic closeout](references/closeout.md) before closing a parent or + umbrella epic. + +Resolve issue-tracker ownership independently from repository and PR-host +ownership. Let `implement-ticket` load the applicable single-ticket adapters. + +## Resolve the epic contract + +Before selecting work, discover or receive and verify: + +- the in-scope epic, epic series, named child, or named child subset; +- owning tracker, live native parent/sub-issue and blocker relationships, and + current issue states; +- repository, PR host, current remote base, and applicable local instructions; +- named architecture, design, contract, migration, and rollout documents; +- completion policy: ready PRs only, merge children after gates, or merge plus + separately authorized epic closeout; +- serial execution by default, with parallel execution only when explicitly + authorized and proven non-overlapping; and +- authority for child execution, merge, manual transitions, graph edits, + follow-up creation, branch deletion, parent closeout, deployment, production + mutation, and destructive operations. + +Pass authority into `implement-ticket` without expansion. Ready-PR authority +does not imply merge. Child merge authority does not imply parent closeout. +Words such as `finish`, `complete`, or `end to end` do not independently grant +merge, graph mutation, deployment, or closeout authority. + +Use this source order: + +1. Current user instructions. +2. Live epic, child, dependency, branch, and PR state. +3. Repository instructions. +4. Named specifications and rollout documents. +5. Current code and tests. +6. Prior summaries or memory. + +Stop on material conflicts rather than selecting a convenient interpretation. + +## Run the graph loop + +Repeat until the requested scope reaches its completion policy or a genuine +blocker requires user input. + +### 1. Refresh live state + +- Read every in-scope epic and its current children. +- Read native parent, sub-issue, `blockedBy`, and `blocking` relationships. +- Read dispositions and delivered outcomes of closed blockers. +- Inspect existing branches and open or merged PRs before selecting a child. +- Separate the serial critical-path recommendation from other parallel-ready + work. + +Never infer the current graph from an old plan, issue list order, Markdown task +list, label, or previous loop iteration when native relationships are available. + +### 2. Select one child + +Select an open, in-scope, PR-sized child only when native graph state shows no +open blocker. At the graph boundary, verify that every required closed-blocker +outcome exists in its authoritative repository, artifact registry, tracker, or +environment. Treat canceled or not-planned blockers with missing required +outcomes as unresolved. + +When multiple children are ready, prefer contracts and additive foundations +before consumers or cutovers, then prefer the child that unlocks the most +downstream work without widening scope. Do not absorb a missing sibling outcome +into the selected child. + +### 3. Invoke exactly one ticket execution + +Invoke `implement-ticket` once with a concise handoff containing: + +- selected ticket identity and owning tracker; +- parent outcome and only the dependency/sibling evidence needed for safe + independent shipping; +- repository, PR host, base, and named specifications; +- completion policy and every granted or withheld authority; and +- any epic-level rollout or merge-order constraint that qualifies the child. + +The primary context may follow `implement-ticket` directly. A delegated worker, +subagent, or equivalent context must have exclusive ownership of one verified +ticket worktree and branch. Never run two mutating contexts against the same +candidate. Parallelize only explicitly authorized children whose graph, +repository, file/contract ownership, rollout, and merge-order analysis proves no +material overlap. + +### 4. Verify the terminal result + +Do not trust a reported result until ticket identity, repository, base, +branch/worktree, candidate, PR, validation, review, remote-gate, merge, +transition, and cleanup evidence are internally consistent and match live state. + +- `ready_pr`: record the candidate and remaining gates. Do not count the child + complete or unblock dependents that require merge. Continue only with another + independently ready child when the requested scope permits it. +- `merged`: verify mainline and tracker evidence, then refresh the complete live + graph before any selection or completion claim. +- `blocked`: preserve the exact reason and partial artifacts. Never count it as + complete. Select another independently ready child only when the requested + scope permits; otherwise stop for the missing decision, outcome, or + capability. +- `requires_epic`: treat it as an invalid child selection or malformed handoff. + Stop or refresh and resolve scope; never recursively invoke this skill, bounce + back to `implement-ticket`, or flatten the returned epic into the child. + +### 5. Refresh or stop at the requested boundary + +After every verified merge or tracker transition, reread the native graph. Do +not reuse an earlier ready set. Report newly unblocked work even when the +requested boundary has been reached. + +For one named child, stop after that child's completion policy. For a named +subset, process only that subset in dependency order. Do not implement unnamed +siblings or close the parent. For a full epic, continue until closeout is +eligible or a genuine blocker remains. + +## Close epics conservatively + +Follow [epic closeout](references/closeout.md). Close a parent only with +explicit parent-close authority and current evidence that: + +- every required child and blocker outcome is satisfied; +- every required PR result is represented on the base; +- parent acceptance criteria hold against resulting behavior; +- required clean-main, documentation, migration, compatibility, rollout, and + cleanup checks pass; and +- the epic-wide late-feedback sweep has no undispositioned material finding. + +Validate and close each epic separately before an umbrella parent. A nearly +complete graph is not complete. + +## Stop conditions + +Stop and report `blocked` when: + +- live graph and requested scope conflict materially; +- a required dependency outcome or `implement-ticket` capability is missing; +- a product, architecture, migration, destructive, or authorization decision is + unresolved; +- delegated mutation ownership or returned evidence is ambiguous; +- epic-wide validation or late feedback shows a required unresolved gap; or +- parent closeout lacks authority. + +Difficulty, ordinary CI wait time, or unrelated ready children are not blockers. + +## Report the epic result + +Report the requested scope, each invoked ticket and its terminal state, merged +and ready PRs, refreshed graph state, serial critical-path and parallel-ready +work, parent acceptance and closeout evidence, intentionally deferred work, and +one concrete next action. Never report a child or parent complete from stale or +unverified evidence. diff --git a/skills/implement-epic/agents/openai.yaml b/skills/implement-epic/agents/openai.yaml new file mode 100644 index 0000000..3e3d209 --- /dev/null +++ b/skills/implement-epic/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Implement Epic" + short_description: "Orchestrate epics through implement-ticket" + default_prompt: "Use $implement-epic to implement this epic through its live dependency graph and the repository-owned ticket workflow." diff --git a/skills/implement-epic/evals/cases.json b/skills/implement-epic/evals/cases.json new file mode 100644 index 0000000..3b6e2da --- /dev/null +++ b/skills/implement-epic/evals/cases.json @@ -0,0 +1,78 @@ +[ + { + "id": "two-child-refresh-chain", + "request": "Implement epic G-100 through authorized child merges.", + "graph": "G-101 is open and ready; G-102 is open and natively blocked by G-101.", + "ticket_results": ["G-101 merged with verified transition", "after refresh G-102 has no open blocker", "G-102 merged"] + }, + { + "id": "named-child-boundary", + "request": "Implement and merge G-111 only from epic G-110.", + "graph": "G-111 and G-112 are independently ready.", + "authority": "merge G-111; no parent-close or sibling authority" + }, + { + "id": "ready-pr-does-not-unblock", + "request": "Implement epic G-120 to ready PRs only.", + "graph": "G-122 depends on G-121 merging.", + "ticket_results": ["G-121 ready_pr with merge pending"] + }, + { + "id": "blocked-missing-sibling-outcome", + "request": "Implement epic G-130.", + "graph": "G-131 appears ready but requires an unimplemented contract owned by sibling G-132.", + "ticket_results": ["G-131 blocked with missing G-132 outcome"] + }, + { + "id": "blocked-then-independent-ready", + "request": "Implement all currently possible work in epic G-140.", + "graph": "G-141 and G-142 are independently ready.", + "ticket_results": ["G-141 blocked by a missing product decision", "G-142 merged"] + }, + { + "id": "missing-implement-ticket", + "request": "Implement epic G-150.", + "graph": "One child is ready.", + "capabilities": [] + }, + { + "id": "exclusive-delegated-worktree", + "request": "Delegate ready child G-161 from epic G-160.", + "graph": "G-161 is the only ready child.", + "delegation": "Worker returns a candidate from a named exclusive branch/worktree plus terminal evidence." + }, + { + "id": "ticket-evidence-pass-through", + "request": "Continue epic G-170 after child G-171.", + "ticket_results": ["G-171 merged with validation, review, CI, human, connector, comment, and thread evidence"] + }, + { + "id": "late-feedback-blocks-closeout", + "request": "Close epic G-180 after all children merged.", + "graph": "Every required child is closed.", + "closeout": "A merged child PR has an undispositioned late blocking correctness finding." + }, + { + "id": "authorized-full-epic-closeout", + "request": "Implement and close epic G-190.", + "authority": "child merge and parent closeout explicitly granted", + "closeout": "All children merged and closed; graph, base representation, acceptance, clean-main, rollout, and late-feedback gates pass." + }, + { + "id": "umbrella-closeout-separately", + "request": "Close umbrella G-200 after epics G-201 and G-202.", + "authority": "parent and umbrella closeout granted", + "closeout": "G-201 passes all gates; G-202 still has a required open child." + }, + { + "id": "unexpected-requires-epic-child-result", + "request": "Continue epic G-210 after selecting child G-211.", + "ticket_results": ["G-211 returns requires_epic for G-211"] + }, + { + "id": "parallel-nonoverlap-required", + "request": "Implement ready children G-221 and G-222 in parallel.", + "graph": "Both are graph-independent, but both edit the same shared contract and have ordered rollout semantics.", + "authority": "parallel execution requested" + } +] diff --git a/skills/implement-epic/evals/results.json b/skills/implement-epic/evals/results.json new file mode 100644 index 0000000..641af0e --- /dev/null +++ b/skills/implement-epic/evals/results.json @@ -0,0 +1,67 @@ +[ + { + "case_id": "two-child-refresh-chain", + "workflow_state": "epic_children_merged", + "required_actions": ["invoke G-101 once", "verify merged", "refresh the complete graph", "select G-102 only after refresh", "invoke G-102 once"] + }, + { + "case_id": "named-child-boundary", + "workflow_state": "requested_child_merged", + "required_actions": ["invoke only G-111", "refresh and report ready work", "leave G-112 and G-110 open"] + }, + { + "case_id": "ready-pr-does-not-unblock", + "workflow_state": "waiting_for_child_merge", + "required_actions": ["record G-121 ready_pr", "do not select G-122", "do not claim child or epic complete"] + }, + { + "case_id": "blocked-missing-sibling-outcome", + "workflow_state": "blocked", + "required_actions": ["preserve the G-131 reason", "do not absorb G-132", "do not count G-131 complete"] + }, + { + "case_id": "blocked-then-independent-ready", + "workflow_state": "partial_progress", + "required_actions": ["preserve G-141 blocked", "select independently ready G-142", "refresh after G-142 merge"] + }, + { + "case_id": "missing-implement-ticket", + "workflow_state": "blocked", + "required_actions": ["fail before mutation", "name implement-ticket", "do not inline a ticket workflow"] + }, + { + "case_id": "exclusive-delegated-worktree", + "workflow_state": "ticket_result_verified", + "required_actions": ["verify exclusive ownership", "verify returned candidate and worktree", "reject ambiguous mutation state"] + }, + { + "case_id": "ticket-evidence-pass-through", + "workflow_state": "graph_refresh_required", + "required_actions": ["verify ticket evidence without reproducing review rubrics", "verify merge and transition", "refresh graph"] + }, + { + "case_id": "late-feedback-blocks-closeout", + "workflow_state": "closeout_blocked", + "required_actions": ["keep G-180 open", "record the finding", "route authorized remediation through implement-ticket"] + }, + { + "case_id": "authorized-full-epic-closeout", + "workflow_state": "epic_closed", + "required_actions": ["verify every closeout gate", "close G-190", "report final graph and evidence"] + }, + { + "case_id": "umbrella-closeout-separately", + "workflow_state": "umbrella_open", + "required_actions": ["verify G-201 separately", "keep G-202 open", "keep G-200 open"] + }, + { + "case_id": "unexpected-requires-epic-child-result", + "workflow_state": "blocked", + "required_actions": ["treat handoff as invalid", "do not recurse or bounce", "refresh or request scope correction"] + }, + { + "case_id": "parallel-nonoverlap-required", + "workflow_state": "serial_execution_required", + "required_actions": ["reject parallel mutation", "sequence shared-contract work", "recompute readiness after each result"] + } +] diff --git a/skills/implement-epic/references/closeout.md b/skills/implement-epic/references/closeout.md new file mode 100644 index 0000000..c5dc91f --- /dev/null +++ b/skills/implement-epic/references/closeout.md @@ -0,0 +1,55 @@ +# Epic closeout + +Use current tracker, repository, and PR evidence. Do not close a parent from +merged-PR count or stale graph state. + +## Verify every required outcome + +- Reread the complete native child and blocker graph. +- Confirm every required child disposition and every required outcome from a + closed, canceled, duplicate, or superseded blocker. +- Confirm every required PR result is represented on the current remote base. +- Verify parent acceptance criteria against resulting behavior, not ticket + labels or administrative state. +- Run required clean-main validation and any parent-level smoke check. +- Verify required documentation, migration, compatibility, rollout, deployment, + and cleanup outcomes. Deployment and production mutation still require their + own authority. + +Keep the parent open when a not-planned or canceled child leaves an unsatisfied +outcome. + +## Sweep late feedback + +Before closeout, reread conversation comments, formal reviews, connector +feedback, and thread state for every merged PR in scope. Record a disposition +for every late actionable item. + +When late feedback reveals a required correctness, security, acceptance, +architecture, or validation gap: + +- keep the epic open; +- use or propose the correctly owned ticket; +- invoke `implement-ticket` from a fresh branch based on current remote state + when implementation is authorized; and +- require the normal one-ticket validation, review, merge, transition, and + cleanup result before retrying closeout. + +Never reopen or reuse a merged feature branch as the fix path. Do not reproduce +single-ticket review or merge mechanics here. + +## Apply close authority + +Parent-close authority is separate from child merge and manual child-transition +authority. Without explicit close authority, report that every closeout gate +passes and leave the parent open. + +Close each epic separately. For a series or umbrella, verify all component epics +and umbrella acceptance criteria before the umbrella transition. + +## Report closeout + +Record child dispositions, merged PRs and base representation, clean-main and +acceptance evidence, late-feedback dispositions, remaining deferred or blocked +work, the parent transition performed or withheld, and the exact reason any +parent remains open. diff --git a/skills/implement-epic/references/github.md b/skills/implement-epic/references/github.md new file mode 100644 index 0000000..63712f1 --- /dev/null +++ b/skills/implement-epic/references/github.md @@ -0,0 +1,49 @@ +# GitHub epic graph adapter + +Use this adapter only for parent, child, dependency, selection, refresh, and +epic-closeout state owned by GitHub. `implement-ticket` owns GitHub PR-host and +single-ticket mechanics. + +## Read native graph state + +- Resolve the repository and live epic identity. +- Read epic and child titles, bodies, issue types, states, and scope-affecting + comments. +- Read native `parent`, `subIssues`, `blockedBy`, and `blocking` relationships + through GraphQL or an equivalent structured API. +- Read relevant closed-blocker dispositions and verify their delivered outcomes + in the authoritative source. +- Inspect existing open and merged PR identities only to prevent duplicate child + selection; delegate detailed PR state to `implement-ticket`. + +Do not derive graph ownership or dependency order from issue number, title, +label, Markdown task list, or prose when native relationships exist. + +## Select and refresh + +Choose only an open in-scope child whose native `blockedBy` set has no open +issue and whose required closed-blocker outcomes exist. Treat canceled or +not-planned blockers with missing outcomes as unresolved. + +After a returned `merged` result, verify the GitHub issue transition and reread +the complete parent/sub-issue/blocker graph before selecting another child or +claiming completion. A `ready_pr` result does not satisfy a dependency that +requires merge. + +When duplicate implementation paths exist, do not choose a competing path; pass +the canonical ownership evidence into `implement-ticket` or return `blocked` +when ownership is unresolved. + +## Separate tracker and PR host + +When GitHub owns issue state and another system hosts code or PRs, retain GitHub +as the graph authority. When Linear owns issue state and GitHub hosts PRs, do +not inspect or mutate same-numbered GitHub issues as graph substitutes; use the +Linear adapter and let `implement-ticket` use its GitHub PR-host adapter. + +## Close GitHub epics + +With explicit parent-close authority, transition the GitHub epic only after the +shared closeout reference passes and live native relationships show no required +open child or blocker. Record evidence in the issue when authorized and useful. +Never infer parent completion solely from merged PR count. diff --git a/skills/implement-epic/references/linear.md b/skills/implement-epic/references/linear.md new file mode 100644 index 0000000..fc055c5 --- /dev/null +++ b/skills/implement-epic/references/linear.md @@ -0,0 +1,43 @@ +# Linear epic graph adapter + +Use Linear as the authority when it owns epic, parent, child, dependency, or +status state. Let `implement-ticket` resolve repository and PR-host mechanics. + +## Read native graph state + +- Read the live parent or epic, project context, children, comments, and linked + specifications. +- Read explicit blocking and duplicate relationships plus current dispositions. +- Verify completed, canceled, duplicate, or superseding blocker outcomes in + their authoritative repository, artifact registry, tracker, or environment. +- Resolve repository and PR identities only far enough to avoid duplicate child + selection and build the `implement-ticket` handoff. + +Do not use list order, priority, labels, or an old prompt as dependency order +when explicit relationships are available. When Linear cannot express a required +dependency, report the limitation; do not silently promote prose into native +graph state. + +## Select and refresh + +Select only an open in-scope child with no unresolved blocker and verified +prerequisite outcomes. Treat canceled or not-planned blockers with missing +outcomes as unresolved. + +After `implement-ticket` returns `merged`, verify the expected Linear transition +and reread the complete epic relationship state. Do not treat `ready_pr` as a +completed child or unblock a dependent that requires merge. + +## Separate tracker and PR host + +Pass Linear ticket identity, parent outcome, relationship evidence, and allowed +status transitions into `implement-ticket`. When GitHub hosts the PR, do not use +a same-numbered GitHub issue as a substitute for Linear state. + +## Close Linear epics + +Apply the shared closeout reference and require explicit parent-close authority. +Update the epic only when every required child and blocker outcome is satisfied, +acceptance holds on the base, and late feedback is dispositioned. Preserve +deferred or canceled scope in the final report; never count an unmet canceled +outcome as complete. diff --git a/skills/implement-epic/scripts/tests/test_orchestration_contract.py b/skills/implement-epic/scripts/tests/test_orchestration_contract.py new file mode 100644 index 0000000..7475a8a --- /dev/null +++ b/skills/implement-epic/scripts/tests/test_orchestration_contract.py @@ -0,0 +1,164 @@ +from __future__ import annotations + +import json +import re +import unittest +from pathlib import Path + +SKILL_ROOT = Path(__file__).resolve().parents[2] +REPOSITORY_ROOT = SKILL_ROOT.parents[1] + + +def read(path: Path) -> str: + return path.read_text() + + +def compact(value: str) -> str: + return re.sub(r"\s+", " ", value).strip() + + +class ImplementEpicContractTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.skill = read(SKILL_ROOT / "SKILL.md") + cls.github = read(SKILL_ROOT / "references" / "github.md") + cls.linear = read(SKILL_ROOT / "references" / "linear.md") + cls.closeout = read(SKILL_ROOT / "references" / "closeout.md") + cls.contract = compact(cls.skill + cls.github + cls.linear + cls.closeout) + cls.cases = { + item["id"]: item + for item in json.loads(read(SKILL_ROOT / "evals" / "cases.json")) + } + cls.results = { + item["case_id"]: item + for item in json.loads(read(SKILL_ROOT / "evals" / "results.json")) + } + + def test_canonical_name_and_metadata(self): + self.assertTrue(self.skill.startswith("---\nname: implement-epic\n")) + self.assertNotIn("implement-epic-sequence", self.contract) + self.assertFalse((REPOSITORY_ROOT / "skills/implement-epic-sequence").exists()) + metadata = read(SKILL_ROOT / "agents" / "openai.yaml") + self.assertIn('display_name: "Implement Epic"', metadata) + self.assertIn("$implement-epic", metadata) + self.assertNotIn("$implement-epic-sequence", metadata) + + def test_implement_ticket_is_required_and_owns_one_ticket(self): + self.assertIn("verify that `implement-ticket` is available", self.contract) + self.assertIn( + "supports `ready_pr`, `merged`, `blocked`, and `requires_epic`", + self.contract, + ) + self.assertIn("Return `blocked` before mutation", self.contract) + self.assertIn("Invoke `implement-ticket` once", self.skill) + self.assertIn("never reproduce its one-ticket workflow", self.skill) + + def test_epic_does_not_own_review_or_ticket_mechanics(self): + self.assertIn("Do not invoke individual review lenses", self.contract) + self.assertIn( + "Do not invoke individual review lenses or `review-code-change` directly", + self.contract, + ) + self.assertNotIn("review-solution-simplicity", self.contract) + self.assertNotIn("review-correctness", self.contract) + self.assertNotIn("review-code-simplicity", self.contract) + self.assertNotIn("fix/re-review cycles", self.contract) + self.assertNotIn("per-PR cleanup", self.closeout) + + def test_graph_selection_and_refresh_use_live_native_state(self): + self.assertIn( + "native parent, sub-issue, `blockedBy`, and `blocking`", self.contract + ) + self.assertIn("After every verified merge", self.contract) + self.assertIn("reread the native graph", self.contract) + self.assertIn("Do not reuse an earlier ready set", self.contract) + self.assertIn( + "A `ready_pr` result does not satisfy a dependency", self.contract + ) + + def test_result_handling_is_fail_closed(self): + for state in ("ready_pr", "merged", "blocked", "requires_epic"): + self.assertIn(f"`{state}`", self.contract) + self.assertIn("Do not count the child complete", self.contract) + self.assertIn("Never count it as complete", self.contract) + self.assertIn("never recursively invoke this skill", self.contract) + + def test_scope_and_authority_do_not_expand(self): + self.assertIn( + "Pass authority into `implement-ticket` without expansion", self.contract + ) + self.assertIn( + "Child merge authority does not imply parent closeout", self.contract + ) + self.assertIn("For one named child, stop", self.contract) + self.assertIn("Do not implement unnamed siblings", self.contract) + self.assertIn("Parent-close authority is separate", self.contract) + + def test_delegated_mutation_is_exclusive(self): + self.assertIn( + "exclusive ownership of one verified ticket worktree", self.contract + ) + self.assertIn( + "Never run two mutating contexts against the same candidate", self.contract + ) + self.assertIn( + "reject parallel mutation", + self.results["parallel-nonoverlap-required"]["required_actions"], + ) + self.assertEqual( + "serial_execution_required", + self.results["parallel-nonoverlap-required"]["workflow_state"], + ) + + def test_tracker_and_pr_host_ownership_are_separate(self): + self.assertIn("Resolve issue-tracker ownership independently", self.contract) + self.assertIn("`implement-ticket` owns GitHub PR-host", self.contract) + self.assertIn("same-numbered GitHub issues", self.contract) + self.assertIn("same-numbered GitHub issue", self.contract) + + def test_closeout_is_epic_wide_and_conservative(self): + self.assertIn("Verify parent acceptance criteria", self.contract) + self.assertIn("Sweep late feedback", self.contract) + self.assertIn("keep the epic open", self.contract) + self.assertIn("invoke `implement-ticket`", self.contract) + self.assertIn("Close each epic separately", self.contract) + + def test_forward_cases_cover_composed_contract(self): + required = { + "two-child-refresh-chain", + "named-child-boundary", + "ready-pr-does-not-unblock", + "blocked-missing-sibling-outcome", + "blocked-then-independent-ready", + "missing-implement-ticket", + "exclusive-delegated-worktree", + "ticket-evidence-pass-through", + "late-feedback-blocks-closeout", + "authorized-full-epic-closeout", + "umbrella-closeout-separately", + "unexpected-requires-epic-child-result", + "parallel-nonoverlap-required", + } + self.assertEqual(required, set(self.cases)) + self.assertEqual(required, set(self.results)) + + def test_forward_results_preserve_critical_boundaries(self): + self.assertEqual( + "waiting_for_child_merge", + self.results["ready-pr-does-not-unblock"]["workflow_state"], + ) + self.assertEqual( + "blocked", self.results["missing-implement-ticket"]["workflow_state"] + ) + self.assertEqual( + "closeout_blocked", + self.results["late-feedback-blocks-closeout"]["workflow_state"], + ) + self.assertEqual( + "blocked", + self.results["unexpected-requires-epic-child-result"]["workflow_state"], + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/skills/implement-ticket/SKILL.md b/skills/implement-ticket/SKILL.md index ffe61ff..7fda69f 100644 --- a/skills/implement-ticket/SKILL.md +++ b/skills/implement-ticket/SKILL.md @@ -11,9 +11,9 @@ as execution state; use old plans or summaries only for orientation. Treat this skill as the canonical owner of generic single-ticket readiness, implementation, review, PR, merge, base-drift, feedback, tracker-transition, -cleanup, and terminal-reporting rules. Duplication in `implement-epic-sequence` -is temporary until the dependent epic-orchestrator refactor removes it. Do not -create a third shared workflow abstraction merely to eliminate that transition. +cleanup, and terminal-reporting rules. `implement-epic` consumes this contract +for each selected child. Do not copy these rules back into epic orchestration or +create a third shared workflow abstraction. ## Load the applicable references diff --git a/skills/implement-ticket/scripts/tests/test_implement_ticket_contract.py b/skills/implement-ticket/scripts/tests/test_implement_ticket_contract.py index 3ad43cd..1a1cb9e 100644 --- a/skills/implement-ticket/scripts/tests/test_implement_ticket_contract.py +++ b/skills/implement-ticket/scripts/tests/test_implement_ticket_contract.py @@ -58,9 +58,10 @@ def test_scope_is_exactly_one_ticket(self): self.skill_compact, ) self.assertIn( - "Duplication in `implement-epic-sequence` is temporary", + "`implement-epic` consumes this contract", self.skill_compact, ) + self.assertNotIn("implement-epic-sequence", self.skill_compact) def test_epic_routing_is_pre_mutation_and_acyclic(self): self.assertIn("Guard whole-epic scope before mutation", self.skill_compact)