Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ summary: Chronological history of repository and skill changes.

# Changelog

## 2026-07-20 — Composed ticket and epic execution
## 2026-07-20 — Portable ticket and epic execution

- feat: make ticket and epic execution runtime agnostic
- feat: compose epic execution through implement-ticket
(7c4e500a35d48b5dba311094b4d34d8ca97f25a1)

## 2026-07-19 — Epic workflow and review contract cleanup

Expand Down
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ A personal monorepo for agent skills and supporting scripts.
evaluation fixtures shared by repository-owned review skills
- `justfile` — common tasks for testing, validation, and formatting

Current skills:
Current reusable agent skills:

- `skills/implement-ticket` — implement exactly one standalone ticket or named
epic child through isolated execution, repository-owned review, PR gates, and
Expand All @@ -29,6 +29,18 @@ Current skills:
- `skills/review-code-simplicity` — reduce local cognitive load through
behavior-preserving reuse, DRY, control-flow, and test simplification

The composed implementation dependency chain is:

```text
implement-epic
└── implement-ticket
└── review-code-change
```

Compatible runtimes may provide named subagents or equivalent isolated
implementation and review contexts. OpenAI-facing files under `agents/` are
optional discovery metadata, not part of the skills' portable contracts.

## Quick Start

Run the core checks:
Expand All @@ -52,7 +64,7 @@ Validate a review packet and result together:
python3 review-suite/scripts/validate.py pair packet.json result.json
```

Run deterministic local evals (no Codex required):
Run deterministic local evals without an agent runtime:

```bash
just eval-prepare-changesets
Expand Down
27 changes: 27 additions & 0 deletions skills/implement-epic/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,33 @@ inline a copy of the ticket workflow, or weaken any gate.
cleanup, and terminal evidence. Do not invoke individual review lenses or
`review-code-change` directly from this skill.

## Require compatible runtime capabilities

A compatible agentic runtime must be able to:

- load `implement-epic` and repository-owned `implement-ticket` by stable skill
name or an equivalent repository-owned dependency mechanism;
- read repository instructions plus structured GitHub or Linear relationships;
- retain task state while repeating the dependency-driven graph loop;
- invoke one mutating ticket execution in an exclusively owned branch/worktree
context and wait for its terminal result;
- inspect and verify returned candidate, PR, tracker, and base evidence;
- poll or wait for asynchronous ticket, CI, review, and graph-transition gates;
and
- preserve a fresh read-only reviewer context through the ticket result without
taking ownership of local review.

The portable dependency chain is `implement-epic` → `implement-ticket` →
`review-code-change`. Verify `implement-ticket` directly and require its result
to prove that its own review dependency and applicable capabilities were
available. Do not make this skill invoke `review-code-change` itself.

Stop before child mutation with an explicit limitation when an applicable
capability or dependency is unavailable. Product-specific discovery metadata
such as `agents/openai.yaml` may exist, but it does not constrain the operating
contract or require a particular agent product. Terms such as worker and
subagent describe possible isolated execution roles, not required product APIs.

## Load graph and closeout references

- Read [the GitHub graph adapter](references/github.md) whenever GitHub owns
Expand Down
27 changes: 27 additions & 0 deletions skills/implement-epic/evals/cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"id": "exclusive-delegated-worktree",
"request": "Delegate ready child G-161 from epic G-160.",
"graph": "G-161 is the only ready child.",
"runtime_profile": "Repository-owned skills and named subagents are available.",
"delegation": "Worker returns a candidate from a named exclusive branch/worktree plus terminal evidence."
},
{
Expand Down Expand Up @@ -74,5 +75,31 @@
"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"
},
{
"id": "equivalent-isolated-context-profile",
"request": "Implement epic G-230 through authorized child merges.",
"graph": "G-231 is the only open ready child.",
"runtime_profile": "The runtime calls isolated units jobs rather than subagents, but provides one exclusive mutating job, a fresh read-only audit job inside implement-ticket, retained loop state, and asynchronous waiting.",
"ticket_results": ["G-231 merged with internally consistent evidence"]
},
{
"id": "missing-review-dependency-through-ticket",
"request": "Implement epic G-240.",
"graph": "G-241 is the only open ready child.",
"runtime_profile": "implement-ticket is available, but repository-owned review-code-change cannot be loaded.",
"ticket_results": ["G-241 blocked before mutation because review-code-change is unavailable"]
},
{
"id": "missing-isolation-capability",
"request": "Implement epic G-250.",
"graph": "G-251 is the only open ready child.",
"runtime_profile": "The runtime cannot create or verify an exclusively owned mutating ticket context."
},
{
"id": "missing-asynchronous-wait",
"request": "Implement epic G-260 through child merges.",
"graph": "G-261 is open and ready, with asynchronous CI and review gates required by its ticket workflow.",
"runtime_profile": "The runtime cannot poll, wait, or resume after asynchronous child gates."
}
]
20 changes: 20 additions & 0 deletions skills/implement-epic/evals/results.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,25 @@
"case_id": "parallel-nonoverlap-required",
"workflow_state": "serial_execution_required",
"required_actions": ["reject parallel mutation", "sequence shared-contract work", "recompute readiness after each result"]
},
{
"case_id": "equivalent-isolated-context-profile",
"workflow_state": "epic_children_merged",
"required_actions": ["accept equivalent isolated context terminology", "verify G-231 merged evidence", "refresh the complete graph", "preserve review ownership inside implement-ticket"]
},
{
"case_id": "missing-review-dependency-through-ticket",
"workflow_state": "blocked",
"required_actions": ["preserve the G-241 blocked result", "name review-code-change as unavailable", "do not invoke review directly", "do not count G-241 complete"]
},
{
"case_id": "missing-isolation-capability",
"workflow_state": "blocked",
"required_actions": ["name exclusive mutating context isolation as unavailable", "perform no child mutation", "do not weaken ownership checks"]
},
{
"case_id": "missing-asynchronous-wait",
"workflow_state": "blocked",
"required_actions": ["name asynchronous wait as unavailable", "perform no child mutation", "do not pretend ticket or remote gates passed"]
}
]
40 changes: 40 additions & 0 deletions skills/implement-epic/scripts/tests/test_orchestration_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ def setUpClass(cls):
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.eval_contract = compact(
read(SKILL_ROOT / "evals" / "cases.json")
+ read(SKILL_ROOT / "evals" / "results.json")
)
cls.cases = {
item["id"]: item
for item in json.loads(read(SKILL_ROOT / "evals" / "cases.json"))
Expand All @@ -43,6 +47,28 @@ def test_canonical_name_and_metadata(self):
self.assertIn("$implement-epic", metadata)
self.assertNotIn("$implement-epic-sequence", metadata)

def test_product_neutral_runtime_contract(self):
self.assertNotIn("Codex", self.contract)
self.assertNotIn("OpenAI", self.contract)
self.assertNotIn("Codex", self.eval_contract)
self.assertNotIn("OpenAI", self.eval_contract)
self.assertIn("compatible agentic runtime", self.contract)
self.assertIn(
"`implement-epic` and repository-owned `implement-ticket` by stable skill name",
self.contract,
)
self.assertIn(
"`implement-epic` → `implement-ticket` → `review-code-change`",
self.contract,
)
self.assertIn("retain task state", self.contract)
self.assertIn("poll or wait for asynchronous", self.contract)
self.assertIn(
"worker and subagent describe possible isolated execution roles",
self.contract,
)
self.assertIn("does not constrain the operating contract", self.contract)

def test_implement_ticket_is_required_and_owns_one_ticket(self):
self.assertIn("verify that `implement-ticket` is available", self.contract)
self.assertIn(
Expand Down Expand Up @@ -138,6 +164,10 @@ def test_forward_cases_cover_composed_contract(self):
"umbrella-closeout-separately",
"unexpected-requires-epic-child-result",
"parallel-nonoverlap-required",
"equivalent-isolated-context-profile",
"missing-review-dependency-through-ticket",
"missing-isolation-capability",
"missing-asynchronous-wait",
}
self.assertEqual(required, set(self.cases))
self.assertEqual(required, set(self.results))
Expand All @@ -158,6 +188,16 @@ def test_forward_results_preserve_critical_boundaries(self):
"blocked",
self.results["unexpected-requires-epic-child-result"]["workflow_state"],
)
self.assertEqual(
"epic_children_merged",
self.results["equivalent-isolated-context-profile"]["workflow_state"],
)
for case_id in (
"missing-review-dependency-through-ticket",
"missing-isolation-capability",
"missing-asynchronous-wait",
):
self.assertEqual("blocked", self.results[case_id]["workflow_state"])


if __name__ == "__main__":
Expand Down
15 changes: 9 additions & 6 deletions skills/implement-ticket/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ same-numbered issue from the PR host for the real tracker ticket.

A compatible agentic runtime must be able to:

- load this skill and repository-owned skill dependencies;
- load `implement-ticket` and repository-owned `review-code-change` by stable
skill name or an equivalent repository-owned dependency mechanism;
- read repository instructions, tracker state, and structured relationships;
- inspect and create isolated branch/worktree state;
- edit files, run commands, commit, push, and manage PRs when authorized;
Expand All @@ -46,7 +47,8 @@ A compatible agentic runtime must be able to:
Stop with an explicit missing-capability result when an applicable capability is
unavailable. Product-specific discovery metadata such as `agents/openai.yaml`
may exist, but it does not constrain the operating contract or require a
particular agent product.
particular agent product. Terms such as worker and subagent describe possible
isolated execution roles, not required product APIs.

## Resolve the operating contract

Expand Down Expand Up @@ -93,10 +95,11 @@ manual transition. State that consequence before publication or merge. Do not
use automatic closing syntax when its effect conflicts with the resolved
completion policy.

For a merge-inclusive run, verify before implementation that
`review-code-change` is available and readable. It is the only local
adversarial-review dependency. Return `blocked` when it is unavailable; do not
substitute a third-party skill, generic self-review, or unreviewed merge path.
Before implementation can produce a reviewed PR, verify that
`review-code-change` is available and readable by stable name or an equivalent
repository-owned dependency mechanism. It is the only local adversarial-review
dependency. Return `blocked` before mutation when it is unavailable; do not
substitute a third-party skill, generic self-review, or unreviewed path.

## Establish source-of-truth precedence

Expand Down
29 changes: 28 additions & 1 deletion skills/implement-ticket/evals/cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"request": "Implement GitHub issue G-21.",
"tracker_state": "Open standalone PR-sized ticket with complete requirements, no blockers, and no existing implementation.",
"authority": "ready_pr_only",
"capabilities": ["review-code-change", "isolated_worktree", "github_pr"]
"runtime_profile": "Repository-owned skills and named subagents are available.",
"capabilities": ["stable_skill_loading", "structured_tracker_relationships", "isolated_worktree", "command_execution", "github_pr", "named_read_only_reviewer", "asynchronous_wait", "thread_aware_feedback"]
},
{
"id": "authorized-merge-cleanup",
Expand Down Expand Up @@ -93,5 +94,31 @@
"tracker_state": "G-35 has children and requires whole-epic delivery.",
"incoming_handoff": "implement-ticket:requires-epic:github:G-35",
"capabilities": []
},
{
"id": "equivalent-isolated-context-profile",
"request": "Implement GitHub issue G-38 to a ready PR.",
"tracker_state": "G-38 is a ready standalone ticket with no competing implementation.",
"runtime_profile": "The runtime calls isolated units jobs rather than subagents, but provides an exclusive mutating job and a fresh read-only audit job with equivalent permissions and integrity checks.",
"capabilities": ["stable_skill_loading", "structured_tracker_relationships", "isolated_worktree", "command_execution", "github_pr", "equivalent_read_only_reviewer", "asynchronous_wait", "thread_aware_feedback"]
},
{
"id": "missing-review-code-change",
"request": "Implement GitHub issue G-39.",
"tracker_state": "G-39 is otherwise ready and independently shippable.",
"runtime_profile": "The runtime cannot load repository-owned review-code-change by stable name or an equivalent dependency mechanism."
},
{
"id": "missing-isolation-capability",
"request": "Implement GitHub issue G-40.",
"tracker_state": "G-40 is otherwise ready and independently shippable.",
"runtime_profile": "The runtime cannot create or verify an exclusively owned implementation branch/worktree context."
},
{
"id": "missing-asynchronous-wait",
"request": "Implement and merge GitHub issue G-41.",
"tracker_state": "G-41 is otherwise ready and requires asynchronous CI and review gates.",
"authority": "merge_after_gates",
"runtime_profile": "The runtime cannot poll, wait, or resume after asynchronous gates."
}
]
20 changes: 20 additions & 0 deletions skills/implement-ticket/evals/results.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,25 @@
"case_id": "repeated-epic-handoff",
"terminal_state": "blocked",
"required_actions": ["report routing cycle detected", "perform no mutation", "do not emit another requires_epic"]
},
{
"case_id": "equivalent-isolated-context-profile",
"terminal_state": "ready_pr",
"required_actions": ["accept equivalent isolated context terminology", "verify exclusive mutating ownership", "invoke fresh read-only review", "publish one PR"]
},
{
"case_id": "missing-review-code-change",
"terminal_state": "blocked",
"required_actions": ["name review-code-change as unavailable", "perform no implementation mutation", "do not substitute generic or third-party review"]
},
{
"case_id": "missing-isolation-capability",
"terminal_state": "blocked",
"required_actions": ["name exclusive implementation isolation as unavailable", "perform no implementation mutation", "preserve existing worktrees"]
},
{
"case_id": "missing-asynchronous-wait",
"terminal_state": "blocked",
"required_actions": ["name asynchronous wait as unavailable", "perform no implementation mutation", "do not pretend CI or review gates passed"]
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ def setUpClass(cls):
cls.linear_compact = compact(cls.linear)
cls.gates_compact = compact(cls.gates)
cls.result_compact = compact(cls.result)
cls.eval_contract = compact(
read(SKILL_ROOT / "evals" / "cases.json")
+ read(SKILL_ROOT / "evals" / "results.json")
)
cls.all_contract = compact(
cls.skill + cls.github + cls.linear + cls.gates + cls.result
)
Expand All @@ -45,8 +49,23 @@ def setUpClass(cls):
def test_frontmatter_and_product_neutral_contract(self):
self.assertTrue(self.skill.startswith("---\nname: implement-ticket\n"))
self.assertNotIn("Codex", self.all_contract)
self.assertNotIn("OpenAI", self.all_contract)
self.assertNotIn("Codex", self.eval_contract)
self.assertNotIn("OpenAI", self.eval_contract)
self.assertNotIn("code-review-pro", self.all_contract)
self.assertIn("compatible agentic runtime", self.skill)
self.assertIn(
"`implement-ticket` and repository-owned `review-code-change` by stable skill name",
self.skill_compact,
)
self.assertIn(
"worker and subagent describe possible isolated execution roles",
self.skill_compact,
)
self.assertIn(
"does not constrain the operating contract",
self.skill_compact,
)

def test_scope_is_exactly_one_ticket(self):
self.assertIn("one ticket per branch, worktree, and PR", self.skill_compact)
Expand Down Expand Up @@ -106,6 +125,7 @@ def test_authority_does_not_expand(self):

def test_review_dependency_and_integrity_are_preserved(self):
self.assertIn("only local adversarial-review dependency", self.all_contract)
self.assertIn("Return `blocked` before mutation", self.skill_compact)
self.assertIn("Do not substitute another skill", self.gates_compact)
self.assertIn(
"fresh or minimally inherited read-only context", self.gates_compact
Expand Down Expand Up @@ -162,6 +182,10 @@ def test_forward_cases_cover_required_boundaries(self):
"explicit-child-not-redirected",
"missing-implement-epic",
"repeated-epic-handoff",
"equivalent-isolated-context-profile",
"missing-review-code-change",
"missing-isolation-capability",
"missing-asynchronous-wait",
}
self.assertEqual(required, set(self.cases))
self.assertEqual(required, set(self.results))
Expand All @@ -184,6 +208,16 @@ def test_forward_results_enforce_routing_and_authority(self):
self.assertEqual(
"blocked", self.results["repeated-epic-handoff"]["terminal_state"]
)
self.assertEqual(
"ready_pr",
self.results["equivalent-isolated-context-profile"]["terminal_state"],
)
for case_id in (
"missing-review-code-change",
"missing-isolation-capability",
"missing-asynchronous-wait",
):
self.assertEqual("blocked", self.results[case_id]["terminal_state"])
self.assertIn(
"do not merge",
self.results["clean-local-review-remote-pending"]["required_actions"],
Expand Down
Loading