@mrciphersmith/keryx 0.3.118 → 0.3.120

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 (34) hide show
  1. package/dist/cli.js +104554 -96795
  2. package/dist/core.js +1767 -1061
  3. package/package.json +1 -1
  4. package/src/gdgraph/service.ts +4 -0
  5. package/src/gdgraph/symbol-reach.ts +1069 -0
  6. package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.md +1 -1
  7. package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/SKILL.md +37 -40
  8. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +6 -6
  9. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/orchestrator-prompt.md +1 -1
  10. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.md +12 -18
  11. package/src/gdskills/bundled/skills/orchestration/task-implementer/input-contract.schema.json +8 -0
  12. package/src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json +19 -0
  13. package/src/gdskills/bundled/skills/platform/scheduled-tasks/SKILL.md +2 -2
  14. package/src/gdskills/bundled/skills/review/review-architecture/SKILL.md +1 -1
  15. package/src/gdskills/bundled/skills/review/review-backend/SKILL.md +1 -1
  16. package/src/gdskills/bundled/skills/review/review-clean-code/SKILL.md +1 -1
  17. package/src/gdskills/bundled/skills/review/review-core-boundaries/SKILL.md +1 -1
  18. package/src/gdskills/bundled/skills/review/review-flow-graph/SKILL.md +1 -1
  19. package/src/gdskills/bundled/skills/review/review-frontend/SKILL.md +3 -7
  20. package/src/gdskills/bundled/skills/review/review-frontend-conventions/SKILL.md +4 -6
  21. package/src/gdskills/bundled/skills/review/review-highload/SKILL.md +1 -1
  22. package/src/gdskills/bundled/skills/review/review-logic/SKILL.md +2 -2
  23. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +82 -17
  24. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +78 -111
  25. package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-finding.schema.json +13 -0
  26. package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-input.schema.json +38 -0
  27. package/src/gdskills/bundled/skills/review/review-performance/SKILL.md +1 -1
  28. package/src/gdskills/bundled/skills/review/review-pr-feedback/SKILL.md +1 -1
  29. package/src/gdskills/bundled/skills/review/review-regression/SKILL.md +2 -2
  30. package/src/gdskills/bundled/skills/review/review-security-code/SKILL.md +1 -1
  31. package/src/gdskills/bundled/skills/review/review-style/SKILL.md +1 -1
  32. package/src/gdskills/bundled/skills/review/review-testing-practices/SKILL.md +1 -1
  33. package/src/gdskills/bundled/skills/review/review-verifier/SKILL.md +3 -3
  34. package/src/gdskills/contracts/review-finding.schema.json +25 -0
@@ -113,7 +113,7 @@ Input: codebase_path=<project_root>, scope=changed, base_branch=<base>
113
113
  - `git diff main...HEAD` — review the full diff
114
114
  - Check for: TODOs left behind, console.logs, hardcoded values
115
115
  - Verify all acceptance criteria from Phase 1 spec
116
- 3. Fix any findings (max 2 review-fix cycles)
116
+ 3. Fix any findings (max 2 review-fix cycles). Each next round is `keryx review start` or `keryx review plan` on the new head: the round comes from `round.json`; no own range, no whole-branch re-review (the `git diff main...HEAD` above is round 1 only).
117
117
  4. Re-run `code-verifier` after any fixes
118
118
 
119
119
  ### Phase 8: DELIVER + CHANGE REPORT
@@ -77,11 +77,12 @@ flowchart TD
77
77
  C --> D["flow-init context enrichment"]
78
78
  D --> E["freeze AC + start flow"]
79
79
  E --> F["dispatch tests-creator / task-implementer"]
80
- F --> G["code-verifier + review-orchestrator"]
80
+ F --> FG{"first green: ask operator - open PR or harden first"}
81
+ FG --> G["code-verifier + review-orchestrator"]
81
82
  G --> H{"All tasks, checks, review OK?"}
82
83
  H -- "no" --> F
83
84
  H -- "yes" --> I{"Ask user how to finish"}
84
- I -- "create PR" --> J["create PR and run review/fix loop"]
85
+ I -- "create PR" --> J["create PR; each round = review start on the new head (delta)"]
85
86
  J --> K{"review clean, PR mergeable?"}
86
87
  K -- "no, attempts < 5" --> J
87
88
  K -- "no, attempts = 5 or repetition detected" --> R["enrich context and change fix strategy"]
@@ -177,9 +178,7 @@ Record attempts as they happen, not only on resume:
177
178
  keryx flow task attempt <id> <Tn> --outcome started|failed|blocked [--detail "<what happened>"]
178
179
  ```
179
180
 
180
- `attempts.count` is append-only and lives in `flow.json`. A counter that lives
181
- only in the orchestrator's context resets to zero exactly when the loop bound
182
- matters most, which makes it not a counter.
181
+ `attempts.count` is append-only and lives in `flow.json`. A counter that lives only in the orchestrator's context resets to zero exactly when the loop bound matters most, which makes it not a counter.
183
182
 
184
183
  ### 0.1 Route
185
184
 
@@ -244,27 +243,26 @@ Write or update:
244
243
  - `tasks.md` - task definitions grouped by context, test, implement, review, docs;
245
244
  - `acceptance-criteria.md` - verifiable `ACn` criteria, each ending in one marker: [verify: exec `<command>`] or [verify: invariant `<command>`] (a backticked command), [verify: judged] (a human checks: live run, operator decision, text quality), or [verify: none — <reason>]. If a kind is unclear, ask the operator ONE question listing the criteria with proposed kinds, before `flow freeze`. Behaviour with an external dependency or a failure path gets its own failure-scenario criterion with an observable result (saved, error visible, retry creates no duplicate); a check claimed to catch a regression carries `· sensitive: revert|mutation|failure-scenario` and the confirm note carries `evidence: <ref>` (revert/mutation run or failure-scenario reference).
246
245
 
246
+ ### UI or state scope: a state × event matrix in plan.md
247
+
248
+ When the scope touches UI, a store, or other code that holds state across events, `plan.md` carries a state × event matrix before `flow freeze`. Rows are the states the surface can be in (for example loading, loaded, empty, missing, first-load failed, refresh failed); columns are the events that reach it (for example the user switches selection, unmount, workspace or context change, retry). Each cell is one line: what the user sees and which state survives, citing the project's styling/UX rules where they decide it. Every non-trivial cell gets an `ACn` row, or one AC per group of cells with the same result; a cell that is genuinely "no change" needs none. List only the states and events the change reaches:
249
+
250
+ | state \ event | user switches selection | unmount | retry |
251
+ |---|---|---|---|
252
+ | loading | drop the stale response; loading shows for the new one | no state write after unmount | — |
253
+ | refresh failed | keep loaded data; the error does not follow to the new selection | — | re-fetch; error stays inline until success |
254
+
255
+ It does not apply when the scope holds no UI or state (CLI output, pure functions, docs, build config): write `State × event matrix: not applicable — <why>` in `plan.md`. The matrix is what workers and fix rounds check behavior against; a choice no cell decides is a `behavior_changes` entry for the operator (Phase 2).
256
+
247
257
  ### A verification step in the plan is a task, not a sentence
248
258
 
249
- Every check the plan says must happen before the work is accepted - run the
250
- thing end to end, confirm two components agree, exercise the path no test
251
- covers - is added with `keryx flow task add` and closed with
252
- `keryx flow task done`. Prose in `plan.md` blocks nothing, and an
253
- orchestrator that wrote the step is the same one deciding whether to run it.
259
+ Every check the plan says must happen before the work is accepted - run the thing end to end, confirm two components agree, exercise the path no test covers - is added with `keryx flow task add` and closed with `keryx flow task done`. Prose in `plan.md` blocks nothing, and an orchestrator that wrote the step is the same one deciding whether to run it.
254
260
 
255
- The failure this prevents is specific and has happened: a plan listed
256
- "confirm both components agree on the same input" as step 5, the
257
- implementation shipped without it, and review found that the two did not
258
- agree at all - the change could not work in production. The check had been
259
- identified correctly and then skipped, because nothing made skipping it
260
- visible.
261
+ The failure this prevents is specific and has happened: a plan listed "confirm both components agree on the same input" as step 5, the implementation shipped without it, and review found that the two did not agree at all - the change could not work in production. The check had been identified correctly and then skipped, because nothing made skipping it visible.
261
262
 
262
- Tasks are the mechanism for this. `keryx flow complete` runs a `tasks` gate
263
- over them, so an unrun verification step keeps the flow open instead of being
264
- quietly dropped.
263
+ Tasks are the mechanism for this. `keryx flow complete` runs a `tasks` gate over them, so an unrun verification step keeps the flow open instead of being quietly dropped.
265
264
 
266
- Know the gate's exact scope, because for years this file claimed a gate that
267
- did not exist and 24 completed flows shipped with an open task:
265
+ Know the gate's exact scope, because for years this file claimed a gate that did not exist and 24 completed flows shipped with an open task:
268
266
 
269
267
  - the gate is **opt-in per flow package**, keyed on `gates.tasks` in
270
268
  `flow.json`, which `keryx flow init` writes for every flow it creates. A
@@ -389,11 +387,7 @@ fi
389
387
  keryx flow task done <id> <Tn>
390
388
  ```
391
389
 
392
- Why each path is checked on its own: `git status --porcelain --` with no path
393
- lists the whole tree, `git add` fails on a path that is neither on disk nor
394
- tracked, and `git commit` with nothing staged exits 1. The trailing
395
- `-- "${PENDING[@]}"` commits only these paths, so a file another lane staged is
396
- not swept into this commit.
390
+ Why each path is checked on its own: `git status --porcelain --` with no path lists the whole tree, `git add` fails on a path that is neither on disk nor tracked, and `git commit` with nothing staged exits 1. The trailing `-- "${PENDING[@]}"` commits only these paths, so a file another lane staged is not swept into this commit.
397
391
 
398
392
  If new work is discovered:
399
393
 
@@ -415,6 +409,8 @@ properly formatted `subagent-result`.
415
409
  | `BLOCKED` | `keryx flow block <id> --reason "<worker reason>"`; resolve or escalate one concise question, then `flow unblock` and re-dispatch. |
416
410
  | `FAILED` | Emitted by harness **child** workers (`src/harness/child/contract.ts`), never by skill workers — `task-implementer` maps its own `failed` onto `BLOCKED`. Retry once with the same dispatch. If it fails again, block the flow and surface the error to the user. |
417
411
 
412
+ **A `behavior_changes` entry with `needs_operator: true` is a question, not a concern to journal.** task-implementer lists every user-visible tradeoff it made (message wording, error placement, state reset) there. Before accepting that result, ask the operator with the question tool — one question per entry, the implemented choice and each of its `alternatives` as options — and record the answer in `journal.md`. An answer other than the implemented one is a fix task on the same `Tn` before the boundary commit, not a review finding later.
413
+
418
414
  Carry `run_id`/`dispatch_id` across retries so the flow journal stays traceable,
419
415
  and record every dispatch against the task's persisted counter so a session
420
416
  restart does not reset the budget:
@@ -432,7 +428,8 @@ Before accepting implementation:
432
428
  1. Run focused tests for touched scope.
433
429
  2. Run `code-verifier`.
434
430
  3. Run `keryx health run` when Code Health is enabled.
435
- 4. Check the bounds, then run `review-orchestrator` with relevant domains — in this session,
431
+ 4. **First-green gate, once per flow.** The first time every implement/test task is `done` and steps 1–3 pass, ask the operator one question (question tool, options) before any review round: `issue AC met — open the PR with the standard review`, or `harden first` with each extra matrix cell or failure path you would add as its own option. Record the answer in `journal.md`; picked hardening becomes `keryx flow task add` tasks (plus `keryx flow ac update` when it adds criteria) before review. A dispatched run has no operator: it takes the standard review and journals that the dispatch answered the gate.
432
+ 5. Check the bounds, then run `review-orchestrator` with relevant domains — in this session,
436
433
  never handed whole to one subagent: one without a dispatch tool returns `STATUS: BLOCKED`.
437
434
  ```bash
438
435
  keryx review budget --spent <usd-so-far> --full --outstanding <subagents still running, 0 if none>
@@ -440,16 +437,15 @@ Before accepting implementation:
440
437
  A non-zero exit means the spend ceiling (3 USD by default) has been reached:
441
438
  **stop and ask the user** rather than dispatching another fan-out — never shrink the fan-out to fit.
442
439
  Dispatch prompts point at sources (files, SHAs, the PR) and never paraphrase a contract.
440
+ Every round opens with `keryx review start` on the current head, never a hand-built range (Phase 4 loop, step 1).
443
441
 
444
- `--outstanding` is the part that matters here. `review-orchestrator` runs inline
445
- in this session and dispatches reviewers in parallel; keryx cannot observe other
446
- subagents. Passing the count still running is the only thing that makes the
447
- concurrency cap mean anything; omit it and the cap bounds the reviewer fan-out
448
- alone, which the review record then states plainly. Add `--full` for `--all`.
442
+ `--outstanding` is the part that matters here. `review-orchestrator` runs inline in this session and dispatches reviewers in parallel; keryx cannot observe other subagents. Passing the count still running is the only thing that makes the concurrency cap mean anything; omit it and the cap bounds the reviewer fan-out alone, which the review record then states plainly. Add `--full` for `--all`.
449
443
 
450
- 5. If findings require code changes, dispatch fix work through `task-implementer`
451
- and record the fix task in the flow.
452
- 6. Close the skill-learning loop (see `rules/core/skill-lifecycle.mdc`). Collect
444
+ 6. If findings require code changes, run a fix round through `task-implementer`, recorded as a flow fix task:
445
+ - **Scope freeze.** A fix round fixes the round's findings and nothing else. Hardening, refactors or new cells a fix suggests go to the operator as a question (as in step 4); never grow scope inside a fix round.
446
+ - **Red probes → ONE implementer.** When reviewers left red probes (failing tests or commands on the findings), pass them as `test_case_specs` to one `task-implementer` dispatch — no separate `tests-creator` task. `tests-creator` runs only for a new contract no probe covers.
447
+ - **`suggested_fix` is a hypothesis.** Pass each finding's `id` and `suggested_fix` in `fix_context.review_feedback`; the implementer reproduces the finding (red), checks the suggestion against the state × event matrix, then fixes (task-implementer 1.3). Journal every `suggested_fix diverged` / `did not reproduce` note it returns.
448
+ 7. Close the skill-learning loop (see `rules/core/skill-lifecycle.mdc`). Collect
453
449
  the `skill_drift` fields from task-implementer results and the
454
450
  `## Skill Learning` block from review-orchestrator. For each flagged
455
451
  project-skill, dispatch a subagent — on a cheaper / non-flagship model if one
@@ -536,10 +532,8 @@ the bound plus an escalation — never an unbounded loop.
536
532
 
537
533
  ### PR review/fix loop
538
534
 
539
- 1. Run the relevant `review-orchestrator` checks against the PR and current
540
- branch state.
541
- 2. If findings or required check failures remain, create or update a flow fix
542
- task, dispatch `task-implementer`, push the fix, and run review again.
535
+ 1. Each round — the first and every one after a fix — is `keryx review start` on the PR's current head, then `review-orchestrator` from that package. `review start` finds the previous round itself (local package, git history, the PR's round block, the flow package) and, when the previous reviewed head is an ancestor, reviews the delta: fix changes with a small roster (logic, regression, the findings' authors, the verifier) and the prior findings attached, new changes as round 1, unchanged code carried with its dispositions. Never pass a whole-branch range or `--all` per round; `<package>/round.json` records the round, its source and its mode.
536
+ 2. If findings or required check failures remain, run a fix round under Phase 3 step 6 (flow fix task, `task-implementer`), push the fix, and run the next round as in step 1.
543
537
 
544
538
  **A required check failure is not automatically a fix task.** When `review.jev.ci_triage` is on, run `keryx review ci-triage --run <id> --json` on each failed run first: `top: flaky` (or a `deterministic` override) → rerun once (`gh run rerun --failed <id>`), record it in `journal.md`, and treat a second failure as `real-regression` regardless; `top: real-regression` → investigate and fix as usual; `top: infra` → report it in the completion notes and leave the code alone. The verdict is advisory only — never the sole reason to skip a fix task.
545
539
 
@@ -628,7 +622,7 @@ keryx flow complete <id>
628
622
 
629
623
  Completion is allowed only after the PR merge has been confirmed. The merge
630
624
  target must be the base branch captured when the flow was created; do not
631
- silently retarget or close against another branch. The flow folder is committed in the same PR as the code; the commit at closing is a rule to follow, not one the gate checks. `flow complete` fails the `folder-committed` gate while the flow's `flow.json` is not in `HEAD` (flows created from 0.3.53 on; commit it with `git add .metaproject/flows/<dir> && git commit`, then run `flow complete` again). `flow init` and `flow renumber` avoid numbers used on known remote branches, and `keryx flow check` reports a clash with a remote branch (repair with `keryx flow renumber <dir> --to <free id> --reason "<why>"`) and warns about a folder that is not committed. Flows created since the owner gate fail completion while no owner is set (`keryx flow owner set <id> --owner "<name>" --reason "<why>"`); `ac confirm` and `flow complete` append a signature — `--signed-by "<name>"` names the signer, otherwise `KERYX_ACTOR` or the local git identity is recorded, as a claim. A flow created with `--require-confirmation` needs one more gate — `--confirm-token <token>` from `keryx flow confirm <id>`, a short-lived single-use token minted by a typed challenge in a terminal; it proves an interactive step ran outside the agent's tool roster, not that a human ran it. A flow left stuck in `completing` by a crash or interrupted attempt (never a normal gate failure) recovers with `keryx flow recover <id> --reason "<why>"`.
625
+ silently retarget or close against another branch. The flow folder is committed in the same PR as the code; the commit at closing is a rule to follow, not one the gate checks. `flow complete` fails the `folder-committed` gate while the flow's `flow.json` is not in `HEAD` (flows created from 0.3.53 on; commit it with `git add .metaproject/flows/<dir> && git commit`, then run `flow complete` again). `flow init` and `flow renumber` avoid numbers used on known remote branches, and `keryx flow check` reports a clash with a remote branch (repair with `keryx flow renumber <dir> --to <free id> --reason "<why>"`) and warns about a folder that is not committed. Flows created since the owner gate fail completion while no owner is set (`keryx flow owner set <id> --owner "<name>" --reason "<why>"`); `ac confirm` and `flow complete` append a signature — `--signed-by "<name>"` names the signer, otherwise `KERYX_ACTOR` or the local git identity is recorded, as a claim. A flow created with `--require-confirmation` needs one more gate — `--confirm-token <token>` from `keryx flow confirm <id>`, a short-lived single-use token. In keryx shell call the `flow_confirm` tool (the operator answers it as a card; never send the operator to a terminal); outside it, a typed challenge in a terminal mints it. It proves an interactive step ran, not that a human ran it. A flow left stuck in `completing` by a crash or interrupted attempt (never a normal gate failure) recovers with `keryx flow recover <id> --reason "<why>"`.
632
626
 
633
627
  If gates fail, the CLI returns the flow to `in-progress`. Add a journal note,
634
628
  create fix tasks, and repeat Phase 2.
@@ -691,4 +685,7 @@ Stop and re-read this skill if you are thinking:
691
685
  | "The worker returned BLOCKED twice — faster if I implement this task myself." | The implementer never self-accepts and the orchestrator never implements. Block the flow, escalate one concise question, then unblock and re-dispatch. Doing the work here erases the boundary the whole flow model rests on. |
692
686
  | "Verification is described in the plan, so it will happen." | A verification step in the plan is a task, not a sentence. If it is not a task with a status, nothing records whether it ran, and the flow reaches `implemented` with an unrun gate. |
693
687
  | "The review fan-out is cheap, so the budget check can wait." | `keryx review budget --spent … --outstanding …` gates the fan-out, and `review-orchestrator` runs inline in this same main session, where keryx cannot see the in-flight subagents; declare them with `--outstanding`. Skipping the check means the cap bounds nothing. |
688
+ | "The reviewer wrote the fix; applying it is the fastest close." | A `suggested_fix` is a hypothesis. Most majors in a long fix loop are introduced by fixes: reproduce the finding red, check the suggestion against the state × event matrix, then fix (Phase 3 step 6). |
689
+ | "While I'm in this code, the fix round can also harden the empty state." | Scope freeze: a fix round fixes findings. New hardening is an operator decision at the first-green gate or as an explicit question, never silent growth. |
690
+ | "The implementer journaled its UX choice, so it is decided." | A `behavior_changes` entry with `needs_operator: true` is decided by the operator, asked with options before the result is accepted (Phase 2). A journal line decides nothing. |
694
691
  | "I'll set the verification kind later." | An `unclassified` criterion cannot become data afterwards without a recorded rewrite: `keryx flow ac update <id> --criterion ACn --text "…" --reason "<why>"`. Put the `[verify: …]` marker on every criterion before `flow freeze`. |
@@ -1125,12 +1125,12 @@ runs on the session model. That is a correct answer, not a failure.
1125
1125
 
1126
1126
  ```
1127
1127
  flags: <selected flags, e.g. --backend --security --testing-practices>
1128
- commit_range: <BASE_SHA>..HEAD
1128
+ commit_range: <BASE_SHA>..HEAD # round 1 only; a later round takes its range from round.json
1129
1129
  issue_url: <issue URL, when the job has one — enables the Stage 1 spec gate>
1130
1130
  context_doc: <JOBS_ROOT>/<job-name>/ai/context.md
1131
1131
  verification_mode: annotate
1132
1132
  managed_review: { mode: "review-flow", target: "branch", target_ref: "<feature-branch>" }
1133
- is_fix_round: <true on any round after the first>
1133
+ is_fix_round: <true on any round after the first; the round, its range and prior_findings come from `review start`/`review plan`, never from your own range>
1134
1134
  pr_comments: { enabled: <true when a PR exists> }
1135
1135
  ```
1136
1136
 
@@ -1298,11 +1298,11 @@ FOR iteration in [1, 2, 3]:
1298
1298
  4. Run the sanity check (step 2.5.2 logic) — verify commits were made
1299
1299
  5. Run the next managed round — the FULL 2.6.1 sequence, not a bare re-dispatch:
1300
1300
  keryx review budget --spent <usd> --outstanding <n>
1301
- keryx review start --target branch --ref <feature-branch> --head <new-head>
1301
+ keryx review plan --target branch --ref <feature-branch> --head <new-head>
1302
+ # start + scope + blast-radius + slice + required + estimate on the new head;
1303
+ # round.json names the delta, scope.json and blast-radius.json land in the package
1302
1304
  keryx review comments collect --repo <r> --pr <n> --sha <new-head> --round <N+1> --out <file>
1303
- keryx review scope --ref "$BASE_SHA" --json > scope.json
1304
- keryx review blast-radius --ref "$BASE_SHA" --previous blast-radius.json --json > blast-radius.json
1305
- <run review-orchestrator inline, in this session, with is_fix_round: true>
1305
+ <run review-orchestrator inline, in this session; it takes the round from round.json>
1306
1306
  keryx review ingest --report <new-report> --ref <feature-branch> --head <new-head> \
1307
1307
  --scope scope.json --blast-radius blast-radius.json \
1308
1308
  --verifications <file> --refuted <file> --outstanding <n>
@@ -179,7 +179,7 @@ If `blocker` or `major` findings exist:
179
179
  1. Group by file
180
180
  2. Launch task-implementer (`task_type: "fix"`) with the findings
181
181
  3. Run the sanity check — verify commits were made
182
- 4. Run the NEXT managed round (the whole REVIEW sequence above, `is_fix_round: true`)
182
+ 4. Run the NEXT managed round: `keryx review plan --target branch --ref <feature-branch> --head <new-head>` (or `review start`) on the new head, then review-orchestrator. The round, range and prior findings come from `round.json`: no own range, no `--all` per round, no whole-branch re-review.
183
183
  5. Disposition every finding of the previous round:
184
184
  ```bash
185
185
  keryx review complete <review-id> --finding F-001 --disposition acted-on --evidence "<commit>"
@@ -98,11 +98,13 @@ For a description-based task, omit `issue_number` and issue references in commit
98
98
 
99
99
  ```
100
100
  FIX_CONTEXT:
101
- review_feedback: structured findings from reviewer (file, line, severity, message)
101
+ review_feedback: structured findings from reviewer (id, file, line, severity, message, suggested_fix)
102
102
  original_task_ids: the tasks that introduced the findings (array)
103
103
  iteration: fix iteration number, 1..3 — the same repair bound as 5.4
104
104
  ```
105
105
 
106
+ **A reviewer's `suggested_fix` is a hypothesis, not an instruction.** Per finding: reproduce it first — a test or probe that fails on the current head (red); check the suggested fix against the plan's state × event matrix (`plan.md`, when it has one) and every caller the change reaches; then fix and show the probe green. A finding that does not reproduce is reported, not fixed. Record each divergence in `notes`: `suggested_fix diverged: <id> — <what you did instead and why>` or `did not reproduce: <id>`.
107
+
106
108
  **1.4 Validate the request against the contract:**
107
109
 
108
110
  Write the request you were handed to a file and run:
@@ -143,7 +145,7 @@ git -C "<codebase_path>" rev-parse --abbrev-ref HEAD # must equal <branch>
143
145
 
144
146
  **1.5 TDD Check (if `test_case_specs` is present):**
145
147
 
146
- If the task object contains `test_case_specs` (provided by `tests-creator`):
148
+ If the task object contains `test_case_specs` (provided by `tests-creator`, or in a fix round the reviewers' red probes):
147
149
  1. Read each test file listed in `test_case_specs.test_files`
148
150
  2. Run the tests using `test_case_specs.run_command` — confirm they FAIL
149
151
  3. If tests pass already → report `DONE_WITH_CONCERNS` (tests may not be testing the right thing)
@@ -427,6 +429,7 @@ Write full JSON to `<JOBS_ROOT>/<JOB_NAME>/results/<task_id>.json`:
427
429
  "skill_drift": "<none | stale: <module>/<skill> — <what diverged> | missing: <module> should have a project-skill>",
428
430
  "noticed_not_touched": [{ "what": "<problem>", "where": "<path|symbol>" }],
429
431
  "assumptions": ["<what you assumed where the task did not say>"],
432
+ "behavior_changes": [{ "description": "<what now behaves differently>", "user_visible": true, "needs_operator": true, "alternatives": ["<option you did not take>"] }],
430
433
  "not_touched": [{ "path": "src/path/file.ts", "reason": "<why left alone>" }],
431
434
  "notes": "<any warnings, blockers, or additional context>"
432
435
  }
@@ -434,23 +437,14 @@ Write full JSON to `<JOBS_ROOT>/<JOB_NAME>/results/<task_id>.json`:
434
437
 
435
438
  Set `skill_drift` from Phase 2.0b: if the project-skill you used was not `fresh`, or the code you wrote diverged from what a skill documents, name the skill and the divergence. The orchestrator uses this to decide whether to trigger `skills learn` (do NOT run `learn` yourself — it is a mutating step the orchestrator dispatches; see `rules/core/skill-lifecycle.mdc`).
436
439
 
437
- Three of those fields have a bar, and a field that collects noise trains the
438
- reader to skim all three. Omit one rather than pad it. The trio is adapted (MIT;
439
- see THIRD_PARTY_NOTICES.md) as contract fields rather than a free-text template; the bars below are ours.
440
+ Four of those fields have a bar, and a field that collects noise trains the reader to skim all of them. Omit one rather than pad it. The first three are adapted (MIT; see THIRD_PARTY_NOTICES.md) as contract fields rather than a free-text template; the bars below are ours.
440
441
 
441
- - `assumptions` — what you assumed where the task was silent AND acted on. If
442
- the assumption being wrong would not change a line you wrote, it is a hedge,
443
- not an assumption; leave it out.
444
- - `noticed_not_touched` — a problem in code you actually read, stated so a
445
- follow-up task could be written from it alone, with `where` it lives. Not a
446
- dumping ground for every smell seen in passing, and not style opinion.
447
- - `not_touched` — of the files this task was aimed at (`target_files`), the ones
448
- absent from `files_modified`/`files_created`/`files_deleted`, each with why.
449
- It answers that one question and no other: a reader subtracts those lists and
450
- this one from `target_files`, and flags the remainder as an unexplained omission.
442
+ - `assumptions` — what you assumed where the task was silent AND acted on. If the assumption being wrong would not change a line you wrote, it is a hedge, not an assumption; leave it out.
443
+ - `noticed_not_touched` — a problem in code you actually read, stated so a follow-up task could be written from it alone, with `where` it lives. Not a dumping ground for every smell seen in passing, and not style opinion.
444
+ - `not_touched` — of the files this task was aimed at (`target_files`), the ones absent from `files_modified`/`files_created`/`files_deleted`, each with why. It answers that one question and no other: a reader subtracts those lists and this one from `target_files`, and flags the remainder as an unexplained omission.
445
+ - `behavior_changes` — behavior you chose where the task and the plan's state × event matrix did not decide it. **Every user-visible tradeoff goes here with `user_visible: true, needs_operator: true`** and the `alternatives` you rejected: message wording, where an error appears, which state resets or survives a switch, retry or reload. Never only in `assumptions`, `notes` or a journal line — the orchestrator asks the operator before it accepts a result carrying one, and a tradeoff it cannot see is accepted unasked.
451
446
 
452
- Then check the shape and record the file — two commands, both of which refuse
453
- rather than warn:
447
+ Then check the shape and record the file — two commands, both of which refuse rather than warn:
454
448
 
455
449
  ```bash
456
450
  keryx skills contracts validate <JOBS_ROOT>/<JOB_NAME>/results/<task_id>.json \
@@ -489,7 +483,7 @@ The step's own status is the orchestrator's to write (`keryx job step <JOB_NAME>
489
483
 
490
484
  Return a compact STATUS response following `rules/core/subagent-status-protocol.md`.
491
485
  **Do NOT include the full JSON block inline** — the orchestrator reads the result file when it needs details.
492
- The inline response must contain only: STATUS line + Completed bullets + Files changed + Verification summary.
486
+ The inline response must contain only: STATUS line + Completed bullets + Files changed + Verification summary + a `## Behavior changes` list (each entry's description, `user_visible`, `needs_operator`, alternatives) whenever `behavior_changes` is non-empty — a flow dispatch writes no result file, so this list is the only copy the orchestrator reads. Any `needs_operator: true` entry makes the status at best `DONE_WITH_CONCERNS`, never `DONE`.
493
487
 
494
488
  **Status classification:**
495
489
  - `success` → `STATUS: DONE`, or `DONE_WITH_CONCERNS` when work that cleared the
@@ -149,6 +149,14 @@
149
149
  "additionalProperties": false,
150
150
  "required": ["file", "severity", "message"],
151
151
  "properties": {
152
+ "id": {
153
+ "type": "string",
154
+ "description": "The finding's id in the review package, cited back in the result's `notes` when the fix diverged from `suggested_fix` or the finding did not reproduce"
155
+ },
156
+ "suggested_fix": {
157
+ "type": "string",
158
+ "description": "The reviewer's proposed fix. A hypothesis, not an instruction: SKILL.md 1.3 reproduces the finding first and checks this against the plan's state × event matrix before applying anything"
159
+ },
152
160
  "file": {
153
161
  "type": "string",
154
162
  "description": "File path where the issue was found"
@@ -65,6 +65,25 @@
65
65
  "description": "What was assumed where the task did not say. A wrong assumption is the usual cause of a rejected result, so these are the first thing a reviewer should check. Read only by a human — nothing in the result or the request can confirm an assumption, so this is deliberately an array of sentences rather than a structure that would imply a check nobody performs.",
66
66
  "items": { "type": "string", "minLength": 1 }
67
67
  },
68
+ "behavior_changes": {
69
+ "type": "array",
70
+ "description": "Behavior the implementer chose where the task and the plan's state × event matrix did not decide it. Every user-visible tradeoff (message wording, error placement, which state resets or survives) is listed with `user_visible: true, needs_operator: true`; flow-orchestrator Phase 2 asks the operator before accepting a result carrying such an entry, so a tradeoff recorded only in `assumptions` or `notes` is one nobody decided.",
71
+ "items": {
72
+ "type": "object",
73
+ "additionalProperties": false,
74
+ "required": ["description", "user_visible", "needs_operator"],
75
+ "properties": {
76
+ "description": { "type": "string", "minLength": 1, "description": "What now behaves differently, stated as the user or caller would observe it" },
77
+ "user_visible": { "type": "boolean", "description": "A user of the product can see the difference" },
78
+ "needs_operator": { "type": "boolean", "description": "The choice is a product decision the operator must confirm before the result is accepted" },
79
+ "alternatives": {
80
+ "type": "array",
81
+ "description": "The options not taken, so the operator chooses between real alternatives rather than approving the one shown",
82
+ "items": { "type": "string", "minLength": 1 }
83
+ }
84
+ }
85
+ }
86
+ },
68
87
  "not_touched": {
69
88
  "type": "array",
70
89
  "description": "Files a reader would expect in `files_modified` and will not find there, each with the reason. `path` is a separate field because this is machine-checkable: a reader holding the request can subtract `files_modified`/`files_created`/`files_deleted` and this list from `task.target_files` and flag whatever is left as an unexplained omission.",
@@ -55,7 +55,7 @@ operator reads in the shell's Schedules section.
55
55
  Call `schedule_create`. The operator then sees a confirmation card: cadence and
56
56
  next runs, prompt, runner, budget, network, every granted tool with its binary
57
57
  and account, and exactly what gets installed. You do not confirm it. Only the
58
- operator can, and every permission mode asks, `auto` included.
58
+ operator can, and every permission mode asks, `auto` included. Changing an existing schedule works the same way (`schedule_pause`, `schedule_resume`, `schedule_run`, `schedule_remove`): the operator answers a card; never send them to a terminal.
59
59
 
60
60
  ### Step 4: Report back
61
61
  After the operator's answer, say what was scheduled and when it next runs, or
@@ -72,7 +72,7 @@ that nothing was written. Point to `/schedules` for the list and the reports.
72
72
  - NEVER read or copy the schedule signing key (`schedule-hmac.key`), and never try to
73
73
  reach the scheduler another way (quoting, `env -u`, a script). The text floor can be
74
74
  worked around, and that is exactly why it is not the gate. `keryx schedule add|resume|run`
75
- need the operator's own terminal, and a schedule without this machine's signature never runs.
75
+ need the operator's own terminal (inside keryx shell the operator answers the card instead), and a schedule without this machine's signature never runs.
76
76
  - NEVER run `loginctl enable-linger`. If linger is off, tell the operator the
77
77
  timer runs only while they are logged in and that the command is theirs to run.
78
78
  - State the limits when they matter: the machine must be on; systemd and launchd
@@ -26,7 +26,7 @@ license: "MIT"
26
26
 
27
27
  Specialized reviewer for **architectural patterns and structural decisions**.
28
28
  Reviews only changes introduced in the current branch (merge-base to HEAD).
29
- Focuses exclusively on verifiable structural violations — not style opinions or hypothetical concerns. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs.
29
+ Focuses exclusively on verifiable structural violations — not style opinions or hypothetical concerns. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs. It runs no command unless its payload's `allowed_tools` says `run-tests`.
30
30
 
31
31
  ---
32
32
 
@@ -26,7 +26,7 @@ license: "MIT"
26
26
 
27
27
  Focused backend reviewer covering NestJS patterns, REST API design, database access patterns,
28
28
  and TypeScript correctness for service-layer code. This skill does NOT duplicate security or
29
- performance checks — those belong to `review-security-code` and `review-performance`. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs.
29
+ performance checks — those belong to `review-security-code` and `review-performance`. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs. It runs no command unless its payload's `allowed_tools` says `run-tests`.
30
30
 
31
31
  ---
32
32
 
@@ -27,7 +27,7 @@ license: "MIT"
27
27
  # Review: Clean Code + SOLID
28
28
 
29
29
  Specialized reviewer for **Clean Code principles** (Robert C. Martin) and **SOLID** at the
30
- function and class level. Focuses on code in the current branch diff only. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs.
30
+ function and class level. Focuses on code in the current branch diff only. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs. It runs no command unless its payload's `allowed_tools` says `run-tests`.
31
31
 
32
32
  The goal is not stylistic nitpicking — every finding here points to a concrete
33
33
  maintainability, readability, or extensibility problem that will cause friction
@@ -24,7 +24,7 @@ license: "MIT"
24
24
  # Review — Core Boundaries
25
25
 
26
26
  Reviewer for shared infrastructure modules. A core module should provide stable foundations
27
- used by feature modules; it should not accumulate feature-specific behaviour. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs.
27
+ used by feature modules; it should not accumulate feature-specific behaviour. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs. It runs no command unless its payload's `allowed_tools` says `run-tests`.
28
28
 
29
29
  ---
30
30
 
@@ -24,7 +24,7 @@ license: "MIT"
24
24
  # Review — Flow Graph Abstractions
25
25
 
26
26
  Reviewer for reusable ReactFlow/graph integration layers. Use it when a repository has a shared
27
- graph surface consumed by domain modules. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs.
27
+ graph surface consumed by domain modules. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs. It runs no command unless its payload's `allowed_tools` says `run-tests`.
28
28
 
29
29
  ---
30
30
 
@@ -2,18 +2,14 @@
2
2
  name: review-frontend
3
3
  model_tier: standard
4
4
  description: "Use when a frontend review is requested, checking React component patterns, MobX state management (observer, actions, computed, reactions, lifecycle), View-Store boundaries, and TypeScript safety in changed frontend code. NOT for backend patterns, security vulnerabilities, performance bottlenecks, or cross-layer architecture."
5
- triggers:
6
- - "frontend review"
7
- - "component review"
8
- - "ui review"
9
- - "review React"
10
- - "review components"
5
+ triggers: ["frontend review", "component review", "ui review", "review React", "review components"]
11
6
  metadata:
12
7
  author: "MrCipherSmith"
13
8
  version: "1.1.0"
14
9
  category: "review"
15
10
  compatible_harnesses: "cursor,codex,zed,opencode,claude"
16
11
  stack_requires: "react,mobx"
12
+ overlap_group: "frontend"
17
13
  license: "MIT"
18
14
  ---
19
15
 
@@ -21,7 +17,7 @@ license: "MIT"
21
17
 
22
18
  ## Purpose
23
19
 
24
- Validates frontend-specific patterns in the changed code of the current branch. Covers React component correctness (observer wrapping, MVVM boundary, useEffect misuse, accessibility basics, key props), full MobX store patterns (structure, member ordering, accessibility modifiers, observable collections, reactions, async actions, bidirectional sync, lifecycle, API placement), and TypeScript safety. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs.
20
+ Validates frontend-specific patterns in the changed code of the current branch. Covers React component correctness (observer wrapping, MVVM boundary, useEffect misuse, accessibility basics, key props), full MobX store patterns (structure, member ordering, accessibility modifiers, observable collections, reactions, async actions, bidirectional sync, lifecycle, API placement), and TypeScript safety. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs. It runs no command unless its payload's `allowed_tools` says `run-tests`.
25
21
 
26
22
  This skill consolidates what was previously split between `code-style-review` (frontend parts) and `code-mobx-store-review`. It supersedes both for frontend changes.
27
23
 
@@ -11,16 +11,14 @@ description: |
11
11
  NOT for: generic React and MobX correctness with no local rule behind it
12
12
  (review-frontend), what the CSS actually renders (review-layout), or naming and
13
13
  readability as a matter of taste (review-style).
14
- triggers:
15
- - "frontend conventions"
16
- - "local frontend rules"
17
- - "CLAUDE frontend"
18
- - "review frontend conventions"
14
+ triggers: ["frontend conventions", "local frontend rules", "CLAUDE frontend", "review frontend conventions"]
19
15
  metadata:
20
16
  author: "MrCipherSmith"
21
17
  version: "1.0.0"
22
18
  category: "review"
23
19
  stack_requires: "react,mobx"
20
+ overlap_group: "frontend"
21
+ overlap_rank: "generic"
24
22
  license: "MIT"
25
23
  ---
26
24
 
@@ -28,7 +26,7 @@ license: "MIT"
28
26
 
29
27
  Reviewer for frontend repository conventions that are more specific than generic React/MobX
30
28
  correctness. Use it alongside `review-frontend`, `review-style`, `review-performance`, and
31
- `review-logic`. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs.
29
+ `review-logic`. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs. It runs no command unless its payload's `allowed_tools` says `run-tests`.
32
30
 
33
31
  Before reviewing, read the nearest project guide files if they exist: root `CLAUDE.md`,
34
32
  `AGENTS.md`, `.junie/guidelines.md`, `ARCHITECTURE.md`, and module-level `CLAUDE.md` files.
@@ -28,7 +28,7 @@ license: "MIT"
28
28
 
29
29
  Specialized reviewer for **code that will run under significant concurrent load**.
30
30
  Focuses on correctness under concurrency, resource contention, back-pressure, failure
31
- modes at scale, and distributed systems invariants — not general performance profiling. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs.
31
+ modes at scale, and distributed systems invariants — not general performance profiling. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs. It runs no command unless its payload's `allowed_tools` says `run-tests`.
32
32
 
33
33
  Reviews only changes introduced in the current branch (merge-base to HEAD).
34
34
 
@@ -27,7 +27,7 @@ license: "MIT"
27
27
 
28
28
  Specialized reviewer for **logic correctness and algorithmic soundness**.
29
29
  Inherits from the original `code-ai-review` + `code-learned-review` correctness phases
30
- and unifies them into a single focused pass. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs.
30
+ and unifies them into a single focused pass. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs. It runs no command unless its payload's `allowed_tools` says `run-tests`.
31
31
 
32
32
  This reviewer does not care about formatting, naming, or architecture opinions.
33
33
  Every finding must describe an observable, reproducible defect or a spec gap.
@@ -193,7 +193,7 @@ earlier review round, because there the finding is recorded as closed and is not
193
193
 
194
194
  ## Orchestrated Review Contract
195
195
 
196
- When dispatched by `review-orchestrator`, follow the provided `reviewer-input.schema.json` payload. Return a `REVIEW_RESULT` object compatible with `.metaproject/skills/gdskills/review/review-orchestrator/reviewer-finding.schema.json`, then a concise markdown summary. Keep findings evidence-based, include concrete `suggested_fix` for every blocker/major, and return `NEEDS_CONTEXT` instead of guessing when required context is missing.
196
+ When dispatched by `review-orchestrator`, follow the provided `reviewer-input.schema.json` payload. Return a `REVIEW_RESULT` object compatible with `.metaproject/skills/gdskills/review/review-orchestrator/reviewer-finding.schema.json`, then a concise markdown summary. Keep findings evidence-based, include concrete `suggested_fix` for every blocker/major, and return `NEEDS_CONTEXT` instead of guessing when required context is missing. On a FIX slice, also return `prior_dispositions: [{id, disposition}]` for every attached finding id.
197
197
 
198
198
  ---
199
199