@mrciphersmith/keryx 0.3.121 → 0.3.123
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/README.md +21 -1
- package/dist/cli.js +8829 -4479
- package/dist/core.js +1914 -1236
- package/package.json +2 -1
- package/src/gdgraph/staleness.ts +20 -7
- package/src/gdskills/bundled/rules/core/skill-lifecycle.mdc +9 -0
- package/src/gdskills/bundled/rules/core/skills-storage-workflow.mdc +6 -0
- package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/SKILL.md +12 -12
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +21 -22
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/orchestrator-prompt.md +12 -4
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.md +11 -12
- package/src/gdskills/bundled/skills/orchestration/task-implementer/input-contract.schema.json +5 -0
- package/src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json +16 -0
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.md +9 -9
- package/src/gdskills/bundled/skills/quality/tests-creator/input-contract.schema.json +4 -0
- package/src/gdskills/bundled/skills/quality/tests-creator/output-contract.schema.json +17 -1
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +3 -2
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +4 -4
- package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-finding.schema.json +10 -1
- package/src/gdskills/bundled/skills/review/review-premise/SKILL.md +1 -1
- package/src/gdskills/contracts/review-finding.schema.json +14 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.123",
|
|
4
4
|
"description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"check": "bun run lint && bun run typecheck && bun run typecheck:scripts && bun test",
|
|
55
55
|
"check:core": "bun run lint && bun run typecheck && bun run typecheck:scripts && bun run test:core",
|
|
56
56
|
"docs:build": "mkdocs build --strict",
|
|
57
|
+
"docs:check": "bun run check:doc-links && bun run check:retired-spellings && bun test src/cli-reference-coverage.test.ts src/standard/commands-by-task.test.ts src/standard/help-groups.test.ts",
|
|
57
58
|
"check:doc-links": "bun scripts/check-doc-links.ts",
|
|
58
59
|
"check:retired-spellings": "bun scripts/check-retired-cli-spellings.ts",
|
|
59
60
|
"baseline:review-precision": "bun scripts/review-precision-baseline.ts",
|
package/src/gdgraph/staleness.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { stat } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { revisionOperand } from "../lib/git-revision";
|
|
5
|
-
import { gitCmd,
|
|
5
|
+
import { gitCmd, readProvenance } from "../sync/provenance";
|
|
6
6
|
|
|
7
7
|
// W7 AC4 follow-up (flow 304 T8) — a real, previously-latent defect found
|
|
8
8
|
// while wiring up the `modified` bucket below: `gitCmd`/`gitCmdResult`
|
|
@@ -35,7 +35,7 @@ import { gitCmd, gitHead, readProvenance } from "../sync/provenance";
|
|
|
35
35
|
// splitting: an ordinary entry is `XY PATH\0`, but a rename/copy (X or Y is
|
|
36
36
|
// `R`/`C`) is `XY PATH\0ORIG_PATH\0` — TWO NUL-terminated fields, not one —
|
|
37
37
|
// which `categorizeStatusEntries` below accounts for.
|
|
38
|
-
function gitStatusPorcelain(cwd: string): Promise<string | null> {
|
|
38
|
+
export function gitStatusPorcelain(cwd: string): Promise<string | null> {
|
|
39
39
|
return new Promise((resolve) => {
|
|
40
40
|
try {
|
|
41
41
|
const child = spawn("git", ["status", "--porcelain=v1", "-z"], { cwd, stdio: ["ignore", "pipe", "ignore"] });
|
|
@@ -258,7 +258,10 @@ async function committedChangedFiles(
|
|
|
258
258
|
* The structured staleness check (AFC-10). Never throws — a git failure is
|
|
259
259
|
* reported as `status: "unknown"` with a reason, not thrown and not "fresh".
|
|
260
260
|
*/
|
|
261
|
-
export async function checkGraphStaleness(
|
|
261
|
+
export async function checkGraphStaleness(
|
|
262
|
+
cwd: string,
|
|
263
|
+
statusObservation?: Promise<string | null>,
|
|
264
|
+
): Promise<StalenessCheck> {
|
|
262
265
|
const nodesStat = await stat(nodesJsonlPath(cwd)).catch(() => null);
|
|
263
266
|
if (!nodesStat) {
|
|
264
267
|
return { status: "stale", reasons: ["graph has not been built yet (no nodes.jsonl)"] };
|
|
@@ -303,19 +306,30 @@ export async function checkGraphStaleness(cwd: string): Promise<StalenessCheck>
|
|
|
303
306
|
// `unknown` (a weaker claim — "not verified", not "confirmed stale") rather
|
|
304
307
|
// than staying silently fresh.
|
|
305
308
|
let provenanceMissing = false;
|
|
306
|
-
|
|
309
|
+
// Independent local probes overlap. The shared status belongs to this call
|
|
310
|
+
// only; standalone checks always obtain a new observation.
|
|
311
|
+
const [commit, prefix, porcelain, provenance] = await Promise.all([
|
|
312
|
+
gitCmd(cwd, ["rev-parse", "HEAD"]),
|
|
313
|
+
gitCmd(cwd, ["rev-parse", "--show-prefix"]),
|
|
314
|
+
statusObservation ?? gitStatusPorcelain(cwd),
|
|
315
|
+
readProvenance(cwd, "gdgraph"),
|
|
316
|
+
]);
|
|
317
|
+
const head = commit ? { commit } : null;
|
|
307
318
|
// T19 finding 3: `--show-prefix` is the project root's path relative to the
|
|
308
319
|
// git root (empty string AT the git root) — needed both here (to scope a
|
|
309
320
|
// commit-range diff to real source, excluding the graph's own bookkeeping)
|
|
310
321
|
// and below (the working-tree `.metaproject/` exclusion). Fetched once and
|
|
311
322
|
// reused, so this stays within the module's stated "one or two git calls"
|
|
312
323
|
// budget rather than asking twice.
|
|
313
|
-
const rootPrefix =
|
|
324
|
+
const rootPrefix = prefix ?? "";
|
|
325
|
+
if (prefix === null) {
|
|
326
|
+
gitFailed = true;
|
|
327
|
+
reasons.push("git rev-parse --show-prefix failed");
|
|
328
|
+
}
|
|
314
329
|
if (head === null) {
|
|
315
330
|
gitFailed = true;
|
|
316
331
|
reasons.push("git rev-parse HEAD failed (not a git repository, or git is unavailable)");
|
|
317
332
|
} else {
|
|
318
|
-
const provenance = await readProvenance(cwd, "gdgraph");
|
|
319
333
|
if (provenance) {
|
|
320
334
|
if (provenance.commit !== head.commit) {
|
|
321
335
|
const changedFiles = await committedChangedFiles(cwd, provenance.commit, head.commit, rootPrefix);
|
|
@@ -343,7 +357,6 @@ export async function checkGraphStaleness(cwd: string): Promise<StalenessCheck>
|
|
|
343
357
|
}
|
|
344
358
|
|
|
345
359
|
// --- untracked / deleted / renamed / modified ------------------------------
|
|
346
|
-
const porcelain = await gitStatusPorcelain(cwd);
|
|
347
360
|
if (porcelain === null) {
|
|
348
361
|
gitFailed = true;
|
|
349
362
|
reasons.push("git status failed");
|
|
@@ -14,6 +14,15 @@ with context. It is NOT a git hook — the `gdskills` post-commit hook only runs
|
|
|
14
14
|
`skills verify --all --dry-run` as an advisory staleness signal and never
|
|
15
15
|
mutates.
|
|
16
16
|
|
|
17
|
+
## Project rules — read first, then say so
|
|
18
|
+
|
|
19
|
+
Before writing code or tests, run `keryx skills resolve-context --files <files>
|
|
20
|
+
--for implement|test`. The `must_read` list puts project rules and project
|
|
21
|
+
skills ahead of the bundled ones. Read every entry, then return
|
|
22
|
+
`project_context_applied` with each path and its `sha256`. A missing entry is a
|
|
23
|
+
warning by default and a refusal under `gdskills.projectContext.enforcement:
|
|
24
|
+
block`.
|
|
25
|
+
|
|
17
26
|
## Verify — cheap, inline, before you trust a skill
|
|
18
27
|
|
|
19
28
|
Whoever is about to rely on a project-skill verifies it first:
|
|
@@ -23,6 +23,12 @@ Before creating or editing a skill, ask and confirm:
|
|
|
23
23
|
is an installed mirror, regenerated by `keryx init`/`keryx update` — never
|
|
24
24
|
edit it directly, edits there are overwritten on the next run.
|
|
25
25
|
- Project-local skill: `.metaproject/project-skills/<skill-name>/`.
|
|
26
|
+
- Project rule: `.metaproject/rules/project/<rule>.mdc`, brought in with
|
|
27
|
+
`keryx skills import --rules <dir>` (`--link` reads a folder in place). Project
|
|
28
|
+
rules and project implementation skills are resolved first, bundled ones
|
|
29
|
+
second (`keryx skills resolve-context`). A project skill supplements the
|
|
30
|
+
built-in `task-implementer` and `tests-creator`; it never takes a bundled
|
|
31
|
+
skill's name.
|
|
26
32
|
|
|
27
33
|
## Required Source Layout
|
|
28
34
|
- `SKILL.md` — canonical source of truth, the shared build used by every runtime
|
|
@@ -143,9 +143,8 @@ it already tried. The flow package does.
|
|
|
143
143
|
or close the task (`keryx flow task done <id> <Tn>`). Re-dispatching
|
|
144
144
|
over an unresolved attempt is how the same work gets done twice.
|
|
145
145
|
|
|
146
|
-
`keryx flow next` also lists every OTHER not-done task with an
|
|
147
|
-
attempt
|
|
148
|
-
Resolve them the same way before assuming the flow is idle.
|
|
146
|
+
`keryx flow next` also lists, under `unresolved`, every OTHER not-done task with an
|
|
147
|
+
unresolved attempt (parallel dispatches that never reported back); resolve them the same way.
|
|
149
148
|
|
|
150
149
|
5. Before dispatching a worker for that task, record the attempt:
|
|
151
150
|
|
|
@@ -164,10 +163,8 @@ it already tried. The flow package does.
|
|
|
164
163
|
keryx review loop --flow <id> --task <Tn>
|
|
165
164
|
```
|
|
166
165
|
|
|
167
|
-
A non-zero exit means the same finding
|
|
168
|
-
|
|
169
|
-
Do not spend the remaining attempts on the same approach because the
|
|
170
|
-
budget has some left — that is the failure this check exists to catch.
|
|
166
|
+
A non-zero exit means the same finding recurred or two consecutive rounds
|
|
167
|
+
produced identical output: go straight to the re-planning step, whatever budget is left.
|
|
171
168
|
8. If the flow is `blocked`, read the blocking reason from `journal.md`,
|
|
172
169
|
resolve or escalate it, then `keryx flow unblock <id>`.
|
|
173
170
|
4. If the user wants a new flow, continue at 0.1.
|
|
@@ -323,6 +320,8 @@ auto-commit off (task-implementer's `automation.auto_commit: false`). A
|
|
|
323
320
|
`subagent-dispatch` has no `automation` field, so the setting travels as the
|
|
324
321
|
explicit constraint below — never as the worker's default, which is `true`.
|
|
325
322
|
|
|
323
|
+
Before every `test`, `implement` and fix dispatch, run `keryx skills resolve-context --for test|implement --files <the task's target files, comma-joined> --task-type <type> --task-text '<the task's title and description>' --out .metaproject/flows/<dir>/resolved/<Tn>.<for>.json` (a fix dispatch re-resolves on the new head) and pass that path as `resolved_context_file`: a `context_refs` entry plus the constraint below. Write the text single-quoted (a `'` inside it becomes `'\''`): double quotes let the shell run any backtick or `$(...)` a task description contains. `--task-text` (the harness and MCP field is `task_text`) is what lets a keyword of a document declared in `references.json` match; without it only globs promote. A promoted entry comes back as `must_read` with reason `condition:glob:<g>` or `condition:keyword:<k>`, and its `project_context_applied` item must carry the `path`, the `sha256` of the whole file as pinned, the declared `anchor`, a `summary` (at least 40 characters, in the worker's own words, reusing at least three distinct words of five or more letters from the anchored section) and `how` `read` or `followed`, never `n/a`. Before accepting the reply, run `keryx skills contracts validate <result.json> --schema task-implementer-output|tests-creator-output --resolved <that file>`; under `gdskills.projectContext.enforcement: block` a miss is a refusal, under `warn` journal it.
|
|
324
|
+
|
|
326
325
|
Dispatch payload, bound to the flow (map `target_skill` from the routing table):
|
|
327
326
|
|
|
328
327
|
```json
|
|
@@ -337,12 +336,14 @@ Dispatch payload, bound to the flow (map `target_skill` from the routing table):
|
|
|
337
336
|
"context_refs": [
|
|
338
337
|
{ "path": ".metaproject/flows/<dir>/context.md", "kind": "context", "exists": true },
|
|
339
338
|
{ "path": ".metaproject/flows/<dir>/plan.md", "kind": "plan", "exists": true },
|
|
340
|
-
{ "path": ".metaproject/flows/<dir>/acceptance-criteria.md", "kind": "custom", "exists": true }
|
|
339
|
+
{ "path": ".metaproject/flows/<dir>/acceptance-criteria.md", "kind": "custom", "exists": true },
|
|
340
|
+
{ "path": ".metaproject/flows/<dir>/resolved/<Tn>.<for>.json", "kind": "custom", "exists": true }
|
|
341
341
|
],
|
|
342
342
|
"files_to_read": ["<only files gdgraph/gdctx narrowed to>"],
|
|
343
343
|
"constraints": [
|
|
344
344
|
"Never edit flow.json.",
|
|
345
345
|
"Never edit frozen acceptance criteria.",
|
|
346
|
+
"resolved_context_file: .metaproject/flows/<dir>/resolved/<Tn>.<for>.json — read its must_read entries and cite each in project_context_applied; an entry promoted by a condition also needs its anchor and a summary.",
|
|
346
347
|
"Return a subagent-result; first line must be STATUS:.",
|
|
347
348
|
"Follow rules/core/git-concurrency.mdc: never git stash; never git add -A.",
|
|
348
349
|
"Root: <worktree_path>. Before the first write run `cd <worktree_path> && pwd && git branch --show-current`; run every git command as `git -C <worktree_path>`.",
|
|
@@ -359,9 +360,8 @@ Dispatch payload, bound to the flow (map `target_skill` from the routing table):
|
|
|
359
360
|
`DONE_WITH_CONCERNS`), then mark task progress. It commits exactly those
|
|
360
361
|
`changed_files` from the worker's `subagent-result` that git still shows as
|
|
361
362
|
changed, never `-A`/`--all`/`.` (`rules/core/git-concurrency.mdc` rule 4 and
|
|
362
|
-
"Reporting Back"). It skips cleanly when none remain
|
|
363
|
-
|
|
364
|
-
committed on its own (tests-creator's stubs):
|
|
363
|
+
"Reporting Back"). It skips cleanly when none remain (expected for a worker that
|
|
364
|
+
reported no files or committed on its own, like tests-creator's stubs):
|
|
365
365
|
|
|
366
366
|
```bash
|
|
367
367
|
set -- <each changed_files path, single-quoted: 'src/a.ts' 'docs/b c.md'> # may be none; an unquoted path with a space splits and is skipped
|
|
@@ -526,7 +526,7 @@ the bound plus an escalation — never an unbounded loop.
|
|
|
526
526
|
|
|
527
527
|
### PR review/fix loop
|
|
528
528
|
|
|
529
|
-
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.
|
|
529
|
+
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, security-code when a security finding is attached, the content roles of the fix lines themselves, 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.
|
|
530
530
|
2. If findings (held ones aside: Phase 3 step 6) 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.
|
|
531
531
|
|
|
532
532
|
**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.
|
|
@@ -746,8 +746,10 @@ FOR wave_index, wave_tasks in enumerate(WAVES):
|
|
|
746
746
|
|
|
747
747
|
keryx job step <job-name> tests-creator --status in-progress # wave 1 only
|
|
748
748
|
# Step A — tests-creator (MANDATORY, run first)
|
|
749
|
-
|
|
750
|
-
|
|
749
|
+
keryx skills resolve-context --for test --files <task target_files, comma-joined> --task-type <task_type> --task-text '<task title and description, each ' written as '\''>' --out <JOBS_ROOT>/<job-name>/results/<task_id>.test.resolved.json
|
|
750
|
+
Dispatch one tests-creator per task in this wave, in a SINGLE turn (parallel), each carrying resolved_context_file.
|
|
751
|
+
Wait for ALL of them. Collect TEST_SPECS[task_id] from each response, then per result:
|
|
752
|
+
keryx skills contracts validate <task_id>-tests.json --schema tests-creator-output --resolved <that resolved file>
|
|
751
753
|
keryx job step <job-name> tests-creator --status completed # last wave only
|
|
752
754
|
|
|
753
755
|
# Parallel safety check, before Step B:
|
|
@@ -755,9 +757,10 @@ FOR wave_index, wave_tasks in enumerate(WAVES):
|
|
|
755
757
|
|
|
756
758
|
keryx job step <job-name> implement --status in-progress # wave 1 only
|
|
757
759
|
# Step B — task-implementer (after all test stubs are committed)
|
|
760
|
+
keryx skills resolve-context --for implement ... --out <JOBS_ROOT>/<job-name>/results/<task_id>.implement.resolved.json
|
|
758
761
|
Dispatch one task-implementer per task in this wave, in a SINGLE turn (parallel),
|
|
759
|
-
each carrying test_case_specs: TEST_SPECS[task_id].
|
|
760
|
-
Wait for ALL of them.
|
|
762
|
+
each carrying test_case_specs: TEST_SPECS[task_id] and resolved_context_file (an entry promoted by a declared condition, reason condition:glob:<g> or condition:keyword:<k>, needs its project_context_applied item to carry path, sha256 of the whole file, the declared anchor, a summary of 40+ characters in the worker's own words, and how read|followed, never n/a).
|
|
763
|
+
Wait for ALL of them; per result: keryx skills contracts validate <result.json> --schema task-implementer-output --resolved <that resolved file>
|
|
761
764
|
|
|
762
765
|
Read each result's STATUS line:
|
|
763
766
|
DONE / DONE_WITH_CONCERNS → accept it (record every concern)
|
|
@@ -789,6 +792,7 @@ Task({
|
|
|
789
792
|
- package_manager: <pm>
|
|
790
793
|
- run_command: <runner>
|
|
791
794
|
- context_path: <JOBS_ROOT>/<job-name>/ai/context.md
|
|
795
|
+
- resolved_context_file: <JOBS_ROOT>/<job-name>/results/<task_id>.test.resolved.json
|
|
792
796
|
|
|
793
797
|
## Required response
|
|
794
798
|
Begin with STATUS: <STATUS>. Return the test_case_specs for this task and
|
|
@@ -819,6 +823,7 @@ Task({
|
|
|
819
823
|
- issue_number: <N>
|
|
820
824
|
- job_name: <job-name>
|
|
821
825
|
- context_path: <JOBS_ROOT>/<job-name>/ai/context.md
|
|
826
|
+
- resolved_context_file: <JOBS_ROOT>/<job-name>/results/<task_id>.implement.resolved.json
|
|
822
827
|
|
|
823
828
|
## Automation (task-implementer input `automation`)
|
|
824
829
|
- auto_commit: <implementer_settings.auto_commit> # false: do not commit; report exact paths
|
|
@@ -1114,10 +1119,8 @@ keryx review tier --scope <scope> --diff-lines <n> --findings <n> [--security] [
|
|
|
1114
1119
|
```
|
|
1115
1120
|
|
|
1116
1121
|
Paste the `model` block it prints into that dispatch. `model_strategy: "current"` is
|
|
1117
|
-
gone
|
|
1118
|
-
|
|
1119
|
-
and, when it cannot rank anything, prints `inherit: true`, which means the dispatch
|
|
1120
|
-
runs on the session model. That is a correct answer, not a failure.
|
|
1122
|
+
gone. The command names no model: it ranks what the provider reports at runtime and,
|
|
1123
|
+
when it cannot, prints `inherit: true` — the dispatch runs on the session model, a correct answer.
|
|
1121
1124
|
|
|
1122
1125
|
**Step 6 — run `review-orchestrator` inline, in this main session.** Load
|
|
1123
1126
|
`skills/gdskills/review/review-orchestrator/SKILL.md` yourself; do **not** wrap it in a
|
|
@@ -1159,10 +1162,9 @@ keryx review ingest --report <path/to/review-report.md> --ref <feature-branch> \
|
|
|
1159
1162
|
--spent <usd-so-far> --outstanding <subagents in flight>
|
|
1160
1163
|
```
|
|
1161
1164
|
|
|
1162
|
-
An unrecognised option is **refused, not ignored
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
A first ingest without `--premises` holds nothing; a re-ingest carries earlier holds.
|
|
1165
|
+
An unrecognised option is **refused, not ignored**. `--refuted` carries findings this round
|
|
1166
|
+
raised and then dismissed; without it the package keeps only the survivors of an unlogged
|
|
1167
|
+
triage. A first ingest without `--premises` holds nothing; a re-ingest carries earlier holds.
|
|
1166
1168
|
|
|
1167
1169
|
**Findings are the canonical shape.** One vocabulary, everywhere in this skill:
|
|
1168
1170
|
|
|
@@ -1278,15 +1280,11 @@ cumulative ever-correct is **0.847**
|
|
|
1278
1280
|
([arXiv:2607.24604](https://arxiv.org/abs/2607.24604)). Other agent tools
|
|
1279
1281
|
hardcode the same bound (`max_reflections = 3`).
|
|
1280
1282
|
|
|
1281
|
-
The bound is a ceiling, not a target. Repetition ends the loop earlier
|
|
1282
|
-
|
|
1283
|
-
slowly" from "stuck", and an agent emitting the identical failing output three
|
|
1284
|
-
times spends the whole budget before anything notices.
|
|
1283
|
+
The bound is a ceiling, not a target. Repetition ends the loop earlier, **regardless of
|
|
1284
|
+
remaining iterations**: a counter cannot tell "converging slowly" from "stuck".
|
|
1285
1285
|
|
|
1286
|
-
**A finding leaves this loop by being dispositioned, never by being absent.**
|
|
1287
|
-
|
|
1288
|
-
still reported — so a finding the next reviewer simply did not look at was recorded as
|
|
1289
|
-
fixed. That is absence-as-evidence, and the completion gate refuses it.
|
|
1286
|
+
**A finding leaves this loop by being dispositioned, never by being absent.** One the next
|
|
1287
|
+
reviewer did not look at is not fixed: absence-as-evidence, which the completion gate refuses.
|
|
1290
1288
|
|
|
1291
1289
|
```
|
|
1292
1290
|
UNRESOLVED_FINDINGS = OPEN from step 2.6 # never a HELD finding (Classify, 2.6.1)
|
|
@@ -1306,9 +1304,10 @@ FOR iteration in [1, 2, 3]:
|
|
|
1306
1304
|
previously_unresolved: <findings that were in UNRESOLVED_FINDINGS last iteration but still present>
|
|
1307
1305
|
→ Prefix: "These specific findings were NOT fixed in iteration <N-1>: [list]"
|
|
1308
1306
|
|
|
1309
|
-
3.
|
|
1307
|
+
3. Re-run `keryx skills resolve-context --for implement` on the fix files (with `--task-text '<fix task description, each ' written as '\''>'` and a fresh `--out`), then launch task-implementer
|
|
1308
|
+
with the fix prompt and `resolved_context_file` (subagent_type: "general-purpose"),
|
|
1310
1309
|
on the model `keryx review tier --fix-attempt <N> --findings <n> --json` computes
|
|
1311
|
-
4. Run the sanity check (step 2.5.2 logic) — verify commits were made
|
|
1310
|
+
4. Run the sanity check (step 2.5.2 logic) — verify commits were made — and `contracts validate --resolved` on the result
|
|
1312
1311
|
5. Run the next managed round — the FULL 2.6.1 sequence, not a bare re-dispatch:
|
|
1313
1312
|
keryx review budget --spent <usd> --outstanding <n>
|
|
1314
1313
|
keryx review plan --target branch --ref <feature-branch> --head <new-head>
|
|
@@ -129,10 +129,18 @@ All subsequent operations ONLY in the worktree directory.
|
|
|
129
129
|
WAVES = dependency_order from ANALYSIS_RESULT (already topologically sorted)
|
|
130
130
|
|
|
131
131
|
FOR wave_index, wave_tasks in enumerate(WAVES):
|
|
132
|
-
1.
|
|
132
|
+
1. keryx skills resolve-context --for test --files <target_files> --task-type <type> --task-text '<task title and description, each ' written as '\''>' --out <JOBS_ROOT>/<job-name>/results/<task_id>.test.resolved.json
|
|
133
|
+
Dispatch one tests-creator per task, in a SINGLE turn (parallel), each carrying
|
|
134
|
+
resolved_context_file. Wait for all; per result run
|
|
135
|
+
keryx skills contracts validate <result.json> --schema tests-creator-output --resolved <that file>
|
|
133
136
|
Load skill: skills/gdskills/quality/tests-creator/SKILL.md
|
|
134
137
|
2. Parallel safety check: tasks sharing a target_file run sequentially.
|
|
135
|
-
3.
|
|
138
|
+
3. keryx skills resolve-context --for implement ... --out <...>/<task_id>.implement.resolved.json
|
|
139
|
+
A promoted entry (reason condition:glob:<g> or condition:keyword:<k>) needs path, sha256, the declared anchor, a summary
|
|
140
|
+
(40+ characters, own words) and how read|followed in the worker's project_context_applied; validate refuses it otherwise.
|
|
141
|
+
Dispatch one task-implementer per task, in a SINGLE turn (parallel), each carrying
|
|
142
|
+
resolved_context_file. Wait for all; per result run
|
|
143
|
+
keryx skills contracts validate <result.json> --schema task-implementer-output --resolved <that file>
|
|
136
144
|
Load skill: skills/gdskills/orchestration/task-implementer/SKILL.md
|
|
137
145
|
4. Read each STATUS line:
|
|
138
146
|
all DONE → next wave
|
|
@@ -177,8 +185,8 @@ ingest carrying a scope-B finding without it is refused. Severities are
|
|
|
177
185
|
|
|
178
186
|
If `blocker` or `major` findings exist:
|
|
179
187
|
1. Group by file
|
|
180
|
-
2.
|
|
181
|
-
3. Run the sanity check — verify commits were made
|
|
188
|
+
2. Re-run `keryx skills resolve-context --for implement` on the fix files (with `--task-text '<fix task description, each ' written as '\''>'`), then launch task-implementer (`task_type: "fix"`) with the findings and `resolved_context_file`
|
|
189
|
+
3. Run the sanity check — verify commits were made — and `contracts validate --resolved` on the result
|
|
182
190
|
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
191
|
5. Disposition every finding of the previous round:
|
|
184
192
|
```bash
|
|
@@ -173,35 +173,34 @@ Before you rely on a project-skill's guidance, confirm it still matches the code
|
|
|
173
173
|
- `keryx skills route <target_file>` — find the project-skill for this module/entity (if any).
|
|
174
174
|
- If one exists: `keryx skills verify <module>/<skill>` — classifies it `fresh | stale | needs-review | blocked`.
|
|
175
175
|
- If it is **not `fresh`**: do not follow it blindly. Verify each claim against the code you read in Phase 2, and note the drift in `notes` (Phase 6.1) so the orchestrator can trigger `skills learn`.
|
|
176
|
+
- If it is **`fresh`**: apply it. Its rules, templates and conventions for the target are part of the task, not background reading, and they supplement this skill's phases: a project skill never replaces a phase, the TDD order or the output contract. Where it and a bundled rule disagree, the project skill wins; say so in `notes`.
|
|
176
177
|
- If no skill exists for a non-trivial module you had to reverse-engineer, note that too — it's a candidate for `skills create`.
|
|
177
178
|
|
|
178
179
|
This step is read-only and inline; do not spawn a subagent for it.
|
|
179
180
|
|
|
181
|
+
**2.0c Resolve the project's standard (before 2.1):**
|
|
182
|
+
- Use `workspace.resolved_context_file` when given; otherwise run `keryx skills resolve-context --for implement --files <target_files, comma-joined> --task-type <task_type> --task-text '<task.description, each ' written as '\''>' --out <project-relative path>` (`--task-text` lets a declared keyword promote a document).
|
|
183
|
+
- Read every `must_read` entry in full, project entries first; read `on_demand` ones only when the work touches them. A project rule listed with `shadows` replaces the bundled one. An entry with `also_at` is one file under several paths: citing any of them, with the entry's sha256, counts.
|
|
184
|
+
- An entry with reason `condition:glob:<g>` or `condition:keyword:<k>` was promoted by a declared condition and carries an `anchor`, a `description` and `section_bytes`: read the anchored section in full. Its `project_context_applied` item needs the `path`, the `sha256` of the whole file as pinned, the `anchor` exactly as declared, a `summary` and `how` `read` or `followed`, never `n/a`. Write the `summary` in your own words, at least 40 characters, naming at least three distinct words of five or more letters taken from the anchored section; `contracts validate --resolved` rejects a copied hash without it. Cite what you used in `project_context_applied` (6.1) with the resolver's `sha256`; `n/a` needs a reason in `notes`. Never edit a resolved file: `keryx skills contracts validate <result.json> --schema task-implementer-output --resolved <resolved.json>` fails on drift.
|
|
185
|
+
|
|
180
186
|
**2.1 Read all target files:**
|
|
181
187
|
- Read each file from `target_files` in full
|
|
182
188
|
- If a file does not exist yet, note it as "new file to create"
|
|
183
189
|
- Read the `context` field for additional type/signature info
|
|
184
190
|
|
|
185
191
|
**2.2 Read existing tests and stories:**
|
|
186
|
-
- `existing_tests` and `existing_stories`
|
|
187
|
-
(`input-contract.schema.json`). Empty means none; the string `"none"` is not a
|
|
188
|
-
legal value and a request carrying it is refused by 1.4
|
|
192
|
+
- `existing_tests` and `existing_stories` default to `[]`; the string `"none"` is not a legal value and 1.4 refuses it
|
|
189
193
|
- Read each file listed in either array
|
|
190
194
|
- Understand existing test patterns (describe/it structure, mocks, fixtures) and, for every file in `existing_stories`, the story patterns it uses (`Meta`/`StoryObj` shape, how variants are declared, how callbacks are stubbed) — 4.4 builds on what you record here
|
|
191
195
|
|
|
192
196
|
**2.3 Read module neighbors:**
|
|
193
197
|
- List sibling files in the same directory as each target file
|
|
194
198
|
- Read 2-3 similar files to understand module patterns (naming, exports, structure)
|
|
195
|
-
- Pay attention to
|
|
196
|
-
- Import aliases used (e.g., `@components`, `@utils`)
|
|
197
|
-
- Export patterns (named vs default)
|
|
198
|
-
- TypeScript patterns (interfaces vs types, generics usage)
|
|
199
|
-
- Component patterns (observer wrapping, props interface naming)
|
|
200
|
-
- Store patterns (makeObservable(this), explicit decorators, private fields before public fields, thin public @action.bound UI actions, non-mutating public helpers, private API methods, runInAction inside private mutation blocks after await)
|
|
199
|
+
- Pay attention to import aliases, named vs default exports, interfaces vs types, observer wrapping and props naming, and store patterns (makeObservable(this), explicit decorators, private fields before public, thin public @action.bound UI actions, non-mutating public helpers, private API methods, runInAction after await)
|
|
201
200
|
|
|
202
201
|
**2.4 Load relevant rules (from `module_patterns` or by detection):**
|
|
203
202
|
|
|
204
|
-
|
|
203
|
+
Rules `resolve-context` listed are already read (2.0c); load the rest by what you're implementing.
|
|
205
204
|
|
|
206
205
|
**Always load (all task types):**
|
|
207
206
|
- `tdd-workflow.mdc` — red-green-refactor, STATUS: DONE requires passing tests
|
|
@@ -224,8 +223,7 @@ Based on what you're implementing, load and follow the relevant project rules.
|
|
|
224
223
|
- `async`/`await` or queue code → `async-patterns.mdc`
|
|
225
224
|
- New architectural layers or modules → `clean-architecture.mdc`
|
|
226
225
|
|
|
227
|
-
|
|
228
|
-
one tree `keryx init` installs and the one every build of this skill reads.
|
|
226
|
+
Resolve every rule above project first, bundled second: read `.metaproject/rules/project/<rule>.mdc` when it exists, otherwise `.metaproject/rules/core/<rule>.mdc`. A project rule with the same name replaces the bundled one (never read both as if they agreed). `.metaproject/rules/core/` is the tree `keryx init` installs and `keryx update` overwrites, so a project's own rules live only in `rules/project/`.
|
|
229
227
|
|
|
230
228
|
### Phase 3: PLAN
|
|
231
229
|
|
|
@@ -427,6 +425,7 @@ Write full JSON to `<JOBS_ROOT>/<JOB_NAME>/results/<task_id>.json`:
|
|
|
427
425
|
"story_result": "<pass|build error: details|not applicable>",
|
|
428
426
|
"acceptance_criteria_met": "<all|partial: list of unmet criteria|none>",
|
|
429
427
|
"skill_drift": "<none | stale: <module>/<skill> — <what diverged> | missing: <module> should have a project-skill>",
|
|
428
|
+
"project_context_applied": [{ "path": "<as the resolver printed it>", "sha256": "<resolver's sha256>", "how": "read|followed|n/a", "anchor": "<declared heading; promoted entries only>", "summary": "<own words, 40+ chars; promoted entries only>" }],
|
|
430
429
|
"noticed_not_touched": [{ "what": "<problem>", "where": "<path|symbol>" }],
|
|
431
430
|
"assumptions": ["<what you assumed where the task did not say>"],
|
|
432
431
|
"behavior_changes": [{ "description": "<what now behaves differently>", "user_visible": true, "needs_operator": true, "alternatives": ["<option you did not take>"] }],
|
package/src/gdskills/bundled/skills/orchestration/task-implementer/input-contract.schema.json
CHANGED
|
@@ -134,6 +134,11 @@
|
|
|
134
134
|
"context_path": {
|
|
135
135
|
"type": "string",
|
|
136
136
|
"description": "Job context document read at the start of Phase 2, e.g. .metaproject/jobs/<job_name>/ai/context.md"
|
|
137
|
+
},
|
|
138
|
+
"resolved_context_file": {
|
|
139
|
+
"type": "string",
|
|
140
|
+
"description": "Output of `keryx skills resolve-context --for implement --out <path>` for this task, made by the dispatcher. Present => Phase 2 reads its `must_read` entries instead of resolving again, and the result must carry `project_context_applied`. Absent => Phase 2 runs the resolver itself.",
|
|
141
|
+
"examples": [".metaproject/data/flows/461/task-1.resolved.json"]
|
|
137
142
|
}
|
|
138
143
|
}
|
|
139
144
|
},
|
package/src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json
CHANGED
|
@@ -97,6 +97,22 @@
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
},
|
|
100
|
+
"project_context_applied": {
|
|
101
|
+
"type": "array",
|
|
102
|
+
"description": "Which of the files `keryx skills resolve-context` listed this run actually used, so a reader can subtract it from the resolver's `must_read` list. `how`: `read` - the file was opened and its text used; `followed` - its rules were applied through a skill or another file that already contains them; `n/a` - the file does not touch the task, with the reason in `notes`. `sha256` is the resolver's value for the file, not a fresh hash: `keryx skills contracts validate --resolved` compares the two.",
|
|
103
|
+
"items": {
|
|
104
|
+
"type": "object",
|
|
105
|
+
"additionalProperties": false,
|
|
106
|
+
"required": ["path", "sha256", "how"],
|
|
107
|
+
"properties": {
|
|
108
|
+
"path": { "type": "string", "minLength": 1, "description": "Repo-relative path exactly as the resolver printed it" },
|
|
109
|
+
"sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$", "description": "The resolver's sha256 of the file" },
|
|
110
|
+
"how": { "type": "string", "enum": ["read", "followed", "n/a"] },
|
|
111
|
+
"anchor": { "type": "string", "description": "Optional. The heading of the section read, exactly as the resolver printed it for an entry promoted by a declared condition (flow 462)" },
|
|
112
|
+
"summary": { "type": "string", "description": "Optional. What the section says, in the worker's own words; for a promoted entry `keryx skills contracts validate --resolved` checks it against the anchored section (flow 462)" }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
100
116
|
"notes": {
|
|
101
117
|
"type": "string",
|
|
102
118
|
"description": "Warnings, blockers, or additional context. The three fields above were carved out of this one: free text is where they went before they were declared, and anything that fits one of them belongs there instead."
|
|
@@ -90,9 +90,10 @@ not a hardcoded `cat`/`ls`/`find` invocation).
|
|
|
90
90
|
- Fixture/factory patterns (how test data is created)
|
|
91
91
|
- Before/after hook usage
|
|
92
92
|
|
|
93
|
+
**1.2b Load test-convention rules, project first.** Read the testing rules the task touches (`testing-conventions`, `tdd-workflow`, and any rule the task names) in this order: `.metaproject/rules/project/<rule>.mdc` first, then `.metaproject/rules/core/<rule>.mdc`. A project rule with the same name replaces the bundled one; a project rule with a different name adds to it. Take the list from `workspace.resolved_context_file` when the task carries one, otherwise run `keryx skills resolve-context --for test --files <target_files, comma-joined> --task-text '<task description, each ' written as '\''>' --out <project-relative path>` (`--task-text` lets a declared keyword promote a document); read its `must_read` entries in full and record each in `project_context_applied` with the resolver's `sha256`. An entry with reason `condition:glob:<g>` or `condition:keyword:<k>` was promoted by a declared condition: read its anchored section, and its item needs the `path`, the `sha256` of the whole file as pinned, the `anchor` exactly as declared, a `summary` (at least 40 characters, in your own words, naming at least three distinct words of five or more letters from that section) and `how` `read` or `followed`, never `n/a`. Where a project test convention and a bundled one conflict, the project wins. A project skill only supplements these steps; it never replaces this skill.
|
|
94
|
+
|
|
93
95
|
**1.3 Identify test file naming:**
|
|
94
|
-
- Pattern
|
|
95
|
-
- Location: co-located with source or in `__tests__`/`tests/` directory
|
|
96
|
+
- Pattern (`<component>.test.ts`, `<service>.spec.ts`, `test_<module>.py`) and location (co-located, or `__tests__`/`tests/`)
|
|
96
97
|
|
|
97
98
|
**Output of Phase 1:**
|
|
98
99
|
```
|
|
@@ -200,7 +201,6 @@ The generated tests MUST:
|
|
|
200
201
|
```typescript
|
|
201
202
|
// Use todo() for unimplemented tests
|
|
202
203
|
it.todo('<test description>');
|
|
203
|
-
|
|
204
204
|
// OR use a stub assertion that fails
|
|
205
205
|
it('<test description>', () => {
|
|
206
206
|
expect(true).toBe(false); // RED: remove this when implementing
|
|
@@ -265,6 +265,8 @@ TEST_CASE_SPECS:
|
|
|
265
265
|
type: happy_path | edge_case | error_path
|
|
266
266
|
status: written # test file exists, test is RED
|
|
267
267
|
|
|
268
|
+
project_context_applied: [{path: <rule path, project first>, sha256: <resolver's sha256>, how: read|followed|n/a, anchor: <declared heading, promoted entries only>, summary: <own words, 40+ chars, promoted entries only>}]
|
|
269
|
+
|
|
268
270
|
run_command: "<command to run just these tests>"
|
|
269
271
|
expected_result: "all failing (RED phase)"
|
|
270
272
|
notes: "<any test design decisions or assumptions>"
|
|
@@ -285,12 +287,10 @@ all_criteria_covered: true | false (with explanation)
|
|
|
285
287
|
|
|
286
288
|
When `task-implementer` receives a task that includes `test_case_specs`:
|
|
287
289
|
|
|
288
|
-
1. Read the test files (already committed as RED)
|
|
289
|
-
2.
|
|
290
|
-
3.
|
|
291
|
-
4.
|
|
292
|
-
5. Re-run tests — confirm GREEN
|
|
293
|
-
6. Report SUCCESS
|
|
290
|
+
1. Read the test files (already committed as RED) and run them — confirm they FAIL
|
|
291
|
+
2. Implement code until all tests are GREEN
|
|
292
|
+
3. Commit the implementation, re-run tests — confirm GREEN
|
|
293
|
+
4. Report SUCCESS
|
|
294
294
|
|
|
295
295
|
This ensures the TDD cycle is maintained end-to-end.
|
|
296
296
|
|
|
@@ -75,6 +75,10 @@
|
|
|
75
75
|
"type": "integer",
|
|
76
76
|
"minimum": 1,
|
|
77
77
|
"description": "GitHub issue number for commit references"
|
|
78
|
+
},
|
|
79
|
+
"resolved_context_file": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"description": "Output of `keryx skills resolve-context --for test --out <path>` for this task, made by the dispatcher. Present => Phase 1 reads its `must_read` entries instead of resolving again, and the result must carry `project_context_applied`. Absent => Phase 1 runs the resolver itself."
|
|
78
82
|
}
|
|
79
83
|
}
|
|
80
84
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "tests-creator-output-contract",
|
|
4
4
|
"title": "Tests Creator Output Contract",
|
|
5
|
-
"description": "Output from tests-creator: test case specifications for task-implementer plus status report.",
|
|
5
|
+
"description": "Output from tests-creator: test case specifications for task-implementer plus status report. Registered as contract `tests-creator-output`: validate a result with `keryx skills contracts validate <result.json> --schema tests-creator-output`.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": ["test_case_specs", "status_report"],
|
|
8
8
|
"properties": {
|
|
@@ -85,6 +85,22 @@
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
},
|
|
88
|
+
"project_context_applied": {
|
|
89
|
+
"type": "array",
|
|
90
|
+
"description": "Which of the files `keryx skills resolve-context` listed this run actually used, so a reader can subtract it from the resolver's `must_read` list. `how`: `read` - the file was opened and its text used; `followed` - its rules were applied through a skill or another file that already contains them; `n/a` - the file does not touch the task, with the reason in `notes`. `sha256` is the resolver's value for the file, not a fresh hash: `keryx skills contracts validate --resolved` compares the two.",
|
|
91
|
+
"items": {
|
|
92
|
+
"type": "object",
|
|
93
|
+
"additionalProperties": false,
|
|
94
|
+
"required": ["path", "sha256", "how"],
|
|
95
|
+
"properties": {
|
|
96
|
+
"path": { "type": "string", "minLength": 1, "description": "Repo-relative path exactly as the resolver printed it" },
|
|
97
|
+
"sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$", "description": "The resolver's sha256 of the file" },
|
|
98
|
+
"how": { "type": "string", "enum": ["read", "followed", "n/a"] },
|
|
99
|
+
"anchor": { "type": "string", "description": "Optional. The heading of the section read, exactly as the resolver printed it for an entry promoted by a declared condition (flow 462)" },
|
|
100
|
+
"summary": { "type": "string", "description": "Optional. What the section says, in the worker's own words; for a promoted entry `keryx skills contracts validate --resolved` checks it against the anchored section (flow 462)" }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
88
104
|
"status_report": {
|
|
89
105
|
"type": "object",
|
|
90
106
|
"required": ["status", "tests_written", "all_criteria_covered"],
|
|
@@ -651,7 +651,7 @@ certifies the flow is the one that checked the least.
|
|
|
651
651
|
|
|
652
652
|
## Readiness — the checklist behind `completed` and behind publishing
|
|
653
653
|
|
|
654
|
-
`keryx review readiness <review-id-or-path> [--json]` reads the package and prints
|
|
654
|
+
`keryx review readiness <review-id-or-path> [--json]` reads the package and prints every readiness item, READY or NOT READY, each with the evidence and the command that repairs it. It writes nothing, and exits non-zero when the verdict is NOT READY. Only item 9 reaches GitHub (the PR's own file list); the TUI and `review status` read the package alone.
|
|
655
655
|
|
|
656
656
|
1. `files-covered` — every changed file is in a wave or carries a recorded waiver.
|
|
657
657
|
2. `blast-radius` — no file the blast radius cut is left unreviewed. A cut radius writes `chunks.json`; run `keryx review chunks plan`, `estimate`, `next`/`record`, `merge`. In an unattended run every wave runs without a question, up to the 25-pass ceiling; in an interactive run the operator is asked once: in keryx shell call `review_chunks_answer` (a card, never a terminal); elsewhere the operator runs `keryx review chunks estimate --package <dir> --answer yes --quote "<their words>"` in their terminal; unattended, record Not run.
|
|
@@ -664,6 +664,7 @@ certifies the flow is the one that checked the least.
|
|
|
664
664
|
9. `pr-files` — the changed-file count equals the PR's own file list. A recount more than 3x the recorded scope is refused: it came from the wrong base. Recount with `keryx review blast-radius --package <ref> --changed <the PR's files>`.
|
|
665
665
|
10. `memory-outside-stage` — the orchestrator read memory only in its memory stage ("Clean context" above). Outside keryx shell it reads "not observable on this host" unless this session's keryx calls carry the caller token `keryx review start` printed.
|
|
666
666
|
11. `premise-decisions` — every finding `review-premise` held (`challenged` or `owner-decision`) has an operator decision: in keryx shell the card `review_decide_premise`; elsewhere the operator runs `keryx review decide-premise --package <dir> --finding <global_id> --answer uphold|dismiss --reason "<why>"` in their terminal (SKILL.md "Wave D"). Not waivable: each held finding needs its own answer, and `review_waive_readiness` / `--waive` refuse this item.
|
|
667
|
+
12. `quotes-located` — every open finding whose quote `review ingest` could not place (absent from the file at the reviewed head, or in it more than once) has a disposition. Ingest keeps such a finding with no line, flagged `quote_unlocated` and listed in scope.md, instead of refusing the round. Re-quote one distinct line and re-ingest, record a disposition, or the operator waives the item.
|
|
667
668
|
|
|
668
669
|
`keryx review complete` refuses status `completed` while any item fails. A failing item other than `premise-decisions` is waived only by the operator. In keryx shell call `review_waive_readiness` for the item: the operator answers it as a card, the waiver is recorded with the card text and the time, and then you run `keryx review complete`. Never send the operator to a terminal. Outside keryx shell the operator runs `keryx review complete --waive <item> "<their own words>"` in a terminal, repeatable. Unattended, record the item as Not run. `keryx review publish` is held by the same checklist. Publishing is the operator's decision too: in keryx shell call `review_publish` (a card shows the target and the body; never send the operator to a terminal). A not-ready package goes to GitHub only as a partial review (`review_publish` with `partial: true`, or `keryx review publish --partial`), which puts a "Partial review" header on the post and never approves. The shell guard stops a publish command that runs through the keryx shell; a publisher outside it (CI, another agent, a script file) is not stopped, and that limit is named, not hidden.
|
|
669
670
|
|
|
@@ -695,7 +696,7 @@ on lines no test could reach.
|
|
|
695
696
|
| `mode` | `range` | Review |
|
|
696
697
|
|---|---|---|
|
|
697
698
|
| `first` | merge-base..head | A first round. `sourcesTried` records what was searched when nothing was found. |
|
|
698
|
-
| `delta` | previous head..head | `fix` slices (a hunk that answers a prior finding): `review-logic`, `review-regression`, the authors of the attached findings and the verifier, each with its `prior_findings`. `new` slices: round 1, with the roles the slice names. Unchanged code: not re-reviewed. `carried` findings keep their dispositions. |
|
|
699
|
+
| `delta` | previous head..head | `fix` slices (a hunk that answers a prior finding): `review-logic`, `review-regression`, the authors of the attached findings, `review-security-code` when one of them is a security finding, the roles the fix lines themselves route by content (e.g. `review-security-code` for a fix that spawns a process), and the verifier, each with its `prior_findings`. `new` slices: round 1, with the roles the slice names. Unchanged code: not re-reviewed. `carried` findings keep their dispositions. |
|
|
699
700
|
| `fallback-first` | merge-base..head | The previous head is not an ancestor (force-push, rebase). Review the whole range with the prior findings attached. |
|
|
700
701
|
|
|
701
702
|
A fix hunk still has a radius: enumerate what names the thing it changed (`review_context.scope.files`), as in any fix round. The attached findings are the contract: each leaves the round with a disposition, below.
|