@ngockhoale/ukit 3.4.49 → 3.4.51
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/CHANGELOG.md +18 -0
- package/manifests/documentation.yaml +22 -0
- package/package.json +1 -1
- package/src/core/executionContracts.js +36 -4
- package/src/core/reviewPanelAggregate.js +71 -3
- package/template_project/.claude/agents/code-reviewer.md +41 -6
- package/template_project/.claude/commands/ukit/handoff-fullstack.md +43 -11
- package/template_project/.claude/commands/ukit/handoff-review.md +34 -8
- package/template_project/.claude/hooks/handoff-resume.sh +17 -3
- package/template_project/.claude/hooks/skill-router.sh +89 -11
- package/template_project/.claude/ukit/index/review-panel-aggregate.mjs +71 -3
- package/template_project/.claude/ukit/index/route-resolver.mjs +36 -4
- package/template_project/.claude/ukit/index/route-task.mjs +24 -1
- package/template_project/.claude/ukit/runtime/execution-ledger.mjs +187 -45
- package/template_project/.claude/ukit/runtime/omp-task-dispatch-guard.mjs +262 -0
- package/template_project/.claude/ukit/runtime/resumable-run.mjs +8 -0
- package/template_project/.omp/agents/code-reviewer.md +41 -6
- package/template_project/.omp/hooks/pre/ukit-bridge.js +38 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to UKit are documented here.
|
|
4
4
|
|
|
5
|
+
## 3.4.51 - 2026-10-21
|
|
6
|
+
|
|
7
|
+
**Handoff orchestration integrity (CKit/OMP 18.12.5 incident audit).**
|
|
8
|
+
- OMP `Task` preflight (block-only): an ordinary agent carrying `sourceSession`/`model` (any value, even empty) or batch-root `sourceSession`/`model` is blocked with exact remove-field guidance; args are never rewritten, seance stays native-owned. Retries are counted per session + task instance + fingerprint (prose/name/status excluded), hashed, bounded and persisted; STOP guidance after two identical blocks.
|
|
9
|
+
- Stop-gate continuation provenance: only positively native/system-origin continuations (OMP branch `session-stop-continuation` match) reuse the task contract; a user paste of the gate text mints a fresh task. Pending-continuation marker is session/task/reason-hash scoped and lock-serialized.
|
|
10
|
+
- Resumable records now require exact stored task-id equality (lossy filename normalization could collide).
|
|
11
|
+
- Classifier: diagnostic "which config changed" reports are READ_ONLY; forward configuration orders and mixed source mutation + report stay CONFIGURATION / CODE_IMPLEMENTATION.
|
|
12
|
+
- Review-panel aggregation: FAIL/NOT_RUN dominate, underscore verdict aliases normalized, executable N/A becomes NOT_RUN; explicit product / integration-evidence / status-only review kinds. Handoff commands carry retry-fingerprint and pending-review continuity rules.
|
|
13
|
+
- Audit: `docs/analysis/HANDOFF-INTENT-DISPATCH-AUDIT.md`. Historical CKit wrapper/contract origin remains unknown; verified offline on macOS/Node 22 only; no live host proof, no CI dispatch.
|
|
14
|
+
|
|
15
|
+
## 3.4.50 - 2026-10-21
|
|
16
|
+
|
|
17
|
+
**Verification exit-proof integrity (long-run OMP field audit).**
|
|
18
|
+
- Fix execution-ledger false PASS receipts: a bare `pipefail`/`PIPESTATUS` word (e.g. `echo pipefail`) no longer exempts a masked pipe; only real `set -o pipefail` before the pipe or an immediate `exit ${PIPESTATUS[n]}` with the correct index proves the runner status.
|
|
19
|
+
- A runner followed by `|| …`, backgrounded with `&`, inside a `for`/`while`/`case` flow, behind `bash -c` hiding a masked pipe, or followed by `exit 0`/`true`/`:`/leading `!` is recorded as `exitStatusUnproven` (not passing). Claude `tool_response.interrupted:true` now counts as failure.
|
|
20
|
+
- Bare runners, `cd repo; runner`, `&&` chains, real pipefail and terminal subshell/group runners stay accepted. Sequential consumers (including `; echo "exit=$?"`) and `if runner; then echo ok; fi` no longer receive PASS credit: recognizing a runner attempt does not prove its exit. Loop keywords as runner arguments (`vitest -t case`) are not loops.
|
|
21
|
+
- Audit report: `UKIT_LONG_RUN_ORCHESTRATION_AUDIT.md`. Verified locally on macOS/Node 22 only; OMP live host-ordering attribution remains UNVERIFIED. No CI dispatch.
|
|
22
|
+
|
|
5
23
|
## 3.4.49 - 2026-10-19
|
|
6
24
|
|
|
7
25
|
**Conditional consumer verification intent closure.**
|
|
@@ -71,6 +71,17 @@ entries:
|
|
|
71
71
|
validation: [manual]
|
|
72
72
|
archive_policy: never
|
|
73
73
|
notes: release record
|
|
74
|
+
- id: root-long-run-orchestration-audit
|
|
75
|
+
path: UKIT_LONG_RUN_ORCHESTRATION_AUDIT.md
|
|
76
|
+
class: canonical
|
|
77
|
+
audience: [owner, maintainer]
|
|
78
|
+
owner: runtime
|
|
79
|
+
source_of_truth: UKIT_LONG_RUN_ORCHESTRATION_AUDIT.md
|
|
80
|
+
merge_strategy: none
|
|
81
|
+
load_policy: on-demand
|
|
82
|
+
validation: [manual, test:tests/hooks/executionLedger.test.js, test:tests/hooks/verificationExitProof.test.js]
|
|
83
|
+
archive_policy: replace-with-snapshot
|
|
84
|
+
notes: evidence-based long-run field audit; current reproductions are distinct from incident attribution
|
|
74
85
|
- id: registry-self
|
|
75
86
|
path: manifests/documentation.yaml
|
|
76
87
|
class: canonical
|
|
@@ -517,6 +528,17 @@ entries:
|
|
|
517
528
|
validation: [none]
|
|
518
529
|
archive_policy: immutable
|
|
519
530
|
notes: 2026-10-10 orchestration regression test inventory
|
|
531
|
+
- id: docs-analysis-handoff-intent-dispatch-audit
|
|
532
|
+
path: docs/analysis/HANDOFF-INTENT-DISPATCH-AUDIT.md
|
|
533
|
+
class: archive
|
|
534
|
+
audience: [maintainer]
|
|
535
|
+
owner: product
|
|
536
|
+
source_of_truth: docs/analysis/HANDOFF-INTENT-DISPATCH-AUDIT.md
|
|
537
|
+
merge_strategy: none
|
|
538
|
+
load_policy: never
|
|
539
|
+
validation: [none]
|
|
540
|
+
archive_policy: immutable
|
|
541
|
+
notes: 2026-10-21 retained CKit intent/dispatch evidence, repair verification and recovery limits
|
|
520
542
|
- id: docs-analysis-completion-task-ownership
|
|
521
543
|
path: docs/analysis/COMPLETION-TASK-OWNERSHIP.md
|
|
522
544
|
class: archive
|
package/package.json
CHANGED
|
@@ -305,14 +305,46 @@ export function classifyTaskSemantics({ promptText = '', targetFile = '' } = {})
|
|
|
305
305
|
if (!releasePolicy && !operation && isVerificationOnlyRequest(promptText)) return { taskKind: 'READ_ONLY', verificationOnly: true };
|
|
306
306
|
// A config MENTION is not a config change: the same clause needs a mutation verb, and a
|
|
307
307
|
// request that also changes source/code is a coding task (config is part of its tests/fixtures).
|
|
308
|
-
|
|
309
|
-
|
|
308
|
+
// A DIAGNOSTIC report of past changes ("which parameters/configuration has changed, if any" /
|
|
309
|
+
// "tham so/cau hinh nao da thay doi, neu co") is a question about history, never an authorization
|
|
310
|
+
// to mutate: only a forward order ("change X to Y") stays a config mutation.
|
|
311
|
+
const CONFIG_CHANGE_REPORT_CLAUSE = /\b(?:da|has|have|had|already)\s+(?:doi|thay doi|changed?|updated?)\b|\b(?:nao|gi|which|what|report|bao cao)\b[^.;]*?\b(?:doi|thay doi|changed?|updated?)\b/;
|
|
312
|
+
// A relative past-change clause inside a FORWARD imperative ("Set the config that
|
|
313
|
+
// has changed to dark mode") names WHICH config — it does not make the sentence a
|
|
314
|
+
// report. The override holds only when the clause's FIRST mutation word precedes
|
|
315
|
+
// the config noun AND no reporting/question frame opens the clause or precedes
|
|
316
|
+
// that word: in "Report what change was made to the config" the mutation word is
|
|
317
|
+
// a question's noun, never an order.
|
|
318
|
+
const CONFIG_MUTATION_VERB = /\b(?:set|change|toggle|enable|disable|switch|turn|configure|modify|update|edit|doi|bat|tat|chinh)\b/;
|
|
319
|
+
const CONFIG_NOUN = /\b(?:configuration|config|toggle|cau hinh)\b/;
|
|
320
|
+
const CONFIG_REPORT_FRAME = /^\s*(?:report|show|tell|list|describe|explain|summari[sz]e|bao cao|cho biet|liet ke|mo ta)\b/;
|
|
321
|
+
const CONFIG_QUESTION_FRAME_PREFIX = /\b(?:what|which|how|report|bao cao|nao|gi)\b/;
|
|
322
|
+
const config = !sourceOrder && actionable.some((clause) => {
|
|
323
|
+
if (!CONFIG_NOUN.test(clause) || !CONFIG_MUTATION_VERB.test(clause)) return false;
|
|
324
|
+
if (CONFIG_REPORT_FRAME.test(clause)) return false;
|
|
325
|
+
const verb = CONFIG_MUTATION_VERB.exec(clause);
|
|
326
|
+
if (CONFIG_QUESTION_FRAME_PREFIX.test(clause.slice(0, verb.index))) return false;
|
|
327
|
+
const noun = CONFIG_NOUN.exec(clause);
|
|
328
|
+
return !CONFIG_CHANGE_REPORT_CLAUSE.test(clause) || Boolean(noun && noun.index > verb.index);
|
|
329
|
+
});
|
|
310
330
|
const repository = Boolean(targetFile) || /[\w.-]+\.(?:json|ya?ml|toml|ini)\b/.test(text);
|
|
311
331
|
// A status/result lookup ("check the suite output and report") orders no change: it needs a
|
|
312
332
|
// lookup verb AND no mutation vocabulary anywhere in the request (fail-closed otherwise).
|
|
313
333
|
const mutationWord = /\b(?:implement|fix|refactor|modify|edit|write|update|add|create|build|make|install|deploy|remove|delete|rename|replace|change|sua|viet|chinh|them|tao|xoa|doi|cai|cap nhat)\b/.test(text);
|
|
314
|
-
|
|
315
|
-
|
|
334
|
+
// A bounded, recognizable reporting QUESTION about what changed ("Tham số/cấu hình
|
|
335
|
+
// nào đã thay đổi, nếu có." / "which configuration has changed, if any") is a
|
|
336
|
+
// status report even without a lookup verb — but ONLY when no actionable clause
|
|
337
|
+
// actually orders a mutation: a mixed request ("Add a dark-mode button; report
|
|
338
|
+
// which configuration changed") keeps its implementation contract and evidence.
|
|
339
|
+
// The two lookbehinds keep a question's noun ("what change was made") and the
|
|
340
|
+
// Vietnamese past-tense verb "thay đổi" from counting as mutation orders.
|
|
341
|
+
const ACTIONABLE_MUTATION_ORDER = /(?<!\b(?:what|which|how|nao|gi)\s)(?<!thay )\b(?:implement|fix(?:es)?|refactor|modify|edit|write|update|add|create|build|make|install|deploy|remove|delete|rename|replace|change|sua|viet|chinh|them|tao|xoa|doi|cai|cap nhat)\b/;
|
|
342
|
+
const configReportQuestion = !actionable.some((clause) => ACTIONABLE_MUTATION_ORDER.test(clause))
|
|
343
|
+
&& actionable.some((clause) => /\b(?:configuration|config|toggle|cau hinh|tham so)\b/.test(clause)
|
|
344
|
+
&& CONFIG_CHANGE_REPORT_CLAUSE.test(clause));
|
|
345
|
+
const statusReport = (configReportQuestion || (!mutationWord
|
|
346
|
+
&& /\b(?:check|status|report|show|summari[sz]e|result|results|kiem tra|bao cao|trang thai|ket qua|da len chua)\b/.test(text)))
|
|
347
|
+
&& !operation && !config;
|
|
316
348
|
const readOnly = (/\b(?:audit|inspect|review|analyze|analyse|explain|read|investigate|debug|forensic|root cause|kiem tra|phan tich|dieu tra|doc log|tim hieu)\b/.test(text.replace(/\bread[\s-]*only\b/g, ' ')) || statusReport)
|
|
317
349
|
&& !sourceOrder && !codeChangeVerb && !operation && !config;
|
|
318
350
|
const taskKind = operation ? (sourceOrder || (nativeDomain !== 'scheduled-job' && codeChangeVerb) ? 'MIXED' : 'OPERATIONAL')
|
|
@@ -37,12 +37,73 @@ const SEVERITIES = ['critical', 'important', 'minor'];
|
|
|
37
37
|
// counted as absent and named in `unparsed`.
|
|
38
38
|
export function parseVerdictBlock(text) {
|
|
39
39
|
const src = String(text ?? '');
|
|
40
|
-
|
|
40
|
+
// `-` and `_` are one vocabulary: the handoff commands and some reviewers
|
|
41
|
+
// write `changes_requested` / `critical_block` while others write the hyphen
|
|
42
|
+
// forms — both spellings parse to the same canonical verdict so a blocking
|
|
43
|
+
// reviewer can never be dropped as unparsed while the panel approves.
|
|
44
|
+
// `[ \t]` (not `\s`) around the colon: `\s` would swallow the newline and
|
|
45
|
+
// let a stray word on the next line fabricate a verdict.
|
|
46
|
+
const verdictMatch = src.match(
|
|
47
|
+
/^VERDICT[ \t]*:[ \t]*(APPROVED[-_]WITH[-_]MINOR|APPROVED|CHANGES[-_]REQUESTED|CRITICAL[-_]BLOCK|CRITICAL)[ \t]*\r?$/im,
|
|
48
|
+
);
|
|
41
49
|
if (!verdictMatch) return null;
|
|
50
|
+
const rawVerdict = verdictMatch[1].toUpperCase().replace(/_/g, '-');
|
|
51
|
+
const verdict = rawVerdict === 'CRITICAL-BLOCK' ? 'CRITICAL' : rawVerdict;
|
|
42
52
|
|
|
43
53
|
const memberMatch = src.match(/^PANEL_MEMBER\s*:\s*(\S+)\s*$/im);
|
|
44
54
|
const member = memberMatch && memberMatch[1] !== '-' ? memberMatch[1] : null;
|
|
45
55
|
|
|
56
|
+
// VERIFICATION_RERUN status (PASS | FAIL | NOT_RUN | N/A). The status may
|
|
57
|
+
// sit inline on the heading line AND/OR under the indented `result:` line —
|
|
58
|
+
// both positions are read, and an explicit FAIL or NOT_RUN in either wins
|
|
59
|
+
// over a PASS elsewhere (fail closed on contradiction). Bare prose
|
|
60
|
+
// (`N pass / M fail`) and a literal template echo (`VERIFICATION_RERUN:
|
|
61
|
+
// PASS | FAIL | …` — still carrying the `|` vocabulary) parse as no status,
|
|
62
|
+
// leaving the field null (= no required metadata → pre-existing behavior).
|
|
63
|
+
// FAIL = the re-run executed and failed; NOT_RUN = the required verification
|
|
64
|
+
// never executed — either blocks approval.
|
|
65
|
+
//
|
|
66
|
+
// Layout is read exhaustively and conservatively: `[ \t]` (not `\s`) after
|
|
67
|
+
// the heading colon so the match cannot swallow the newline before an
|
|
68
|
+
// indented child (a `result:` FIRST child used to be hidden this way), the
|
|
69
|
+
// global match reads REPEATED VERIFICATION_RERUN headers, and every
|
|
70
|
+
// `result:` child line of each header is collected so a contradictory
|
|
71
|
+
// second line cannot hide behind the first. FAIL/NOT_RUN dominate.
|
|
72
|
+
const STATUS_RANK = { FAIL: 3, NOT_RUN: 2, PASS: 1, 'N/A': 0 };
|
|
73
|
+
const statuses = [];
|
|
74
|
+
const commands = [];
|
|
75
|
+
const vrRe = /^VERIFICATION_RERUN[ \t]*:[ \t]*(.*)$/img;
|
|
76
|
+
let vr;
|
|
77
|
+
while ((vr = vrRe.exec(src)) !== null) {
|
|
78
|
+
const inline = vr[1].trim();
|
|
79
|
+
const inlineStatus = inline.includes('|') ? null : inline.match(/^(PASS|FAIL|NOT_RUN|N\/A)\b/i);
|
|
80
|
+
if (inlineStatus) statuses.push(inlineStatus[1].toUpperCase());
|
|
81
|
+
for (const line of src.slice(vr.index + vr[0].length).split('\n')) {
|
|
82
|
+
if (/^\S/.test(line)) break; // next top-level field ends this header's children
|
|
83
|
+
const result = line.match(/^[ \t]*result[ \t]*:[ \t]*(.*)$/i);
|
|
84
|
+
if (result) {
|
|
85
|
+
const status = result[1].trim().match(/^(PASS|FAIL|NOT_RUN|N\/A)\b/i);
|
|
86
|
+
if (status) statuses.push(status[1].toUpperCase());
|
|
87
|
+
continue; // keep reading — a contradictory later `result:` must still block
|
|
88
|
+
}
|
|
89
|
+
const command = line.match(/^[ \t]*command[ \t]*:[ \t]*(.*)$/i);
|
|
90
|
+
if (command) commands.push(command[1].trim());
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const dominant = statuses.length > 0
|
|
94
|
+
? [...statuses].sort((a, b) => STATUS_RANK[b] - STATUS_RANK[a])[0]
|
|
95
|
+
: null;
|
|
96
|
+
// Fail closed on applicability: an N/A claim beside a DECLARED executable
|
|
97
|
+
// command contradicts the block's own evidence — the required check exists,
|
|
98
|
+
// so the N/A counts as NOT_RUN (the check was not run). A placeholder
|
|
99
|
+
// command (`-`, `none`, `(none …)`, `n/a …`) or no command line keeps N/A
|
|
100
|
+
// legitimate for genuinely check-free reviews.
|
|
101
|
+
const hasExecutableCommand = commands.some((c) => {
|
|
102
|
+
const t = c.trim().toLowerCase();
|
|
103
|
+
return t !== '' && t !== '-' && t !== 'none' && !t.startsWith('(none') && !t.startsWith('n/a');
|
|
104
|
+
});
|
|
105
|
+
const verificationRerun = dominant === 'N/A' && hasExecutableCommand ? 'NOT_RUN' : dominant;
|
|
106
|
+
|
|
46
107
|
const findings = { critical: [], important: [], minor: [] };
|
|
47
108
|
const findingsMatch = src.match(/^FINDINGS:\s*$/im);
|
|
48
109
|
if (findingsMatch) {
|
|
@@ -77,7 +138,7 @@ export function parseVerdictBlock(text) {
|
|
|
77
138
|
}
|
|
78
139
|
}
|
|
79
140
|
|
|
80
|
-
return { member, verdict
|
|
141
|
+
return { member, verdict, findings, verificationRerun };
|
|
81
142
|
}
|
|
82
143
|
|
|
83
144
|
// Normalize a finding bullet for identity comparison: two members report the
|
|
@@ -127,8 +188,15 @@ export function aggregatePanelVerdicts(verdicts) {
|
|
|
127
188
|
const hasCritical =
|
|
128
189
|
parsed.some((p) => p.verdict === 'CRITICAL') || parsed.some((p) => p.findings.critical.length > 0);
|
|
129
190
|
|
|
191
|
+
// Fail closed on explicit verification evidence: a re-run that FAILED, or
|
|
192
|
+
// one never executed (NOT_RUN), cannot sit behind an approving prose
|
|
193
|
+
// verdict — the block stops counting toward approval entirely.
|
|
194
|
+
const hasBlockingVerification = parsed.some(
|
|
195
|
+
(p) => p.verificationRerun === 'FAIL' || p.verificationRerun === 'NOT_RUN',
|
|
196
|
+
);
|
|
197
|
+
|
|
130
198
|
let verdict;
|
|
131
|
-
if (hasCritical || changeRequests >= 1 || parsed.length === 0) verdict = 'changes_requested';
|
|
199
|
+
if (hasCritical || changeRequests >= 1 || hasBlockingVerification || parsed.length === 0) verdict = 'changes_requested';
|
|
132
200
|
else if (approvals >= 2) verdict = 'approved';
|
|
133
201
|
else verdict = 'approved_minor';
|
|
134
202
|
|
|
@@ -16,6 +16,38 @@ You are the independent reviewer for UKit's handoff Quality Gate. Your model is
|
|
|
16
16
|
- `code` (default, if not specified) — reviewing a handoff task diff. Follow **Code Review** below, unchanged.
|
|
17
17
|
- `spec` | `plan` — reviewing a document (e.g. `docs/plans/*.md`), no diff/task file/executor report involved. Skip straight to **Spec/Plan Review** at the end of this file instead.
|
|
18
18
|
|
|
19
|
+
## REVIEW_KIND (code reviews only)
|
|
20
|
+
|
|
21
|
+
Set one kind per review — from the task's own classification when the spawning
|
|
22
|
+
command names one, else from what the diff shows:
|
|
23
|
+
|
|
24
|
+
- `product` (default when the diff changes source or tests) — the full Code
|
|
25
|
+
Review rubric below applies unchanged, including Test Plan adherence and
|
|
26
|
+
RED_OUTPUT evidence.
|
|
27
|
+
- `integration-evidence` — the diff verifies or mirrors behavior already
|
|
28
|
+
accepted elsewhere (template mirrors, parity copies, runtime/docs evidence).
|
|
29
|
+
The new-tests + RED_OUTPUT TDD requirements are inapplicable when the task
|
|
30
|
+
introduces no new behavior under test; review the evidence chain instead and
|
|
31
|
+
never fabricate tests or edits to satisfy them.
|
|
32
|
+
- `status-only` — the task only records status/evidence; an empty diff is
|
|
33
|
+
legitimate. Review the executor report and verification output on disk.
|
|
34
|
+
|
|
35
|
+
The review kind scopes only the implementation-TDD evidence (new tests +
|
|
36
|
+
RED_OUTPUT). It never waives the required executable checks — the task's
|
|
37
|
+
Verification Commands still re-run in your session (fresh PASS or the severity
|
|
38
|
+
ladder applies; `NOT_RUN` on a required command blocks in every kind) — and it
|
|
39
|
+
never lowers the task's original acceptance criteria.
|
|
40
|
+
|
|
41
|
+
**Verdict ≠ acceptance.** The review verdict and the task's original acceptance
|
|
42
|
+
are separate judgments. An `integration-evidence` review may legitimately be
|
|
43
|
+
`APPROVED` as a review (the evidence chain is sound and complete) while the
|
|
44
|
+
verdict's NOTES/BLOCKING explicitly report that product acceptance remains
|
|
45
|
+
unmet or pending external observation — that report flows through
|
|
46
|
+
`NEXT_STATUS_FOR_INDEX`/the task discussion and must not flip the product task
|
|
47
|
+
to `done` or close a pending parent item. A `status-only` recording is
|
|
48
|
+
bookkeeping, not an independent review pass: it never counts as evidence that
|
|
49
|
+
independent review approved the underlying work.
|
|
50
|
+
|
|
19
51
|
## Code Review (REVIEW_TARGET_TYPE=code)
|
|
20
52
|
|
|
21
53
|
### Inputs you expect
|
|
@@ -29,7 +61,7 @@ If any input is missing, return `CHANGES-REQUESTED` with reason "incomplete hand
|
|
|
29
61
|
### Review order
|
|
30
62
|
|
|
31
63
|
0. **Verification package completeness** — Check whether the project has a lint or typecheck script (`package.json` scripts, or the stack's equivalent). If it does and the task's Verification Commands don't run it, that is `CHANGES-REQUESTED`: "verification commands missing lint/typecheck — re-run planner or add the command and re-verify" — do this before anything else below.
|
|
32
|
-
1. **Test Plan adherence** — Were all tests in §4 actually implemented, including the ≥2 edge cases required by `handoff.plan.minTestsEdgeCase`? Check the Executor Report's `RED_OUTPUT` field: it must contain actual failing-test output (assertion failure, stack trace, non-zero exit), not a bare claim like "confirmed" or "yes". Missing or vague `RED_OUTPUT` → `CHANGES-REQUESTED`: "no evidence tests were RED before implementation — re-run TDD cycle and paste real output".
|
|
64
|
+
1. **Test Plan adherence (REVIEW_KIND=product)** — Were all tests in §4 actually implemented, including the ≥2 edge cases required by `handoff.plan.minTestsEdgeCase`? Check the Executor Report's `RED_OUTPUT` field: it must contain actual failing-test output (assertion failure, stack trace, non-zero exit), not a bare claim like "confirmed" or "yes". Missing or vague `RED_OUTPUT` → `CHANGES-REQUESTED`: "no evidence tests were RED before implementation — re-run TDD cycle and paste real output". For `integration-evidence`/`status-only` reviews that TDD evidence is inapplicable — grade the task's own evidence chain instead of demanding new tests or RED_OUTPUT. Either way, run the required checks yourself: `<task Verification Commands>`. Fresh PASS required, no trusting executor's output blindly.
|
|
33
65
|
2. **Correctness** — Does the diff implement the requested behavior? Any obvious wrong assumptions, stale refs, missing cases?
|
|
34
66
|
3. **Regression risk** — What existing behavior could this break? Are shared paths/tests/contracts still aligned? Run the wider suite only when the diff touched shared code; otherwise the task's own targeted commands are the gate and the wave-boundary full `yarn test` (see docs/AI_HANDOFF/RULES.md "Test selection") is the regression net.
|
|
35
67
|
4. **Safety / security / data loss** — Destructive actions, auth/permission, path handling, unsafe shell/DB/file ops.
|
|
@@ -76,13 +108,14 @@ model, so reviewer ≠ executor still stands.
|
|
|
76
108
|
## Reviewer Verdict
|
|
77
109
|
|
|
78
110
|
VERDICT: APPROVED | APPROVED-WITH-MINOR | CHANGES-REQUESTED | CRITICAL
|
|
111
|
+
REVIEW_KIND: product | integration-evidence | status-only
|
|
79
112
|
REVIEWER_MODEL: [model name actually used]
|
|
80
113
|
EXECUTOR_MODEL: [from executor report]
|
|
81
114
|
PANEL_MEMBER: [panel tier — `smart`/`code`/`lite`, or `solo` when the panel degenerates; `-` outside panel runs]
|
|
82
115
|
AGREEMENT_MAP: [lead member only — finding → panel members reporting it, from review-panel-aggregate.mjs output; `-` otherwise]
|
|
83
|
-
VERIFICATION_RERUN:
|
|
84
|
-
command: [exact command]
|
|
85
|
-
result: [N pass / M fail]
|
|
116
|
+
VERIFICATION_RERUN: PASS | FAIL | NOT_RUN | N/A
|
|
117
|
+
command: [exact command, or `-` when N/A]
|
|
118
|
+
result: [N pass / M fail — or NOT_RUN — <reason>, or N/A — <reason>]
|
|
86
119
|
TEST_PLAN_COVERAGE: [all-followed | partial — list gaps | missing — list]
|
|
87
120
|
FINDINGS:
|
|
88
121
|
critical:
|
|
@@ -103,7 +136,7 @@ After writing the verdict, update `docs/AI_HANDOFF/INDEX.md` row for this task:
|
|
|
103
136
|
TASK: TASK-xxx
|
|
104
137
|
VERDICT: approved | approved_minor | changes_requested | critical_block
|
|
105
138
|
REVIEWER_MODEL: <exact model ID>
|
|
106
|
-
VERIFICATION_RERUN: PASS | FAIL
|
|
139
|
+
VERIFICATION_RERUN: PASS | FAIL | NOT_RUN | N/A (+ one-line reason for NOT_RUN/N/A)
|
|
107
140
|
BLOCKING: <one line per critical/important finding, or "none">
|
|
108
141
|
```
|
|
109
142
|
|
|
@@ -139,7 +172,9 @@ Same model is the most common silent failure. Do not skip this check.
|
|
|
139
172
|
### Rules
|
|
140
173
|
|
|
141
174
|
- **Always re-run** the task's Verification Commands. If they fail, VERDICT = CRITICAL regardless of executor claims.
|
|
142
|
-
-
|
|
175
|
+
- **VERIFICATION_RERUN is evidence, not prose.** `NOT_RUN` = the required verification never executed in your session (blocked, timed out, environment unavailable); `FAIL` = the re-run executed and did not pass. Either one must never accompany APPROVED or APPROVED-WITH-MINOR — with `NOT_RUN` the verdict is at most CHANGES-REQUESTED, and with `FAIL` it is CRITICAL per the ladder above. State the blocking status once, with the reason — a contradicting second line (e.g. header `PASS` next to `result: NOT_RUN`) is read fail-closed: the blocking status wins. `N/A` is only legitimate when this review has no executable required checks (e.g. a read-only spec/plan review, with `command: -` or no command line); when the task package defines Verification Commands, the re-run is required, so an `N/A` claim there counts as `NOT_RUN`. An `N/A` result that sits beside a declared executable `command:` (e.g. `command: yarn test`) contradicts its own evidence and counts as `NOT_RUN` — the aggregator fails it closed too. Never fabricate a PASS — a fabricated rerun is a fake test → CRITICAL.
|
|
176
|
+
- If executor said `TEST_PLAN_FOLLOWED: N/A` without a real justification, downgrade to at minimum CHANGES-REQUESTED (REVIEW_KIND=product; a check-free `integration-evidence`/`status-only` review may declare N/A legitimately when no Test Plan applies — the required Verification Commands still re-run).
|
|
177
|
+
- Verdict spelling is `-`/`_`-insensitive at the aggregator: `CHANGES_REQUESTED`, `CRITICAL_BLOCK`, `APPROVED_WITH_MINOR` parse to the same canonical verdicts as the hyphen forms, so write either spelling — a blocking verdict is never dropped as unparsed.
|
|
143
178
|
- Never approve when test file has no real `expect`/`assert` - that is a fake test -> CRITICAL.
|
|
144
179
|
- Keep the verdict block <= 30 lines. Findings are bullet points, not essays.
|
|
145
180
|
- The same-model refusal above is non-negotiable: bypassing it defeats the entire Quality Gate.
|
|
@@ -59,8 +59,8 @@ resumable. If compaction does happen, the run resumes from the cursor automatica
|
|
|
59
59
|
|
|
60
60
|
### Run cursor — write after every step
|
|
61
61
|
|
|
62
|
-
Maintain `docs/AI_HANDOFF/RUN.md`. Rewrite it (whole file
|
|
63
|
-
numbered step completes:
|
|
62
|
+
Maintain `docs/AI_HANDOFF/RUN.md`. Rewrite it (whole file — the fixed field block below,
|
|
63
|
+
exactly as many lines as the fields in play) immediately after every numbered step completes:
|
|
64
64
|
|
|
65
65
|
```
|
|
66
66
|
Command: handoff-fullstack
|
|
@@ -71,8 +71,21 @@ Cursor: wave <N> batch <M> — <what just finished>
|
|
|
71
71
|
Next: <the exact next step to run>
|
|
72
72
|
ExitPredicate: all(<term>[,<term>...]) # written once at P2; never relaxed
|
|
73
73
|
QuietScans: <n>/<required> # only while sweeping for stragglers near the end
|
|
74
|
+
Acceptance: <the acceptance line the CurrentTask must satisfy> # optional
|
|
75
|
+
CurrentTask: <TASK-xxx> # optional; the single task mid-flight right now
|
|
76
|
+
RetryFingerprint: <stable id of the failing step> # optional; retry-loop identity
|
|
77
|
+
RetryCount: <n> # optional; consecutive attempts of that fingerprint
|
|
74
78
|
```
|
|
75
79
|
|
|
80
|
+
**Compact-carry fields.** `Acceptance`, `CurrentTask`, `RetryFingerprint` and `RetryCount`
|
|
81
|
+
are what a compacted session carries forward — these four lines plus `Goal`/`Next:`, never
|
|
82
|
+
the full subagent prompts. The SessionStart resume hook prints them verbatim after a
|
|
83
|
+
compact. On resume, work from the cursor: read `RUN.md` + `INDEX.md` and make a bounded
|
|
84
|
+
read of only the section for the active phase in this file — never a full re-read of the
|
|
85
|
+
whole command. Retry rule: a `RetryFingerprint` whose `RetryCount` has reached
|
|
86
|
+
`orchestration.maxAttemptsPerFailure` (2) must continue with a changed strategy, never the
|
|
87
|
+
same edit again.
|
|
88
|
+
|
|
76
89
|
`ExitPredicate` grammar: `all(<term>[,<term>...])` or a single term; terms are
|
|
77
90
|
`index:all-done`, `git:clean`, `file-exists:<relpath>`; goal-level runs add
|
|
78
91
|
`agents:none-running` and `verify:pass` (an unprovable term reads as unmet, never
|
|
@@ -126,8 +139,10 @@ Wherever a step below says **verify the write**, it means this rule.
|
|
|
126
139
|
If `docs/AI_HANDOFF/RUN.md` exists with `Phase:` not `done` or `blocked`, this is a
|
|
127
140
|
**continuation, not a new cycle**. Do NOT re-plan, do NOT overwrite `PLAN.md`, do NOT ask
|
|
128
141
|
the human whether to continue. Read the cursor, then jump straight to `Next:` and carry on.
|
|
129
|
-
Tasks already `done`
|
|
130
|
-
`changes_requested` and `blocked` tasks
|
|
142
|
+
Tasks already `done` are skipped. `pending_review` tasks are NOT re-implemented — they go to
|
|
143
|
+
the R-phase review (R2–R4). `ready`, `in_progress`, `changes_requested` and `blocked` tasks
|
|
144
|
+
are routed to implementation/fix (I-phase). Any nonterminal state gets a branch; none is
|
|
145
|
+
skipped silently.
|
|
131
146
|
|
|
132
147
|
`Phase: blocked` is terminal for the run — the machinery (Stop gate, hooks, stop-coordinator)
|
|
133
148
|
all treats it as closed. It is *paused*, not abandoned: an explicit `/ukit:handoff-fullstack`
|
|
@@ -387,7 +402,15 @@ If the lite agent reports it has no shell/Bash tool (or its shell call is reject
|
|
|
387
402
|
- `docs/AI_HANDOFF/ACTIVE.md` → get `Base: <BASE>`
|
|
388
403
|
- `docs/AI_HANDOFF/INDEX.md` → collect all `ready` tasks
|
|
389
404
|
|
|
390
|
-
|
|
405
|
+
Branch on the nonterminal states — never report-and-stop while work is nonterminal:
|
|
406
|
+
- `pending_review` tasks exist → route to R1/R2 (review), do not implement them.
|
|
407
|
+
- `changes_requested` tasks exist → route to the I-phase fix round for those tasks.
|
|
408
|
+
- `in_progress` / stale `blocked` tasks exist → apply the Recovery rule below (finish in place
|
|
409
|
+
or supersede with `-R1`).
|
|
410
|
+
- No `ready` task but any of the above exists → continue the independent authorized work
|
|
411
|
+
in the other branches; a blocked branch does not block unrelated tasks.
|
|
412
|
+
- Only when every task is `done`/`cancelled_superseded` (or dependency-blocked with a recorded
|
|
413
|
+
reason) does this step end the wave with nothing to implement, and the run moves to R/F.
|
|
391
414
|
|
|
392
415
|
Verify working tree is clean after the plan commit:
|
|
393
416
|
```bash
|
|
@@ -625,7 +648,12 @@ PLAN_COMMIT=$(git log --format=%H --grep='^handoff: plan' -n 1)
|
|
|
625
648
|
git diff $PLAN_COMMIT # all handoff changes: committed waves + anything uncommitted
|
|
626
649
|
```
|
|
627
650
|
|
|
628
|
-
|
|
651
|
+
Classify the review by what the diff can show — an empty diff is NOT automatically "implement incomplete":
|
|
652
|
+
- **Product review** (the tasks change source/tests): an empty diff means implementation did not land. Re-enter Phase 3 from the run cursor with the remaining `ready` tasks.
|
|
653
|
+
- **Integration-evidence review** (the tasks verify behaviour, docs or runtime evidence without source edits): an empty diff is expected. Review the executor reports and the verification output on disk; a read-only evidence verdict is legitimate.
|
|
654
|
+
- **Status-only recording** (the task only moves status / records a verdict): an empty diff is expected. Write the status through the verify-the-write rule; do not edit source to make the diff non-empty.
|
|
655
|
+
|
|
656
|
+
Never make fake edits or fake tests to satisfy an empty diff, and never lower acceptance criteria to pass a review. Declaring P00 complete is not implied by any of the above. The review verdict and the original task acceptance are separate judgments: an integration-evidence review may approve the evidence chain while explicitly reporting product acceptance unmet or pending external observation — that report must not flip the product task to done — and a status-only recording is bookkeeping, never an independent review pass.
|
|
629
657
|
|
|
630
658
|
> Orchestrator (this session) handles the R1 guard check directly.
|
|
631
659
|
|
|
@@ -650,7 +678,7 @@ The spawned reviewer agent reads `EXECUTOR_MODEL` from each task file `## Execut
|
|
|
650
678
|
<each command from §Verification Commands across all pending_review tasks>
|
|
651
679
|
```
|
|
652
680
|
|
|
653
|
-
If any command fails →
|
|
681
|
+
If any command fails → `VERDICT: CRITICAL` (`NEXT_STATUS_FOR_INDEX: critical_block`) for this task. This task's R4 write-up stops here — task-local, not run-local: the task enters the R4.5 auto-fix loop and the remaining batch tasks continue. Do NOT proceed to R4 for this task.
|
|
654
682
|
|
|
655
683
|
### R4 — Review unified diff + append verdict (strong model)
|
|
656
684
|
|
|
@@ -666,25 +694,29 @@ Stage gate (unic-decision): reviewers still generate findings; when `decisionPla
|
|
|
666
694
|
Append verdict to each task file:
|
|
667
695
|
```
|
|
668
696
|
## Reviewer Verdict
|
|
669
|
-
VERDICT:
|
|
697
|
+
VERDICT: APPROVED | APPROVED-WITH-MINOR | CHANGES-REQUESTED | CRITICAL
|
|
670
698
|
REVIEWER_MODEL: <your exact model ID>
|
|
671
699
|
EXECUTOR_MODEL: <from Executor Report>
|
|
672
|
-
|
|
700
|
+
REVIEW_KIND: product | integration-evidence | status-only
|
|
701
|
+
VERIFICATION_RERUN: PASS | FAIL | NOT_RUN | N/A
|
|
673
702
|
FINDINGS:
|
|
674
703
|
critical: <file:line — what fails> | none
|
|
675
704
|
important: <file:line — risk> | none
|
|
676
705
|
minor: <style/naming> | none
|
|
677
|
-
NEXT_STATUS_FOR_INDEX:
|
|
706
|
+
NEXT_STATUS_FOR_INDEX: approved | approved_minor | changes_requested | critical_block
|
|
678
707
|
```
|
|
679
708
|
|
|
709
|
+
`VERDICT:` uses the aggregator's canonical hyphen vocabulary — underscore spellings (`changes_requested`, `critical_block`, `approved_with_minor`) parse to the same verdicts, so a blocking reviewer is never dropped as unparsed. `NOT_RUN` = the required re-run never executed in your session; `FAIL` = it executed and failed; either blocks approval. `N/A` is legitimate only when this review has no executable required checks (`command: -` or none) — an `N/A` beside a declared executable `command:` counts as `NOT_RUN`. `REVIEW_KIND` comes from the R1 classification; it scopes only the implementation-TDD evidence (new tests + RED_OUTPUT) — required executable checks and the task's original acceptance criteria are never waived, and approving an evidence review never marks product acceptance met.
|
|
710
|
+
|
|
680
711
|
Then return to the orchestrator **at most 6 lines** — the full verdict is already on disk:
|
|
681
712
|
```
|
|
682
713
|
TASK: TASK-xxx
|
|
683
714
|
VERDICT: approved | approved_minor | changes_requested | critical_block
|
|
684
715
|
REVIEWER_MODEL: <exact model ID>
|
|
685
|
-
VERIFICATION_RERUN: PASS | FAIL
|
|
716
|
+
VERIFICATION_RERUN: PASS | FAIL | NOT_RUN | N/A (+ one-line reason)
|
|
686
717
|
BLOCKING: <one line per critical/important finding, or "none">
|
|
687
718
|
```
|
|
719
|
+
The return `VERDICT:` carries the NEXT_STATUS_FOR_INDEX vocabulary; the on-disk block's `VERDICT:` field uses the canonical hyphen vocabulary above.
|
|
688
720
|
Do not paste the diff, the findings prose, or verification logs back into the orchestrator.
|
|
689
721
|
|
|
690
722
|
### R4.5 — Auto-fix loop (max 2 rounds)
|
|
@@ -47,8 +47,9 @@ git diff --stat $PLAN_COMMIT # every handoff change: wave commits + working tr
|
|
|
47
47
|
git status # no handoff branches/worktrees should remain
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
`
|
|
50
|
+
Classify by what the diff can show. An empty diff is NOT automatically "implement incomplete":
|
|
51
|
+
- If `pending_review` tasks exist and the diff is empty, check the task kind first. A product task (changes source/tests) with an empty diff means implementation did not land — report those tasks as still `ready`, do not review them. An evidence-only or status-only task with an empty diff is a legitimate read-only review: review the executor report and verification output on disk, never create fake edits or fake tests to make the diff non-empty, and never lower acceptance criteria.
|
|
52
|
+
- If there are no `pending_review` tasks at all, report which tasks are still `ready` and stop; there is nothing to review yet.
|
|
52
53
|
|
|
53
54
|
## Step 2 — Review the diff
|
|
54
55
|
|
|
@@ -58,6 +59,20 @@ If that diff is empty → handoff-implement was not completed. Report which task
|
|
|
58
59
|
|
|
59
60
|
Degenerate panel: if `modelRoles['review-panel']` is empty or has one entry, spawn a single reviewer and require `PANEL_MEMBER: solo` in its verdict.
|
|
60
61
|
|
|
62
|
+
**omp `tasks[]` dispatch — ordinary entries only.** When batching reviewers through the omp `task` tool, every `tasks[]` entry carries only the fields ordinary dispatch supports — `agent`, `name`, `task` — and nothing else:
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
tasks:
|
|
66
|
+
- agent: "code-reviewer"
|
|
67
|
+
name: "TASK-001-smart"
|
|
68
|
+
task: "Review docs/AI_HANDOFF/tasks/TASK-001.md per /ukit:handoff-review Step 2a-2d. PANEL_MEMBER: smart. Return at most 6 lines."
|
|
69
|
+
- agent: "code-reviewer"
|
|
70
|
+
name: "TASK-001-code"
|
|
71
|
+
task: "Review docs/AI_HANDOFF/tasks/TASK-001.md per /ukit:handoff-review Step 2a-2d. PANEL_MEMBER: code. Return at most 6 lines."
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Never add fields ordinary dispatch does not support: `sourceSession` and a per-task `model` are not supported there — omp 18.12.5 batch validation rejects them deterministically (confirmed in the installed binary's validator; not advertised as a live-probe pass) — and model binding comes from agent frontmatter via `modelRoles`, never from a task field. There is no seance workaround for reviewer dispatch. A deterministic validation rejection is never retried with identical args. A retry is allowed only after a relevant argument changes — `agent`, or the `tasks[]` entry's unsupported-field set (removing `sourceSession`/`model`). Prompt reword, `read`, and `status` text are NOT relevant changes: they never make a retry legitimate and never reset the failure budget, which stays the existing `maxAttemptsPerFailure: 2`. When that budget is spent, preserve across resume — in the task's `## Discussion` or the `RUN.md` cursor — a compact normalized error (prose stripped) plus the relevant key fingerprint and the attempt count, mark that task's verification `NOT_RUN` (never `PASS` — unverified work is not verified), and continue with independent tasks. Host pre-hook rejection behavior is unknown: never assume a hook will or will not catch the same call.
|
|
75
|
+
|
|
61
76
|
Each spawned reviewer agent performs 2a–2d below per task:
|
|
62
77
|
|
|
63
78
|
### 2a — Model isolation check (always first)
|
|
@@ -77,7 +92,7 @@ Read `EXECUTOR_MODEL` from each task file `## Executor Report`.
|
|
|
77
92
|
<each command from §Verification Commands across all tasks>
|
|
78
93
|
```
|
|
79
94
|
|
|
80
|
-
If any command fails →
|
|
95
|
+
If any command fails → `VERDICT: CRITICAL` with `NEXT_STATUS_FOR_INDEX: critical_block` for this task. That ends **this task's**
|
|
81
96
|
review, not the run — the orchestrator picks it up in Step 3's auto-fix loop.
|
|
82
97
|
|
|
83
98
|
### 2c — Review unified diff
|
|
@@ -97,26 +112,30 @@ matches the task file's wording.
|
|
|
97
112
|
|
|
98
113
|
```
|
|
99
114
|
## Reviewer Verdict
|
|
100
|
-
VERDICT:
|
|
115
|
+
VERDICT: APPROVED | APPROVED-WITH-MINOR | CHANGES-REQUESTED | CRITICAL
|
|
101
116
|
REVIEWER_MODEL: <your exact model ID>
|
|
102
117
|
EXECUTOR_MODEL: <from Executor Report>
|
|
118
|
+
REVIEW_KIND: product | integration-evidence | status-only
|
|
103
119
|
PANEL_MEMBER: <panel tier | solo | ->
|
|
104
|
-
VERIFICATION_RERUN: PASS | FAIL
|
|
120
|
+
VERIFICATION_RERUN: PASS | FAIL | NOT_RUN | N/A
|
|
105
121
|
FINDINGS:
|
|
106
122
|
critical: <file:line — what fails> | none
|
|
107
123
|
important: <file:line — risk> | none
|
|
108
124
|
minor: <style/naming> | none
|
|
109
|
-
NEXT_STATUS_FOR_INDEX:
|
|
125
|
+
NEXT_STATUS_FOR_INDEX: approved | approved_minor | changes_requested | critical_block
|
|
110
126
|
```
|
|
111
127
|
|
|
128
|
+
`VERDICT:` uses the aggregator's canonical hyphen vocabulary — underscore spellings (`changes_requested`, `critical_block`, `approved_with_minor`) parse to the same verdicts, so a blocking reviewer is never dropped as unparsed. `NOT_RUN` = the required re-run never executed in your session; `FAIL` = it executed and failed; either blocks approval. `N/A` is legitimate only when this review has no executable required checks (`command: -` or none) — an `N/A` beside a declared executable `command:` counts as `NOT_RUN`. `REVIEW_KIND` comes from the Step 1 classification; it scopes only the implementation-TDD evidence (new tests + RED_OUTPUT) — required executable checks and the task's original acceptance criteria are never waived, and approving an evidence review never marks product acceptance met.
|
|
129
|
+
|
|
112
130
|
Then return to the orchestrator **at most 6 lines** — the full verdict is already on disk:
|
|
113
131
|
```
|
|
114
132
|
TASK: TASK-xxx
|
|
115
133
|
VERDICT: approved | approved_minor | changes_requested | critical_block
|
|
116
134
|
REVIEWER_MODEL: <exact model ID>
|
|
117
|
-
VERIFICATION_RERUN: PASS | FAIL
|
|
135
|
+
VERIFICATION_RERUN: PASS | FAIL | NOT_RUN | N/A (+ one-line reason)
|
|
118
136
|
BLOCKING: <one line per critical/important finding, or "none">
|
|
119
137
|
```
|
|
138
|
+
The return `VERDICT:` carries the NEXT_STATUS_FOR_INDEX vocabulary; the on-disk block's `VERDICT:` field uses the canonical hyphen vocabulary above.
|
|
120
139
|
Do not paste the diff, the findings prose, or verification logs back into the orchestrator.
|
|
121
140
|
|
|
122
141
|
### 2e — Aggregate the panel + lead bucketing
|
|
@@ -135,7 +154,14 @@ changes, or zero parseable verdict blocks — it fails closed; else
|
|
|
135
154
|
(round N)` blocks supersede earlier rounds.
|
|
136
155
|
`consensus≥2 identical findings = high signal` — a finding reported by two or more
|
|
137
156
|
members is high-signal. The consensus verdict, not any single member's vote, drives
|
|
138
|
-
`NEXT_STATUS_FOR_INDEX`.
|
|
157
|
+
`NEXT_STATUS_FOR_INDEX`. The aggregate reads every `VERIFICATION_RERUN` header and
|
|
158
|
+
`result:` line in a block and fails closed: `FAIL`/`NOT_RUN` in any position —
|
|
159
|
+
including repeated or contradicting headers — block approval, and an `N/A` beside a
|
|
160
|
+
declared executable command counts as `NOT_RUN` (a placeholder `command: -` or no
|
|
161
|
+
command line keeps a genuine no-check review legitimate). Underscore verdict aliases
|
|
162
|
+
parse to the canonical hyphen verdicts, so a member following the template always
|
|
163
|
+
votes; a genuinely unparsable block never votes (named in `UNPARSED`), and zero
|
|
164
|
+
parseable blocks fail closed to `changes_requested`.
|
|
139
165
|
|
|
140
166
|
Then the **lead member** (first entry in `modelRoles['review-panel']` = the judgment
|
|
141
167
|
tier) applies the lead-judgment buckets to every finding — **Act on** / **Consider** /
|
|
@@ -216,6 +216,13 @@ async function emitOrdinaryResume(payload, source) {
|
|
|
216
216
|
const base = field('Base');
|
|
217
217
|
const cursor = field('Cursor');
|
|
218
218
|
const next = field('Next');
|
|
219
|
+
// Compact-resume carry (bounded): the goal/acceptance, current task and retry
|
|
220
|
+
// fingerprint+count survive compaction without re-reading the full prompt or the
|
|
221
|
+
// whole command file. Absent fields are simply omitted.
|
|
222
|
+
const acceptance = field('Acceptance');
|
|
223
|
+
const currentTask = field('CurrentTask');
|
|
224
|
+
const retryFingerprint = field('RetryFingerprint');
|
|
225
|
+
const retryCount = field('RetryCount');
|
|
219
226
|
|
|
220
227
|
// The session's CURRENT handoff intent — structured transcript evidence, the
|
|
221
228
|
// same shared module the Stop gate uses. A session whose latest request is
|
|
@@ -287,15 +294,22 @@ async function emitOrdinaryResume(payload, source) {
|
|
|
287
294
|
` Phase: ${phase}`,
|
|
288
295
|
` Cursor: ${cursor || '(not recorded)'}`,
|
|
289
296
|
` Next: ${next || '(not recorded)'}`,
|
|
297
|
+
...(acceptance ? [` Acceptance: ${acceptance}`] : []),
|
|
298
|
+
...(currentTask ? [` CurrentTask: ${currentTask}`] : []),
|
|
299
|
+
...(retryFingerprint ? [` RetryFingerprint: ${retryFingerprint} (RetryCount: ${retryCount || '0'})`] : []),
|
|
290
300
|
'',
|
|
291
301
|
'This is a CONTINUATION, not a new cycle. Do not re-plan, do not overwrite PLAN.md or the',
|
|
292
302
|
'task files, and do not ask the user whether to continue — they already asked for a',
|
|
293
303
|
'one-shot run and the pipeline was interrupted, not cancelled.',
|
|
294
304
|
'',
|
|
295
305
|
'Read docs/AI_HANDOFF/RUN.md and docs/AI_HANDOFF/INDEX.md, then execute the `Next:` step',
|
|
296
|
-
'above
|
|
297
|
-
'
|
|
298
|
-
'
|
|
306
|
+
'above with a bounded read of only the section for the active phase in',
|
|
307
|
+
'.claude/commands/ukit/handoff-fullstack.md — never a full re-read of the file. Route',
|
|
308
|
+
'tasks by status: `done` is skipped; `pending_review` goes to the R-phase review (it is',
|
|
309
|
+
'reviewed there, not re-implemented); `ready`, `in_progress`, `changes_requested` and',
|
|
310
|
+
'`blocked` are picked up for implementation/fix. Keep writing the cursor after every step.',
|
|
311
|
+
'If a retry loop was live (RetryFingerprint + RetryCount above), continue it: same',
|
|
312
|
+
'fingerprint at/over two attempts requires a changed strategy, never the same edit again.',
|
|
299
313
|
];
|
|
300
314
|
|
|
301
315
|
if (source === 'compact') {
|