@ijfw/memory-server 1.4.3 → 1.5.0
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/fixtures/truncation-corpus/_generate-corpus.js +367 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-01/events.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-01/intent-journal.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-01/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-01/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-02/events.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-02/intent-journal.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-02/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-02/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-03/events.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-03/intent-journal.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-03/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-03/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-04/events.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-04/intent-journal.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-04/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-04/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-05/events.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-05/intent-journal.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-05/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-01-clean-exit-05/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-01/events.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-01/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-01/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-01/snapshots/v-midO-1-advance.json +11 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-01/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-02/events.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-02/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-02/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-02/snapshots/v-midO-2-advance.json +11 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-02/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-03/events.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-03/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-03/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-03/snapshots/v-midO-3-advance.json +11 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-03/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-04/events.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-04/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-04/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-04/snapshots/v-midO-4-advance.json +11 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-04/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-05/events.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-05/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-05/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-05/snapshots/v-midO-5-advance.json +11 -0
- package/fixtures/truncation-corpus/fx-02-mid-overwrite-05/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-01/events.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-01/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-01/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-01/target/.ijfw/blackboard/decisions.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-02/events.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-02/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-02/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-02/target/.ijfw/blackboard/decisions.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-03/events.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-03/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-03/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-03/target/.ijfw/blackboard/decisions.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-04/events.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-04/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-04/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-04/target/.ijfw/blackboard/decisions.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-05/events.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-05/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-05/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-03-mid-append-05/target/.ijfw/blackboard/decisions.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-04-no-events-01/events.jsonl +0 -0
- package/fixtures/truncation-corpus/fx-04-no-events-01/intent-journal.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-04-no-events-01/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-04-no-events-01/snapshots/v-noEv-1-set-phase.json +11 -0
- package/fixtures/truncation-corpus/fx-04-no-events-01/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-04-no-events-02/events.jsonl +0 -0
- package/fixtures/truncation-corpus/fx-04-no-events-02/intent-journal.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-04-no-events-02/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-04-no-events-02/snapshots/v-noEv-2-set-phase.json +11 -0
- package/fixtures/truncation-corpus/fx-04-no-events-02/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-04-no-events-03/events.jsonl +0 -0
- package/fixtures/truncation-corpus/fx-04-no-events-03/intent-journal.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-04-no-events-03/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-04-no-events-03/snapshots/v-noEv-3-set-phase.json +11 -0
- package/fixtures/truncation-corpus/fx-04-no-events-03/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-04-no-events-04/events.jsonl +0 -0
- package/fixtures/truncation-corpus/fx-04-no-events-04/intent-journal.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-04-no-events-04/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-04-no-events-04/snapshots/v-noEv-4-set-phase.json +11 -0
- package/fixtures/truncation-corpus/fx-04-no-events-04/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-04-no-events-05/events.jsonl +0 -0
- package/fixtures/truncation-corpus/fx-04-no-events-05/intent-journal.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-04-no-events-05/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-04-no-events-05/snapshots/v-noEv-5-set-phase.json +11 -0
- package/fixtures/truncation-corpus/fx-04-no-events-05/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-01/events.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-01/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-01/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-01/snapshots/v-errT-1-partial.json +11 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-01/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-02/events.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-02/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-02/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-02/target/.ijfw/blackboard/decisions.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-03/events.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-03/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-03/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-03/snapshots/v-errT-3-partial.json +11 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-03/target/.ijfw/state/workflow.json +1 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-04/events.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-04/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-04/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-04/target/.ijfw/blackboard/decisions.jsonl +1 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-05/events.jsonl +2 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-05/intent-journal.jsonl +3 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-05/meta.json +18 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-05/snapshots/v-errT-5-partial.json +11 -0
- package/fixtures/truncation-corpus/fx-05-error-terminated-05/target/.ijfw/state/workflow.json +1 -0
- package/package.json +1 -1
- package/src/active-extension-writer.js +144 -64
- package/src/api-client.js +43 -5
- package/src/audit-roster.js +80 -5
- package/src/blackboard.js +298 -6
- package/src/cli-run.js +33 -5
- package/src/codex-agents.js +96 -5
- package/src/cost/aggregator.js +39 -9
- package/src/cost/pricing.js +57 -0
- package/src/cost/readers/gemini.js +1 -1
- package/src/cross-audit-chunker.js +189 -0
- package/src/cross-dispatcher.js +124 -21
- package/src/cross-orchestrator-cli.js +550 -14
- package/src/cross-orchestrator.js +1171 -10
- package/src/cross-project-search.js +195 -9
- package/src/dashboard-client-planning.html +273 -0
- package/src/dashboard-client-waves.html +304 -0
- package/src/dashboard-client.html +17 -2
- package/src/dashboard-server.js +152 -0
- package/src/deploy-alerts.js +150 -0
- package/src/design/iframe-bridge.js +242 -0
- package/src/design-companion.js +144 -0
- package/src/dispatch/checkpoint-cli.js +97 -0
- package/src/dispatch/colon-syntax.js +81 -1
- package/src/dispatch/extension.js +27 -1
- package/src/dispatch/registry-cli.js +4 -1
- package/src/dispatch/wave-cli.js +323 -0
- package/src/dispatch/worktree-cli.js +40 -0
- package/src/dispatch-planner.js +97 -2
- package/src/dream/runner.mjs +47 -11
- package/src/dream/stage-runner.js +40 -0
- package/src/dream/state-file.js +102 -0
- package/src/extension-installer.js +70 -24
- package/src/extension-quota-tracker.js +4 -2
- package/src/extension-registry.js +289 -35
- package/src/feedback-detector.js +26 -0
- package/src/fs-lock.js +259 -7
- package/src/gate-result.js +95 -1
- package/src/hero-line.js +86 -5
- package/src/intent-router.js +35 -0
- package/src/lib/a11y-contract.js +117 -0
- package/src/lib/atomic-io.js +29 -8
- package/src/lib/cache-keepalive.js +150 -0
- package/src/lib/jsonl-rotation.js +104 -0
- package/src/lib/lighthouse-pillar.js +121 -0
- package/src/lib/llm-call.js +121 -0
- package/src/lib/playwright-baseline.js +205 -0
- package/src/lib/rekor-bridge.js +221 -0
- package/src/lib/repo-map.js +392 -0
- package/src/lib/shasum-verify.js +164 -0
- package/src/lib/sketches-gc.js +132 -0
- package/src/lib/tmp-suffix.js +62 -0
- package/src/lib/ui-review-runner.js +554 -0
- package/src/lib/uispec-drift.js +301 -0
- package/src/lib/uispec-intake.js +381 -0
- package/src/lib/worktree-guards.js +118 -0
- package/src/lib/worktree-recovery.js +100 -0
- package/src/memory/auto-linker.js +152 -0
- package/src/memory/benchmark.js +498 -0
- package/src/memory/dedup.js +126 -0
- package/src/memory/embedding-cache.js +136 -0
- package/src/memory/fact-extractor.js +168 -0
- package/src/memory/fts5.js +65 -1
- package/src/memory/migrations/004-bitemporal.js +91 -0
- package/src/memory/migrations/005-vector-cache.js +61 -0
- package/src/memory/migrations/006-obsidian-graph.js +46 -0
- package/src/memory/migrations/007-skill-telemetry.js +24 -0
- package/src/memory/migrations/008-write-provenance.js +41 -0
- package/src/memory/obsidian-parser.js +91 -0
- package/src/memory/query-dataview.js +86 -0
- package/src/memory/search.js +10 -0
- package/src/memory/temporal.js +529 -0
- package/src/memory/tokenize.js +10 -0
- package/src/memory-facts-handler.js +37 -0
- package/src/memory-feedback.js +260 -2
- package/src/model-refresh.js +292 -0
- package/src/observability/cost-anomaly.js +166 -0
- package/src/observability/evaluator-checkpoint-contract.js +117 -0
- package/src/observability/trace-id.js +163 -0
- package/src/orchestrator/agents-md-blackboard.js +152 -0
- package/src/orchestrator/checkpoint-contract.md +140 -0
- package/src/orchestrator/debug-trident.js +570 -0
- package/src/orchestrator/merge-block-aware.js +350 -0
- package/src/orchestrator/plan-checker.js +475 -0
- package/src/orchestrator/post-done-runner.js +249 -0
- package/src/orchestrator/review.js +136 -0
- package/src/orchestrator/runtime-loop.js +430 -0
- package/src/orchestrator/skill-telemetry-sink.js +29 -0
- package/src/orchestrator/skill-telemetry.js +37 -0
- package/src/orchestrator/state-events.js +459 -0
- package/src/orchestrator/state-sdk.js +1764 -0
- package/src/orchestrator/status-protocol.js +235 -0
- package/src/orchestrator/subagent-telemetry.js +452 -0
- package/src/orchestrator/termination.js +160 -0
- package/src/orchestrator/verification-gate.js +281 -0
- package/src/orchestrator/wave-state.js +564 -0
- package/src/orchestrator/worktree-provision.js +77 -0
- package/src/override-use-registry.js +111 -5
- package/src/receipts.js +36 -4
- package/src/recovery/checkpoint.js +56 -3
- package/src/recovery/code-fixer.js +656 -0
- package/src/recovery/truncation.js +317 -0
- package/src/redactor.js +75 -6
- package/src/runtime-mediator.js +15 -0
- package/src/sanitizer.js +10 -0
- package/src/search-hybrid.js +139 -0
- package/src/server.js +603 -59
- package/src/swarm/worktree.js +27 -4
- package/src/swarm-config.js +113 -12
- package/src/team/domain-templates/book.json +51 -0
- package/src/team/domain-templates/business.json +41 -0
- package/src/team/domain-templates/content.json +50 -0
- package/src/team/domain-templates/design.json +44 -0
- package/src/team/domain-templates/research.json +41 -0
- package/src/team/domain-templates/software.json +40 -0
- package/src/team/generator.js +278 -3
- package/src/team/modify.js +203 -0
- package/src/team/schemas.js +48 -0
- package/src/update-apply.js +19 -3
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* post-done-runner.js — v1.5.0-major S02: enforced post-DONE pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Runs after a subagent's DONE has been verified by runtime-loop.js. Wraps
|
|
5
|
+
* reviewTask (v1.4.4 N3 two-stage review) and checkVerificationGate
|
|
6
|
+
* (v1.4.4 N5) into a single callable the orchestrator-LLM invokes via MCP,
|
|
7
|
+
* so the post-DONE contract isn't satisfied by markdown prose.
|
|
8
|
+
*
|
|
9
|
+
* v1.5.0 T13: the standalone `ijfw_subagent_post_done` MCP tool was retired and
|
|
10
|
+
* absorbed into the single `ijfw_state` MCP tool as the `subagent.post-done`
|
|
11
|
+
* verb (see STATE-SDK-CONTRACT §7). `runSelfCheck` is re-exported through
|
|
12
|
+
* `state-sdk.js` for that verb; `runPostDone` is still exported here for the
|
|
13
|
+
* direct-import test path (`test-orchestrator-post-done-runner.js`).
|
|
14
|
+
*
|
|
15
|
+
* Outcome shape (uniform regardless of branch taken):
|
|
16
|
+
* {
|
|
17
|
+
* verdict: 'approved' | 'spec_failed' | 'quality_failed',
|
|
18
|
+
* reviewStage: 'spec' | 'quality',
|
|
19
|
+
* reviewOk: boolean,
|
|
20
|
+
* reviewFindings: string[],
|
|
21
|
+
* gatePassed: boolean,
|
|
22
|
+
* gateAction: 'block' | 'advise' | 'pass',
|
|
23
|
+
* gateViolation: object | null,
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* `gateAction` (W12-F/F4 — RT2-H1) tells the MCP tool handler what to do
|
|
27
|
+
* with a gate failure:
|
|
28
|
+
* - 'pass' → gate passed; advance normally.
|
|
29
|
+
* - 'block' → strict mode (default) AND gate failed. Caller MUST refuse
|
|
30
|
+
* to claim success. The MCP handler should surface a structured
|
|
31
|
+
* `block: true` so the orchestrator-LLM treats it as a hard stop.
|
|
32
|
+
* - 'advise' → caller opted out of strict via `strictGate: false` AND gate
|
|
33
|
+
* failed. Caller may proceed but should still surface the
|
|
34
|
+
* violation so it gets routed into memory-feedback.
|
|
35
|
+
*
|
|
36
|
+
* The `dispatch` parameter is the reviewTask injected dispatcher:
|
|
37
|
+
* (kind: 'spec-compliance'|'code-quality', ctx: object)
|
|
38
|
+
* => Promise<{ verdict: 'PASS'|'FAIL', findings: string[] }>
|
|
39
|
+
*
|
|
40
|
+
* If `dispatch` is null/undefined we still run the gate check (the orchestrator
|
|
41
|
+
* may invoke the reviewers itself via the Agent tool); verdict becomes
|
|
42
|
+
* 'no_review' to signal that.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import { existsSync } from 'node:fs';
|
|
46
|
+
import { execFileSync } from 'node:child_process';
|
|
47
|
+
import { reviewTask } from './review.js';
|
|
48
|
+
import { checkVerificationGate, recordViolation } from './verification-gate.js';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Extract paths claimed in the report. Naive but effective: looks for
|
|
52
|
+
* "created/modified/file: <path>" plus bullet-list `- path/...` patterns.
|
|
53
|
+
* Skip lines under "Self-Check" section (don't recurse into reported self-checks).
|
|
54
|
+
*/
|
|
55
|
+
function extractClaimedPaths(reportText) {
|
|
56
|
+
const lines = String(reportText || '').split('\n');
|
|
57
|
+
const paths = new Set();
|
|
58
|
+
let inSelfCheck = false;
|
|
59
|
+
for (const line of lines) {
|
|
60
|
+
if (/^##\s*Self-Check/i.test(line)) { inSelfCheck = true; continue; }
|
|
61
|
+
if (inSelfCheck) continue;
|
|
62
|
+
const m = line.match(/(?:created|modified|file):\s*[`"]?([^\s`"]+)[`"]?/i);
|
|
63
|
+
if (m && m[1].includes('.')) paths.add(m[1]);
|
|
64
|
+
const m2 = line.match(/^\s*-\s+[`"]?([^\s`"]+\.\w+)[`"]?/);
|
|
65
|
+
if (m2) paths.add(m2[1]);
|
|
66
|
+
}
|
|
67
|
+
return [...paths];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Extract plausible commit SHAs (hex, 7-40 chars) from the report.
|
|
72
|
+
*/
|
|
73
|
+
function extractClaimedCommits(reportText) {
|
|
74
|
+
const matches = String(reportText || '').match(/\b[0-9a-f]{7,40}\b/g) || [];
|
|
75
|
+
return [...new Set(matches.filter((s) => /^[0-9a-f]+$/.test(s) && s.length >= 7))];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* runSelfCheck — verify claimed files + commits actually exist before review.
|
|
80
|
+
* @param {string} reportText
|
|
81
|
+
* @param {string} projectRoot
|
|
82
|
+
* @returns {{
|
|
83
|
+
* verdict: 'PASSED'|'FAILED',
|
|
84
|
+
* files_claimed: number,
|
|
85
|
+
* files_present: number,
|
|
86
|
+
* files_missing: string[],
|
|
87
|
+
* commits_claimed: number,
|
|
88
|
+
* commits_present: number,
|
|
89
|
+
* commits_missing: string[],
|
|
90
|
+
* }}
|
|
91
|
+
*/
|
|
92
|
+
export function runSelfCheck(reportText, projectRoot) {
|
|
93
|
+
const claimedPaths = extractClaimedPaths(reportText);
|
|
94
|
+
const claimedCommits = extractClaimedCommits(reportText);
|
|
95
|
+
const filesPresent = claimedPaths.filter((p) =>
|
|
96
|
+
existsSync(p.startsWith('/') ? p : `${projectRoot}/${p}`),
|
|
97
|
+
);
|
|
98
|
+
let commitsPresent = [];
|
|
99
|
+
try {
|
|
100
|
+
const allShas = execFileSync('git', ['log', '--all', '--format=%H'], {
|
|
101
|
+
cwd: projectRoot,
|
|
102
|
+
encoding: 'utf8',
|
|
103
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
104
|
+
}).split('\n');
|
|
105
|
+
commitsPresent = claimedCommits.filter((c) => allShas.some((sha) => sha.startsWith(c)));
|
|
106
|
+
} catch {
|
|
107
|
+
/* not a git repo — skip commit check */
|
|
108
|
+
}
|
|
109
|
+
const verdict =
|
|
110
|
+
filesPresent.length === claimedPaths.length &&
|
|
111
|
+
commitsPresent.length === claimedCommits.length
|
|
112
|
+
? 'PASSED'
|
|
113
|
+
: 'FAILED';
|
|
114
|
+
return {
|
|
115
|
+
verdict,
|
|
116
|
+
files_claimed: claimedPaths.length,
|
|
117
|
+
files_present: filesPresent.length,
|
|
118
|
+
files_missing: claimedPaths.filter((p) => !filesPresent.includes(p)),
|
|
119
|
+
commits_claimed: claimedCommits.length,
|
|
120
|
+
commits_present: commitsPresent.length,
|
|
121
|
+
commits_missing: claimedCommits.filter((c) => !commitsPresent.includes(c)),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
/**
|
|
127
|
+
* @param {object} params
|
|
128
|
+
* @param {string} params.taskId
|
|
129
|
+
* @param {string} [params.taskSpec]
|
|
130
|
+
* @param {string} params.commitSha
|
|
131
|
+
* @param {string} [params.branch]
|
|
132
|
+
* @param {string} params.reportText
|
|
133
|
+
* @param {Array<{tool: string, input?: {command?: string}}>} [params.toolCallsInMessage]
|
|
134
|
+
* @param {Function|null} [params.dispatch] Reviewer dispatcher; null = skip review
|
|
135
|
+
* @param {string} params.projectRoot
|
|
136
|
+
* @param {string} [params.projectConventions]
|
|
137
|
+
* @param {boolean} [params.strictGate=true]
|
|
138
|
+
* W12-F/F4 — RT2-H1. When true (default) and the verification gate fails,
|
|
139
|
+
* the result includes `gateAction: 'block'` and the MCP handler MUST refuse
|
|
140
|
+
* to claim success. Pass `false` for legacy advisory-only behavior.
|
|
141
|
+
* @returns {Promise<{
|
|
142
|
+
* verdict: 'approved'|'spec_failed'|'quality_failed'|'no_review',
|
|
143
|
+
* reviewStage: 'spec'|'quality'|null,
|
|
144
|
+
* reviewOk: boolean,
|
|
145
|
+
* reviewFindings: string[],
|
|
146
|
+
* gatePassed: boolean,
|
|
147
|
+
* gateAction: 'block'|'advise'|'pass',
|
|
148
|
+
* gateViolation: object|null,
|
|
149
|
+
* selfCheck: {
|
|
150
|
+
* verdict: 'PASSED'|'FAILED',
|
|
151
|
+
* files_claimed: number,
|
|
152
|
+
* files_present: number,
|
|
153
|
+
* files_missing: string[],
|
|
154
|
+
* commits_claimed: number,
|
|
155
|
+
* commits_present: number,
|
|
156
|
+
* commits_missing: string[],
|
|
157
|
+
* },
|
|
158
|
+
* }>}
|
|
159
|
+
*/
|
|
160
|
+
export async function runPostDone({
|
|
161
|
+
taskId,
|
|
162
|
+
taskSpec = '',
|
|
163
|
+
commitSha,
|
|
164
|
+
branch = '',
|
|
165
|
+
reportText,
|
|
166
|
+
toolCallsInMessage,
|
|
167
|
+
dispatch,
|
|
168
|
+
projectRoot,
|
|
169
|
+
projectConventions = '',
|
|
170
|
+
strictGate = true,
|
|
171
|
+
}) {
|
|
172
|
+
if (typeof projectRoot !== 'string' || projectRoot.length === 0) {
|
|
173
|
+
throw new TypeError('runPostDone: projectRoot is required');
|
|
174
|
+
}
|
|
175
|
+
if (typeof reportText !== 'string') {
|
|
176
|
+
throw new TypeError('runPostDone: reportText must be a string');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ---- Self-Check (S09) ------------------------------------------------
|
|
180
|
+
// Verify claimed files + commits exist before spending review tokens.
|
|
181
|
+
// Additive: doesn't gate downstream — surfaces the divergence in result.
|
|
182
|
+
const selfCheck = runSelfCheck(reportText, projectRoot);
|
|
183
|
+
|
|
184
|
+
// ---- Two-stage review (N3) -------------------------------------------
|
|
185
|
+
let reviewOk = false;
|
|
186
|
+
let reviewStage = null;
|
|
187
|
+
let reviewFindings = [];
|
|
188
|
+
let verdict = 'no_review';
|
|
189
|
+
|
|
190
|
+
if (typeof dispatch === 'function') {
|
|
191
|
+
const r = await reviewTask({
|
|
192
|
+
taskId,
|
|
193
|
+
taskSpec,
|
|
194
|
+
commitSha,
|
|
195
|
+
branch,
|
|
196
|
+
projectConventions,
|
|
197
|
+
dispatch,
|
|
198
|
+
});
|
|
199
|
+
reviewOk = !!r.ok;
|
|
200
|
+
reviewStage = r.stage ?? null;
|
|
201
|
+
reviewFindings = Array.isArray(r.findings) ? r.findings : [];
|
|
202
|
+
if (reviewOk) {
|
|
203
|
+
verdict = 'approved';
|
|
204
|
+
} else {
|
|
205
|
+
verdict = r.stage === 'spec' ? 'spec_failed' : 'quality_failed';
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ---- Verification gate (N5) ------------------------------------------
|
|
210
|
+
const gateOutcome = checkVerificationGate(
|
|
211
|
+
reportText,
|
|
212
|
+
Array.isArray(toolCallsInMessage) ? toolCallsInMessage : [],
|
|
213
|
+
);
|
|
214
|
+
if (!gateOutcome.ok) {
|
|
215
|
+
try {
|
|
216
|
+
// recordViolation signature is (violation, projectRoot) -- see verification-gate.js
|
|
217
|
+
await recordViolation(
|
|
218
|
+
{ taskId, ...gateOutcome },
|
|
219
|
+
projectRoot,
|
|
220
|
+
);
|
|
221
|
+
} catch {
|
|
222
|
+
// Advisory -- never block on violation log failure (matches v1.4.4 N5 contract)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// W12-F/F4 — RT2-H1: classify the gate outcome so the caller knows whether
|
|
227
|
+
// to BLOCK (strict default + failure), ADVISE (caller opted out + failure),
|
|
228
|
+
// or PASS (gate succeeded). The MCP tool handler reads `gateAction` and
|
|
229
|
+
// surfaces a structured `block: true` to the orchestrator-LLM when 'block'.
|
|
230
|
+
let gateAction;
|
|
231
|
+
if (gateOutcome.ok) {
|
|
232
|
+
gateAction = 'pass';
|
|
233
|
+
} else if (strictGate === false) {
|
|
234
|
+
gateAction = 'advise';
|
|
235
|
+
} else {
|
|
236
|
+
gateAction = 'block';
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
verdict,
|
|
241
|
+
reviewStage,
|
|
242
|
+
reviewOk,
|
|
243
|
+
reviewFindings,
|
|
244
|
+
gatePassed: gateOutcome.ok === true,
|
|
245
|
+
gateAction,
|
|
246
|
+
gateViolation: gateOutcome.ok ? null : { violation: gateOutcome.violation, claim: gateOutcome.claim },
|
|
247
|
+
selfCheck,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* review.js — Two-stage per-task review for the IJFW orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Stage 1: spec-compliance reviewer — confirms the diff faithfully
|
|
5
|
+
* implements every requirement in the task spec.
|
|
6
|
+
* Stage 2: code-quality reviewer — checks correctness, security, and
|
|
7
|
+
* project-convention adherence (runs only after Stage 1 passes).
|
|
8
|
+
*
|
|
9
|
+
* The `dispatch` parameter is injected by the caller so this module is
|
|
10
|
+
* testable without a live Agent tool. Signature:
|
|
11
|
+
* (kind: 'spec-compliance' | 'code-quality', ctx: object)
|
|
12
|
+
* => Promise<{ verdict: 'PASS' | 'FAIL', findings: string[] }>
|
|
13
|
+
*
|
|
14
|
+
* Landed in W10-A2 (v1.4.4 — N3).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Constants
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
/** Maximum re-review iterations before escalation. */
|
|
22
|
+
export const REVIEW_MAX_ITERATIONS = 3;
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Helpers
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Returns true when re-review should be triggered.
|
|
30
|
+
*
|
|
31
|
+
* @param {'PASS'|'FAIL'} prevVerdict
|
|
32
|
+
* @param {number} iteration 1-based iteration count (1 = first attempt)
|
|
33
|
+
* @returns {boolean}
|
|
34
|
+
*/
|
|
35
|
+
export function shouldReReview(prevVerdict, iteration) {
|
|
36
|
+
return prevVerdict !== 'PASS' && iteration < REVIEW_MAX_ITERATIONS;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Main export
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Run two-stage review for a completed task.
|
|
45
|
+
*
|
|
46
|
+
* v1.5.0 audit-MED-work-M7: optional `bothStages: true` lets callers surface
|
|
47
|
+
* code-quality findings even when the spec stage FAILs. Quality findings are
|
|
48
|
+
* downgraded to INFO severity (prefixed `[INFO]`) so the orchestrator's
|
|
49
|
+
* fail-on-block path still keys off the spec failure — but the operator
|
|
50
|
+
* gets a fuller picture of what's wrong instead of only seeing spec gaps.
|
|
51
|
+
*
|
|
52
|
+
* @param {object} params
|
|
53
|
+
* @param {string} params.taskId Blackboard task ID
|
|
54
|
+
* @param {string} params.taskSpec Full task specification text
|
|
55
|
+
* @param {string} params.commitSha SHA of the implementer's commit
|
|
56
|
+
* @param {string} params.branch Branch name
|
|
57
|
+
* @param {string} [params.projectConventions] CLAUDE.md / AGENTS.md excerpt
|
|
58
|
+
* @param {boolean} [params.bothStages=false] Run quality stage even on spec FAIL (M7).
|
|
59
|
+
* @param {Function} params.dispatch Injected reviewer dispatcher
|
|
60
|
+
*
|
|
61
|
+
* @returns {Promise<{
|
|
62
|
+
* ok: boolean,
|
|
63
|
+
* stage: 'spec' | 'quality',
|
|
64
|
+
* findings: string[],
|
|
65
|
+
* qualityFindings?: string[]
|
|
66
|
+
* }>}
|
|
67
|
+
*/
|
|
68
|
+
export async function reviewTask({
|
|
69
|
+
taskId,
|
|
70
|
+
taskSpec,
|
|
71
|
+
commitSha,
|
|
72
|
+
branch,
|
|
73
|
+
projectConventions = '',
|
|
74
|
+
bothStages = false,
|
|
75
|
+
dispatch,
|
|
76
|
+
}) {
|
|
77
|
+
// ------------------------------------------------------------------
|
|
78
|
+
// Stage 1: spec-compliance reviewer
|
|
79
|
+
// ------------------------------------------------------------------
|
|
80
|
+
const spec = await dispatch('spec-compliance', {
|
|
81
|
+
taskId,
|
|
82
|
+
taskSpec,
|
|
83
|
+
commitSha,
|
|
84
|
+
branch,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (spec.verdict !== 'PASS') {
|
|
88
|
+
const base = {
|
|
89
|
+
ok: false,
|
|
90
|
+
stage: 'spec',
|
|
91
|
+
findings: spec.findings ?? [],
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// M7: when bothStages is true, also fire the quality reviewer and
|
|
95
|
+
// surface its findings with an [INFO] downgrade prefix. The outer ok
|
|
96
|
+
// / stage remains 'spec' fail — quality verdict here is advisory only.
|
|
97
|
+
if (bothStages) {
|
|
98
|
+
try {
|
|
99
|
+
const quality = await dispatch('code-quality', {
|
|
100
|
+
taskId,
|
|
101
|
+
commitSha,
|
|
102
|
+
branch,
|
|
103
|
+
projectConventions,
|
|
104
|
+
});
|
|
105
|
+
const downgraded = (quality.findings ?? []).map(
|
|
106
|
+
(f) => (typeof f === 'string' && f.startsWith('[INFO] ') ? f : `[INFO] ${f}`),
|
|
107
|
+
);
|
|
108
|
+
return {
|
|
109
|
+
...base,
|
|
110
|
+
qualityFindings: downgraded,
|
|
111
|
+
};
|
|
112
|
+
} catch {
|
|
113
|
+
// Quality dispatch failure is non-fatal when we're only running it
|
|
114
|
+
// for advisory surfacing — fall through to the spec-only result.
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return base;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ------------------------------------------------------------------
|
|
122
|
+
// Stage 2: code-quality reviewer (always after spec PASS)
|
|
123
|
+
// ------------------------------------------------------------------
|
|
124
|
+
const quality = await dispatch('code-quality', {
|
|
125
|
+
taskId,
|
|
126
|
+
commitSha,
|
|
127
|
+
branch,
|
|
128
|
+
projectConventions,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
ok: quality.verdict === 'PASS',
|
|
133
|
+
stage: 'quality',
|
|
134
|
+
findings: quality.findings ?? [],
|
|
135
|
+
};
|
|
136
|
+
}
|