@jesdi/skills 0.0.30 → 0.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +12 -6
  2. package/package.json +2 -2
  3. package/skills/implement-spec/ARCHITECTURE.md +113 -0
  4. package/skills/implement-spec/README.md +214 -0
  5. package/skills/implement-spec/SKILL.md +18 -70
  6. package/skills/implement-spec/driver.py +580 -0
  7. package/skills/implement-spec/driver_agents.py +145 -0
  8. package/skills/implement-spec/driver_answers.py +79 -0
  9. package/skills/implement-spec/driver_decisions.py +152 -0
  10. package/skills/implement-spec/driver_final.py +505 -0
  11. package/skills/implement-spec/driver_fixloop.py +170 -0
  12. package/skills/implement-spec/driver_gate.py +47 -0
  13. package/skills/implement-spec/driver_graph.py +42 -0
  14. package/skills/implement-spec/driver_merge.py +105 -0
  15. package/skills/implement-spec/driver_report.py +115 -0
  16. package/skills/implement-spec/driver_review.py +126 -0
  17. package/skills/implement-spec/driver_review_page.py +51 -0
  18. package/skills/implement-spec/driver_run.py +262 -0
  19. package/skills/implement-spec/driver_slots.py +64 -0
  20. package/skills/implement-spec/driver_state.py +105 -0
  21. package/skills/implement-spec/driver_summary.py +40 -0
  22. package/skills/implement-spec/driver_tickets.py +132 -0
  23. package/skills/implement-spec/driver_tidy.py +57 -0
  24. package/skills/implement-spec/models.json +10 -0
  25. package/skills/implement-spec/prompts/conflict.md +32 -0
  26. package/skills/implement-spec/prompts/correction.md +4 -0
  27. package/skills/implement-spec/prompts/correctness.md +34 -0
  28. package/skills/implement-spec/prompts/final-fixer.md +36 -0
  29. package/skills/implement-spec/prompts/final-reviewer.md +36 -0
  30. package/skills/implement-spec/prompts/final-scoped.md +1 -0
  31. package/skills/implement-spec/prompts/fixer.md +35 -0
  32. package/skills/implement-spec/prompts/implementer.md +33 -0
  33. package/skills/implement-spec/prompts/interrupted.md +1 -0
  34. package/skills/implement-spec/prompts/moved.md +2 -0
  35. package/skills/implement-spec/prompts/overruled.md +5 -0
  36. package/skills/implement-spec/prompts/pr-body.md +19 -0
  37. package/skills/implement-spec/prompts/rebase-conflict.md +33 -0
  38. package/skills/implement-spec/prompts/rereview.md +3 -0
  39. package/skills/implement-spec/prompts/rerun.md +2 -0
  40. package/skills/implement-spec/prompts/reviewer.md +34 -0
  41. package/skills/implement-spec/prompts/stands.md +2 -0
  42. package/skills/implement-spec/prompts/test-writer.md +34 -0
  43. package/skills/implement-spec/prompts/timeout-restored.md +2 -0
  44. package/skills/implement-spec/prompts/timeout-shared.md +2 -0
  45. package/skills/implement-spec/prompts/timeout.md +2 -0
  46. package/skills/implement-spec/run.sh +101 -0
  47. package/skills/implement-spec/tests/conftest.py +38 -0
  48. package/skills/implement-spec/tests/test_agents.py +358 -0
  49. package/skills/implement-spec/tests/test_amendments.py +117 -0
  50. package/skills/implement-spec/tests/test_cli.py +220 -0
  51. package/skills/implement-spec/tests/test_decisions.py +434 -0
  52. package/skills/implement-spec/tests/test_driver.py +767 -0
  53. package/skills/implement-spec/tests/test_final.py +442 -0
  54. package/skills/implement-spec/tests/test_fixloop.py +523 -0
  55. package/skills/implement-spec/tests/test_graph.py +32 -0
  56. package/skills/implement-spec/tests/test_landing.py +355 -0
  57. package/skills/implement-spec/tests/test_merge.py +144 -0
  58. package/skills/implement-spec/tests/test_models.py +47 -0
  59. package/skills/implement-spec/tests/test_report.py +162 -0
  60. package/skills/implement-spec/tests/test_review.py +338 -0
  61. package/skills/implement-spec/tests/test_review_page.py +67 -0
  62. package/skills/implement-spec/tests/test_slots.py +32 -0
  63. package/skills/implement-spec/tests/test_state.py +68 -0
  64. package/skills/implement-spec/tests/test_stops.py +272 -0
  65. package/skills/implement-spec/tests/test_summary.py +28 -0
  66. package/skills/implement-spec/tests/test_tickets.py +72 -0
  67. package/skills/implement-spec/tests/test_tidy.py +582 -0
  68. package/skills/implement-spec/tests/test_walk.py +457 -0
  69. package/skills/review-page/template.html +1 -1
  70. package/skills/to-tickets/SKILL.md +19 -2
  71. package/skills-manifest.json +7 -7
  72. package/skills/implement-spec/history.md +0 -52
@@ -0,0 +1,57 @@
1
+ """History of the PR branch as commits made with git plumbing: the tidy into one commit per ticket, and the new
2
+ messages of a rebased history and of a fix batch.
3
+
4
+ Nothing here checks out a tree or moves a ref: a new commit takes the tree of an existing commit.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import os
9
+ import subprocess
10
+
11
+ import driver_merge
12
+ from driver_slots import git
13
+ from driver_tickets import Ticket
14
+
15
+
16
+ def message(ticket: Ticket) -> str:
17
+ return f"feat({ticket.number}): {ticket.title}\n\nTicket: {ticket.file}\n"
18
+
19
+
20
+ def text(commit: str) -> str:
21
+ """The message of `commit`."""
22
+ return git(".", "log", "-1", "--format=%B", commit).stdout
23
+
24
+
25
+ def same_tree(one: str, other: str) -> bool:
26
+ """Both commits exist and have the same tree."""
27
+ trees = [git(".", "rev-parse", "-q", "--verify", f"{commit}^{{tree}}").stdout.strip() for commit in (one, other)]
28
+ return bool(trees[0]) and trees[0] == trees[1]
29
+
30
+
31
+ def _commit(source: str, parent: str, words: str) -> str:
32
+ """A commit on `parent` with the tree and the author of `source` and the message `words`; "" when git fails."""
33
+ name, email, date = git(".", "log", "-1", "--format=%an%n%ae%n%aI", source).stdout.splitlines() or [""] * 3
34
+ env = {**os.environ, **driver_merge.identity(source), "GIT_AUTHOR_NAME": name, "GIT_AUTHOR_EMAIL": email,
35
+ "GIT_AUTHOR_DATE": date}
36
+ made = subprocess.run(["git", "commit-tree", f"{source}^{{tree}}", "-p", parent, "-m", words.strip() + "\n"],
37
+ capture_output=True, text=True, env=env)
38
+ return made.stdout.strip()
39
+
40
+
41
+ def rewrite(parent: str, steps: list[tuple[str, str]]) -> list[str]:
42
+ """One commit per (source commit, message), in a line on `parent`: each has the tree of its source."""
43
+ made = []
44
+ for source, words in steps:
45
+ parent = _commit(source, parent, words)
46
+ made.append(parent)
47
+ return made
48
+
49
+
50
+ def tidy(parent: str, landed: list[list[str]], tickets: dict[str, Ticket]) -> dict[str, str]:
51
+ """ticket number -> its one commit, in a line on `parent`; a merge that changed no file gets the one before it."""
52
+ commits = {}
53
+ for number, source in landed:
54
+ if not same_tree(source, parent):
55
+ parent = _commit(source, parent, message(tickets[number]))
56
+ commits[number] = parent
57
+ return commits
@@ -0,0 +1,10 @@
1
+ {
2
+ "explorer": ["claude-sonnet-5@medium"],
3
+ "test-writer": ["claude-sonnet-5@medium", "openai/gpt-sol@medium"],
4
+ "implementer": ["claude-sonnet-5@medium", "openai/gpt-sol@medium"],
5
+ "reviewer": ["openai/gpt-astra@medium", "claude-opus-5@medium"],
6
+ "correctness": ["openai/gpt-astra@medium", "claude-opus-5@medium"],
7
+ "fixer": ["claude-opus-5@medium"],
8
+ "final-reviewer": ["openai/gpt-astra@medium", "claude-opus-5@medium"],
9
+ "conflict": ["claude-opus-5@medium"]
10
+ }
@@ -0,0 +1,32 @@
1
+ # Conflict
2
+
3
+ Spec: {spec}
4
+ Ticket: {ticket}
5
+ Branch: {branch}, at commit {start_sha}
6
+ PR branch: {pr_branch}, at commit {pr_sha}
7
+ Files in conflict: {files}
8
+
9
+ The merge of the PR branch into the ticket branch is open in this worktree and it has conflicts in those files.
10
+ Resolve each conflict so that the behavior of both sides holds: the spec and the ticket say what each side is for. Change nothing that the resolution does not need.
11
+ Run the targeted tests of the files you resolved. The driver runs the gate on your commit.
12
+ Finish the merge with `git commit` on the checked-out ticket branch. Do not abort the merge and do not dispatch subagents.
13
+ In `files`, give each file you resolved.
14
+
15
+ Write one JSON report at {report} that matches this schema:
16
+
17
+ ```json
18
+ {
19
+ "type": "object",
20
+ "required": ["status", "commits", "files", "concerns"],
21
+ "properties": {
22
+ "status": {"enum": ["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"]},
23
+ "commits": {"type": "array", "items": {"type": "string", "pattern": "^[0-9a-f]{7,40}$"}},
24
+ "files": {"type": "array", "items": {"type": "string"}},
25
+ "concerns": {"type": "array", "items": {"type": "string"}}
26
+ },
27
+ "if": {"properties": {"status": {"enum": ["DONE", "DONE_WITH_CONCERNS"]}}},
28
+ "then": {"properties": {"commits": {"minItems": 1}}}
29
+ }
30
+ ```
31
+
32
+ Never run `git stash`, never reset, and never check out another branch: worktrees share one stash and one ref store.
@@ -0,0 +1,4 @@
1
+
2
+ This call is a fixture correction, not a new test. The review found that a locked test encodes a stale path, format, fixture layout or setup detail: read each finding of kind `fixture` in {findings}.
3
+ Correct only that detail in the locked test file and keep what each test asserts. For this you may read the implementation.
4
+ The implementation exists, so a corrected test is green: for `red`, break the production line that the test protects, run the test, print its failing output, then revert the edit. In `locked`, list each test file you corrected.
@@ -0,0 +1,34 @@
1
+ # Correctness pass
2
+
3
+ Spec: {spec}
4
+ Ticket: {ticket}
5
+ Seam: {seam}
6
+ Range: {pr_branch}...{branch}, with the ticket tip at {start_sha}
7
+ Locked files: {locked}
8
+ Checks: {correctness}
9
+
10
+ Read the checks file and apply each of its checks to the diff of the range, with the Risk line of the ticket first: races, idempotency, time, authorization, persistence keys and identities, migrations, error paths.
11
+ The output section of the checks file does not apply here: your findings go into the report.
12
+ Read file content from git at {start_sha} (`git diff`, `git show {start_sha}:<path>`), never from the working tree, and run no tests: another agent makes temporary edits in this worktree while you work.
13
+ Report only a defect that you can show with a concrete sequence of events. `mutations` is optional for this pass.
14
+ Give each finding a severity (critical, important or minor), the file and line, a title, the evidence, and a kind: `code`, `test`, or `fixture` when a locked test encodes a stale path, format or setup detail.
15
+ Do not commit and do not dispatch subagents. Leave the tree as you found it.
16
+
17
+ Write one JSON report at {report}. When your sandbox is read-only, give the JSON as your whole final message and the driver saves it there. The report matches this schema:
18
+
19
+ ```json
20
+ {
21
+ "type": "object",
22
+ "required": ["status", "commits", "files", "concerns", "findings"],
23
+ "properties": {
24
+ "status": {"enum": ["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"]},
25
+ "commits": {"type": "array", "items": {"type": "string", "pattern": "^[0-9a-f]{7,40}$"}},
26
+ "files": {"type": "array", "items": {"type": "string"}},
27
+ "concerns": {"type": "array", "items": {"type": "string"}},
28
+ "findings": {"type": "array", "items": {"type": "object", "required": ["severity", "file", "line", "title", "evidence", "kind"], "properties": {"severity": {"enum": ["critical", "important", "minor"]}, "file": {"type": "string"}, "line": {"type": "integer"}, "title": {"type": "string", "minLength": 1}, "evidence": {"type": "string", "minLength": 1}, "kind": {"enum": ["code", "fixture", "test"]}}}},
29
+ "mutations": {"type": "array", "items": {"type": "object", "required": ["test", "file", "line", "red"], "properties": {"test": {"type": "string", "minLength": 1}, "file": {"type": "string", "minLength": 1}, "line": {"type": "integer"}, "red": {"type": "string", "minLength": 1}}}}
30
+ }
31
+ }
32
+ ```
33
+
34
+ Never run `git stash`, never reset, and never check out another branch: worktrees share one stash and one ref store.
@@ -0,0 +1,36 @@
1
+ # Fixer of the final review
2
+
3
+ Spec: {spec}
4
+ Tickets: {tickets}
5
+ PR branch: {pr_branch}, at commit {start_sha}
6
+ Locked files: {locked}
7
+ Findings file of the final review: {findings}
8
+ Findings to fix:
9
+ {findings_list}
10
+
11
+ Fix each finding in the list as one batch, in one commit or in several. Leave the minor findings of the findings file.
12
+ Each commit belongs to one ticket: the ticket whose criterion or code the commit changes. A fix for two tickets is two commits.
13
+ Never edit a locked file: each one stays as it is at {start_sha}, and a locked file that differs is a critical finding of the next review.
14
+ Use targeted tests while you work. The driver runs the gate on your final commit, and a scoped review reads your commits.
15
+ Commit your work on the checked-out PR branch and change no commit that is on it now. Do not dispatch subagents.
16
+ In `commits`, give every commit you made with the number of its ticket: the driver names each commit `fix(NN): <your subject>` from it, and a commit with no ticket is a violation. In `green`, give the summary lines of the passing test output.
17
+
18
+ Write one JSON report at {report} that matches this schema:
19
+
20
+ ```json
21
+ {
22
+ "type": "object",
23
+ "required": ["status", "commits", "files", "concerns", "green"],
24
+ "properties": {
25
+ "status": {"enum": ["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"]},
26
+ "commits": {"type": "array", "items": {"type": "object", "required": ["sha", "ticket"], "properties": {"sha": {"type": "string", "pattern": "^[0-9a-f]{7,40}$"}, "ticket": {"type": "string", "pattern": "^[0-9]+$"}}}},
27
+ "files": {"type": "array", "items": {"type": "string"}},
28
+ "concerns": {"type": "array", "items": {"type": "string"}},
29
+ "green": {"type": "array", "items": {"type": "string"}}
30
+ },
31
+ "if": {"properties": {"status": {"enum": ["DONE", "DONE_WITH_CONCERNS"]}}},
32
+ "then": {"properties": {"commits": {"minItems": 1}}}
33
+ }
34
+ ```
35
+
36
+ Never run `git stash`, never reset, and never check out another branch: worktrees share one stash and one ref store.
@@ -0,0 +1,36 @@
1
+ # Final reviewer
2
+
3
+ Spec: {spec}
4
+ Tickets: {tickets}
5
+ PR branch: {pr_branch}, at commit {start_sha}
6
+ Range: {range}
7
+ Gate record of the driver for {start_sha}: {gate}
8
+ Review method: {review_diff}
9
+ Ledger of the run, one sentence per event: {ledger}
10
+ Minor findings that the ticket reviews left for this review, each with its findings file:
11
+ {minors}
12
+ Rulings of the operator:
13
+ {rulings}
14
+
15
+ Review the range with the method of the review-diff skill in the file above, with the spec as its spec path. The gate of that method is done: check that the gate record names {start_sha}, a clean status and exit 0, and run the gate yourself only when it does not.
16
+ Decide for each minor finding in the list whether it is still true at {start_sha}; report each one that is, with the severity it has now. A ruling is settled: do not report what a ruling accepted.
17
+ Give each finding a severity (critical, important or minor), the file and line, a title, the evidence, and a kind: `code`, `test`, or `fixture`.
18
+ Do not commit. Leave the branch and the tree as you found them.
19
+
20
+ Write one JSON report at {report} that matches this schema:
21
+
22
+ ```json
23
+ {
24
+ "type": "object",
25
+ "required": ["status", "commits", "files", "concerns", "findings"],
26
+ "properties": {
27
+ "status": {"enum": ["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"]},
28
+ "commits": {"type": "array", "items": {"type": "string", "pattern": "^[0-9a-f]{7,40}$"}},
29
+ "files": {"type": "array", "items": {"type": "string"}},
30
+ "concerns": {"type": "array", "items": {"type": "string"}},
31
+ "findings": {"type": "array", "items": {"type": "object", "required": ["severity", "file", "line", "title", "evidence", "kind"], "properties": {"severity": {"enum": ["critical", "important", "minor"]}, "file": {"type": "string"}, "line": {"type": "integer"}, "title": {"type": "string", "minLength": 1}, "evidence": {"type": "string", "minLength": 1}, "kind": {"enum": ["code", "fixture", "test"]}}}}
32
+ }
33
+ }
34
+ ```
35
+
36
+ Never run `git stash`, never reset, and never check out another branch: worktrees share one stash and one ref store.
@@ -0,0 +1 @@
1
+ This is the scoped review after the fix batch: the range holds only the `fix(NN)` commits, and the commits up to {reviewed} have their review. Check that each critical or important finding in {findings} is fixed and that the fix commits break nothing else.
@@ -0,0 +1,35 @@
1
+ # Fixer
2
+
3
+ Spec: {spec}
4
+ Ticket: {ticket}
5
+ Seam: {seam}
6
+ Branch: {branch}, at commit {start_sha}
7
+ Locked files: {locked}
8
+ Findings of the review: {findings}
9
+
10
+ Fix each finding of severity critical or important in the findings file. Leave the minor findings: the final review gets them. Leave each finding of kind `fixture`: the test-writer corrects those.
11
+ Never edit a locked file. The one exception is a finding `locked file changed`: make that file equal to its content at {test_writer_sha}.
12
+ Use targeted tests while you work. The driver runs the gate on your final commit, and a re-review reads the lines you changed.
13
+ Commit your work on the checked-out ticket branch. Do not dispatch subagents.
14
+ In `ticket`, give the number of the ticket that owns your commits. In `green`, give the summary lines of the passing test output.
15
+
16
+ Write one JSON report at {report} that matches this schema:
17
+
18
+ ```json
19
+ {
20
+ "type": "object",
21
+ "required": ["status", "commits", "files", "concerns", "green", "ticket"],
22
+ "properties": {
23
+ "status": {"enum": ["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"]},
24
+ "commits": {"type": "array", "items": {"type": "string", "pattern": "^[0-9a-f]{7,40}$"}},
25
+ "files": {"type": "array", "items": {"type": "string"}},
26
+ "concerns": {"type": "array", "items": {"type": "string"}},
27
+ "green": {"type": "array", "items": {"type": "string"}},
28
+ "ticket": {"type": "string", "pattern": "^[0-9]+$"}
29
+ },
30
+ "if": {"properties": {"status": {"enum": ["DONE", "DONE_WITH_CONCERNS"]}}},
31
+ "then": {"properties": {"commits": {"minItems": 1}}}
32
+ }
33
+ ```
34
+
35
+ Never run `git stash`, never reset, and never check out another branch: worktrees share one stash and one ref store.
@@ -0,0 +1,33 @@
1
+ # Implementer
2
+
3
+ Spec: {spec}
4
+ Ticket: {ticket}
5
+ Seam: {seam}
6
+ Start commit, which holds the locked tests when the ticket has them: {start_sha}
7
+ Locked files: {locked}
8
+
9
+ Make the locked tests green and meet each criterion of the ticket. For internal code, work in vertical test-first slices: one failing test, the smallest code that passes it, then the next slice.
10
+ Never edit a locked file. If a locked test contradicts the spec or the ticket, stop and report BLOCKED with the evidence in `concerns`.
11
+ Commit your work on the checked-out ticket branch. Do not dispatch subagents.
12
+ Run targeted tests while you work. The driver runs the gate on your final commit.
13
+ In `green`, give the summary lines of the passing test output.
14
+
15
+ Write one JSON report at {report} that matches this schema:
16
+
17
+ ```json
18
+ {
19
+ "type": "object",
20
+ "required": ["status", "commits", "files", "concerns", "green"],
21
+ "properties": {
22
+ "status": {"enum": ["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"]},
23
+ "commits": {"type": "array", "items": {"type": "string", "pattern": "^[0-9a-f]{7,40}$"}},
24
+ "files": {"type": "array", "items": {"type": "string"}},
25
+ "concerns": {"type": "array", "items": {"type": "string"}},
26
+ "green": {"type": "array", "items": {"type": "string"}}
27
+ },
28
+ "if": {"properties": {"status": {"enum": ["DONE", "DONE_WITH_CONCERNS"]}}},
29
+ "then": {"properties": {"commits": {"minItems": 1}}}
30
+ }
31
+ ```
32
+
33
+ Never run `git stash`, never reset, and never check out another branch: worktrees share one stash and one ref store.
@@ -0,0 +1 @@
1
+ The earlier attempt was interrupted. Its uncommitted work is in the tree: continue from it.
@@ -0,0 +1,2 @@
1
+
2
+ The earlier attempt left the slot off the tip of {branch}. The driver reset the slot to {branch} at {tip}: work there and stay there.
@@ -0,0 +1,5 @@
1
+
2
+ This call is a correction after a ruling, not a new test set. The implementer reported that a locked test contradicts the spec or the ticket, and the operator ruled that the test is wrong: read `concerns` in {blocked_report}.
3
+ Correct only the tests that those concerns name, so that they state what the spec and the ticket require. Keep each other test as it is.
4
+ The branch can hold commits of the implementer, and the tree can hold its uncommitted work: change neither, and commit only the test files you correct.
5
+ A corrected test fails because the behavior is missing: run it and print that failing output. In `locked`, list each test file you corrected.
@@ -0,0 +1,19 @@
1
+ ## Tickets
2
+
3
+ One commit per ticket, in merge order:
4
+
5
+ {map}
6
+
7
+ ## Fix commits of the final review
8
+
9
+ {fixes}
10
+
11
+ ## Rulings
12
+
13
+ {rulings}
14
+
15
+ ## Review coverage
16
+
17
+ - Branch tip and reviewed tip: {tip}
18
+ - Full review: {onto}..{tidied}
19
+ - Scoped review after the fix batch: {scoped}
@@ -0,0 +1,33 @@
1
+ # Conflict
2
+
3
+ Spec: {spec}
4
+ Tickets: {tickets}
5
+ PR branch: {pr_branch}
6
+ Base branch: {base}, at commit {start_sha}
7
+ Files in conflict: {files}
8
+
9
+ A rebase of the PR branch onto the base branch is open in this worktree, on a detached HEAD, and it stopped with conflicts in those files. The commit that stopped is the one commit of a ticket: its message names the ticket file.
10
+ Resolve each conflict so that the behavior of both sides holds: the spec and the ticket say what each side is for. Change nothing that the resolution does not need.
11
+ Run the targeted tests of the files you resolved. The driver runs the gate on the rebased tip.
12
+ Stage the files you resolved, then go on with `git rebase --continue`. Do not abort the rebase and do not dispatch subagents.
13
+ You resolve one stop only. When the rebase stops again at a later commit, leave that stop as it is and write your report.
14
+ In `commits`, give `git rev-parse HEAD` as it is when you are done. In `files`, give each file you resolved.
15
+
16
+ Write one JSON report at {report} that matches this schema:
17
+
18
+ ```json
19
+ {
20
+ "type": "object",
21
+ "required": ["status", "commits", "files", "concerns"],
22
+ "properties": {
23
+ "status": {"enum": ["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"]},
24
+ "commits": {"type": "array", "items": {"type": "string", "pattern": "^[0-9a-f]{7,40}$"}},
25
+ "files": {"type": "array", "items": {"type": "string"}},
26
+ "concerns": {"type": "array", "items": {"type": "string"}}
27
+ },
28
+ "if": {"properties": {"status": {"enum": ["DONE", "DONE_WITH_CONCERNS"]}}},
29
+ "then": {"properties": {"commits": {"minItems": 1}}}
30
+ }
31
+ ```
32
+
33
+ Never run `git stash`, never reset, and never check out another branch: worktrees share one stash and one ref store.
@@ -0,0 +1,3 @@
1
+
2
+ This is the re-review after fix round {round}. The fix is {from_sha}..{start_sha}: review only the lines it changed, and check that it resolves each critical or important finding in {findings}.
3
+ Repeat the mutation proof for these production lines only, numbered as they are at {start_sha} (an empty list means that no proof is due): {proofs}
@@ -0,0 +1,2 @@
1
+
2
+ The driver rejected the earlier attempt: {error}
@@ -0,0 +1,34 @@
1
+ # Reviewer
2
+
3
+ Spec: {spec}
4
+ Ticket: {ticket}
5
+ Seam: {seam}
6
+ Range: {pr_branch}...{branch}, with the ticket tip at {start_sha}
7
+ Locked files: {locked}
8
+ Test-writer report, when the ticket has a test-writer: {writer_report}
9
+
10
+ Check the diff against the ticket and the spec, then for quality. Check that each criterion has a locked test. Test each suspicion that the ticket names in its Suspicions line.
11
+ In round 1, for each locked test, break the production line the test protects, run the test, print its failing output, then revert the edit. A test that stays green is a critical finding. Run again each mutation that the test-writer report names in `concerns`.
12
+ A mutation proof is keyed to its test and its production line: a later round repeats it only when that line changed.
13
+ In `mutations`, give for each proof the test, the production file and line, and one failure line copied exactly from the output you printed.
14
+ Give each finding a severity (critical, important or minor), the file and line, a title, the evidence, and a kind: `code`, `test`, or `fixture` when a locked test encodes a stale path, format or setup detail.
15
+ Do not commit and do not dispatch subagents. Leave the tree as you found it.
16
+
17
+ Write one JSON report at {report}. When your sandbox is read-only, give the JSON as your whole final message and the driver saves it there. The report matches this schema:
18
+
19
+ ```json
20
+ {
21
+ "type": "object",
22
+ "required": ["status", "commits", "files", "concerns", "findings"],
23
+ "properties": {
24
+ "status": {"enum": ["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"]},
25
+ "commits": {"type": "array", "items": {"type": "string", "pattern": "^[0-9a-f]{7,40}$"}},
26
+ "files": {"type": "array", "items": {"type": "string"}},
27
+ "concerns": {"type": "array", "items": {"type": "string"}},
28
+ "findings": {"type": "array", "items": {"type": "object", "required": ["severity", "file", "line", "title", "evidence", "kind"], "properties": {"severity": {"enum": ["critical", "important", "minor"]}, "file": {"type": "string"}, "line": {"type": "integer"}, "title": {"type": "string", "minLength": 1}, "evidence": {"type": "string", "minLength": 1}, "kind": {"enum": ["code", "fixture", "test"]}}}},
29
+ "mutations": {"type": "array", "items": {"type": "object", "required": ["test", "file", "line", "red"], "properties": {"test": {"type": "string", "minLength": 1}, "file": {"type": "string", "minLength": 1}, "line": {"type": "integer"}, "red": {"type": "string", "minLength": 1}}}}
30
+ }
31
+ }
32
+ ```
33
+
34
+ Never run `git stash`, never reset, and never check out another branch: worktrees share one stash and one ref store.
@@ -0,0 +1,2 @@
1
+
2
+ The earlier attempt reported that a locked test contradicts the spec, and the operator ruled that the locked tests stand as they are. Your earlier commits are on the branch and your uncommitted work is in the tree: continue from them and make the locked tests green. Do not report that contradiction again.
@@ -0,0 +1,34 @@
1
+ # Test-writer
2
+
3
+ Spec: {spec}
4
+ Ticket: {ticket}
5
+ Seam: {seam}
6
+ Branch: {branch}, at commit {start_sha}
7
+
8
+ Read the spec, the ticket, CONTEXT.md and the ADRs when they exist, and the public interface of the Seam. Do not read an implementation plan or implementation code.
9
+ Write one black-box acceptance test for each criterion of the ticket, through the Seam only. Define the interface when it does not exist.
10
+ Run each test and confirm that it fails because the behavior is missing, not because of an import error or a syntax error. Print that failing output.
11
+ A test that is green before any implementation is not a proof. Rewrite it until it fails, or name in `concerns` the production-code mutation that turns it red.
12
+ Commit the tests red on the checked-out branch. Do not write implementation code and do not dispatch subagents.
13
+ In `locked`, list each test file of your commit. In `red`, give for each test one failure line, copied exactly from the output you printed.
14
+
15
+ Write one JSON report at {report} that matches this schema:
16
+
17
+ ```json
18
+ {
19
+ "type": "object",
20
+ "required": ["status", "commits", "files", "concerns", "locked", "red"],
21
+ "properties": {
22
+ "status": {"enum": ["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"]},
23
+ "commits": {"type": "array", "items": {"type": "string", "pattern": "^[0-9a-f]{7,40}$"}},
24
+ "files": {"type": "array", "items": {"type": "string"}},
25
+ "concerns": {"type": "array", "items": {"type": "string"}},
26
+ "locked": {"type": "array", "items": {"type": "string"}},
27
+ "red": {"type": "array", "items": {"type": "object", "required": ["test", "line"], "properties": {"test": {"type": "string", "minLength": 1}, "line": {"type": "string", "minLength": 1}}}}
28
+ },
29
+ "if": {"properties": {"status": {"enum": ["DONE", "DONE_WITH_CONCERNS"]}}},
30
+ "then": {"properties": {"commits": {"minItems": 1}, "locked": {"minItems": 1}, "red": {"minItems": 1}}}
31
+ }
32
+ ```
33
+
34
+ Never run `git stash`, never reset, and never check out another branch: worktrees share one stash and one ref store.
@@ -0,0 +1,2 @@
1
+
2
+ The earlier attempt timed out after {minutes} minutes. The driver restored the worktree to {tip}: start again from there.
@@ -0,0 +1,2 @@
1
+
2
+ The earlier attempt timed out after {minutes} minutes. Start again from {tip}: first revert each edit of the earlier attempt that `git status` shows.
@@ -0,0 +1,2 @@
1
+
2
+ The earlier attempt timed out after {minutes} minutes. Its uncommitted work is in the tree: continue from it.
@@ -0,0 +1,101 @@
1
+ #!/bin/sh
2
+ # Keep bootstrap dependencies to POSIX tools, curl, tar, and SHA-256.
3
+ set -eu
4
+
5
+ fail() {
6
+ printf 'implement-spec: %s\n' "$1" >&2
7
+ exit 3
8
+ }
9
+
10
+ # Resolve file symlinks as well as symlinked skill directories.
11
+ entry=$0
12
+ while [ -L "$entry" ]; do
13
+ parent=$(CDPATH= cd -- "$(dirname -- "$entry")" && pwd)
14
+ entry=$(readlink "$entry")
15
+ case "$entry" in /*) ;; *) entry=$parent/$entry ;; esac
16
+ done
17
+ skill_dir=$(CDPATH= cd -- "$(dirname -- "$entry")" && pwd)
18
+ cache=${IMPLEMENT_SPEC_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/implement-spec}
19
+ case "$cache" in /*) ;; *) fail 'IMPLEMENT_SPEC_CACHE_DIR must be an absolute path' ;; esac
20
+ uv_version=0.12.13
21
+ python_version=3.13.7
22
+ case "$(uname -s):$(uname -m)" in
23
+ Darwin:arm64|Darwin:aarch64)
24
+ target=aarch64-apple-darwin
25
+ digest=7e6ddb9316acc00f2296c82ff4d99977870ee34b2f0ddcae9444d714db9364ed ;;
26
+ Darwin:x86_64)
27
+ target=x86_64-apple-darwin
28
+ digest=5e287ef61cb6a9b61b3a83fef124fd143e400468a7dac794230147a810e17119 ;;
29
+ Linux:aarch64|Linux:arm64)
30
+ target=aarch64-unknown-linux-musl
31
+ digest=f44bc1037a17889fe562fffd2002d4ed108e499fbe68b4f022af244dc7b8244f ;;
32
+ Linux:x86_64)
33
+ target=x86_64-unknown-linux-musl
34
+ digest=4e2bfd0c9007b1032a50e539e965fd0a6037d87ad93ae1580d220a92d4c94098 ;;
35
+ *) fail 'Unsupported platform: use macOS or Linux on x86_64 or ARM64 (Windows: WSL)' ;;
36
+ esac
37
+ uv_dir=$cache/uv/$uv_version/$target
38
+ uv=$uv_dir/uv
39
+ if [ ! -x "$uv" ]; then
40
+ [ "${IMPLEMENT_SPEC_OFFLINE:-0}" != 1 ] || fail 'uv is not cached; provision this cache online first'
41
+ mkdir -p "$uv_dir" || fail 'Cannot create runtime cache'
42
+ stage=$(mktemp -d "$uv_dir/.download.XXXXXX") || fail 'Cannot create download directory'
43
+ trap 'rm -rf "$stage"' EXIT
44
+ trap 'exit 3' HUP INT TERM
45
+ printf 'implement-spec: downloading uv %s\n' "$uv_version" >&2
46
+ curl --fail --location --silent --show-error --connect-timeout 15 --max-time 300 \
47
+ "https://github.com/astral-sh/uv/releases/download/$uv_version/uv-$target.tar.gz" \
48
+ -o "$stage/uv.tar.gz" || fail 'Cannot download uv; check network access and retry'
49
+ if command -v sha256sum >/dev/null 2>&1; then
50
+ actual=$(sha256sum "$stage/uv.tar.gz")
51
+ elif command -v shasum >/dev/null 2>&1; then
52
+ actual=$(shasum -a 256 "$stage/uv.tar.gz")
53
+ else
54
+ fail 'SHA-256 verification requires sha256sum or shasum'
55
+ fi
56
+ [ "${actual%% *}" = "$digest" ] || fail 'uv archive checksum mismatch'
57
+ tar -xzf "$stage/uv.tar.gz" -C "$stage" || fail 'Cannot extract uv'
58
+ # Atomic publication: concurrent first runs never see a partial executable.
59
+ mv -f "$stage/uv-$target/uv" "$uv" || fail 'Cannot install cached uv'
60
+ rm -rf "$stage"
61
+ trap - EXIT HUP INT TERM
62
+ fi
63
+
64
+ # Limit uv settings to this subprocess. Do not activate a venv or change PATH:
65
+ # coverage commands inherit the caller's project environment (the engine adds
66
+ # only the repo's own .venv/bin, when no virtualenv is active).
67
+ managed_uv() (
68
+ unset UV_NO_MANAGED_PYTHON UV_PYTHON UV_PYTHON_DOWNLOADS UV_PYTHON_BIN_DIR
69
+ export UV_PYTHON_INSTALL_DIR="$cache/python"
70
+ export UV_CACHE_DIR="$cache/uv-cache"
71
+ export UV_MANAGED_PYTHON=true
72
+ "$uv" --no-config "$@"
73
+ )
74
+ python=$(managed_uv python find --no-project --system --managed-python --no-python-downloads "$python_version" 2>/dev/null) || {
75
+ [ "${IMPLEMENT_SPEC_OFFLINE:-0}" != 1 ] || fail 'Python is not cached; provision this cache online first'
76
+ managed_uv python install --no-bin "$python_version" >&2 || fail 'Cannot provision Python; check network access and retry'
77
+ python=$(managed_uv python find --no-project --system --managed-python --no-python-downloads "$python_version") || fail 'Cannot locate managed Python'
78
+ }
79
+ # The one dependency (design.md): jsonschema with its closure pinned, in a venv built once per pin set.
80
+ jsonschema_version=4.26.0
81
+ venv=$cache/venv/$python_version-jsonschema-$jsonschema_version
82
+ if [ ! -x "$venv/bin/python" ]; then
83
+ [ "${IMPLEMENT_SPEC_OFFLINE:-0}" != 1 ] || fail 'jsonschema is not cached; provision this cache online first'
84
+ mkdir -p "$cache/venv" || fail 'Cannot create runtime cache'
85
+ stage=$(mktemp -d "$cache/venv/.build.XXXXXX") || fail 'Cannot create build directory'
86
+ trap 'rm -rf "$stage"' EXIT
87
+ trap 'exit 3' HUP INT TERM
88
+ managed_uv venv --quiet --python "$python" "$stage/venv" >&2 &&
89
+ managed_uv pip install --quiet --python "$stage/venv/bin/python" "jsonschema==$jsonschema_version" \
90
+ attrs==26.1.0 referencing==0.37.0 rpds-py==2026.9.1 jsonschema-specifications==2025.9.1 >&2 ||
91
+ fail 'Cannot provision jsonschema; check network access and retry'
92
+ # Publish by rename: a concurrent first run never sees a partial venv. A run that loses the race
93
+ # (the rename fails, or lands inside the winner's directory) uses the winner's venv.
94
+ [ -x "$venv/bin/python" ] || mv "$stage/venv" "$venv" 2>/dev/null || :
95
+ [ -x "$venv/bin/python" ] || fail 'Cannot install cached venv'
96
+ rm -rf "$stage"
97
+ trap - EXIT HUP INT TERM
98
+ fi
99
+ # Ignore Python-specific ambient settings for the engine, but preserve them for
100
+ # child processes. -I would also remove the sibling module directory.
101
+ exec "$venv/bin/python" -E -s "$skill_dir/driver.py" "$@"
@@ -0,0 +1,38 @@
1
+ """The fixture repository of the in-process tests: a clean git worktree on feat/x with .agent ignored."""
2
+ import subprocess
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ import pytest
7
+
8
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1])) # the driver modules are one folder up
9
+
10
+ import driver_agents # noqa: E402
11
+
12
+ GIT = ["git", "-c", "user.name=t", "-c", "user.email=t@example.com", "-c", "commit.gpgsign=false"]
13
+
14
+
15
+ def ticket(number, blocked="None (can start immediately)", touches="`demo/x.py: f`", seam="`f()`"):
16
+ return f"# {number}: Ticket {number}\n\n**Blocked by:** {blocked}\n\n**Seam:** {seam}\n\n**Touches:** {touches}\n\n- [ ] It works\n"
17
+
18
+
19
+ @pytest.fixture
20
+ def repo(tmp_path, monkeypatch):
21
+ """A clean git worktree with .agent ignored; returns a writer for the ticket set."""
22
+ monkeypatch.chdir(tmp_path)
23
+ monkeypatch.setattr(driver_agents.shutil, "which", lambda cli: cli) # CI has no claude or codex
24
+ for index, setting in enumerate(GIT[2::2]): # the fake agents commit too
25
+ monkeypatch.setenv(f"GIT_CONFIG_KEY_{index}", setting.partition("=")[0])
26
+ monkeypatch.setenv(f"GIT_CONFIG_VALUE_{index}", setting.partition("=")[2])
27
+ monkeypatch.setenv("GIT_CONFIG_COUNT", "3")
28
+ subprocess.run([*GIT, "init", "-q", "-b", "feat/x"], check=True)
29
+ (tmp_path / ".gitignore").write_text(".agent/\n")
30
+ subprocess.run([*GIT, "add", "-A"], check=True)
31
+ subprocess.run([*GIT, "commit", "-q", "-m", "init"], check=True)
32
+
33
+ def write(**blocked):
34
+ (tmp_path / ".agent/tickets").mkdir(parents=True, exist_ok=True)
35
+ for number, text in blocked.items():
36
+ (tmp_path / f".agent/tickets/{number[1:]}-t.md").write_text(text)
37
+ return tmp_path
38
+ return write