@jesdi/skills 0.0.47 → 0.0.48

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jesdi/skills",
3
- "version": "0.0.47",
3
+ "version": "0.0.48",
4
4
  "description": "jesdi's agent skills (content package consumed by @jesdi/skills-cli)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -97,13 +97,13 @@ Everything is under `.agent/`, which is never committed.
97
97
 
98
98
  A ticket is ready when each ticket in its **Blocked by** line is merged and no running ticket touches the same place. The places are the backticked entries of the **Touches** lines: `file` or `file: function`. Two entries are the same place when they name the same file and the same function, or when one of them names the whole file. Each ready ticket takes the lowest free slot.
99
99
 
100
- 1. **test-writer** (`prompts/test-writer.md`). It writes one black-box test for each criterion through the Seam and commits them red. Those files are locked. A ticket with Seam `none — refactor` has no test-writer.
100
+ 1. **test-writer** (`prompts/test-writer.md`). It writes one black-box test for each criterion through the Seam and commits them red. Those files are locked. Its prompt names the locked tests of merged tickets: each fixture must set up the precondition of its scenario under the spec rules and those tests. A ticket with Seam `none — refactor` has no test-writer.
101
101
  2. **implementer** (`prompts/implementer.md`). It starts from that commit and makes the locked tests green. It makes the gate (`--gate`) green on its final commit before it reports DONE or DONE_WITH_CONCERNS.
102
102
  3. **Gate.** The driver runs `--gate` on the ticket tip.
103
103
  4. **Review.** The driver compares each locked file of the ticket with the test-writer commit. A file that differs is a critical finding `locked file changed: <path>`. A locked file of a merged ticket that the ticket changed since its merge base with the PR branch is a critical `fixture` finding `the ticket changed the locked file of a merged ticket: <path>`, so it goes to a correction (see **Before a correction**). Then the **reviewer** (`prompts/reviewer.md`) runs, and for a ticket with a **Risk** line the **correctness** pass (`prompts/correctness.md`) runs at the same time in the same slot. A read-only role (reviewer, correctness, final-reviewer, pr-writer) leaves its slot and the git config as it found them: all worktrees share one config, and one key written there, such as a `core.worktree` on a temp directory, breaks git in each of them. To run a check off the slot, a reviewer reads the driver's gate record or makes its own `git worktree add --detach` copy and removes it. After read-only roles, the driver checks the slot and the shared config (`git rev-parse --git-common-dir`, then `config`) against what they were before. When one of them changed, the driver restores it, writes `a read-only role broke its rule: ...` to the ledger, and runs the review one more time; a second change blocks the ticket.
104
104
  5. **Merge.** The driver merges the reviewed tip into the PR branch with git plumbing, then deletes the ticket branch. A conflict goes to the **conflict** agent (`prompts/conflict.md`) once. Its resolution may change no locked file of the ticket. A second conflict blocks the ticket.
105
105
  **The landing check.** Merges are serial. Before the PR branch moves, the driver compares the tree that is about to land (the ticket tip of a fast-forward, or the merge commit) with the PR tip: each locked file of each merged ticket must be the same tree entry in both: the same blob with the same mode, so a file that became a symlink is a difference. A file that only one of them has is a difference. A file that the landing ticket lists in its own `locked` is not checked: the test-writer of a later ticket can add a test to a file that an earlier ticket locked, and must list that file. A test-writer report that changes such a file and does not list it is rejected, with the usual one rerun. The review finds such a change first; the landing check catches a change that no review saw, for example of a ticket that merged after the last review. A difference refuses the landing: the ticket is BLOCKED with `locked file changed: <path>` in the ledger, the PR branch does not move, and no fix round runs. The PR branch moves only from the tip that the check read; when another hand moved the branch in between, the driver makes the merge, the check and the gate again, one time. Only that case starts the landing again: a refused landing and a red merge gate block the ticket. The second try runs no conflict agent, so a conflict there blocks the ticket, and so does a branch that moved again.
106
- **Before a correction.** A test-writer that corrects locked tests (after `overrule-test`, or for a `fixture` finding or a `test` finding on a locked file) starts from the locked versions: the driver puts back each locked file of the ticket and each locked file of a merged ticket that the ticket changed, commits them in one commit of its own, and the ledger names each file. A file of a merged ticket goes back to the version of the PR branch. The commit keeps the tree of the next fixer and of the gate clean, whatever files the correction commits. The correction's prompt names the restored files: when a test in one of them fails only because the ticket changed an interface that the test uses, the correction updates the file to the new interface, keeps what each test asserts, and lists the file in `locked`.
106
+ **Before a correction.** A test-writer that corrects locked tests (after `overrule-test`, or for a `fixture` finding or a `test` finding on a locked file) starts from the locked versions: the driver puts back each locked file of the ticket and each locked file of a merged ticket that the ticket changed, commits them in one commit of its own, and the ledger names each file. A file of a merged ticket goes back to the version of the PR branch. The commit keeps the tree of the next fixer and of the gate clean, whatever files the correction commits. The correction's prompt names the restored files: when a test in one of them fails only because the ticket changed an interface that the test uses, the correction updates the file to the new interface, keeps what each test asserts, and lists the file in `locked`. Each correction also gets `prompts/corrected.md`: when it adds a requirement, it updates each shared fake or fixture that other locked tests use, runs each whole locked file and each locked test of merged tickets, and names each red test in `concerns`.
107
107
  **The gate of the restore.** In the fix loop, the tip before the restore has a green gate, so the driver gates the restore commit and does not leave the question to the correction. A red gate means that a restored file fails with the ticket's code: the correction gets the gate record (`prompts/restored-red.md`), and a DONE correction must list a restored file in `locked`; else it reports BLOCKED with `locked test contradicts spec:`. A green gate with only driver findings to correct closes them with the restore: no test-writer runs. After `overrule-test` the driver does not gate the restore, because the tip before it has no green gate; a change that the next implementer makes to a merged ticket's file is a finding of the next review.
108
108
 
109
109
  Rules for each agent:
@@ -105,6 +105,9 @@ class Run:
105
105
  # Its report may change a locked file of a merged ticket only when it lists the file.
106
106
  merged = driver_merge.protected(self.state.read()["tickets"], number)
107
107
  options["check"] = {**(options.get("check") or {}), "merged": merged, "pr": self.tip()}
108
+ values = {**values, "protected": " ".join(merged) or "none"}
109
+ if options["check"].get("correction"):
110
+ options["notes"] = [*options.get("notes", ()), "corrected"]
108
111
  report = _attempts(self, role, number, slot, values, **options)
109
112
  if role == "test-writer":
110
113
  report["locked"] = driver_report.locked_files(report["locked"]) # as validate read it
@@ -0,0 +1,3 @@
1
+
2
+ When this correction adds a requirement, update each shared fake or fixture that other locked tests use, so that those tests satisfy the requirement too.
3
+ Then run each whole file of `locked` and each locked test of merged tickets. In `concerns`, name each red test and why it is expected to be red, or write that all are green.
@@ -7,6 +7,7 @@ Branch: {branch}, at commit {start_sha}
7
7
 
8
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
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
+ Each fixture sets up exactly the precondition of its scenario. Check it against the rules of the spec and against the locked tests of merged tickets: {protected}.
10
11
  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
12
  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
13
  Commit the tests red on the checked-out branch. Do not write implementation code and do not dispatch subagents.
@@ -341,6 +341,22 @@ def test_a_fixture_correction_starts_from_the_locked_file_of_the_merged_ticket(t
341
341
  assert rig.led("Ticket 02:", "restored") == [RESTORED_01] and rig.code == 0
342
342
 
343
343
 
344
+ def test_the_test_writer_checks_fixture_preconditions_and_a_correction_keeps_shared_fixtures_and_runs_the_locked_files(tmp_path):
345
+ """Issue #97: a fixture misses the precondition of its scenario or a rule that a merged ticket locks, and a
346
+ correction adds a requirement that the shared fakes of other locked tests do not satisfy."""
347
+ stale = finding("STALE", kind="fixture", file="tests/test_02.py")
348
+ correction = {**tw("02"), "files": {"tests/test_02.py": "# corrected\n"}, "commit": "test: 02 fixture fix"}
349
+ rig = Inside(tmp_path, three()).run(plan(**{"02/test-writer": [tw("02"), correction],
350
+ "02/reviewer": [review("02", [stale]), review("02")]}), parallel="1")
351
+ assert rig.code == 0, rig.ledger()
352
+ first, corrected = rig.prompt("test-writer-0", "02"), rig.prompt("test-writer-1", "02")
353
+ assert "Each fixture sets up exactly the precondition of its scenario" in first
354
+ assert "the locked tests of merged tickets: tests/test_01.py." in first
355
+ assert "update each shared fake or fixture" not in first
356
+ assert "update each shared fake or fixture that other locked tests use" in corrected
357
+ assert "run each whole file of `locked` and each locked test of merged tickets" in corrected
358
+
359
+
344
360
  def test_the_restore_before_a_correction_is_committed_so_the_next_fixer_and_its_gate_find_a_clean_tree(tmp_path):
345
361
  """Issue #86: the correction and the fixer each commit only their own files. The restore of the merged ticket's
346
362
  file must not stay in the tree, where the gate of the fixer's commit is `not clean` and burns a round."""
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-10-17T15:52:14.213Z",
3
+ "generatedAt": "2026-10-17T23:11:17.693Z",
4
4
  "skills": [
5
5
  {
6
6
  "name": "backlog",
@@ -46,8 +46,8 @@
46
46
  },
47
47
  {
48
48
  "name": "implement-spec",
49
- "version": "0.1.23",
50
- "hash": "sha256-c0a64ae43e2ba3c2e100b4d0a78085fdab558f8ee4eda01d7ae15eb677b8dbb5",
49
+ "version": "0.1.24",
50
+ "hash": "sha256-8df47be43b54ab8ced155594ab3dfa0c23a27960597774d7868ca4e444ab817a",
51
51
  "description": "Implement a specification in code."
52
52
  },
53
53
  {