opencode-swarm 7.187.0 → 7.187.2
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/binaries/win32-arm64/swarm-sandbox-runner.exe +0 -0
- package/binaries/win32-x64/swarm-sandbox-runner.exe +0 -0
- package/dist/background/pr-review-contract.d.ts +2 -2
- package/dist/cli/{auto-checkpoint-r69e82bh.js → auto-checkpoint-2xmptd5t.js} +8 -8
- package/dist/cli/{checkpoint-1sqwbgra.js → checkpoint-2y7z19y5.js} +14 -13
- package/dist/cli/{coder-settlement-b9qx0732.js → coder-settlement-s4d28fa6.js} +9 -9
- package/dist/cli/{config-doctor-s4fr5qwr.js → config-doctor-312afzhr.js} +13 -12
- package/dist/cli/{core-bezezjy9.js → core-2bywxc4w.js} +2 -2
- package/dist/cli/{curation-policy-2x2w3b57.js → curation-policy-sr29thf5.js} +5 -5
- package/dist/cli/{curator-drift-785xnxdv.js → curator-drift-xx8pp4g9.js} +6 -6
- package/dist/cli/{curator-w4hp7a10.js → curator-dvhf6jex.js} +37 -35
- package/dist/cli/{curator-llm-factory-p0wwyswg.js → curator-llm-factory-4g2nvr76.js} +37 -35
- package/dist/cli/doctor-knowledge-receipt-phase-id-m239pgbz.js +83 -0
- package/dist/cli/{evidence-summary-service-33qcm9xc.js → evidence-summary-service-9et04fjh.js} +17 -16
- package/dist/cli/{gate-evidence-9ehj968e.js → gate-evidence-73vj3bgm.js} +6 -6
- package/dist/cli/{guardrail-explain-5715fdzv.js → guardrail-explain-8n4fx9zv.js} +38 -36
- package/dist/cli/{guardrail-log-k1dfzfzm.js → guardrail-log-9gmfvnd7.js} +7 -7
- package/dist/cli/{guardrail-reset-nt88n9dm.js → guardrail-reset-k3mrq25e.js} +37 -35
- package/dist/cli/{hive-promoter-c8fjn186.js → hive-promoter-bp3rz0q6.js} +37 -35
- package/dist/cli/{index-df2p0k0s.js → index-26zs9wsj.js} +2 -2
- package/dist/cli/{index-ectvhv8e.js → index-310h5192.js} +2 -2
- package/dist/cli/{index-en7vzpeq.js → index-33345434.js} +4 -4
- package/dist/cli/{index-2wgvjwpr.js → index-3ckfawjf.js} +3 -3
- package/dist/cli/{index-b2je0z0t.js → index-3g3n9fjq.js} +4 -4
- package/dist/cli/{index-ycz8z2db.js → index-3y0nr248.js} +3 -3
- package/dist/cli/{index-v08ksq0b.js → index-4g39nne8.js} +2 -2
- package/dist/cli/{index-t3hcd6zz.js → index-6t7ygevg.js} +1 -1
- package/dist/cli/{index-5m56c5r6.js → index-7fxy1s86.js} +4 -4
- package/dist/cli/{index-pymr99as.js → index-85cx1mmb.js} +4 -4
- package/dist/cli/{index-2s8wdagg.js → index-9jafghwk.js} +37 -35
- package/dist/cli/{index-evy5yrsw.js → index-b308erem.js} +3 -3
- package/dist/cli/{index-qzgfheb4.js → index-b5tthmy2.js} +2 -2
- package/dist/cli/{index-yw1g78v9.js → index-bstjg81q.js} +1 -1
- package/dist/cli/{index-hvr82s1t.js → index-dqberkr3.js} +4 -4
- package/dist/cli/{index-jtncfvhe.js → index-dx6bhbd6.js} +1 -1
- package/dist/cli/{index-ww8payq9.js → index-e9ry78z5.js} +2 -2
- package/dist/cli/{index-1vvr69ka.js → index-efbhea00.js} +1 -1
- package/dist/cli/{index-xs96nry2.js → index-exj7czz9.js} +4 -4
- package/dist/cli/{index-sztx0vyt.js → index-f6pft90q.js} +3 -3
- package/dist/cli/{index-6ersmqjd.js → index-j5db3v56.js} +318 -767
- package/dist/cli/{index-pts38j6x.js → index-jh9nrshe.js} +2 -2
- package/dist/cli/{index-0yj79yk1.js → index-jyxgkp4a.js} +1 -1
- package/dist/cli/index-kc635w60.js +748 -0
- package/dist/cli/{index-cppqxjw9.js → index-m1vsqmyr.js} +1 -1
- package/dist/cli/index-m9zzj4ee.js +10 -0
- package/dist/cli/{index-rm676s0p.js → index-n45kz69m.js} +1 -1
- package/dist/cli/{index-dypmvvga.js → index-n55exns2.js} +1 -1
- package/dist/cli/{index-pfpnzf6w.js → index-n8kqhxay.js} +1 -1
- package/dist/cli/{index-bbqxt1p5.js → index-pqffq9mm.js} +3 -3
- package/dist/cli/{index-knbjtqwt.js → index-rtdxyjrg.js} +8 -6
- package/dist/cli/{index-r7gt2w1b.js → index-tkm2rj27.js} +1 -1
- package/dist/cli/{index-cx4s7cak.js → index-tmp07h7b.js} +39 -37
- package/dist/cli/{index-ta48pe8j.js → index-tngc1erw.js} +3 -3
- package/dist/cli/{index-akt0arnt.js → index-tq6sbcys.js} +105 -185
- package/dist/cli/{index-c8ffgcvz.js → index-v9y0230d.js} +1 -1
- package/dist/cli/{index-469xmpd6.js → index-w0bvez8c.js} +1 -1
- package/dist/cli/{index-4g6sw4eq.js → index-wpxy7099.js} +1 -1
- package/dist/cli/{index-yf0cc6c5.js → index-xs070yr9.js} +2 -2
- package/dist/cli/{index-zzxnssy3.js → index-xv0t2sed.js} +4 -4
- package/dist/cli/{index-krqq8kbq.js → index-y81jjedn.js} +6 -6
- package/dist/cli/{index-d6kvatwp.js → index-z04d01pv.js} +11 -16
- package/dist/cli/index.js +38 -36
- package/dist/cli/{knowledge-escalator-qttydcx2.js → knowledge-escalator-fnqctvxz.js} +14 -12
- package/dist/cli/{knowledge-events-4vf0vj82.js → knowledge-events-mr1vh3nb.js} +12 -10
- package/dist/cli/{knowledge-link-p2c8agc8.js → knowledge-link-93nzznwd.js} +4 -4
- package/dist/cli/{knowledge-store-bw1dk2eb.js → knowledge-store-dxy4s9k6.js} +5 -5
- package/dist/cli/{knowledge-validator-gefcc36h.js → knowledge-validator-gy39attk.js} +7 -7
- package/dist/cli/{mcp-d5p0k3p1.js → mcp-bxb59wsq.js} +1 -1
- package/dist/cli/{model-preflight-fmx49pwy.js → model-preflight-2kje0sp0.js} +37 -35
- package/dist/cli/{pending-delegations-8333cb3y.js → pending-delegations-fm2nx2k0.js} +7 -7
- package/dist/cli/{pr-subscriptions-p42ny2sn.js → pr-subscriptions-g4f9rehb.js} +7 -7
- package/dist/cli/{pr-workflow-gate-csy32z8w.js → pr-workflow-gate-qtq1wq95.js} +37 -35
- package/dist/cli/{runner-cbpr45cs.js → runner-verr6er5.js} +5 -5
- package/dist/cli/{scan-cursor-14mwymtz.js → scan-cursor-8taj944f.js} +6 -6
- package/dist/cli/{schema-6426g0gw.js → schema-yxq3bjmd.js} +2 -2
- package/dist/cli/{scope-persistence-avkv3s4m.js → scope-persistence-jppvjncr.js} +12 -12
- package/dist/cli/{server-d9rk2zeb.js → server-2s7wqg9e.js} +37 -35
- package/dist/cli/{skill-generator-epdvdsh5.js → skill-generator-pyf95aqw.js} +16 -14
- package/dist/cli/{snapshot-coordination-init-35f7wrqb.js → snapshot-coordination-init-8gbxw9sa.js} +37 -35
- package/dist/cli/{speckit-checkoff-erwa7fbj.js → speckit-checkoff-x2z0jamv.js} +4 -4
- package/dist/cli/{telemetry-2vwb3n42.js → telemetry-xwj899pd.js} +1 -1
- package/dist/cli/{worktree-collision-ownership-3tqkw81h.js → worktree-collision-ownership-455bdcnk.js} +7 -7
- package/dist/cli/{worktree-isolation-0rrjqypt.js → worktree-isolation-8x4p7tvm.js} +37 -35
- package/dist/commands/doctor-knowledge-receipt-phase-id.d.ts +16 -0
- package/dist/hooks/extractors.d.ts +15 -0
- package/dist/hooks/knowledge-injector.d.ts +5 -0
- package/dist/hooks/knowledge-receipt-ledger.d.ts +50 -4
- package/dist/hooks/knowledge-receipt-observability.d.ts +1 -1
- package/dist/hooks/phase-complete-directive-gate.d.ts +18 -1
- package/dist/hooks/phase-directives.d.ts +7 -3
- package/dist/index.js +469 -460
- package/dist/state.d.ts +26 -3
- package/dist/tools/phase-complete.d.ts +2 -2
- package/dist/tools/repair-knowledge-receipt-ledger.d.ts +26 -0
- package/dist/utils/path.d.ts +23 -0
- package/package.json +1 -1
|
@@ -1,60 +1,139 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
import {
|
|
3
|
+
sanitizeContextText
|
|
4
|
+
} from "./index-m9zzj4ee.js";
|
|
2
5
|
import {
|
|
3
6
|
validateSwarmPath
|
|
4
7
|
} from "./index-bejxkee0.js";
|
|
5
8
|
import {
|
|
6
9
|
observeReceiptTransition
|
|
7
|
-
} from "./index-
|
|
8
|
-
import {
|
|
9
|
-
resolveHiveEventsPath
|
|
10
|
-
} from "./index-ae75rja9.js";
|
|
11
|
-
import {
|
|
12
|
-
resolveKnowledgeStoreDir
|
|
13
|
-
} from "./index-rm676s0p.js";
|
|
14
|
-
import {
|
|
15
|
-
atomicWriteFile
|
|
16
|
-
} from "./index-t3hcd6zz.js";
|
|
10
|
+
} from "./index-v9y0230d.js";
|
|
17
11
|
import {
|
|
18
12
|
assertProjectRoot
|
|
19
13
|
} from "./index-t7hmqgjy.js";
|
|
20
14
|
import {
|
|
21
15
|
emit
|
|
22
|
-
} from "./index-
|
|
16
|
+
} from "./index-dx6bhbd6.js";
|
|
23
17
|
import {
|
|
24
|
-
|
|
25
|
-
} from "./index-
|
|
26
|
-
import {
|
|
27
|
-
require_proper_lockfile
|
|
28
|
-
} from "./index-7g4c7s5r.js";
|
|
18
|
+
resolveActivePhaseId
|
|
19
|
+
} from "./index-gx2vp8gx.js";
|
|
29
20
|
import {
|
|
30
21
|
loadPluginConfigWithMeta
|
|
31
|
-
} from "./index-
|
|
22
|
+
} from "./index-w0bvez8c.js";
|
|
32
23
|
import {
|
|
33
24
|
KnowledgeConfigSchema,
|
|
34
25
|
init_schema
|
|
35
|
-
} from "./index-
|
|
36
|
-
import {
|
|
37
|
-
init_logger,
|
|
38
|
-
warn
|
|
39
|
-
} from "./index-zgwm4ryv.js";
|
|
40
|
-
import {
|
|
41
|
-
__require,
|
|
42
|
-
__toESM
|
|
43
|
-
} from "./index-a76rekgs.js";
|
|
26
|
+
} from "./index-b5tthmy2.js";
|
|
44
27
|
|
|
45
|
-
// src/
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
28
|
+
// src/utils/context-decisions.ts
|
|
29
|
+
function extractContextDecisions(content) {
|
|
30
|
+
const decisions = [];
|
|
31
|
+
const MAX_CONTENT_CHARS = 1e6;
|
|
32
|
+
const bounded = content.length > MAX_CONTENT_CHARS ? content.slice(0, MAX_CONTENT_CHARS) : content;
|
|
33
|
+
const lines = bounded.split(`
|
|
34
|
+
`);
|
|
35
|
+
let inDecisionsSection = false;
|
|
36
|
+
let currentPhase = null;
|
|
37
|
+
let lineNumber = 0;
|
|
38
|
+
for (let i = 0;i < lines.length; i++) {
|
|
39
|
+
const line = lines[i];
|
|
40
|
+
lineNumber = i + 1;
|
|
41
|
+
const phaseMatch = line.match(/^## Phase (\d+)/);
|
|
42
|
+
if (phaseMatch) {
|
|
43
|
+
currentPhase = parseInt(phaseMatch[1], 10);
|
|
44
|
+
}
|
|
45
|
+
if (line.trim() === "## Decisions") {
|
|
46
|
+
inDecisionsSection = true;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (inDecisionsSection && line.startsWith("## ")) {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
if (inDecisionsSection && line.trim().startsWith("- ")) {
|
|
53
|
+
const text = line.trim().substring(2);
|
|
54
|
+
const confirmed = text.includes("\u2705") || text.includes("[confirmed]");
|
|
55
|
+
const timestampMatch = text.match(/\[(\d{4}-\d{2}-\d{2}T[\d:.]+Z?)\]/);
|
|
56
|
+
const timestamp = timestampMatch ? timestampMatch[1] : null;
|
|
57
|
+
const decisionPhaseMatch = text.match(/Phase (\d+)/);
|
|
58
|
+
const decisionPhase = decisionPhaseMatch ? parseInt(decisionPhaseMatch[1], 10) : currentPhase;
|
|
59
|
+
decisions.push({
|
|
60
|
+
raw: line,
|
|
61
|
+
text: text.replace(/\s*\[.*?\]\s*/g, "").trim(),
|
|
62
|
+
phase: decisionPhase,
|
|
63
|
+
confirmed,
|
|
64
|
+
timestamp,
|
|
65
|
+
line: lineNumber
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return decisions;
|
|
70
|
+
}
|
|
52
71
|
|
|
53
72
|
// src/hooks/knowledge-receipt-ledger.ts
|
|
54
73
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
|
|
55
74
|
import { existsSync as existsSync2 } from "fs";
|
|
56
75
|
init_schema();
|
|
57
76
|
|
|
77
|
+
// src/hooks/extractors.ts
|
|
78
|
+
function extractCurrentPhase(planContent) {
|
|
79
|
+
if (!planContent) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
planContent = sanitizeContextText(planContent);
|
|
83
|
+
const lines = planContent.split(`
|
|
84
|
+
`);
|
|
85
|
+
for (let i = 0;i < Math.min(20, lines.length); i++) {
|
|
86
|
+
const line = lines[i].trim();
|
|
87
|
+
const progressMatch = line.match(/^## Phase (\d+):?\s*(.*?)\s*\[IN PROGRESS\]/i);
|
|
88
|
+
if (progressMatch) {
|
|
89
|
+
const phaseNum = progressMatch[1];
|
|
90
|
+
const description = progressMatch[2]?.trim() || "";
|
|
91
|
+
return `Phase ${phaseNum}: ${description} [IN PROGRESS]`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
for (let i = 0;i < Math.min(20, lines.length); i++) {
|
|
95
|
+
const line = lines[i].trim();
|
|
96
|
+
const blockedMatch = line.match(/^## Phase (\d+):?\s*(.*?)\s*\[BLOCKED\]/i);
|
|
97
|
+
if (blockedMatch) {
|
|
98
|
+
const phaseNum = blockedMatch[1];
|
|
99
|
+
const description = blockedMatch[2]?.trim() || "";
|
|
100
|
+
return `Phase ${phaseNum}: ${description} [BLOCKED]`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
for (let i = 0;i < Math.min(3, lines.length); i++) {
|
|
104
|
+
const line = lines[i].trim();
|
|
105
|
+
const phaseMatch = line.match(/Phase:\s*(\d+)/i);
|
|
106
|
+
if (phaseMatch) {
|
|
107
|
+
const phaseNum = phaseMatch[1];
|
|
108
|
+
return `Phase ${phaseNum} [PENDING]`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
function extractCurrentPhaseFromPlan(plan) {
|
|
114
|
+
const phaseId = resolveActivePhaseId(plan);
|
|
115
|
+
const phase = plan.phases.find((p) => p.id === phaseId);
|
|
116
|
+
if (!phase)
|
|
117
|
+
return null;
|
|
118
|
+
const statusMap = {
|
|
119
|
+
pending: "PENDING",
|
|
120
|
+
in_progress: "IN PROGRESS",
|
|
121
|
+
complete: "COMPLETE",
|
|
122
|
+
blocked: "BLOCKED"
|
|
123
|
+
};
|
|
124
|
+
const statusText = statusMap[phase.status] || "PENDING";
|
|
125
|
+
return sanitizeContextText(`Phase ${phase.id}: ${phase.name} [${statusText}]`);
|
|
126
|
+
}
|
|
127
|
+
function extractPhaseIdFromLabel(label) {
|
|
128
|
+
if (!label)
|
|
129
|
+
return;
|
|
130
|
+
const m = /^Phase\s+(\d+)/i.exec(label);
|
|
131
|
+
if (!m)
|
|
132
|
+
return;
|
|
133
|
+
const id = Number(m[1]);
|
|
134
|
+
return Number.isSafeInteger(id) && id >= 1 ? id : undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
58
137
|
// src/hooks/knowledge-receipt-ledger-storage.ts
|
|
59
138
|
import { createHash, randomUUID } from "crypto";
|
|
60
139
|
import * as fs from "fs";
|
|
@@ -504,6 +583,7 @@ var TRANSITIONS = new Set([
|
|
|
504
583
|
"gate_release_committed",
|
|
505
584
|
"phase_close_intent",
|
|
506
585
|
"phase_closed",
|
|
586
|
+
"phase_id_backfilled",
|
|
507
587
|
"repair_uncertainty_installed",
|
|
508
588
|
"repair_uncertainty_cleared",
|
|
509
589
|
"legacy_imported",
|
|
@@ -621,6 +701,29 @@ var keyOf = (traceId, entryId) => `${traceId.length}:${traceId}${entryId}`;
|
|
|
621
701
|
var emptyKeyOf = (traceId, sessionId) => `${traceId.length}:${traceId}${sessionId}`;
|
|
622
702
|
var phaseLifecycleKey = (phase, sessionId, taskId) => `${sessionId.length}:${sessionId}${phase.length}:${phase}${taskId === undefined ? "u:" : `s:${taskId.length}:${taskId}`}`;
|
|
623
703
|
var repairUncertaintyKey = (phase, sessionId) => `${sessionId.length}:${sessionId}${phase.length}:${phase}`;
|
|
704
|
+
function membershipMatchesPhaseIdentity(membership, phase, phaseId) {
|
|
705
|
+
if (phaseId === undefined)
|
|
706
|
+
return membership.phase === phase;
|
|
707
|
+
if (membership.phase_id === phaseId)
|
|
708
|
+
return true;
|
|
709
|
+
if (membership.phase_id === undefined && extractPhaseIdFromLabel(membership.phase) === phaseId)
|
|
710
|
+
return true;
|
|
711
|
+
return membership.phase === phase;
|
|
712
|
+
}
|
|
713
|
+
function findClosedLifecycleForPhase(state, scope) {
|
|
714
|
+
const labelKey = phaseLifecycleKey(scope.phase ?? "", scope.session_id, scope.task_id);
|
|
715
|
+
const byLabel = state.phaseLifecycle.get(labelKey);
|
|
716
|
+
if (byLabel !== undefined && (scope.phase_id === undefined || byLabel.phase_id === undefined || byLabel.phase_id === scope.phase_id))
|
|
717
|
+
return byLabel;
|
|
718
|
+
if (scope.phase_id === undefined)
|
|
719
|
+
return;
|
|
720
|
+
for (const lifecycle of state.phaseLifecycle.values()) {
|
|
721
|
+
if (lifecycle.session_id === scope.session_id && lifecycle.task_id === scope.task_id && (lifecycle.phase_id === scope.phase_id || lifecycle.phase_id === undefined && extractPhaseIdFromLabel(lifecycle.phase) === scope.phase_id)) {
|
|
722
|
+
return lifecycle;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
624
727
|
function nowIso() {
|
|
625
728
|
return new Date(_internals3.nowMs()).toISOString();
|
|
626
729
|
}
|
|
@@ -801,6 +904,7 @@ function parseMembership(value) {
|
|
|
801
904
|
"entry_id",
|
|
802
905
|
"session_id",
|
|
803
906
|
"phase",
|
|
907
|
+
"phase_id",
|
|
804
908
|
"task_id",
|
|
805
909
|
"agent",
|
|
806
910
|
"critical",
|
|
@@ -819,7 +923,7 @@ function parseMembership(value) {
|
|
|
819
923
|
"source_link_id",
|
|
820
924
|
"exposure_kind",
|
|
821
925
|
"origin"
|
|
822
|
-
]) || typeof value.trace_id !== "string" || !value.trace_id || typeof value.entry_id !== "string" || !value.entry_id || typeof value.session_id !== "string" || !value.session_id || !isOptionalString(value.phase) || !isOptionalString(value.task_id) || !isOptionalString(value.agent) || typeof value.critical !== "boolean" || value.rank !== undefined && !Number.isFinite(value.rank) || value.score !== undefined && !Number.isFinite(value.score) || !isValidTimestamp(value.committed_at) || typeof value.membership_event_id !== "string" || !value.membership_event_id || !Number.isFinite(value.grace_days) || value.grace_days < 0 || value.phase_close_intent_at !== undefined && !isValidTimestamp(value.phase_close_intent_at) || value.phase_closed_at !== undefined && !isValidTimestamp(value.phase_closed_at) || !isOptionalString(value.cohort_id) || !isOptionalString(value.source_link_id) || !isReceiptExposureKind(value.exposure_kind) || value.origin !== "v2" && value.origin !== "legacy") {
|
|
926
|
+
]) || typeof value.trace_id !== "string" || !value.trace_id || typeof value.entry_id !== "string" || !value.entry_id || typeof value.session_id !== "string" || !value.session_id || !isOptionalString(value.phase) || value.phase_id !== undefined && (!Number.isSafeInteger(value.phase_id) || value.phase_id < 1) || !isOptionalString(value.task_id) || !isOptionalString(value.agent) || typeof value.critical !== "boolean" || value.rank !== undefined && !Number.isFinite(value.rank) || value.score !== undefined && !Number.isFinite(value.score) || !isValidTimestamp(value.committed_at) || typeof value.membership_event_id !== "string" || !value.membership_event_id || !Number.isFinite(value.grace_days) || value.grace_days < 0 || value.phase_close_intent_at !== undefined && !isValidTimestamp(value.phase_close_intent_at) || value.phase_closed_at !== undefined && !isValidTimestamp(value.phase_closed_at) || !isOptionalString(value.cohort_id) || !isOptionalString(value.source_link_id) || !isReceiptExposureKind(value.exposure_kind) || value.origin !== "v2" && value.origin !== "legacy") {
|
|
823
927
|
return null;
|
|
824
928
|
}
|
|
825
929
|
const terminal = value.terminal === undefined ? undefined : parseTerminal(value.terminal);
|
|
@@ -848,6 +952,7 @@ function parseMembership(value) {
|
|
|
848
952
|
entry_id: value.entry_id,
|
|
849
953
|
session_id: value.session_id,
|
|
850
954
|
phase: value.phase,
|
|
955
|
+
phase_id: value.phase_id,
|
|
851
956
|
task_id: value.task_id,
|
|
852
957
|
agent: value.agent,
|
|
853
958
|
critical: value.critical,
|
|
@@ -924,17 +1029,19 @@ function parsePhaseLifecycle(value) {
|
|
|
924
1029
|
return null;
|
|
925
1030
|
if (!hasOnlyKeys(value, [
|
|
926
1031
|
"phase",
|
|
1032
|
+
"phase_id",
|
|
927
1033
|
"session_id",
|
|
928
1034
|
"task_id",
|
|
929
1035
|
"intent_event_id",
|
|
930
1036
|
"intent_at",
|
|
931
1037
|
"closed_event_id",
|
|
932
1038
|
"closed_at"
|
|
933
|
-
]) || typeof value.phase !== "string" || !value.phase || typeof value.session_id !== "string" || !value.session_id || !isOptionalString(value.task_id) || !isOptionalString(value.intent_event_id) || value.intent_at !== undefined && !isValidTimestamp(value.intent_at) || !isOptionalString(value.closed_event_id) || value.closed_at !== undefined && !isValidTimestamp(value.closed_at)) {
|
|
1039
|
+
]) || typeof value.phase !== "string" || !value.phase || value.phase_id !== undefined && (!Number.isSafeInteger(value.phase_id) || value.phase_id < 1) || typeof value.session_id !== "string" || !value.session_id || !isOptionalString(value.task_id) || !isOptionalString(value.intent_event_id) || value.intent_at !== undefined && !isValidTimestamp(value.intent_at) || !isOptionalString(value.closed_event_id) || value.closed_at !== undefined && !isValidTimestamp(value.closed_at)) {
|
|
934
1040
|
return null;
|
|
935
1041
|
}
|
|
936
1042
|
return {
|
|
937
1043
|
phase: value.phase,
|
|
1044
|
+
phase_id: value.phase_id,
|
|
938
1045
|
session_id: value.session_id,
|
|
939
1046
|
task_id: value.task_id,
|
|
940
1047
|
intent_event_id: value.intent_event_id,
|
|
@@ -971,6 +1078,7 @@ function parseAuditSummary(value) {
|
|
|
971
1078
|
"gate_release_committed",
|
|
972
1079
|
"phase_close_intent",
|
|
973
1080
|
"phase_closed",
|
|
1081
|
+
"phase_id_backfilled",
|
|
974
1082
|
"repair_uncertainty_installed",
|
|
975
1083
|
"repair_uncertainty_cleared",
|
|
976
1084
|
"legacy_imported",
|
|
@@ -998,7 +1106,8 @@ function summarizeRecord(record) {
|
|
|
998
1106
|
payload.memberships,
|
|
999
1107
|
payload.transitions,
|
|
1000
1108
|
payload.markers,
|
|
1001
|
-
payload.rejected
|
|
1109
|
+
payload.rejected,
|
|
1110
|
+
payload.items
|
|
1002
1111
|
]) {
|
|
1003
1112
|
if (!Array.isArray(collection))
|
|
1004
1113
|
continue;
|
|
@@ -1181,13 +1290,30 @@ function applyRecord(state, record) {
|
|
|
1181
1290
|
const phase = record.payload.phase;
|
|
1182
1291
|
const sessionId = record.payload.session_id;
|
|
1183
1292
|
const taskId = record.payload.task_id;
|
|
1293
|
+
const rawPhaseId = record.payload.phase_id;
|
|
1294
|
+
const phaseId = Number.isSafeInteger(rawPhaseId) && rawPhaseId >= 1 ? rawPhaseId : undefined;
|
|
1184
1295
|
if (typeof phase === "string" && typeof sessionId === "string" && isOptionalString(taskId)) {
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1296
|
+
let lifecycleKey = phaseLifecycleKey(phase, sessionId, taskId);
|
|
1297
|
+
let lifecycle = state.phaseLifecycle.get(lifecycleKey);
|
|
1298
|
+
if (lifecycle === undefined && phaseId !== undefined) {
|
|
1299
|
+
for (const [candidateKey, candidate] of state.phaseLifecycle) {
|
|
1300
|
+
if (candidate.session_id === sessionId && candidate.task_id === taskId && (candidate.phase_id === phaseId || candidate.phase_id === undefined && extractPhaseIdFromLabel(candidate.phase) === phaseId)) {
|
|
1301
|
+
lifecycleKey = candidateKey;
|
|
1302
|
+
lifecycle = candidate;
|
|
1303
|
+
break;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
if (lifecycle === undefined) {
|
|
1308
|
+
lifecycle = {
|
|
1309
|
+
phase,
|
|
1310
|
+
session_id: sessionId,
|
|
1311
|
+
task_id: taskId
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
if (lifecycle.phase_id === undefined && phaseId !== undefined) {
|
|
1315
|
+
lifecycle.phase_id = phaseId;
|
|
1316
|
+
}
|
|
1191
1317
|
if (record.kind === "phase_close_intent" && !lifecycle.intent_event_id) {
|
|
1192
1318
|
lifecycle.intent_event_id = record.event_id;
|
|
1193
1319
|
lifecycle.intent_at = record.timestamp;
|
|
@@ -1197,7 +1323,7 @@ function applyRecord(state, record) {
|
|
|
1197
1323
|
}
|
|
1198
1324
|
state.phaseLifecycle.set(lifecycleKey, lifecycle);
|
|
1199
1325
|
for (const membership of state.memberships.values()) {
|
|
1200
|
-
if (membership
|
|
1326
|
+
if (!membershipMatchesPhaseIdentity(membership, phase, phaseId) || membership.session_id !== sessionId || membership.task_id !== taskId)
|
|
1201
1327
|
continue;
|
|
1202
1328
|
if (record.kind === "phase_close_intent" && !membership.phase_close_intent_at)
|
|
1203
1329
|
membership.phase_close_intent_at = record.timestamp;
|
|
@@ -1214,6 +1340,16 @@ function applyRecord(state, record) {
|
|
|
1214
1340
|
}
|
|
1215
1341
|
}
|
|
1216
1342
|
}
|
|
1343
|
+
} else if (record.kind === "phase_id_backfilled") {
|
|
1344
|
+
for (const item of Array.isArray(record.payload.items) ? record.payload.items : []) {
|
|
1345
|
+
const candidate = item;
|
|
1346
|
+
if (typeof candidate.trace_id !== "string" || typeof candidate.entry_id !== "string" || !Number.isSafeInteger(candidate.phase_id) || candidate.phase_id < 1)
|
|
1347
|
+
continue;
|
|
1348
|
+
const membership = state.memberships.get(keyOf(candidate.trace_id, candidate.entry_id));
|
|
1349
|
+
if (membership && membership.phase_id === undefined) {
|
|
1350
|
+
membership.phase_id = candidate.phase_id;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1217
1353
|
} else if (record.kind === "repair_uncertainty_installed") {
|
|
1218
1354
|
const uncertainty = parseRepairUncertainty(record.payload.uncertainty);
|
|
1219
1355
|
if (uncertainty) {
|
|
@@ -1314,7 +1450,9 @@ function validateRecordPayload(row) {
|
|
|
1314
1450
|
return hasOnlyKeys(payload, ["trace_id", "rejected"]) && typeof payload.trace_id === "string" && Array.isArray(payload.rejected) && payload.rejected.every((item) => isPlainRecord(item) && hasOnlyKeys(item, ["entry_id", "reason"]) && typeof item.entry_id === "string" && typeof item.reason === "string");
|
|
1315
1451
|
case "phase_close_intent":
|
|
1316
1452
|
case "phase_closed":
|
|
1317
|
-
return hasOnlyKeys(payload, ["phase", "session_id", "task_id"]) && typeof payload.phase === "string" && !!payload.phase && typeof payload.session_id === "string" && !!payload.session_id && isOptionalString(payload.task_id);
|
|
1453
|
+
return hasOnlyKeys(payload, ["phase", "phase_id", "session_id", "task_id"]) && typeof payload.phase === "string" && !!payload.phase && (payload.phase_id === undefined || Number.isSafeInteger(payload.phase_id) && payload.phase_id >= 1) && typeof payload.session_id === "string" && !!payload.session_id && isOptionalString(payload.task_id);
|
|
1454
|
+
case "phase_id_backfilled":
|
|
1455
|
+
return hasOnlyKeys(payload, ["items"]) && Array.isArray(payload.items) && payload.items.length > 0 && payload.items.every((item) => isPlainRecord(item) && hasOnlyKeys(item, ["trace_id", "entry_id", "phase_id"]) && typeof item.trace_id === "string" && !!item.trace_id && typeof item.entry_id === "string" && !!item.entry_id && Number.isSafeInteger(item.phase_id) && item.phase_id >= 1);
|
|
1318
1456
|
case "repair_uncertainty_installed":
|
|
1319
1457
|
return hasOnlyKeys(payload, ["uncertainty"]) && parseRepairUncertainty(payload.uncertainty) !== null;
|
|
1320
1458
|
case "repair_uncertainty_cleared":
|
|
@@ -1360,6 +1498,7 @@ function validateRecord(value, state) {
|
|
|
1360
1498
|
"gate_release_committed",
|
|
1361
1499
|
"phase_close_intent",
|
|
1362
1500
|
"phase_closed",
|
|
1501
|
+
"phase_id_backfilled",
|
|
1363
1502
|
"repair_uncertainty_installed",
|
|
1364
1503
|
"repair_uncertainty_cleared",
|
|
1365
1504
|
"legacy_imported",
|
|
@@ -1816,15 +1955,28 @@ async function commitDisplayedMembership(directory, input) {
|
|
|
1816
1955
|
return runLocked(directory, input.grace_days, async (paths, state, graceDays) => {
|
|
1817
1956
|
if (!input.trace_id || !input.session_id || input.entries.length === 0)
|
|
1818
1957
|
throw new ReceiptStoreError("store_unavailable", "invalid displayed membership");
|
|
1958
|
+
if (input.phase_id !== undefined && (!Number.isSafeInteger(input.phase_id) || input.phase_id < 1))
|
|
1959
|
+
throw new ReceiptStoreError("store_unavailable", "invalid displayed membership: phase_id must be a safe integer >= 1");
|
|
1819
1960
|
const eventId = randomUUID2();
|
|
1820
1961
|
const committedAt = nowIso();
|
|
1821
1962
|
const exposureKind = normalizeExposureKind(input.exposure_kind);
|
|
1822
1963
|
const memberships = [];
|
|
1823
1964
|
const newMemberships = [];
|
|
1965
|
+
const closedLifecycle = findClosedLifecycleForPhase(state, {
|
|
1966
|
+
phase: input.phase,
|
|
1967
|
+
phase_id: input.phase_id,
|
|
1968
|
+
session_id: input.session_id,
|
|
1969
|
+
task_id: input.task_id
|
|
1970
|
+
}) ?? (input.task_id !== undefined ? findClosedLifecycleForPhase(state, {
|
|
1971
|
+
phase: input.phase,
|
|
1972
|
+
phase_id: input.phase_id,
|
|
1973
|
+
session_id: input.session_id,
|
|
1974
|
+
task_id: undefined
|
|
1975
|
+
}) : undefined);
|
|
1824
1976
|
for (const entry of input.entries) {
|
|
1825
1977
|
const existing = state.memberships.get(keyOf(input.trace_id, entry.entry_id));
|
|
1826
1978
|
if (existing) {
|
|
1827
|
-
if (existing.session_id !== input.session_id || existing.phase !== input.phase || existing.task_id !== input.task_id || existing.critical !== entry.critical || existing.cohort_id !== input.cohort_id || existing.source_link_id !== input.source_link_id || existing.exposure_kind !== exposureKind) {
|
|
1979
|
+
if (existing.session_id !== input.session_id || existing.phase !== input.phase || existing.phase_id !== undefined && input.phase_id !== undefined && existing.phase_id !== input.phase_id || existing.task_id !== input.task_id || existing.critical !== entry.critical || existing.cohort_id !== input.cohort_id || existing.source_link_id !== input.source_link_id || existing.exposure_kind !== exposureKind) {
|
|
1828
1980
|
throw new ReceiptStoreError("store_unavailable", `conflicting immutable membership for ${input.trace_id}/${entry.entry_id}`);
|
|
1829
1981
|
}
|
|
1830
1982
|
memberships.push({
|
|
@@ -1833,7 +1985,6 @@ async function commitDisplayedMembership(directory, input) {
|
|
|
1833
1985
|
});
|
|
1834
1986
|
continue;
|
|
1835
1987
|
}
|
|
1836
|
-
const closedLifecycle = state.phaseLifecycle.get(phaseLifecycleKey(input.phase ?? "", input.session_id, input.task_id)) ?? (input.task_id !== undefined ? state.phaseLifecycle.get(phaseLifecycleKey(input.phase ?? "", input.session_id)) : undefined);
|
|
1837
1988
|
if (input.phase && closedLifecycle?.closed_event_id) {
|
|
1838
1989
|
throw new ReceiptStoreError("store_unavailable", `cannot add receipt membership to closed lifecycle ${input.session_id}/${input.phase}`);
|
|
1839
1990
|
}
|
|
@@ -1842,6 +1993,7 @@ async function commitDisplayedMembership(directory, input) {
|
|
|
1842
1993
|
entry_id: entry.entry_id,
|
|
1843
1994
|
session_id: input.session_id,
|
|
1844
1995
|
phase: input.phase,
|
|
1996
|
+
phase_id: input.phase_id,
|
|
1845
1997
|
task_id: input.task_id,
|
|
1846
1998
|
agent: input.agent,
|
|
1847
1999
|
critical: entry.critical,
|
|
@@ -2060,7 +2212,7 @@ async function queryLiveMemberships(directory, filters = {}) {
|
|
|
2060
2212
|
throw new ReceiptStoreError("store_unavailable", repairUncertaintyUnavailable(overlaps).detail);
|
|
2061
2213
|
}
|
|
2062
2214
|
return {
|
|
2063
|
-
memberships: [...state.memberships.values()].filter((m) => (!filters.phase || m.phase === filters.phase) && (!filters.task_id || m.task_id === filters.task_id) && (!filters.session_id || m.session_id === filters.session_id) && (filters.include_terminal !== false || !m.terminal) && (filters.include_phase_closed !== false || !m.phase_closed_at) && (!filters.exposure_kind || m.exposure_kind === filters.exposure_kind)).map((m) => ({
|
|
2215
|
+
memberships: [...state.memberships.values()].filter((m) => (filters.phase_id === undefined || membershipMatchesPhaseIdentity(m, filters.phase ?? "", filters.phase_id)) && (!filters.phase || filters.phase_id !== undefined || m.phase === filters.phase) && (!filters.task_id || m.task_id === filters.task_id) && (!filters.session_id || m.session_id === filters.session_id) && (filters.include_terminal !== false || !m.terminal) && (filters.include_phase_closed !== false || !m.phase_closed_at) && (!filters.exposure_kind || m.exposure_kind === filters.exposure_kind)).map((m) => ({
|
|
2064
2216
|
...m,
|
|
2065
2217
|
terminal: m.terminal ? { ...m.terminal } : undefined
|
|
2066
2218
|
}))
|
|
@@ -2097,11 +2249,12 @@ async function queryHistoricalOutcomes(directory, entryIds, graceDays) {
|
|
|
2097
2249
|
return { memberships: [...memberships.values()] };
|
|
2098
2250
|
}, { compact: false, writeSnapshot: false });
|
|
2099
2251
|
}
|
|
2100
|
-
async function phaseTransition(directory, phase, kind, sessionId, taskId, graceDays) {
|
|
2252
|
+
async function phaseTransition(directory, phase, kind, sessionId, taskId, graceDays, phaseId) {
|
|
2101
2253
|
return runLocked(directory, graceDays, async (paths, state) => {
|
|
2102
2254
|
if (!phase.trim()) {
|
|
2103
2255
|
throw new ReceiptStoreError("store_unavailable", "phase is required");
|
|
2104
2256
|
}
|
|
2257
|
+
const effectivePhaseId = phaseId !== undefined && Number.isSafeInteger(phaseId) && phaseId >= 1 ? phaseId : undefined;
|
|
2105
2258
|
const scopeKeys = new Map;
|
|
2106
2259
|
const addScope = (scopeSessionId, scopeTaskId) => {
|
|
2107
2260
|
if (sessionId !== undefined && scopeSessionId !== sessionId || taskId !== undefined && scopeTaskId !== taskId)
|
|
@@ -2112,7 +2265,7 @@ async function phaseTransition(directory, phase, kind, sessionId, taskId, graceD
|
|
|
2112
2265
|
});
|
|
2113
2266
|
};
|
|
2114
2267
|
for (const membership of state.memberships.values()) {
|
|
2115
|
-
if (membership
|
|
2268
|
+
if (membershipMatchesPhaseIdentity(membership, phase, effectivePhaseId))
|
|
2116
2269
|
addScope(membership.session_id, membership.task_id);
|
|
2117
2270
|
}
|
|
2118
2271
|
for (const trace of state.emptyTraces.values()) {
|
|
@@ -2120,9 +2273,20 @@ async function phaseTransition(directory, phase, kind, sessionId, taskId, graceD
|
|
|
2120
2273
|
addScope(trace.session_id, trace.task_id);
|
|
2121
2274
|
}
|
|
2122
2275
|
for (const lifecycle of state.phaseLifecycle.values()) {
|
|
2123
|
-
if (lifecycle.phase === phase)
|
|
2276
|
+
if (effectivePhaseId !== undefined ? (lifecycle.phase_id === effectivePhaseId || lifecycle.phase_id === undefined && extractPhaseIdFromLabel(lifecycle.phase) === effectivePhaseId) && (sessionId === undefined || lifecycle.session_id === sessionId) : lifecycle.phase === phase)
|
|
2124
2277
|
addScope(lifecycle.session_id, lifecycle.task_id);
|
|
2125
2278
|
}
|
|
2279
|
+
if (scopeKeys.size === 0 && sessionId !== undefined) {
|
|
2280
|
+
const closedLifecycle = findClosedLifecycleForPhase(state, {
|
|
2281
|
+
phase,
|
|
2282
|
+
phase_id: effectivePhaseId,
|
|
2283
|
+
session_id: sessionId,
|
|
2284
|
+
task_id: taskId
|
|
2285
|
+
});
|
|
2286
|
+
if (closedLifecycle) {
|
|
2287
|
+
addScope(closedLifecycle.session_id, closedLifecycle.task_id);
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2126
2290
|
if (scopeKeys.size === 0 && sessionId !== undefined) {
|
|
2127
2291
|
addScope(sessionId, taskId);
|
|
2128
2292
|
}
|
|
@@ -2134,14 +2298,45 @@ async function phaseTransition(directory, phase, kind, sessionId, taskId, graceD
|
|
|
2134
2298
|
}
|
|
2135
2299
|
let eventId = "";
|
|
2136
2300
|
for (const [key, scope] of scopeKeys) {
|
|
2137
|
-
const lifecycle = state
|
|
2301
|
+
const lifecycle = findClosedLifecycleForPhase(state, {
|
|
2302
|
+
phase,
|
|
2303
|
+
phase_id: effectivePhaseId,
|
|
2304
|
+
session_id: scope.session_id,
|
|
2305
|
+
task_id: scope.task_id
|
|
2306
|
+
}) ?? state.phaseLifecycle.get(key);
|
|
2138
2307
|
const existingEventId = kind === "phase_close_intent" ? lifecycle?.intent_event_id : lifecycle?.closed_event_id;
|
|
2139
2308
|
if (existingEventId) {
|
|
2140
2309
|
eventId ||= existingEventId;
|
|
2141
2310
|
continue;
|
|
2142
2311
|
}
|
|
2143
|
-
const row = makeRecord(state, kind, {
|
|
2312
|
+
const row = makeRecord(state, kind, {
|
|
2313
|
+
phase,
|
|
2314
|
+
...effectivePhaseId !== undefined ? { phase_id: effectivePhaseId } : {},
|
|
2315
|
+
...scope
|
|
2316
|
+
});
|
|
2144
2317
|
await appendRecord(paths, state, row);
|
|
2318
|
+
if (lifecycle === undefined) {
|
|
2319
|
+
state.phaseLifecycle.set(key, {
|
|
2320
|
+
phase,
|
|
2321
|
+
...effectivePhaseId !== undefined ? { phase_id: effectivePhaseId } : {},
|
|
2322
|
+
session_id: scope.session_id,
|
|
2323
|
+
task_id: scope.task_id,
|
|
2324
|
+
intent_event_id: kind === "phase_close_intent" ? row.event_id : undefined,
|
|
2325
|
+
intent_at: kind === "phase_close_intent" ? row.timestamp : undefined,
|
|
2326
|
+
closed_event_id: kind === "phase_closed" ? row.event_id : undefined,
|
|
2327
|
+
closed_at: kind === "phase_closed" ? row.timestamp : undefined
|
|
2328
|
+
});
|
|
2329
|
+
} else {
|
|
2330
|
+
if (lifecycle.phase_id === undefined && effectivePhaseId !== undefined)
|
|
2331
|
+
lifecycle.phase_id = effectivePhaseId;
|
|
2332
|
+
if (kind === "phase_close_intent" && !lifecycle.intent_event_id) {
|
|
2333
|
+
lifecycle.intent_event_id = row.event_id;
|
|
2334
|
+
lifecycle.intent_at = row.timestamp;
|
|
2335
|
+
} else if (kind === "phase_closed" && !lifecycle.closed_event_id) {
|
|
2336
|
+
lifecycle.closed_event_id = row.event_id;
|
|
2337
|
+
lifecycle.closed_at = row.timestamp;
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2145
2340
|
eventId ||= row.event_id;
|
|
2146
2341
|
}
|
|
2147
2342
|
if (kind === "phase_closed")
|
|
@@ -2149,12 +2344,12 @@ async function phaseTransition(directory, phase, kind, sessionId, taskId, graceD
|
|
|
2149
2344
|
return { event_id: eventId };
|
|
2150
2345
|
});
|
|
2151
2346
|
}
|
|
2152
|
-
var recordPhaseCloseIntent = (directory, phase, sessionId, taskId) => phaseTransition(directory, phase, "phase_close_intent", sessionId, taskId);
|
|
2153
|
-
var commitPhaseClosed = (directory, phase, sessionId, taskId) => phaseTransition(directory, phase, "phase_closed", sessionId, taskId);
|
|
2154
|
-
async function reconcilePhaseClose(directory, phase, durablePlanClosed, sessionId, taskId) {
|
|
2347
|
+
var recordPhaseCloseIntent = (directory, phase, sessionId, taskId, phaseId) => phaseTransition(directory, phase, "phase_close_intent", sessionId, taskId, undefined, phaseId);
|
|
2348
|
+
var commitPhaseClosed = (directory, phase, sessionId, taskId, phaseId) => phaseTransition(directory, phase, "phase_closed", sessionId, taskId, undefined, phaseId);
|
|
2349
|
+
async function reconcilePhaseClose(directory, phase, durablePlanClosed, sessionId, taskId, phaseId) {
|
|
2155
2350
|
if (!durablePlanClosed)
|
|
2156
2351
|
return { ok: true, reconciled: false };
|
|
2157
|
-
const result = await commitPhaseClosed(directory, phase, sessionId, taskId);
|
|
2352
|
+
const result = await commitPhaseClosed(directory, phase, sessionId, taskId, phaseId);
|
|
2158
2353
|
return result.ok ? { ok: true, reconciled: true } : result;
|
|
2159
2354
|
}
|
|
2160
2355
|
async function compactIfNeeded(paths, state) {
|
|
@@ -2258,729 +2453,85 @@ function serializeArchive(summaries) {
|
|
|
2258
2453
|
`)}
|
|
2259
2454
|
` : "";
|
|
2260
2455
|
}
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
maxArchiveRecords: MAX_ARCHIVE_RECORDS,
|
|
2269
|
-
maxArchiveBytes: MAX_ARCHIVE_BYTES,
|
|
2270
|
-
maxRepairQuarantineRecords: MAX_REPAIR_QUARANTINE_RECORDS
|
|
2271
|
-
};
|
|
2272
|
-
|
|
2273
|
-
// src/hooks/knowledge-events.ts
|
|
2274
|
-
var KNOWLEDGE_EVENT_SCHEMA_VERSION = 1;
|
|
2275
|
-
var MAX_EVENT_LOG_ENTRIES = 5000;
|
|
2276
|
-
var counterRollupCache = new Map;
|
|
2277
|
-
var MAX_COUNTER_ROLLUP_CACHE_DIRS = 32;
|
|
2278
|
-
var RECEIPT_EVENT_TYPES = new Set([
|
|
2279
|
-
"acknowledged",
|
|
2280
|
-
"applied",
|
|
2281
|
-
"ignored",
|
|
2282
|
-
"contradicted",
|
|
2283
|
-
"violated",
|
|
2284
|
-
"n_a",
|
|
2285
|
-
"override",
|
|
2286
|
-
"no_relevant"
|
|
2287
|
-
]);
|
|
2288
|
-
function resolveKnowledgeEventsPath(directory) {
|
|
2289
|
-
return path2.join(resolveKnowledgeStoreDir(directory), "knowledge-events.jsonl");
|
|
2290
|
-
}
|
|
2291
|
-
function resolveKnowledgeCounterBaselinePath(directory) {
|
|
2292
|
-
return path2.join(resolveKnowledgeStoreDir(directory), "knowledge-counter-baseline.json");
|
|
2293
|
-
}
|
|
2294
|
-
var resolveHiveEventsPath2 = resolveHiveEventsPath;
|
|
2295
|
-
function resolveLegacyApplicationLogPath(directory) {
|
|
2296
|
-
return path2.join(resolveKnowledgeStoreDir(directory), "knowledge-application.jsonl");
|
|
2297
|
-
}
|
|
2298
|
-
function newTraceId() {
|
|
2299
|
-
return randomUUID3();
|
|
2300
|
-
}
|
|
2301
|
-
function newEventId() {
|
|
2302
|
-
return randomUUID3();
|
|
2303
|
-
}
|
|
2304
|
-
function withDefaults(event) {
|
|
2305
|
-
return {
|
|
2306
|
-
schema_version: KNOWLEDGE_EVENT_SCHEMA_VERSION,
|
|
2307
|
-
...event,
|
|
2308
|
-
event_id: event.event_id || newEventId(),
|
|
2309
|
-
timestamp: event.timestamp || new Date().toISOString()
|
|
2310
|
-
};
|
|
2311
|
-
}
|
|
2312
|
-
async function appendKnowledgeEvent(directory, event) {
|
|
2313
|
-
const [populated] = await appendKnowledgeEventsBatch(directory, [event]);
|
|
2314
|
-
return populated;
|
|
2315
|
-
}
|
|
2316
|
-
async function appendKnowledgeEventsBatch(directory, events) {
|
|
2317
|
-
if (events.length === 0)
|
|
2318
|
-
return [];
|
|
2319
|
-
const populated = events.map(withDefaults);
|
|
2320
|
-
const filePath = resolveKnowledgeEventsPath(directory);
|
|
2321
|
-
const dirPath = path2.dirname(filePath);
|
|
2322
|
-
await mkdir2(dirPath, { recursive: true });
|
|
2323
|
-
let release;
|
|
2324
|
-
try {
|
|
2325
|
-
release = await import_proper_lockfile.default.lock(dirPath, {
|
|
2326
|
-
retries: { retries: 200, minTimeout: 10, maxTimeout: 100 }
|
|
2327
|
-
});
|
|
2328
|
-
await appendFile(filePath, `${populated.map((e) => JSON.stringify(e)).join(`
|
|
2329
|
-
`)}
|
|
2330
|
-
`, "utf-8");
|
|
2331
|
-
try {
|
|
2332
|
-
const content = await readFile2(filePath, "utf-8");
|
|
2333
|
-
const lines = content.split(`
|
|
2334
|
-
`).filter((line) => line.trim().length > 0);
|
|
2335
|
-
if (lines.length > MAX_EVENT_LOG_ENTRIES) {
|
|
2336
|
-
const evicted = lines.slice(0, lines.length - MAX_EVENT_LOG_ENTRIES);
|
|
2337
|
-
const trimmed = lines.slice(lines.length - MAX_EVENT_LOG_ENTRIES);
|
|
2338
|
-
await foldEvictedEventsIntoBaseline(directory, evicted, filePath);
|
|
2339
|
-
await atomicWriteFile(filePath, `${trimmed.join(`
|
|
2340
|
-
`)}
|
|
2341
|
-
`);
|
|
2342
|
-
}
|
|
2343
|
-
} catch (err) {
|
|
2344
|
-
warn(`[knowledge-events] local cap trim failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
2345
|
-
}
|
|
2346
|
-
} finally {
|
|
2347
|
-
if (release)
|
|
2348
|
-
await release().catch(() => {});
|
|
2349
|
-
}
|
|
2350
|
-
return populated;
|
|
2351
|
-
}
|
|
2352
|
-
async function recordKnowledgeEvent(directory, event) {
|
|
2353
|
-
try {
|
|
2354
|
-
return await appendKnowledgeEvent(directory, event);
|
|
2355
|
-
} catch (err) {
|
|
2356
|
-
warn(`[knowledge-events] recordKnowledgeEvent failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2357
|
-
return null;
|
|
2358
|
-
}
|
|
2359
|
-
}
|
|
2360
|
-
async function recordKnowledgeEventsBatch(directory, events) {
|
|
2361
|
-
try {
|
|
2362
|
-
return await appendKnowledgeEventsBatch(directory, events);
|
|
2363
|
-
} catch (err) {
|
|
2364
|
-
warn(`[knowledge-events] recordKnowledgeEventsBatch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2365
|
-
return null;
|
|
2366
|
-
}
|
|
2367
|
-
}
|
|
2368
|
-
async function appendHiveKnowledgeEvent(event) {
|
|
2369
|
-
const populated = withDefaults(event);
|
|
2370
|
-
const filePath = resolveHiveEventsPath2();
|
|
2371
|
-
const dirPath = path2.dirname(filePath);
|
|
2372
|
-
await mkdir2(dirPath, { recursive: true });
|
|
2373
|
-
let release;
|
|
2374
|
-
try {
|
|
2375
|
-
release = await import_proper_lockfile.default.lock(dirPath, {
|
|
2376
|
-
retries: { retries: 200, minTimeout: 10, maxTimeout: 100 },
|
|
2377
|
-
stale: 5000
|
|
2378
|
-
});
|
|
2379
|
-
await appendFile(filePath, `${JSON.stringify(populated)}
|
|
2380
|
-
`, "utf-8");
|
|
2381
|
-
try {
|
|
2382
|
-
const content = await readFile2(filePath, "utf-8");
|
|
2383
|
-
const lines = content.split(`
|
|
2384
|
-
`).filter((line) => line.trim().length > 0);
|
|
2385
|
-
if (lines.length > MAX_EVENT_LOG_ENTRIES) {
|
|
2386
|
-
const trimmed = lines.slice(lines.length - MAX_EVENT_LOG_ENTRIES);
|
|
2387
|
-
await atomicWriteFile(filePath, `${trimmed.join(`
|
|
2388
|
-
`)}
|
|
2389
|
-
`);
|
|
2390
|
-
}
|
|
2391
|
-
} catch (err) {
|
|
2392
|
-
warn(`[knowledge-events] hive cap trim failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
2393
|
-
}
|
|
2394
|
-
} finally {
|
|
2395
|
-
if (release)
|
|
2396
|
-
await release().catch(() => {});
|
|
2397
|
-
}
|
|
2398
|
-
return populated;
|
|
2399
|
-
}
|
|
2400
|
-
async function recordHiveKnowledgeEvent(event) {
|
|
2401
|
-
try {
|
|
2402
|
-
return await appendHiveKnowledgeEvent(event);
|
|
2403
|
-
} catch (err) {
|
|
2404
|
-
warn(`[knowledge-events] recordHiveKnowledgeEvent failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2405
|
-
return null;
|
|
2406
|
-
}
|
|
2407
|
-
}
|
|
2408
|
-
async function readKnowledgeEvents(directory, maxEvents) {
|
|
2409
|
-
const filePath = resolveKnowledgeEventsPath(directory);
|
|
2410
|
-
if (!existsSync3(filePath))
|
|
2411
|
-
return [];
|
|
2412
|
-
const content = await readFile2(filePath, "utf-8");
|
|
2413
|
-
const out = [];
|
|
2414
|
-
const max = maxEvents !== undefined && maxEvents > 0 ? maxEvents : Infinity;
|
|
2415
|
-
for (const line of content.split(`
|
|
2416
|
-
`)) {
|
|
2417
|
-
if (out.length >= max)
|
|
2418
|
-
break;
|
|
2419
|
-
const trimmed = line.trim();
|
|
2420
|
-
if (!trimmed)
|
|
2421
|
-
continue;
|
|
2422
|
-
try {
|
|
2423
|
-
out.push(JSON.parse(trimmed));
|
|
2424
|
-
} catch {
|
|
2425
|
-
warn(`[knowledge-events] Skipping corrupted JSONL line in ${filePath}: ${trimmed.slice(0, 80)}`);
|
|
2426
|
-
}
|
|
2427
|
-
}
|
|
2428
|
-
return out;
|
|
2429
|
-
}
|
|
2430
|
-
async function readHiveKnowledgeEvents() {
|
|
2431
|
-
const filePath = resolveHiveEventsPath2();
|
|
2432
|
-
if (!existsSync3(filePath))
|
|
2433
|
-
return [];
|
|
2434
|
-
const content = await readFile2(filePath, "utf-8");
|
|
2435
|
-
return parseEventLines(content.split(`
|
|
2436
|
-
`), filePath);
|
|
2437
|
-
}
|
|
2438
|
-
async function readLegacyApplicationRecords(directory) {
|
|
2439
|
-
const filePath = resolveLegacyApplicationLogPath(directory);
|
|
2440
|
-
if (!existsSync3(filePath))
|
|
2441
|
-
return [];
|
|
2442
|
-
const content = await readFile2(filePath, "utf-8");
|
|
2443
|
-
const out = [];
|
|
2444
|
-
for (const line of content.split(`
|
|
2445
|
-
`)) {
|
|
2446
|
-
const trimmed = line.trim();
|
|
2447
|
-
if (!trimmed)
|
|
2448
|
-
continue;
|
|
2449
|
-
try {
|
|
2450
|
-
out.push(JSON.parse(trimmed));
|
|
2451
|
-
} catch {
|
|
2452
|
-
warn(`[knowledge-events] Skipping corrupted JSONL line in ${filePath}: ${trimmed.slice(0, 80)}`);
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2455
|
-
return out;
|
|
2456
|
-
}
|
|
2457
|
-
var MAX_VIOLATION_TIMESTAMPS = 10;
|
|
2458
|
-
function emptyRollup() {
|
|
2459
|
-
return {
|
|
2460
|
-
shown_count: 0,
|
|
2461
|
-
acknowledged_count: 0,
|
|
2462
|
-
applied_explicit_count: 0,
|
|
2463
|
-
ignored_count: 0,
|
|
2464
|
-
violated_count: 0,
|
|
2465
|
-
contradicted_count: 0,
|
|
2466
|
-
n_a_count: 0,
|
|
2467
|
-
succeeded_after_shown_count: 0,
|
|
2468
|
-
failed_after_shown_count: 0,
|
|
2469
|
-
partial_after_shown_count: 0,
|
|
2470
|
-
violation_timestamps: []
|
|
2471
|
-
};
|
|
2472
|
-
}
|
|
2473
|
-
function cloneRollup(input) {
|
|
2474
|
-
return {
|
|
2475
|
-
...emptyRollup(),
|
|
2476
|
-
...input,
|
|
2477
|
-
violation_timestamps: [...input.violation_timestamps ?? []]
|
|
2478
|
-
};
|
|
2479
|
-
}
|
|
2480
|
-
function cloneRollupMap(input) {
|
|
2481
|
-
const out = new Map;
|
|
2482
|
-
for (const [id, rollup] of input) {
|
|
2483
|
-
out.set(id, cloneRollup(rollup));
|
|
2484
|
-
}
|
|
2485
|
-
return out;
|
|
2486
|
-
}
|
|
2487
|
-
function normalizeRollupTimestamps(rollup) {
|
|
2488
|
-
if (rollup.violation_timestamps.length > 1) {
|
|
2489
|
-
rollup.violation_timestamps.sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
|
|
2490
|
-
}
|
|
2491
|
-
if (rollup.violation_timestamps.length > MAX_VIOLATION_TIMESTAMPS) {
|
|
2492
|
-
rollup.violation_timestamps = rollup.violation_timestamps.slice(0, MAX_VIOLATION_TIMESTAMPS);
|
|
2493
|
-
}
|
|
2494
|
-
return rollup;
|
|
2495
|
-
}
|
|
2496
|
-
function mergeRollupInto(target, source) {
|
|
2497
|
-
target.shown_count += source.shown_count ?? 0;
|
|
2498
|
-
target.acknowledged_count += source.acknowledged_count ?? 0;
|
|
2499
|
-
target.applied_explicit_count += source.applied_explicit_count ?? 0;
|
|
2500
|
-
target.ignored_count += source.ignored_count ?? 0;
|
|
2501
|
-
target.violated_count += source.violated_count ?? 0;
|
|
2502
|
-
target.contradicted_count += source.contradicted_count ?? 0;
|
|
2503
|
-
target.n_a_count += source.n_a_count ?? 0;
|
|
2504
|
-
target.succeeded_after_shown_count += source.succeeded_after_shown_count ?? 0;
|
|
2505
|
-
target.failed_after_shown_count += source.failed_after_shown_count ?? 0;
|
|
2506
|
-
target.partial_after_shown_count += source.partial_after_shown_count ?? 0;
|
|
2507
|
-
if (source.last_applied_at) {
|
|
2508
|
-
target.last_applied_at = maxIso(target.last_applied_at, source.last_applied_at);
|
|
2509
|
-
}
|
|
2510
|
-
if (source.last_acknowledged_at) {
|
|
2511
|
-
target.last_acknowledged_at = maxIso(target.last_acknowledged_at, source.last_acknowledged_at);
|
|
2512
|
-
}
|
|
2513
|
-
target.violation_timestamps.push(...source.violation_timestamps ?? []);
|
|
2514
|
-
normalizeRollupTimestamps(target);
|
|
2515
|
-
}
|
|
2516
|
-
function get(map, id) {
|
|
2517
|
-
let r = map.get(id);
|
|
2518
|
-
if (!r) {
|
|
2519
|
-
r = emptyRollup();
|
|
2520
|
-
map.set(id, r);
|
|
2521
|
-
}
|
|
2522
|
-
return r;
|
|
2523
|
-
}
|
|
2524
|
-
function maxIso(current, candidate) {
|
|
2525
|
-
if (!current)
|
|
2526
|
-
return candidate;
|
|
2527
|
-
return candidate > current ? candidate : current;
|
|
2528
|
-
}
|
|
2529
|
-
async function readCounterBaseline(directory) {
|
|
2530
|
-
const filePath = resolveKnowledgeCounterBaselinePath(directory);
|
|
2531
|
-
if (!existsSync3(filePath))
|
|
2532
|
-
return new Map;
|
|
2533
|
-
let raw;
|
|
2534
|
-
try {
|
|
2535
|
-
raw = JSON.parse(await readFile2(filePath, "utf-8"));
|
|
2536
|
-
} catch (err) {
|
|
2537
|
-
warn(`[knowledge-events] corrupted counter baseline at ${filePath}; ignoring folded baseline and replaying live events only: ${err instanceof Error ? err.message : String(err)}`);
|
|
2538
|
-
return new Map;
|
|
2539
|
-
}
|
|
2540
|
-
const map = new Map;
|
|
2541
|
-
for (const [id, rollup] of Object.entries(raw)) {
|
|
2542
|
-
map.set(id, normalizeRollupTimestamps(cloneRollup(rollup)));
|
|
2543
|
-
}
|
|
2544
|
-
return map;
|
|
2545
|
-
}
|
|
2546
|
-
async function writeCounterBaseline(directory, baseline) {
|
|
2547
|
-
const filePath = resolveKnowledgeCounterBaselinePath(directory);
|
|
2548
|
-
const out = {};
|
|
2549
|
-
for (const [id, rollup] of [...baseline.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
2550
|
-
out[id] = normalizeRollupTimestamps(cloneRollup(rollup));
|
|
2551
|
-
}
|
|
2552
|
-
await atomicWriteFile(filePath, `${JSON.stringify(out, null, 2)}
|
|
2553
|
-
`);
|
|
2554
|
-
}
|
|
2555
|
-
async function statCacheKey(filePath) {
|
|
2556
|
-
try {
|
|
2557
|
-
const fileStat = await stat2(filePath);
|
|
2558
|
-
return `${fileStat.mtimeMs}:${fileStat.ctimeMs}:${fileStat.size}`;
|
|
2559
|
-
} catch (err) {
|
|
2560
|
-
if (err?.code === "ENOENT")
|
|
2561
|
-
return "missing";
|
|
2562
|
-
throw err;
|
|
2563
|
-
}
|
|
2564
|
-
}
|
|
2565
|
-
async function buildCounterRollupCacheKey(directory) {
|
|
2566
|
-
const [eventsKey, legacyKey, baselineKey] = await Promise.all([
|
|
2567
|
-
statCacheKey(resolveKnowledgeEventsPath(directory)),
|
|
2568
|
-
statCacheKey(resolveLegacyApplicationLogPath(directory)),
|
|
2569
|
-
statCacheKey(resolveKnowledgeCounterBaselinePath(directory))
|
|
2570
|
-
]);
|
|
2571
|
-
return `${eventsKey}|${legacyKey}|${baselineKey}`;
|
|
2572
|
-
}
|
|
2573
|
-
function setCounterRollupCache(directory, key, rollups) {
|
|
2574
|
-
const cacheKey = canonicalRootKey(directory);
|
|
2575
|
-
if (counterRollupCache.has(cacheKey)) {
|
|
2576
|
-
counterRollupCache.delete(cacheKey);
|
|
2577
|
-
}
|
|
2578
|
-
counterRollupCache.set(cacheKey, {
|
|
2579
|
-
key,
|
|
2580
|
-
rollups: cloneRollupMap(rollups)
|
|
2581
|
-
});
|
|
2582
|
-
while (counterRollupCache.size > MAX_COUNTER_ROLLUP_CACHE_DIRS) {
|
|
2583
|
-
const oldestKey = counterRollupCache.keys().next().value;
|
|
2584
|
-
if (oldestKey === undefined)
|
|
2585
|
-
break;
|
|
2586
|
-
counterRollupCache.delete(oldestKey);
|
|
2587
|
-
}
|
|
2588
|
-
}
|
|
2589
|
-
function parseEventLines(lines, filePath) {
|
|
2590
|
-
const out = [];
|
|
2591
|
-
for (const line of lines) {
|
|
2592
|
-
const trimmed = line.trim();
|
|
2593
|
-
if (!trimmed)
|
|
2594
|
-
continue;
|
|
2595
|
-
try {
|
|
2596
|
-
out.push(JSON.parse(trimmed));
|
|
2597
|
-
} catch {
|
|
2598
|
-
warn(`[knowledge-events] Skipping corrupted JSONL line in ${filePath}: ${trimmed.slice(0, 80)}`);
|
|
2599
|
-
}
|
|
2600
|
-
}
|
|
2601
|
-
return out;
|
|
2602
|
-
}
|
|
2603
|
-
async function foldEvictedEventsIntoBaseline(directory, evictedLines, filePath) {
|
|
2604
|
-
const evictedEvents = parseEventLines(evictedLines, filePath);
|
|
2605
|
-
if (evictedEvents.length === 0)
|
|
2606
|
-
return;
|
|
2607
|
-
const baseline = await readCounterBaseline(directory);
|
|
2608
|
-
for (const [id, rollup] of recomputeCounters(evictedEvents)) {
|
|
2609
|
-
mergeRollupInto(get(baseline, id), rollup);
|
|
2610
|
-
}
|
|
2611
|
-
await writeCounterBaseline(directory, baseline);
|
|
2612
|
-
}
|
|
2613
|
-
function recomputeCounters(events, legacyRecords = [], baseline = new Map) {
|
|
2614
|
-
const map = new Map;
|
|
2615
|
-
const retrievedIds = new Set;
|
|
2616
|
-
for (const [id, rollup] of baseline) {
|
|
2617
|
-
map.set(id, cloneRollup(rollup));
|
|
2618
|
-
if ((rollup.shown_count ?? 0) > 0)
|
|
2619
|
-
retrievedIds.add(id);
|
|
2620
|
-
}
|
|
2621
|
-
for (const e of events) {
|
|
2622
|
-
switch (e.type) {
|
|
2623
|
-
case "retrieved": {
|
|
2624
|
-
for (const id of e.result_ids) {
|
|
2625
|
-
retrievedIds.add(id);
|
|
2626
|
-
get(map, id).shown_count += 1;
|
|
2627
|
-
}
|
|
2628
|
-
break;
|
|
2629
|
-
}
|
|
2630
|
-
case "acknowledged": {
|
|
2631
|
-
if (!e.knowledge_id)
|
|
2632
|
-
break;
|
|
2633
|
-
const r = get(map, e.knowledge_id);
|
|
2634
|
-
r.acknowledged_count += 1;
|
|
2635
|
-
r.last_acknowledged_at = maxIso(r.last_acknowledged_at, e.timestamp);
|
|
2636
|
-
break;
|
|
2637
|
-
}
|
|
2638
|
-
case "applied": {
|
|
2639
|
-
if (!e.knowledge_id)
|
|
2640
|
-
break;
|
|
2641
|
-
const r = get(map, e.knowledge_id);
|
|
2642
|
-
r.applied_explicit_count += 1;
|
|
2643
|
-
r.last_applied_at = maxIso(r.last_applied_at, e.timestamp);
|
|
2644
|
-
break;
|
|
2645
|
-
}
|
|
2646
|
-
case "ignored":
|
|
2647
|
-
if (!e.knowledge_id)
|
|
2648
|
-
break;
|
|
2649
|
-
get(map, e.knowledge_id).ignored_count += 1;
|
|
2650
|
-
break;
|
|
2651
|
-
case "violated": {
|
|
2652
|
-
if (!e.knowledge_id)
|
|
2653
|
-
break;
|
|
2654
|
-
const r = get(map, e.knowledge_id);
|
|
2655
|
-
r.violated_count += 1;
|
|
2656
|
-
r.violation_timestamps.push(e.timestamp);
|
|
2657
|
-
break;
|
|
2658
|
-
}
|
|
2659
|
-
case "contradicted":
|
|
2660
|
-
if (!e.knowledge_id)
|
|
2661
|
-
break;
|
|
2662
|
-
get(map, e.knowledge_id).contradicted_count += 1;
|
|
2663
|
-
break;
|
|
2664
|
-
case "n_a":
|
|
2665
|
-
if (!e.knowledge_id)
|
|
2666
|
-
break;
|
|
2667
|
-
get(map, e.knowledge_id).n_a_count += 1;
|
|
2668
|
-
break;
|
|
2669
|
-
case "no_relevant":
|
|
2670
|
-
break;
|
|
2671
|
-
case "unacknowledged":
|
|
2672
|
-
break;
|
|
2673
|
-
case "outcome": {
|
|
2674
|
-
if (!e.knowledge_id)
|
|
2675
|
-
break;
|
|
2676
|
-
const r = get(map, e.knowledge_id);
|
|
2677
|
-
if (e.outcome === "success")
|
|
2678
|
-
r.succeeded_after_shown_count += 1;
|
|
2679
|
-
else if (e.outcome === "failure")
|
|
2680
|
-
r.failed_after_shown_count += 1;
|
|
2681
|
-
else if (e.outcome === "partial")
|
|
2682
|
-
r.partial_after_shown_count += 1;
|
|
2683
|
-
break;
|
|
2684
|
-
}
|
|
2685
|
-
}
|
|
2686
|
-
}
|
|
2687
|
-
for (const rec of legacyRecords) {
|
|
2688
|
-
const r = get(map, rec.knowledgeId);
|
|
2689
|
-
switch (rec.result) {
|
|
2690
|
-
case "shown":
|
|
2691
|
-
if (!retrievedIds.has(rec.knowledgeId))
|
|
2692
|
-
r.shown_count += 1;
|
|
2693
|
-
break;
|
|
2694
|
-
case "acknowledged":
|
|
2695
|
-
r.acknowledged_count += 1;
|
|
2696
|
-
r.last_acknowledged_at = maxIso(r.last_acknowledged_at, rec.timestamp);
|
|
2697
|
-
break;
|
|
2698
|
-
case "applied":
|
|
2699
|
-
r.applied_explicit_count += 1;
|
|
2700
|
-
r.last_applied_at = maxIso(r.last_applied_at, rec.timestamp);
|
|
2701
|
-
break;
|
|
2702
|
-
case "ignored":
|
|
2703
|
-
r.ignored_count += 1;
|
|
2704
|
-
break;
|
|
2705
|
-
case "contradicted":
|
|
2706
|
-
r.contradicted_count += 1;
|
|
2707
|
-
break;
|
|
2708
|
-
case "violated":
|
|
2709
|
-
r.violated_count += 1;
|
|
2710
|
-
r.violation_timestamps.push(rec.timestamp);
|
|
2711
|
-
break;
|
|
2712
|
-
}
|
|
2713
|
-
}
|
|
2714
|
-
for (const r of map.values()) {
|
|
2715
|
-
normalizeRollupTimestamps(r);
|
|
2716
|
-
}
|
|
2717
|
-
return map;
|
|
2718
|
-
}
|
|
2719
|
-
function countViolationsInWindow(timestamps, windowDays, now = new Date) {
|
|
2720
|
-
const cutoff = now.getTime() - windowDays * 24 * 60 * 60 * 1000;
|
|
2721
|
-
let count = 0;
|
|
2722
|
-
for (const ts of timestamps) {
|
|
2723
|
-
const t = Date.parse(ts);
|
|
2724
|
-
if (!Number.isNaN(t) && t >= cutoff)
|
|
2725
|
-
count += 1;
|
|
2726
|
-
}
|
|
2727
|
-
return count;
|
|
2728
|
-
}
|
|
2729
|
-
function countEmptyTraceTerminals(events) {
|
|
2730
|
-
let n = 0;
|
|
2731
|
-
for (const e of events) {
|
|
2732
|
-
if (e.type === "no_relevant")
|
|
2733
|
-
n += 1;
|
|
2734
|
-
}
|
|
2735
|
-
return n;
|
|
2736
|
-
}
|
|
2737
|
-
async function countEntryViolationsInWindow(directory, entryId, windowDays, now = new Date) {
|
|
2738
|
-
try {
|
|
2739
|
-
const cutoff = now.getTime() - windowDays * 24 * 60 * 60 * 1000;
|
|
2740
|
-
const [events, legacyRecords] = await Promise.all([
|
|
2741
|
-
readKnowledgeEvents(directory),
|
|
2742
|
-
readLegacyApplicationRecords(directory)
|
|
2743
|
-
]);
|
|
2744
|
-
let count = 0;
|
|
2745
|
-
for (const e of events) {
|
|
2746
|
-
if (e.type !== "violated" || e.knowledge_id !== entryId)
|
|
2456
|
+
async function inspectMembershipPhaseIds(directory) {
|
|
2457
|
+
return runLocked(directory, undefined, async (_paths, state) => {
|
|
2458
|
+
let backfillable = 0;
|
|
2459
|
+
const unparsable = [];
|
|
2460
|
+
let missing = 0;
|
|
2461
|
+
for (const membership of state.memberships.values()) {
|
|
2462
|
+
if (membership.phase_id !== undefined)
|
|
2747
2463
|
continue;
|
|
2748
|
-
|
|
2749
|
-
if (
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2464
|
+
missing++;
|
|
2465
|
+
if (membership.phase === undefined) {
|
|
2466
|
+
unparsable.push({
|
|
2467
|
+
trace_id: membership.trace_id,
|
|
2468
|
+
entry_id: membership.entry_id
|
|
2469
|
+
});
|
|
2754
2470
|
continue;
|
|
2755
|
-
|
|
2756
|
-
if (
|
|
2757
|
-
|
|
2471
|
+
}
|
|
2472
|
+
if (extractPhaseIdFromLabel(membership.phase) !== undefined) {
|
|
2473
|
+
backfillable++;
|
|
2474
|
+
} else {
|
|
2475
|
+
unparsable.push({
|
|
2476
|
+
trace_id: membership.trace_id,
|
|
2477
|
+
entry_id: membership.entry_id,
|
|
2478
|
+
label: membership.phase
|
|
2479
|
+
});
|
|
2480
|
+
}
|
|
2758
2481
|
}
|
|
2759
|
-
return
|
|
2760
|
-
}
|
|
2761
|
-
return 0;
|
|
2762
|
-
}
|
|
2482
|
+
return { missing, backfillable, unparsable };
|
|
2483
|
+
}, { compact: false, writeSnapshot: false });
|
|
2763
2484
|
}
|
|
2764
|
-
async function
|
|
2765
|
-
|
|
2766
|
-
const
|
|
2767
|
-
const
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
if (e.type !== "contradicted" || e.knowledge_id !== entryId)
|
|
2485
|
+
async function backfillMembershipPhaseIds(directory) {
|
|
2486
|
+
return runLocked(directory, undefined, async (paths, state) => {
|
|
2487
|
+
const items = [];
|
|
2488
|
+
const skipped = [];
|
|
2489
|
+
for (const membership of state.memberships.values()) {
|
|
2490
|
+
if (membership.phase_id !== undefined)
|
|
2771
2491
|
continue;
|
|
2772
|
-
const
|
|
2773
|
-
if (
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
}
|
|
2780
|
-
}
|
|
2781
|
-
async function readKnowledgeCounterRollups(directory) {
|
|
2782
|
-
try {
|
|
2783
|
-
const cacheKey = await buildCounterRollupCacheKey(directory);
|
|
2784
|
-
const directoryKey = canonicalRootKey(directory);
|
|
2785
|
-
const cached = counterRollupCache.get(directoryKey);
|
|
2786
|
-
if (cached?.key === cacheKey) {
|
|
2787
|
-
counterRollupCache.delete(directoryKey);
|
|
2788
|
-
counterRollupCache.set(directoryKey, cached);
|
|
2789
|
-
return cloneRollupMap(cached.rollups);
|
|
2790
|
-
}
|
|
2791
|
-
const [events, legacyRecords, baseline] = await Promise.all([
|
|
2792
|
-
readKnowledgeEvents(directory),
|
|
2793
|
-
readLegacyApplicationRecords(directory),
|
|
2794
|
-
readCounterBaseline(directory)
|
|
2795
|
-
]);
|
|
2796
|
-
const rollups = recomputeCounters(events, legacyRecords, baseline);
|
|
2797
|
-
setCounterRollupCache(directory, cacheKey, rollups);
|
|
2798
|
-
return cloneRollupMap(rollups);
|
|
2799
|
-
} catch (err) {
|
|
2800
|
-
warn(`[knowledge-events] readKnowledgeCounterRollups failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2801
|
-
return new Map;
|
|
2802
|
-
}
|
|
2803
|
-
}
|
|
2804
|
-
async function readAuthoritativeKnowledgeCounterRollups(directory) {
|
|
2805
|
-
const history = await queryHistoricalOutcomes(directory);
|
|
2806
|
-
if (!history.ok)
|
|
2807
|
-
return new Map;
|
|
2808
|
-
const rollups = new Map;
|
|
2809
|
-
for (const membership of history.memberships) {
|
|
2810
|
-
const rollup = get(rollups, membership.entry_id);
|
|
2811
|
-
rollup.shown_count += 1;
|
|
2812
|
-
if (membership.application_marker) {
|
|
2813
|
-
rollup.acknowledged_count += 1;
|
|
2814
|
-
rollup.last_acknowledged_at = maxIso(rollup.last_acknowledged_at, membership.application_marker.committed_at);
|
|
2815
|
-
}
|
|
2816
|
-
const compatible = membership;
|
|
2817
|
-
const seen = new Set;
|
|
2818
|
-
for (const terminal of [
|
|
2819
|
-
...compatible.terminal_history ?? [],
|
|
2820
|
-
...compatible.historical_terminals ?? [],
|
|
2821
|
-
...membership.terminal ? [membership.terminal] : []
|
|
2822
|
-
]) {
|
|
2823
|
-
if (seen.has(terminal.event_id))
|
|
2492
|
+
const phaseId = extractPhaseIdFromLabel(membership.phase);
|
|
2493
|
+
if (phaseId === undefined) {
|
|
2494
|
+
skipped.push({
|
|
2495
|
+
trace_id: membership.trace_id,
|
|
2496
|
+
entry_id: membership.entry_id,
|
|
2497
|
+
label: membership.phase
|
|
2498
|
+
});
|
|
2824
2499
|
continue;
|
|
2825
|
-
seen.add(terminal.event_id);
|
|
2826
|
-
switch (terminal.outcome) {
|
|
2827
|
-
case "applied":
|
|
2828
|
-
rollup.applied_explicit_count += 1;
|
|
2829
|
-
rollup.last_applied_at = maxIso(rollup.last_applied_at, terminal.committed_at);
|
|
2830
|
-
break;
|
|
2831
|
-
case "ignored":
|
|
2832
|
-
rollup.ignored_count += 1;
|
|
2833
|
-
break;
|
|
2834
|
-
case "violated":
|
|
2835
|
-
rollup.violated_count += 1;
|
|
2836
|
-
rollup.violation_timestamps.push(terminal.committed_at);
|
|
2837
|
-
break;
|
|
2838
|
-
case "contradicted":
|
|
2839
|
-
rollup.contradicted_count += 1;
|
|
2840
|
-
break;
|
|
2841
|
-
case "n_a":
|
|
2842
|
-
rollup.n_a_count += 1;
|
|
2843
|
-
break;
|
|
2844
2500
|
}
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
}
|
|
2850
|
-
function effectiveRetrievalOutcomes(stored, rollup) {
|
|
2851
|
-
const base = stored ?? {
|
|
2852
|
-
applied_count: 0,
|
|
2853
|
-
succeeded_after_count: 0,
|
|
2854
|
-
failed_after_count: 0
|
|
2855
|
-
};
|
|
2856
|
-
if (!rollup)
|
|
2857
|
-
return base;
|
|
2858
|
-
const { n_a_count: _na, ...rollupWithoutNa } = rollup;
|
|
2859
|
-
return {
|
|
2860
|
-
...base,
|
|
2861
|
-
...rollupWithoutNa,
|
|
2862
|
-
succeeded_after_shown_count: (base.succeeded_after_shown_count ?? 0) + (rollup.succeeded_after_shown_count ?? 0),
|
|
2863
|
-
failed_after_shown_count: (base.failed_after_shown_count ?? 0) + (rollup.failed_after_shown_count ?? 0)
|
|
2864
|
-
};
|
|
2865
|
-
}
|
|
2866
|
-
var VERDICT_CONFIDENCE_BOOST = 0.03;
|
|
2867
|
-
var VERDICT_CONFIDENCE_DECAY = 0.05;
|
|
2868
|
-
async function applyKnowledgeVerdictFeedback(directory, options) {
|
|
2869
|
-
try {
|
|
2870
|
-
const history = await _internals4.queryHistoricalOutcomes(directory);
|
|
2871
|
-
if (!history.ok)
|
|
2872
|
-
return { processed: 0, bumps: 0 };
|
|
2873
|
-
const seenEventIds = new Set;
|
|
2874
|
-
const events = history.memberships.flatMap((membership) => {
|
|
2875
|
-
const compatible = membership;
|
|
2876
|
-
const terminals = [
|
|
2877
|
-
...compatible.terminal_history ?? [],
|
|
2878
|
-
...compatible.historical_terminals ?? [],
|
|
2879
|
-
...membership.terminal ? [membership.terminal] : []
|
|
2880
|
-
];
|
|
2881
|
-
return terminals.flatMap((terminal) => {
|
|
2882
|
-
if (terminal.outcome !== "applied" && terminal.outcome !== "violated" && terminal.outcome !== "ignored" || seenEventIds.has(terminal.event_id)) {
|
|
2883
|
-
return [];
|
|
2884
|
-
}
|
|
2885
|
-
seenEventIds.add(terminal.event_id);
|
|
2886
|
-
return [
|
|
2887
|
-
{
|
|
2888
|
-
type: terminal.outcome,
|
|
2889
|
-
event_id: terminal.event_id,
|
|
2890
|
-
knowledge_id: membership.entry_id,
|
|
2891
|
-
timestamp: terminal.committed_at
|
|
2892
|
-
}
|
|
2893
|
-
];
|
|
2501
|
+
items.push({
|
|
2502
|
+
trace_id: membership.trace_id,
|
|
2503
|
+
entry_id: membership.entry_id,
|
|
2504
|
+
phase_id: phaseId
|
|
2894
2505
|
});
|
|
2895
|
-
}
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
if (
|
|
2903
|
-
|
|
2904
|
-
}
|
|
2905
|
-
if (markerIndex < 0) {
|
|
2906
|
-
return false;
|
|
2506
|
+
}
|
|
2507
|
+
if (items.length > 0) {
|
|
2508
|
+
const eventId = randomUUID2();
|
|
2509
|
+
const row = makeRecord(state, "phase_id_backfilled", { items }, eventId);
|
|
2510
|
+
await appendRecord(paths, state, row);
|
|
2511
|
+
for (const item of items) {
|
|
2512
|
+
const membership = state.memberships.get(keyOf(item.trace_id, item.entry_id));
|
|
2513
|
+
if (membership && membership.phase_id === undefined) {
|
|
2514
|
+
membership.phase_id = item.phase_id;
|
|
2907
2515
|
}
|
|
2908
|
-
return index > markerIndex;
|
|
2909
2516
|
}
|
|
2910
|
-
return true;
|
|
2911
|
-
});
|
|
2912
|
-
if (actionable.length === 0) {
|
|
2913
|
-
return { processed: 0, bumps: 0 };
|
|
2914
|
-
}
|
|
2915
|
-
const groups = new Map;
|
|
2916
|
-
let lastProcessedTimestamp;
|
|
2917
|
-
let lastProcessedEventId;
|
|
2918
|
-
for (const event of actionable) {
|
|
2919
|
-
if (typeof event.timestamp === "string" && (!lastProcessedTimestamp || event.timestamp >= lastProcessedTimestamp)) {
|
|
2920
|
-
lastProcessedTimestamp = event.timestamp;
|
|
2921
|
-
lastProcessedEventId = event.event_id;
|
|
2922
|
-
}
|
|
2923
|
-
const kid = event.knowledge_id;
|
|
2924
|
-
if (!kid)
|
|
2925
|
-
continue;
|
|
2926
|
-
const group = groups.get(kid) ?? [];
|
|
2927
|
-
group.push(event);
|
|
2928
|
-
groups.set(kid, group);
|
|
2929
|
-
}
|
|
2930
|
-
const deltas = [];
|
|
2931
|
-
for (const [id, group] of groups) {
|
|
2932
|
-
const positives = group.filter((event) => event.type === "applied").length;
|
|
2933
|
-
const negatives = group.length - positives;
|
|
2934
|
-
if (positives === 0 && negatives === 0)
|
|
2935
|
-
continue;
|
|
2936
|
-
const delta = positives > negatives ? VERDICT_CONFIDENCE_BOOST : -VERDICT_CONFIDENCE_DECAY;
|
|
2937
|
-
deltas.push({
|
|
2938
|
-
id,
|
|
2939
|
-
delta,
|
|
2940
|
-
receipt_events: group.map((event) => ({
|
|
2941
|
-
event_id: event.event_id,
|
|
2942
|
-
timestamp: event.timestamp,
|
|
2943
|
-
outcome: event.type
|
|
2944
|
-
}))
|
|
2945
|
-
});
|
|
2946
|
-
}
|
|
2947
|
-
let bumps = 0;
|
|
2948
|
-
if (deltas.length > 0) {
|
|
2949
|
-
const { bumpKnowledgeConfidenceBatch } = await import("./knowledge-store-bw1dk2eb.js");
|
|
2950
|
-
bumps = await bumpKnowledgeConfidenceBatch(directory, deltas, options?.floorOptions);
|
|
2951
2517
|
}
|
|
2952
2518
|
return {
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
lastProcessedEventId
|
|
2519
|
+
backfilled: items.length,
|
|
2520
|
+
skipped,
|
|
2521
|
+
journal_records: items.length > 0 ? 1 : 0
|
|
2957
2522
|
};
|
|
2958
|
-
}
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
readCounterBaseline,
|
|
2971
|
-
readLegacyApplicationRecords,
|
|
2972
|
-
readKnowledgeCounterRollups,
|
|
2973
|
-
readAuthoritativeKnowledgeCounterRollups,
|
|
2974
|
-
effectiveRetrievalOutcomes,
|
|
2975
|
-
recomputeCounters,
|
|
2976
|
-
applyKnowledgeVerdictFeedback,
|
|
2977
|
-
newTraceId,
|
|
2978
|
-
newEventId,
|
|
2979
|
-
resolveHiveEventsPath: resolveHiveEventsPath2,
|
|
2980
|
-
appendHiveKnowledgeEvent,
|
|
2981
|
-
recordHiveKnowledgeEvent,
|
|
2982
|
-
readHiveKnowledgeEvents,
|
|
2983
|
-
mergeRollupInto
|
|
2523
|
+
});
|
|
2524
|
+
}
|
|
2525
|
+
var _internals3 = {
|
|
2526
|
+
nowMs: () => Date.now(),
|
|
2527
|
+
writeSnapshot,
|
|
2528
|
+
atomicWriteFsynced,
|
|
2529
|
+
appendFsynced,
|
|
2530
|
+
commitApplicationMarkerBatch,
|
|
2531
|
+
maxJournalRecords: MAX_JOURNAL_RECORDS,
|
|
2532
|
+
maxArchiveRecords: MAX_ARCHIVE_RECORDS,
|
|
2533
|
+
maxArchiveBytes: MAX_ARCHIVE_BYTES,
|
|
2534
|
+
maxRepairQuarantineRecords: MAX_REPAIR_QUARANTINE_RECORDS
|
|
2984
2535
|
};
|
|
2985
2536
|
|
|
2986
|
-
export {
|
|
2537
|
+
export { extractContextDecisions, extractCurrentPhase, extractCurrentPhaseFromPlan, extractPhaseIdFromLabel, commitDisplayedMembership, commitEmptyRetrieval, commitGateReleaseBatch, queryLiveMemberships, queryHistoricalOutcomes, recordPhaseCloseIntent, reconcilePhaseClose, inspectMembershipPhaseIds, backfillMembershipPhaseIds };
|