@mrciphersmith/keryx 0.3.122 → 0.3.124
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 +7278 -3939
- package/dist/core.js +1215 -746
- 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 +11 -11
- 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 -3
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +11 -11
- package/src/gdskills/bundled/skills/review/review-premise/SKILL.md +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.124",
|
|
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
|
|
@@ -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"],
|
|
@@ -383,7 +383,7 @@ A review must be reproducible from its package: the same inputs give the same co
|
|
|
383
383
|
|
|
384
384
|
**One memory read, and the log.** The stage query is `keryx memory search "<changed modules and the concepts they touch>" --status accepted`, once. Pass the same text to `keryx review start … --memory-query "<query>"` so the manifest's `cleanContext` block holds it next to the start time. keryx writes the orchestrator log, `<package>/tool-calls/orchestrator.jsonl`, on its own: `keryx review start` and `keryx review attach` mark the package active, and every `keryx memory` command and every MCP memory tool (`memory_search`, `memory_handoff`, `memory_propose`) appends one line naming its caller. You do not write that file and must not edit it; a hand-written line is a forgery the readiness item cannot tell apart from a record. A call by a `spawn_subagent` child is recorded under the child's reviewer id and is not judged by item 10. A call from a host that names no caller is recorded as `unattributed`: it warns in the item's detail line and never blocks. Reviewer logs stay in their own files.
|
|
385
385
|
|
|
386
|
-
Item 10, `memory-outside-stage`, reads that log. Outside the stage is: a memory search with a different query, a search without `--status accepted`, any other memory command or MCP memory tool, and a stage call stamped before the round started. The same query repeated is the stage. Repair is to rerun from a session whose only memory read is the stage; only the operator's waiver lifts it: in keryx shell call `review_waive_readiness` (the operator answers a card, never a terminal), then
|
|
386
|
+
Item 10, `memory-outside-stage`, reads that log. Outside the stage is: a memory search with a different query, a search without `--status accepted`, any other memory command or MCP memory tool, and a stage call stamped before the round started. The same query repeated is the stage. Repair is to rerun from a session whose only memory read is the stage; only the operator's waiver lifts it: in keryx shell call `review_waive_readiness` (the operator answers a card, never a terminal), then call `review_complete`; outside keryx shell `keryx review complete <package> --waive memory-outside-stage "<operator quote>"` in a terminal; unattended, record Not run. With no orchestrator log the item passes and its evidence says it could not see your reads; with a log that holds no memory call it passes and says so. Outside keryx shell, `keryx review start` prints a caller token (`export KERYX_REVIEW_CALLER_TOKEN=…`): export it on this session's keryx calls so they are attributed to you; without it the item reads "not observable on this host" rather than a clean pass. Only calls that go through keryx are visible: a read through a tool keryx does not wrap is not in the log.
|
|
387
387
|
|
|
388
388
|
**Open questions.** One general rule, replacing per-fork wording: the answer is in this skill or `SKILL.detail.md`, or it is the user's. Interactive: one `AskUserQuestion`. Unattended: the whole output is one line, `STATUS: BLOCKED question — <the question>`, and you stop. A guess and a recollection are both wrong answers.
|
|
389
389
|
|
|
@@ -666,7 +666,7 @@ certifies the flow is the one that checked the least.
|
|
|
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
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.
|
|
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
|
|
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 call the `review_complete` card (the operator answers it in the shell or in the Telegram topic). An agent completes a review only through that card; `keryx review complete` is for a human in a terminal. 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.
|
|
670
670
|
|
|
671
671
|
## Fix rounds — dispositions, regressions, withdrawal
|
|
672
672
|
|
|
@@ -863,7 +863,7 @@ minute 119, and the PR merged first; the other re-dispatched a finished wave and
|
|
|
863
863
|
|
|
864
864
|
As soon as every reviewer result and the verifier's are on disk, save each verifier pass to `research-evidence/verifications.json` first (without it a refuted finding still counts), then run `keryx review verdict <id|path> [--json]`: it reads `<package>/raw`, where every host keeps the round's results (`--raw <dir>` only for results kept elsewhere; the shared `.metaproject/data/review/raw` mixes in other rounds' replies). It prints the interim verdict and writes `verdict-interim.md` into the package. Give the operator that verdict in the same message; do not hold it for the mutation pass, the ledger or `complete`.
|
|
865
865
|
|
|
866
|
-
The tail keeps running after it: the mutation pass (`keryx review mutate`), `ledger build`, `ingest`, `readiness` and `keryx review complete
|
|
866
|
+
The tail keeps running after it: the mutation pass (`keryx review mutate`), `ledger build`, `ingest`, `readiness` and the `review_complete` card (`keryx review complete` for a human at a terminal). Every managed round finishes them. The round is **not done** until `complete`: the verdict you gave is interim, and the report says so and names what the tail may still change (a surviving mutant, a verifier refutation, a late reviewer result). A light round has no mutation pass: its tail is `ledger build`, `ingest`, `readiness`, `complete`.
|
|
867
867
|
|
|
868
868
|
## Early verifier — start on the first blocker or major
|
|
869
869
|
|