instar 1.3.1211 → 1.3.1213

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.
Files changed (45) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +12 -0
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/BetweenWindowAdmissionGate.d.ts +2 -0
  5. package/dist/core/BetweenWindowAdmissionGate.d.ts.map +1 -1
  6. package/dist/core/BetweenWindowAdmissionGate.js +2 -0
  7. package/dist/core/BetweenWindowAdmissionGate.js.map +1 -1
  8. package/dist/core/PeerEndpointRecorder.d.ts +30 -0
  9. package/dist/core/PeerEndpointRecorder.d.ts.map +1 -1
  10. package/dist/core/PeerEndpointRecorder.js +53 -3
  11. package/dist/core/PeerEndpointRecorder.js.map +1 -1
  12. package/dist/core/WindowLifecycleObligationLedger.d.ts +382 -0
  13. package/dist/core/WindowLifecycleObligationLedger.d.ts.map +1 -0
  14. package/dist/core/WindowLifecycleObligationLedger.js +734 -0
  15. package/dist/core/WindowLifecycleObligationLedger.js.map +1 -0
  16. package/dist/core/WriteDomainRegistry.d.ts.map +1 -1
  17. package/dist/core/WriteDomainRegistry.js +7 -0
  18. package/dist/core/WriteDomainRegistry.js.map +1 -1
  19. package/dist/data/standards-guard-index.json +1 -1
  20. package/dist/data/standards-guard-index.meta.json +2 -2
  21. package/dist/data/standards-registry.meta.json +1 -1
  22. package/dist/monitoring/SessionWatchdog.d.ts +29 -0
  23. package/dist/monitoring/SessionWatchdog.d.ts.map +1 -1
  24. package/dist/monitoring/SessionWatchdog.js +52 -0
  25. package/dist/monitoring/SessionWatchdog.js.map +1 -1
  26. package/dist/server/AgentServer.d.ts +5 -0
  27. package/dist/server/AgentServer.d.ts.map +1 -1
  28. package/dist/server/AgentServer.js +22 -0
  29. package/dist/server/AgentServer.js.map +1 -1
  30. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  31. package/dist/server/CapabilityIndex.js +25 -0
  32. package/dist/server/CapabilityIndex.js.map +1 -1
  33. package/dist/server/routes.d.ts +5 -0
  34. package/dist/server/routes.d.ts.map +1 -1
  35. package/dist/server/routes.js +806 -1
  36. package/dist/server/routes.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/data/builtin-manifest.json +47 -47
  39. package/src/data/standards-guard-index.json +1 -1
  40. package/src/data/standards-guard-index.meta.json +2 -2
  41. package/src/data/standards-registry.meta.json +1 -1
  42. package/upgrades/1.3.1212.md +28 -0
  43. package/upgrades/1.3.1213.md +45 -0
  44. package/upgrades/side-effects/mesh-endpoint-degradation-retention.md +106 -0
  45. package/upgrades/side-effects/window-lifecycle-obligation-ledger.md +94 -0
@@ -0,0 +1,734 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import crypto from 'node:crypto';
4
+ import { BETWEEN_WINDOW_ADMISSION_CONTRACT_VERSION, evaluateBetweenWindowAdmission } from './BetweenWindowAdmissionGate.js';
5
+ export const WINDOW_LEDGER_AGENT = 'echo';
6
+ export const WINDOW_LEDGER_SCOPE = 'echo-window-lifecycle';
7
+ export const WINDOW_LEDGER_VERSION = 1;
8
+ export const NATIVE_ADMISSION_COMPATIBILITY = `BetweenWindowAdmissionGate@${BETWEEN_WINDOW_ADMISSION_CONTRACT_VERSION}`;
9
+ export const REQUIRED_WINDOW_DUTIES = [
10
+ ...['pathway.full-reread.observer-1', 'pathway.full-reread.observer-2', 'observer1.full-reread.observer-1', 'observer1.full-reread.observer-2'].map(id => ({ id: `preground.${id}`, phase: 'pre-start', sourcePattern: /re-read the entire Pathway|same for the observer 1 topic/i, sample: 'Both observers must re-read the entire Pathway from July 25 and the observer 1 topic.', role: id.endsWith('observer-1') ? 'observer-1' : 'observer-2', authority: 'live-requeried-message', core: true, failure: 'block-phase' })),
11
+ { id: 'preground.visible-discussion.pathway', phase: 'pre-start', sourcePattern: /coordination.*VISIBLY|discuss it with each other/i, sample: 'Observer coordination must happen VISIBLY and discuss the Pathway assessment.', role: 'observer-2', authority: 'live-requeried-message', core: true, failure: 'block-phase' },
12
+ { id: 'preground.visible-discussion.observer1', phase: 'pre-start', sourcePattern: /coordination.*VISIBLY|again discuss/i, sample: 'Observer coordination must happen VISIBLY for the observer 1 assessment.', role: 'observer-2', authority: 'live-requeried-message', core: true, failure: 'block-phase' },
13
+ { id: 'preground.independent-assessments', phase: 'pre-start', sourcePattern: /independently scores|each observer must actually/i, sample: 'Each observer must create an independent assessment before reconciliation.', role: 'observer-2', authority: 'live-requeried-message', core: true, failure: 'block-phase' },
14
+ { id: 'preground.unresolved-disagreement-shown', phase: 'pre-start', sourcePattern: /unresolved disagreement.*shown/i, sample: 'Every unresolved disagreement must be shown to the operator.', role: 'observer-2', authority: 'live-requeried-message', core: true, failure: 'block-phase' },
15
+ { id: 'preground.combined-recommendation', phase: 'pre-start', sourcePattern: /combine the assessments/i, sample: 'Observers must combine assessments into the next-window recommendation.', role: 'observer-2', authority: 'live-requeried-message', core: true, failure: 'block-phase' },
16
+ { id: 'preground.native-structural-preflight', phase: 'pre-start', sourcePattern: /admission[- ]gate|opening cannot complete/i, sample: 'The native admission-gate structural preflight is required.', role: 'observer-2', authority: 'native-local-store-presence', core: true, failure: 'block-phase' },
17
+ { id: 'start.source-ingestion.tenets', phase: 'start', sourcePattern: /tenets.*compiled|tenets in force/i, sample: 'Current tenets must be ingested with source spans.', role: 'echo', authority: 'content-bound-store-row', core: true, failure: 'block-phase' },
18
+ { id: 'start.source-ingestion.charter', phase: 'start', sourcePattern: /charter|WINDOW LIFECYCLE/i, sample: 'Current charter must be ingested with source spans.', role: 'echo', authority: 'content-bound-store-row', core: true, failure: 'block-phase' },
19
+ { id: 'start.compilation-proof', phase: 'start', sourcePattern: /obligation ledger compiled|negative tests per omitted duty/i, sample: 'Compilation coverage and source-derived challenges are required.', role: 'echo', authority: 'deterministic-replay', core: true, failure: 'block-phase' },
20
+ { id: 'start.reaffirmation', phase: 'start', sourcePattern: /beginning, middle, and end|opening cannot complete.*reaffirmation/i, sample: 'Byte-exact beginning reaffirmation is required.', role: 'observer-1', authority: 'live-requeried-message', core: true, failure: 'block-phase' },
21
+ { id: 'start.plan-input', phase: 'start', sourcePattern: /mandatory INPUT|Plan-tree placement/i, sample: 'Canonical plan input, current node, and leaf-to-root placement are required.', role: 'observer-1', authority: 'live-requeried-message', core: true, failure: 'block-phase' },
22
+ { id: 'start.named-worker-lanes', phase: 'start', sourcePattern: /named lanes|Lane A/i, sample: 'Named worker lanes with artifacts and blockers are required.', role: 'orchestrator', authority: 'runtime-registry-proof', core: true, failure: 'block-phase' },
23
+ { id: 'start.delegation-majority-boundary', phase: 'start', sourcePattern: /majority of development MUST be offloaded/i, sample: 'The orchestrator must not perform the majority of development.', role: 'orchestrator', authority: 'runtime-registry-proof', core: true, failure: 'block-new-scope-and-escalate' },
24
+ { id: 'start.machine-model-distribution', phase: 'start', sourcePattern: /BOTH machines|MULTIPLE.*Codey/i, sample: 'Both-machine and multiple-Codey distribution or a named blocker is required.', role: 'orchestrator', authority: 'runtime-registry-proof', core: false, failure: 'register-debt' },
25
+ { id: 'start.cadence-commitment', phase: 'start', sourcePattern: /every 3 hours|3-hour cadence/i, sample: 'A durable 3-hour reporting cadence is required.', role: 'observer-1', authority: 'runtime-registry-proof', core: true, failure: 'block-phase' },
26
+ { id: 'start.stall-cadence', phase: 'start', sourcePattern: /30 minute checks/i, sample: 'A durable 30-minute all-session stall cadence is required.', role: 'observer-1', authority: 'runtime-registry-proof', core: true, failure: 'block-phase' },
27
+ { id: 'start.window-expiry-recorded', phase: 'start', sourcePattern: /24 hours|24h end/i, sample: 'Charter expiry and server backstop precedence must be recorded.', role: 'observer-1', authority: 'content-bound-store-row', core: true, failure: 'block-phase' },
28
+ { id: 'continuous.telegram.send-path-classified', phase: 'continuous', sourcePattern: /Telegram-through-my-account channel/i, sample: 'Every Telegram operator-account send path must be classified.', role: 'echo', authority: 'live-requeried-message', core: true, failure: 'block-send' },
29
+ { id: 'continuous.telegram.profile-resolved', phase: 'continuous', sourcePattern: /justin-telegram/i, sample: 'The justin-telegram profile must be resolved.', role: 'echo', authority: 'runtime-registry-proof', core: true, failure: 'block-send' },
30
+ { id: 'continuous.telegram.signature-verified', phase: 'continuous', sourcePattern: /agent-signature protocol/i, sample: 'Echo signature provenance must be verified.', role: 'echo', authority: 'live-requeried-message', core: true, failure: 'block-send' },
31
+ { id: 'continuous.telegram.act-as-principal-guard', phase: 'continuous', sourcePattern: /never authorisation to act as Justin/i, sample: 'Echo must never act as Justin.', role: 'echo', authority: 'live-requeried-message', core: true, failure: 'block-send' },
32
+ { id: 'cadence.stall-check.30m', phase: 'cadence', sourcePattern: /30 minute checks/i, sample: 'Every 30 minutes all active window sessions must be checked.', role: 'observer-1', authority: 'runtime-registry-proof', core: true, failure: 'block-new-scope-and-escalate', recurring: true },
33
+ { id: 'cadence.report.3h', phase: 'cadence', sourcePattern: /every 3 hours|3-hour cadence/i, sample: 'Every 3 hours a synthesis report must be delivered.', role: 'observer-1', authority: 'live-requeried-message', core: true, failure: 'block-new-scope-and-escalate', recurring: true },
34
+ { id: 'continuous.high-level-observer', phase: 'continuous', sourcePattern: /high level, simple communication|high-level and out of the weeds/i, sample: 'Observer communication must stay high-level and goal-tied.', role: 'observer-1', authority: 'live-requeried-message', core: false, failure: 'correct-next-synthesis', waiverPolicy: 'phase-transition' },
35
+ { id: 'continuous.visible-observer-coordination', phase: 'continuous', sourcePattern: /coordination.*VISIBLY/i, sample: 'Evidence-bearing observer coordination must remain visible.', role: 'observer-2', authority: 'live-requeried-message', core: true, failure: 'repost-visibly' },
36
+ { id: 'continuous.derive-counts', phase: 'continuous', sourcePattern: /derive every count/i, sample: 'Every count must be derived from evidence.', role: 'echo', authority: 'deterministic-replay', core: false, failure: 'correct-before-report' },
37
+ { id: 'continuous.save-before-words', phase: 'continuous', sourcePattern: /save before words/i, sample: 'Artifacts must be saved before completion words.', role: 'echo', authority: 'content-bound-store-row', core: true, failure: 'block-completion-claim' },
38
+ { id: 'continuous.80-20', phase: 'continuous', sourcePattern: /80\/20|Pareto/i, sample: 'The 80/20 boundary must be reviewed.', role: 'observer-1', authority: 'live-requeried-message', core: false, failure: 'scope-timebox-review', waiverPolicy: 'phase-transition' },
39
+ { id: 'continuous.scope-drift', phase: 'continuous', sourcePattern: /scope change|remains inside charter|Standing rules/i, sample: 'Work must remain inside the charter.', role: 'observer-1', authority: 'live-requeried-message', core: false, failure: 'block-new-scope-and-register-debt', waiverPolicy: 'phase-transition' },
40
+ { id: 'mid.reaffirmation', phase: 'mid', sourcePattern: /middle[^.\n]{0,120}reaffirm|reaffirm[^.\n]{0,120}middle/i, sample: 'Mid-window reaffirmation must be verified.', role: 'observer-1', authority: 'live-requeried-message', core: true, failure: 'block-new-scope-and-escalate' },
41
+ { id: 'mid.plan-position-check', phase: 'mid', sourcePattern: /Plan-tree placement/i, sourcePatterns: [/start\/mid\/close lifecycle/i, /Plan-tree placement/i], sample: 'Mid-window plan-position-check must be verified.', role: 'observer-1', authority: 'live-requeried-message', core: false, failure: 'block-new-scope-and-escalate' },
42
+ { id: 'mid.worker-lane-status', phase: 'mid', sourcePattern: /named lanes/i, sourcePatterns: [/start\/mid\/close lifecycle/i, /named lanes/i], sample: 'Mid-window worker-lane-status must be verified.', role: 'observer-1', authority: 'live-requeried-message', core: false, failure: 'block-new-scope-and-escalate' },
43
+ { id: 'mid.cadence-health', phase: 'mid', sourcePattern: /3-hour cadence|30 minute checks/i, sourcePatterns: [/start\/mid\/close lifecycle/i, /3-hour cadence|30 minute checks/i], sample: 'Mid-window cadence-health must be verified.', role: 'observer-1', authority: 'live-requeried-message', core: false, failure: 'block-new-scope-and-escalate' },
44
+ { id: 'mid.source-challenge-refresh', phase: 'mid', sourcePattern: /tenets[^.\n]{0,100}compiled|compiled[^.\n]{0,100}tenets/i, sourcePatterns: [/start\/mid\/close lifecycle/i, /tenets[^.\n]{0,100}compiled|compiled[^.\n]{0,100}tenets/i], sample: 'Mid-window source-challenge-refresh must be verified.', role: 'observer-1', authority: 'live-requeried-message', core: false, failure: 'block-new-scope-and-escalate' },
45
+ { id: 'mid.executor-inventory', phase: 'mid', sourcePattern: /NO running executor|executor/i, sourcePatterns: [/start\/mid\/close lifecycle/i, /NO running executor|executor/i], sample: 'Mid-window executor-inventory must be verified.', role: 'observer-1', authority: 'runtime-registry-proof', core: false, failure: 'block-new-scope-and-escalate' },
46
+ ...[
47
+ ['expiry-status', /24 hours|actual close time/i, 'content-bound-store-row'], ['end-reaffirmation', /beginning, middle, and end|end reaffirmation/i, 'live-requeried-message'],
48
+ ['plan-outcome.semantic-diff', /mandatory OUTPUT|plan outcome/i, 'live-requeried-message'], ['plan-staleness-guard', /cannot claim closure.*stale/i, 'deterministic-replay'],
49
+ ['consumer-proof', /consumer proof/i, 'deterministic-replay'], ['debt-register', /named debt|gap is Lane C/i, 'content-bound-store-row'],
50
+ ['no-done-without-effect', /no "done" without the effect|effect confirmed/i, 'content-bound-store-row'], ['terminal-census', /closing cannot complete|negative tests per omitted duty/i, 'deterministic-replay'],
51
+ ].map(([id, sourcePattern, authority]) => ({ id: `close.${id}`, phase: 'close', sourcePattern: sourcePattern, sample: `Close ${id} must pass.`, role: 'observer-1', authority: authority, core: true, failure: 'fail-close' })),
52
+ { id: 'postlive.verdict.pass-required', phase: 'post-live', sourcePattern: /post-live verdict|post-live review/i, sample: 'A real or deterministic post-live pass is required.', role: 'observer-2', authority: 'deterministic-replay', core: true, failure: 'fail-close' },
53
+ ];
54
+ export function minimumWindowDutyFixture() {
55
+ return `${REQUIRED_WINDOW_DUTIES.map(d => d.sample).join('\n')}
56
+ Each observer independently scores and each observer must actually perform the read.
57
+ The observers combine the assessments; every unresolved disagreement is shown.
58
+ The admission-gate and opening cannot complete without evidence.
59
+ The tenets are compiled and tenets in force; the charter defines WINDOW LIFECYCLE.
60
+ The obligation ledger compiled with negative tests per omitted duty.
61
+ The start/mid/close lifecycle governs every phase.
62
+ Reaffirm at the beginning, middle, and end.
63
+ Canonical plan mandatory INPUT and mandatory OUTPUT with Plan-tree placement and plan outcome.
64
+ Named lanes include Lane A. The majority of development MUST be offloaded.
65
+ Use BOTH machines and MULTIPLE Codey workers.
66
+ Every 3 hours use a 3-hour cadence; 30 minute checks are required; the window is 24 hours.
67
+ The Telegram-through-my-account channel uses justin-telegram and the agent-signature protocol, never authorisation to act as Justin.
68
+ Communication stays high level, simple communication and high-level and out of the weeds.
69
+ Observer coordination occurs VISIBLY. Always derive every count, save before words, apply 80/20 Pareto, and remains inside charter with no scope change.
70
+ At middle and mid-window verify Plan-tree placement, named lanes, cadence, source, and executor.
71
+ Record actual close time after 24 hours, perform end reaffirmation, and cannot claim closure while stale.
72
+ Consumer proof and a post-live verdict are required; named debt includes owner; no "done" without the effect confirmed; closing cannot complete with an omitted duty.
73
+ `;
74
+ }
75
+ const OPERATIVE = /\b(must|required|cannot|may not|never|shall|should|will not|before|after|by\s+\d|every\s+\d+|within\s+\d+|no later than|deadline|cadence|expires?|reaffirm|prohibit(?:ed|s)?)\b/i;
76
+ // Deliberately broader than the compiler vocabulary. This is the independent
77
+ // source-side tripwire: a newly authored normative form is discovered even
78
+ // when no compiler rule knows how to lower it yet.
79
+ const NORMATIVE_DISCOVERY = /\b(must|required|cannot|may not|never|shall|should|will not|before|after|by\s+\d|every\s+\d+|within\s+\d+|no later than|deadline|cadence|expires?|reaffirm|prohibit(?:ed|s)?|is obligated to|is forbidden from)\b/i;
80
+ const hash = (value) => crypto.createHash('sha256').update(value).digest('hex');
81
+ const canonicalize = (value) => Array.isArray(value) ? value.map(canonicalize) : value && typeof value === 'object'
82
+ ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, canonicalize(item)])) : value;
83
+ const canonical = (value) => JSON.stringify(canonicalize(value));
84
+ export function assertEchoScope(agentId, scope) {
85
+ if (agentId !== WINDOW_LEDGER_AGENT || scope !== WINDOW_LEDGER_SCOPE)
86
+ throw new Error('echo-scope-required');
87
+ }
88
+ export function compileWindowSources(input) {
89
+ assertEchoScope(input.agentId, input.scope);
90
+ const now = input.now ?? new Date().toISOString();
91
+ const sources = [input.tenetsPath, input.charterPath];
92
+ const obligations = [];
93
+ const operativeLines = [];
94
+ const ast = [];
95
+ const hashes = {};
96
+ const byteLengths = {};
97
+ for (const source of sources) {
98
+ const bytes = fs.readFileSync(source);
99
+ const text = bytes.toString('utf8');
100
+ hashes[source] = hash(bytes);
101
+ byteLengths[source] = bytes.length;
102
+ let byteOffset = 0;
103
+ text.split(/\n/).forEach((line, index) => {
104
+ const lineBytes = Buffer.byteLength(line);
105
+ const span = { source, hash: hashes[source], byteStart: byteOffset, byteEnd: byteOffset + lineBytes, lineStart: index + 1, lineEnd: index + 1 };
106
+ const trimmed = line.trim();
107
+ if (trimmed)
108
+ ast.push({ kind: /^#{1,6}\s/.test(trimmed) ? 'heading' : /^[-*+]\s/.test(trimmed) ? 'list-item' : 'paragraph', text: trimmed, span, operative: NORMATIVE_DISCOVERY.test(trimmed) });
109
+ if (OPERATIVE.test(line)) {
110
+ operativeLines.push(span);
111
+ obligations.push({
112
+ id: `source.${hash(`${path.basename(source)}:${hashes[source]}:${byteOffset}:${line}`).slice(0, 20)}`, agentId: input.agentId, scope: input.scope, windowId: input.windowId,
113
+ sourceSpans: [span], statement: line.trim(), phase: inferPhase(line), coreDuty: /tenet|never|must|required|cannot|shall|may not/i.test(line),
114
+ waiverPolicy: /tenet|never|must|required|cannot|shall|may not/i.test(line) ? 'non-waivable' : 'debt-only', responsibleRole: inferRole(line),
115
+ deadline: { dueAt: inferDueAt(line, now), graceMs: 0 }, predicate: { recurring: /every|cadence/i.test(line), requiredAuthority: 'content-bound-store-row' },
116
+ evidencePolicy: { requiredAuthority: 'content-bound-store-row' }, executorBinding: emptyBinding(), failureAction: 'block-and-surface', status: 'pending', evidence: [], lastEvaluatedAt: null,
117
+ });
118
+ }
119
+ byteOffset += lineBytes + 1;
120
+ });
121
+ }
122
+ const facts = {
123
+ profiles: ast.flatMap(n => n.text.match(/`?[a-z]+-telegram`?/gi) ?? []),
124
+ fingerprints: ast.flatMap(n => n.text.match(/\b[a-f0-9]{16,64}\b/gi) ?? []),
125
+ dates: ast.flatMap(n => n.text.match(/\b\d{4}-\d{2}-\d{2}\b/g) ?? []),
126
+ cadences: ast.flatMap(n => n.text.match(/\b(?:(?:every|within)\s+)?\d+[- ](?:minutes?|hours?|hour)\b/gi) ?? []),
127
+ };
128
+ const allText = ast.map(n => n.text).join('\n');
129
+ const challenges = {
130
+ telegramProfile: facts.profiles[0]?.replace(/`/g, '') ?? '', telegramFingerprint: facts.fingerprints[0] ?? '',
131
+ pathwayStart: allText.match(/starting from ([A-Z][a-z]+ \d{1,2})/i)?.[1] ?? '',
132
+ canonicalPlanId: allText.match(/\b[0-9a-f]{8}-[0-9a-f-]{27,36}\b/i)?.[0] ?? '',
133
+ reportCadence: facts.cadences.find(v => /3[- ]hours?/i.test(v)) ?? '', stallCadence: facts.cadences.find(v => /30[- ]minutes?/i.test(v)) ?? '',
134
+ charterExpiry: allText.match(/\bending\s+(\d{4}-\d{2}-\d{2}\s+~?\d{1,2}:\d{2}\s+[A-Z]{2,5})/i)?.[1]?.trim() ?? (allText.match(/24 hours/i)?.[0] ?? ''),
135
+ };
136
+ const charterDurationMs = ast.some(n => /24 hours|24h/i.test(n.text)) ? 24 * 3_600_000 : 24 * 3_600_000;
137
+ for (const definition of input.requireMinimumCatalog === false ? [] : REQUIRED_WINDOW_DUTIES) {
138
+ const matchedSpans = (definition.sourcePatterns ?? [definition.sourcePattern]).map(pattern => { const node = ast.find(candidate => pattern.test(candidate.text)); if (!node)
139
+ throw new Error(`uncompiled-operative-duty:${definition.id}`); pattern.lastIndex = 0; const matched = pattern.exec(node.text); if (!matched)
140
+ throw new Error(`uncompiled-operative-duty:${definition.id}`); const prefixBytes = Buffer.byteLength(node.text.slice(0, matched.index)); const matchBytes = Buffer.byteLength(matched[0]); return { ...node.span, byteStart: node.span.byteStart + prefixBytes, byteEnd: node.span.byteStart + prefixBytes + matchBytes }; });
141
+ const futurePhase = definition.phase === 'mid' || definition.phase === 'close' || definition.phase === 'post-live';
142
+ const sourceText = ast.find(candidate => definition.sourcePattern.test(candidate.text))?.text ?? definition.sample;
143
+ definition.sourcePattern.lastIndex = 0;
144
+ const dueAt = definition.phase === 'mid' ? new Date(Date.parse(now) + charterDurationMs / 2).toISOString() : (definition.phase === 'close' || definition.phase === 'post-live') ? new Date(Date.parse(now) + charterDurationMs).toISOString() : inferDueAt(sourceText, now);
145
+ obligations.push({ id: definition.id, agentId: input.agentId, scope: input.scope, windowId: input.windowId, sourceSpans: matchedSpans, statement: definition.sample,
146
+ phase: definition.phase, coreDuty: definition.core, waiverPolicy: definition.waiverPolicy ?? (definition.core ? 'non-waivable' : 'debt-only'), responsibleRole: definition.role,
147
+ deadline: { dueAt, graceMs: 0 }, predicate: { recurring: definition.recurring, requiredAuthority: definition.authority, expected: expectedFactsForDuty(definition.id, challenges) }, evidencePolicy: { requiredAuthority: definition.authority },
148
+ executorBinding: emptyBinding(), failureAction: definition.failure, status: 'pending', evidence: [], eligibleAt: futurePhase ? dueAt : undefined, lastEvaluatedAt: null });
149
+ }
150
+ const compiled = { hashes, byteLengths, obligations, operativeLines, ast, facts, challenges };
151
+ const coverage = verifyCompilationCoverage(compiled);
152
+ if (!coverage.ok)
153
+ throw new Error(coverage.issues.join(','));
154
+ return compiled;
155
+ }
156
+ function inferPhase(line) {
157
+ if (/post[- ]live/i.test(line))
158
+ return 'post-live';
159
+ if (/close|end reaffirm/i.test(line))
160
+ return 'close';
161
+ if (/middle|mid[- ]window/i.test(line))
162
+ return 'mid';
163
+ if (/every|cadence|interval|hour/i.test(line))
164
+ return 'cadence';
165
+ if (/before|pre[- ]start|reread/i.test(line))
166
+ return 'pre-start';
167
+ if (/start|beginning/i.test(line))
168
+ return 'start';
169
+ return 'continuous';
170
+ }
171
+ function inferRole(line) { const found = line.match(/observer[- ]?[12]|codey|operator/i); return found?.[0].toLowerCase().replace(' ', '-') ?? 'echo'; }
172
+ function inferDueAt(line, now) { const match = line.match(/(?:within|every)\s+(\d+)\s*(minute|hour)/i); if (!match)
173
+ return now; const ms = Number(match[1]) * (match[2].toLowerCase() === 'hour' ? 3_600_000 : 60_000); return new Date(Date.parse(now) + ms).toISOString(); }
174
+ function emptyBinding() { return { kind: 'unassigned', executorId: '', owner: '', registryCoordinates: '', enabled: false, dryRun: true }; }
175
+ function expectedFactsForDuty(id, challenges) {
176
+ if (id.includes('profile-resolved'))
177
+ return { profile: challenges.telegramProfile };
178
+ if (id.includes('signature-verified'))
179
+ return { fingerprint: challenges.telegramFingerprint };
180
+ if (id.includes('pathway'))
181
+ return { pathwayStart: challenges.pathwayStart };
182
+ if (id.includes('plan'))
183
+ return { canonicalPlanId: challenges.canonicalPlanId };
184
+ if (id.includes('report') && id.includes('cadence'))
185
+ return { cadence: challenges.reportCadence };
186
+ if (id.includes('stall') && id.includes('cadence'))
187
+ return { cadence: challenges.stallCadence };
188
+ if (id.includes('expiry'))
189
+ return { charterExpiry: challenges.charterExpiry };
190
+ return {};
191
+ }
192
+ function validTime(value) { return typeof value === 'string' && Number.isFinite(Date.parse(value)); }
193
+ export function verifyCompilationCoverage(compiled) {
194
+ const covered = new Set(compiled.obligations.flatMap(o => o.sourceSpans.map(s => `${s.source}:${s.hash}:${s.byteStart}:${s.byteEnd}`)));
195
+ const independentlyDiscovered = compiled.ast?.filter(n => n.operative).map(n => n.span) ?? compiled.operativeLines;
196
+ const issues = independentlyDiscovered.filter(s => !covered.has(`${s.source}:${s.hash}:${s.byteStart}:${s.byteEnd}`)).map(s => `uncompiled-operative-duty:${s.source}:${s.lineStart}`);
197
+ return { ok: issues.length === 0, issues };
198
+ }
199
+ export function classifyExecutor(obligation, now = new Date().toISOString()) {
200
+ if (obligation.status === 'satisfied' && !obligation.predicate.recurring)
201
+ return 'completed-one-shot';
202
+ if (obligation.eligibleAt && Date.parse(obligation.eligibleAt) > Date.parse(now))
203
+ return 'future-phase';
204
+ return 'pending-executable';
205
+ }
206
+ export function evaluateExecutor(obligation, now = new Date().toISOString()) {
207
+ if (!validTime(now) || !validTime(obligation.deadline.dueAt))
208
+ return { ok: false, class: classifyExecutor(obligation, now), issues: ['invalid-timestamp'] };
209
+ const cls = classifyExecutor(obligation, now);
210
+ const b = obligation.executorBinding;
211
+ const issues = [];
212
+ if (b.class && b.class !== cls)
213
+ issues.push('executor-class-mismatch');
214
+ if (cls === 'completed-one-shot') {
215
+ const required = obligation.evidencePolicy.requiredAuthority;
216
+ if (!obligation.evidence.some(e => evidenceApplies(e, obligation) && authoritySatisfies(e.authority, required)))
217
+ issues.push('durable-completion-evidence-missing');
218
+ }
219
+ else if (cls === 'future-phase') {
220
+ if (!b.owner)
221
+ issues.push('owner-missing');
222
+ if (!b.enabled || b.dryRun)
223
+ issues.push('trigger-disabled');
224
+ if (!b.triggerEnabled || !b.durableTriggerState)
225
+ issues.push('trigger-state-missing');
226
+ if (!b.eligibilityAt || Date.parse(b.eligibilityAt) > Date.parse(obligation.deadline.dueAt) + obligation.deadline.graceMs)
227
+ issues.push('eligibility-after-deadline');
228
+ }
229
+ else {
230
+ if (!b.owner || !b.executorId || !b.registryCoordinates)
231
+ issues.push('runtime-assignment-missing');
232
+ if (!b.enabled || b.dryRun || !b.running)
233
+ issues.push('executor-not-running');
234
+ const heartbeatAge = validTime(b.heartbeatAt) ? Date.parse(now) - Date.parse(b.heartbeatAt) : Infinity;
235
+ if (!validTime(b.heartbeatAt) || heartbeatAge < 0 || heartbeatAge > (b.heartbeatMaxAgeMs ?? 60_000))
236
+ issues.push('heartbeat-stale');
237
+ if (!validTime(b.nextAttemptAt) || Date.parse(b.nextAttemptAt) > Date.parse(obligation.deadline.dueAt) + obligation.deadline.graceMs)
238
+ issues.push('attempt-not-timely');
239
+ if (b.deliversOutput && (!b.sinkReachable || b.suppressionActive))
240
+ issues.push('delivery-path-unavailable');
241
+ if (b.needsClientDriver && (!b.driverPresent || !b.driverMatches))
242
+ issues.push('client-driver-unavailable');
243
+ }
244
+ return { ok: issues.length === 0, class: cls, issues };
245
+ }
246
+ function evidenceApplies(e, o) {
247
+ return e.verifierPassed && e.agentId === o.agentId && e.scope === o.scope && e.windowId === o.windowId && e.obligationId === o.id && e.sourceHashes.length > 0 && e.sourceHashes.every(h => o.sourceSpans.some(s => s.hash === h)) && predicateSatisfied(o, e);
248
+ }
249
+ /** A verified transport proves provenance, not meaning. Semantic duties require
250
+ * a canonical, instance-bound proof envelope emitted by their named authority. */
251
+ export function predicateSatisfied(obligation, evidence) {
252
+ if (obligation.id === 'preground.native-structural-preflight')
253
+ return evidence.authority === 'native-local-store-presence';
254
+ if (!evidence.verifiedPayload)
255
+ return false;
256
+ if (/reaffirmation/.test(obligation.id))
257
+ return evidence.authority === 'live-requeried-message' && obligation.sourceSpans.some(span => hash(evidence.verifiedPayload) === span.hash);
258
+ let proof;
259
+ try {
260
+ proof = JSON.parse(evidence.verifiedPayload);
261
+ }
262
+ catch { /* @silent-fallback-ok — malformed authority evidence fails closed */
263
+ return false;
264
+ }
265
+ if (proof.obligationId !== obligation.id || proof.verdict !== 'pass')
266
+ return false;
267
+ const proofHashes = proof.sourceHashes;
268
+ if (!Array.isArray(proofHashes) || proofHashes.length === 0 || !proofHashes.every(v => typeof v === 'string' && obligation.sourceSpans.some(s => s.hash === v)))
269
+ return false;
270
+ if (/plan-(?:input|position|outcome|staleness)/.test(obligation.id) && (typeof proof.planNodeId !== 'string' || proof.charterIncluded !== true))
271
+ return false;
272
+ if (obligation.id === 'start.named-worker-lanes' && (!Array.isArray(proof.laneIds) || proof.laneIds.length < 2 || typeof proof.laneSourceHash !== 'string' || !proof.laneSourceHash))
273
+ return false;
274
+ if (obligation.id === 'start.delegation-majority-boundary') {
275
+ const assignments = proof.workerAssignments;
276
+ if (!Array.isArray(assignments) || assignments.length < 2 || !assignments.every(item => typeof item.laneId === 'string' && typeof item.transcriptHash === 'string' && /^[a-f0-9]{64}$/.test(item.transcriptHash)) || typeof proof.offloadedLaneShare !== 'number' || proof.offloadedLaneShare < 0.5)
277
+ return false;
278
+ }
279
+ if (obligation.id === 'start.machine-model-distribution') {
280
+ const assignments = proof.workerAssignments;
281
+ const distinctMachines = Array.isArray(assignments) ? new Set(assignments.map(item => item.machineId).filter(value => typeof value === 'string')) : new Set();
282
+ if ((distinctMachines.size < 2 || !assignments?.every(item => typeof item.laneId === 'string')) && proof.machineBlockerVerified !== true)
283
+ return false;
284
+ }
285
+ if (/visible|discussion|disagreement|combined-recommendation/.test(obligation.id) && proof.visibleTopicId !== 43003)
286
+ return false;
287
+ for (const [key, expected] of Object.entries(obligation.predicate.expected ?? {}))
288
+ if (proof[key] !== expected)
289
+ return false;
290
+ if (/profile-resolved/.test(obligation.id) && (proof.profile !== 'justin-telegram' || proof.profileAuthority !== 'playwright-profile-registry'))
291
+ return false;
292
+ if (/send-path-classified/.test(obligation.id) && proof.signedAgentId !== 'echo')
293
+ return false;
294
+ if (/signature-verified/.test(obligation.id) && (proof.signatureVerified !== true || proof.signedAgentId !== 'echo' || proof.fingerprint !== obligation.predicate.expected?.fingerprint))
295
+ return false;
296
+ if (/act-as-principal/.test(obligation.id) && (proof.principalRiskCleared !== true || proof.signedAgentId !== 'echo'))
297
+ return false;
298
+ if (/derive-counts/.test(obligation.id)) {
299
+ const counts = proof.counts;
300
+ if (!Array.isArray(counts) || counts.length === 0 || !counts.every(item => { const row = item; return Number.isInteger(row.value) && Array.isArray(row.items) && row.value === row.items.length; }))
301
+ return false;
302
+ }
303
+ if (/save-before-words/.test(obligation.id)) {
304
+ if (typeof proof.artifactPath !== 'string' || typeof proof.artifactHash !== 'string')
305
+ return false;
306
+ try {
307
+ if (hash(fs.readFileSync(proof.artifactPath)) !== proof.artifactHash)
308
+ return false;
309
+ }
310
+ catch { /* @silent-fallback-ok — unreadable artifact cannot satisfy the duty */
311
+ return false;
312
+ }
313
+ }
314
+ if (/80-20/.test(obligation.id) && (proof.reviewed !== true || proof.visibleTopicId !== 36966))
315
+ return false;
316
+ if (/scope-drift/.test(obligation.id)) {
317
+ const debt = proof.debt;
318
+ if (proof.reviewed !== true || proof.visibleTopicId !== 36966 || typeof debt?.owner !== 'string' || debt.status !== 'open')
319
+ return false;
320
+ }
321
+ if (/^cadence\.report\.3h@/.test(obligation.id) && (proof.deliveredTopicId !== 36966 || typeof proof.reportBodyHash !== 'string' || !/^[a-f0-9]{64}$/.test(proof.reportBodyHash) || proof.synthesisSectionsVerified !== true))
322
+ return false;
323
+ if (/^cadence\.stall-check\.30m@/.test(obligation.id)) {
324
+ if (typeof proof.watchdogAuthorityEpoch !== 'string' || proof.watchdogAuthorityEpoch.length < 8 || typeof proof.watchdogAuthorityProof !== 'string' || !/^[a-f0-9]{64}$/.test(proof.watchdogAuthorityProof))
325
+ return false;
326
+ const inspected = proof.inspectedSessionIds;
327
+ const active = proof.activeSessionIds;
328
+ const results = proof.sessionResults;
329
+ if (proof.watchdogEnabled !== true || !Number.isSafeInteger(proof.watchdogPollRevision) || Number(proof.watchdogPollRevision) < 1 || !Array.isArray(inspected) || !Array.isArray(active) || canonical(inspected) !== canonical(active) || !Array.isArray(results) || results.length !== active.length || !results.every(result => typeof result.name === 'string' && typeof result.outputObserved === 'boolean' && typeof result.escalationActive === 'boolean' && result.decisionEvaluated === true && typeof result.decisionEvaluatedAt === 'string' && validTime(result.decisionEvaluatedAt)) || typeof proof.inspectionHash !== 'string' || !/^[a-f0-9]{64}$/.test(proof.inspectionHash))
330
+ return false;
331
+ }
332
+ if (/expiry-status/.test(obligation.id) && proof.expiryVerified !== true)
333
+ return false;
334
+ if (/consumer-proof/.test(obligation.id) && proof.consumerObserved !== true)
335
+ return false;
336
+ if (/postlive/.test(obligation.id) && proof.postLivePassed !== true)
337
+ return false;
338
+ return true;
339
+ }
340
+ function authoritySatisfies(actual, required) {
341
+ const compatible = {
342
+ 'native-local-store-presence': ['native-local-store-presence'],
343
+ 'content-bound-store-row': ['content-bound-store-row', 'live-requeried-message', 'replicated-export', 'verified-operator-approval'],
344
+ 'live-requeried-message': ['live-requeried-message', 'verified-operator-approval'],
345
+ 'replicated-export': ['replicated-export'], 'verified-operator-approval': ['verified-operator-approval'],
346
+ 'runtime-registry-proof': ['runtime-registry-proof'], 'deterministic-replay': ['deterministic-replay'],
347
+ };
348
+ return compatible[required].includes(actual);
349
+ }
350
+ /** Re-queries the production registry and replaces every caller-supplied runtime
351
+ * fact. The request may name an executor id; it cannot assert its liveness. */
352
+ export function bindRuntimeAuthority(obligation, registry) {
353
+ const copy = structuredClone(obligation);
354
+ const real = registry.resolve(copy.executorBinding.executorId, copy.id);
355
+ copy.executorBinding = real ? { ...real, class: copy.executorBinding.class, heartbeatMaxAgeMs: copy.executorBinding.heartbeatMaxAgeMs } : emptyBinding();
356
+ return copy;
357
+ }
358
+ /** Re-queries evidence and drops vanished/unbound records. Authority labels in
359
+ * ledger JSON never survive unless the verifier independently earns them. */
360
+ export function bindEvidenceAuthority(obligation, authority) {
361
+ const copy = structuredClone(obligation);
362
+ copy.evidence = copy.evidence.flatMap(record => { const verified = authority.requery(record); return verified ? [verified] : []; });
363
+ return copy;
364
+ }
365
+ export function evaluateFromAuthorities(obligations, runtime, evidence, now = new Date().toISOString()) {
366
+ return evaluateObligations(obligations.map(o => bindEvidenceAuthority(bindRuntimeAuthority(o, runtime), evidence)), now);
367
+ }
368
+ /** Production local-store verifier. Local rows earn only content-bound-store-row;
369
+ * live authority requires the supplied live re-query seam and verified actor. */
370
+ export class ProductionMessageEvidenceAuthority {
371
+ storePath;
372
+ liveRequery;
373
+ constructor(storePath, liveRequery) {
374
+ this.storePath = storePath;
375
+ this.liveRequery = liveRequery;
376
+ }
377
+ requery(record) {
378
+ const c = record.nativeCoordinates;
379
+ if (!c || !Number.isInteger(c.topicId) || !Number.isInteger(c.messageId))
380
+ return null;
381
+ let row;
382
+ try {
383
+ row = fs.readFileSync(this.storePath, 'utf8').split(/\n/).filter(Boolean).map(line => JSON.parse(line)).find(item => item.topicId === c.topicId && item.messageId === c.messageId);
384
+ }
385
+ catch { /* @silent-fallback-ok — unreadable evidence store earns no authority */
386
+ return null;
387
+ }
388
+ if (!row || hash(row.text) !== record.canonicalPayloadHash)
389
+ return null;
390
+ if (record.authority === 'content-bound-store-row')
391
+ return { ...record, authority: 'content-bound-store-row', verifierPassed: true, verifiedPayload: row.text };
392
+ const live = this.liveRequery?.(c.topicId, c.messageId);
393
+ if (!live || live.text !== row.text || live.topicId !== row.topicId || live.messageId !== row.messageId)
394
+ return null;
395
+ if (record.authority === 'verified-operator-approval' && !live.verifiedOperator)
396
+ return null;
397
+ return { ...record, authority: record.authority === 'verified-operator-approval' ? 'verified-operator-approval' : 'live-requeried-message', verifierPassed: true, verifiedPayload: live.text };
398
+ }
399
+ }
400
+ export class ProductionRuntimeRegistry {
401
+ snapshot;
402
+ constructor(snapshot) {
403
+ this.snapshot = snapshot;
404
+ }
405
+ resolve(executorId, obligationId) {
406
+ const rows = this.snapshot();
407
+ const matches = rows.filter(row => (!executorId || row.executorId === executorId) && row.registryCoordinates === `obligation:${obligationId}`);
408
+ return matches.length === 1 ? structuredClone(matches[0]) : null;
409
+ }
410
+ }
411
+ export function evaluateObligations(obligations, now = new Date().toISOString()) {
412
+ const issues = [];
413
+ const evaluated = obligations.map(original => {
414
+ const obligation = structuredClone(original);
415
+ const hasProof = obligation.evidence.some(e => evidenceApplies(e, obligation) && authoritySatisfies(e.authority, obligation.evidencePolicy.requiredAuthority));
416
+ if (hasProof && ['pending', 'unknown', 'open-unexecuted', 'blocked', 'failed'].includes(obligation.status))
417
+ obligation.status = 'satisfied';
418
+ if (!hasProof && obligation.status === 'satisfied')
419
+ obligation.status = 'unknown';
420
+ const result = evaluateExecutor(obligation, now);
421
+ obligation.lastEvaluatedAt = now;
422
+ if (!hasProof && (obligation.phase === 'pre-start' || obligation.phase === 'start'))
423
+ result.issues.push('predicate-unsatisfied');
424
+ if (!result.ok && obligation.status === 'pending')
425
+ obligation.status = 'open-unexecuted';
426
+ result.issues.forEach(issue => issues.push(`${obligation.id}:${issue}`));
427
+ return obligation;
428
+ });
429
+ return { admitted: issues.length === 0 && evaluated.every(o => !['pending', 'unknown', 'open-unexecuted', 'failed', 'blocked', 'expired'].includes(o.status) || !['pre-start', 'start'].includes(o.phase)), obligations: evaluated, issues };
430
+ }
431
+ export function transitionFuturePhase(obligation, now) {
432
+ const copy = structuredClone(obligation);
433
+ if (classifyExecutor(copy, now) === 'future-phase')
434
+ return copy;
435
+ if (copy.status !== 'pending')
436
+ return copy;
437
+ const result = evaluateExecutor(copy, now);
438
+ copy.status = result.ok ? 'pending' : 'open-unexecuted';
439
+ return copy;
440
+ }
441
+ export function materializeCadenceInstances(ledger, through) {
442
+ if (!validTime(through))
443
+ throw new Error('invalid-timestamp');
444
+ const copy = structuredClone(ledger);
445
+ for (const template of copy.obligations.filter(o => o.predicate.recurring && !o.id.includes('@'))) {
446
+ const interval = template.id.includes('30m') ? 30 * 60_000 : template.id.includes('3h') ? 3 * 3_600_000 : 0;
447
+ if (!interval)
448
+ continue;
449
+ const start = Date.parse(template.deadline.dueAt);
450
+ if (Date.parse(through) < start)
451
+ continue;
452
+ for (let due = start; due <= Date.parse(through); due += interval) {
453
+ const id = `${template.id}@${new Date(due).toISOString()}`;
454
+ if (copy.compiledObligationIds.includes(id))
455
+ continue;
456
+ const instance = structuredClone(template);
457
+ instance.id = id;
458
+ instance.deadline.dueAt = new Date(due).toISOString();
459
+ instance.eligibleAt = instance.deadline.dueAt;
460
+ instance.predicate.recurring = true;
461
+ instance.status = 'pending';
462
+ instance.evidence = [];
463
+ instance.lastEvaluatedAt = null;
464
+ copy.obligations.push(instance);
465
+ copy.compiledObligationIds.push(id);
466
+ }
467
+ }
468
+ return copy;
469
+ }
470
+ export function evaluateLifecycleTick(ledger, runtime, evidence, now = new Date().toISOString()) {
471
+ let next = materializeCadenceInstances(ledger, now);
472
+ next.obligations = next.obligations.map(o => transitionFuturePhase(o, now));
473
+ const result = evaluateFromAuthorities(next.obligations, runtime, evidence, now);
474
+ next.obligations = result.obligations;
475
+ if (result.issues.length && next.state === 'active_mid_due')
476
+ next.state = 'active_mid_blocked';
477
+ if (result.issues.length && ['close_due', 'delivered_pending_post_live'].includes(next.state))
478
+ next.state = 'close_blocked';
479
+ next.requiredRemediations = next.obligations
480
+ .filter(o => ['pending', 'unknown', 'open-unexecuted', 'failed', 'blocked', 'expired'].includes(o.status))
481
+ .map(deriveFailureRemediation);
482
+ return { ledger: next, issues: result.issues };
483
+ }
484
+ export function evaluateClosure(ledger, _callerExpectedIds, authority = {}) {
485
+ assertEchoScope(ledger.agentId, ledger.scope);
486
+ const issues = [];
487
+ const ids = new Set(ledger.obligations.map(o => o.id));
488
+ if (ids.size !== ledger.obligations.length)
489
+ issues.push('census-duplicate');
490
+ ledger.compiledObligationIds.filter(id => !ids.has(id)).forEach(id => issues.push(`census-missing:${id}`));
491
+ if (ledger.nativeEvaluations.at(-1)?.output?.admitted !== true)
492
+ issues.push('native-structural-preflight-missing-or-refused');
493
+ ids.forEach(id => { if (!ledger.compiledObligationIds.includes(id))
494
+ issues.push(`census-uncompiled:${id}`); });
495
+ for (const o of ledger.obligations) {
496
+ if (['pending', 'unknown', 'open-unexecuted', 'failed', 'blocked', 'expired'].includes(o.status))
497
+ issues.push(`nonterminal:${o.id}:${o.status}`);
498
+ if (o.status === 'waived-for-debt')
499
+ issues.push(`debt-waiver-does-not-close:${o.id}`);
500
+ if (o.status === 'waived-for-phase-transition') {
501
+ const waiver = ledger.waivers?.find(w => w.obligationIds.includes(o.id));
502
+ const now = authority.now ?? new Date().toISOString();
503
+ if (!waiver || !validTime(waiver.expiresAt) || Date.parse(waiver.expiresAt) <= Date.parse(now) || waiver.approvedDigest !== waiver.digest || !waiver.operatorPrincipalId || !waiver.approvalCoordinates || !authority.requeryWaiverApproval?.(waiver))
504
+ issues.push(`waiver-invalid-at-close:${o.id}`);
505
+ else {
506
+ const { digest: _d, operatorPrincipalId: _o, approvedDigest: _a, approvedAt: _t, approvalCoordinates: _c, ...payload } = waiver;
507
+ if (waiver.digest !== waiverDigest(payload))
508
+ issues.push(`waiver-invalid-at-close:${o.id}`);
509
+ }
510
+ }
511
+ if (o.coreDuty && o.status.startsWith('waived'))
512
+ issues.push(`core-duty-waived:${o.id}`);
513
+ if (o.status === 'satisfied' && !o.evidence.some(e => evidenceApplies(e, o) && authoritySatisfies(e.authority, o.evidencePolicy.requiredAuthority)))
514
+ issues.push(`completion-evidence-invalid:${o.id}`);
515
+ }
516
+ if (issues.length)
517
+ return { state: 'close_blocked', issues };
518
+ return { state: ledger.obligations.some(o => o.status === 'waived-for-phase-transition') ? 'closed_with_operator_waiver' : 'closed_clean', issues };
519
+ }
520
+ export function waiverDigest(waiver) { return hash(canonical(waiver)); }
521
+ export const WINDOW_DRY_RUN_INVENTORY = ['tests/unit/window-lifecycle-obligation-ledger.test.ts', 'tests/integration/window-lifecycle-ledger-store.test.ts', 'tests/integration/window-lifecycle-native-adapter.test.ts', 'tests/e2e/window-lifecycle-executor-incident.test.ts', 'tests/e2e/window-lifecycle-production-wiring.test.ts'];
522
+ export function rollbackDigest(request) { return hash(canonical(request)); }
523
+ export function validateWaiver(waiver, ledger, locallyBoundOperatorId, now = new Date().toISOString()) {
524
+ assertEchoScope(waiver.agentId, waiver.scope);
525
+ const issues = [];
526
+ if (waiver.windowId !== ledger.windowId)
527
+ issues.push('wrong-window');
528
+ if (!waiver.obligationIds.length || waiver.obligationIds.some(id => /[*]/.test(id)))
529
+ issues.push('wildcard-or-empty');
530
+ if (ledger.usedNonces.includes(waiver.nonce))
531
+ issues.push('nonce-replay');
532
+ if (!validTime(now) || !validTime(waiver.expiresAt) || !validTime(waiver.createdAt) || Date.parse(waiver.expiresAt) <= Date.parse(now))
533
+ issues.push('waiver-expired');
534
+ if (waiver.nonTransferable !== true)
535
+ issues.push('waiver-transferable');
536
+ const { digest: _d, operatorPrincipalId: _o, approvedDigest: _a, approvedAt: _t, approvalCoordinates: _c, ...payload } = waiver;
537
+ if (waiver.digest !== waiverDigest(payload))
538
+ issues.push('digest-altered');
539
+ if (waiver.operatorPrincipalId !== locallyBoundOperatorId)
540
+ issues.push('wrong-principal');
541
+ if (waiver.approvedDigest !== waiver.digest || !waiver.approvedAt || !validTime(waiver.approvedAt) || Date.parse(waiver.approvedAt) <= Date.parse(waiver.createdAt) || !waiver.approvalCoordinates)
542
+ issues.push('approval-unbound-or-precreated');
543
+ for (const id of waiver.obligationIds) {
544
+ const o = ledger.obligations.find(item => item.id === id);
545
+ if (!o)
546
+ issues.push(`unknown-obligation:${id}`);
547
+ else if (o.phase !== waiver.phase)
548
+ issues.push(`wrong-phase:${id}`);
549
+ else if (o.coreDuty || o.waiverPolicy === 'non-waivable')
550
+ issues.push(`core-duty-non-waivable:${id}`);
551
+ else if (waiver.permit === 'phase-transition' && o.waiverPolicy !== 'phase-transition')
552
+ issues.push(`phase-transition-not-permitted:${id}`);
553
+ }
554
+ return issues;
555
+ }
556
+ export function createLedger(input) {
557
+ assertEchoScope(input.agentId, input.scope);
558
+ const ledger = { version: 1, lifecycleRunId: crypto.randomUUID(), agentId: 'echo', scope: 'echo-window-lifecycle', windowId: input.windowId, state: 'pre_start_gate', sourceHashes: input.compiled.hashes, compiledObligationIds: input.compiled.obligations.map(o => o.id), obligations: input.compiled.obligations, usedNonces: [], nativeEvaluations: [], waivers: [] };
559
+ const closeAt = ledger.obligations.filter(o => o.phase === 'close' || o.phase === 'post-live').map(o => Date.parse(o.deadline.dueAt)).filter(Number.isFinite);
560
+ return closeAt.length ? materializeCadenceInstances(ledger, new Date(Math.max(...closeAt)).toISOString()) : ledger;
561
+ }
562
+ const TRANSITIONS = {
563
+ idle: ['pre_start_gate'], pre_start_gate: ['start_blocked', 'active_start', 'rolled_back'], start_blocked: ['pre_start_gate', 'rolled_back'],
564
+ active_start: ['active_mid_due', 'rolled_back'], active_mid_due: ['active_mid_blocked', 'active_mid_satisfied', 'rolled_back'],
565
+ active_mid_blocked: ['active_mid_due', 'rolled_back'], active_mid_satisfied: ['close_due', 'rolled_back'], close_due: ['close_blocked', 'delivered_pending_post_live', 'rolled_back'],
566
+ close_blocked: ['close_due', 'rolled_back'], delivered_pending_post_live: ['close_blocked', 'closed_clean', 'closed_with_operator_waiver', 'rolled_back'],
567
+ closed_clean: [], closed_with_operator_waiver: [], rolled_back: [],
568
+ };
569
+ export function transitionLedger(ledger, target, closureAuthority) {
570
+ assertEchoScope(ledger.agentId, ledger.scope);
571
+ if (!TRANSITIONS[ledger.state].includes(target))
572
+ throw new Error(`invalid-lifecycle-transition:${ledger.state}->${target}`);
573
+ if (target === 'active_start' && (!ledger.admission || ledger.admission.snapshotDigest !== admissionSnapshotDigest(ledger) || ledger.nativeEvaluations.at(-1)?.output?.admitted !== true))
574
+ throw new Error('admission-not-proven');
575
+ const phaseReady = (phases) => ledger.obligations.filter(o => phases.includes(o.phase)).every(o => o.status === 'waived-for-phase-transition' || (o.status === 'satisfied' && o.evidence.some(e => evidenceApplies(e, o) && authoritySatisfies(e.authority, o.evidencePolicy.requiredAuthority))));
576
+ if (target === 'active_mid_due') {
577
+ const eligible = ledger.obligations.filter(o => o.phase === 'mid').map(o => Date.parse(o.eligibleAt ?? o.deadline.dueAt));
578
+ if (eligible.length && Date.parse(closureAuthority?.now ?? new Date().toISOString()) < Math.min(...eligible))
579
+ throw new Error('mid-not-due');
580
+ }
581
+ if (target === 'active_mid_satisfied' && !phaseReady(['mid']))
582
+ throw new Error('mid-phase-not-satisfied');
583
+ if (target === 'close_due' && !phaseReady(['mid']))
584
+ throw new Error('mid-phase-not-satisfied');
585
+ if (target === 'delivered_pending_post_live' && !phaseReady(['close']))
586
+ throw new Error('close-phase-not-satisfied');
587
+ if (target === 'closed_clean' || target === 'closed_with_operator_waiver') {
588
+ const closure = evaluateClosure(ledger, undefined, closureAuthority);
589
+ if (closure.state !== target)
590
+ throw new Error(`closure-refused:${closure.issues.join(',')}`);
591
+ }
592
+ const copy = structuredClone(ledger);
593
+ copy.state = target;
594
+ return copy;
595
+ }
596
+ export function applyWaiver(ledger, waiver, locallyBoundOperatorId, now = new Date().toISOString()) {
597
+ const issues = validateWaiver(waiver, ledger, locallyBoundOperatorId, now);
598
+ if (issues.length)
599
+ throw new Error(`waiver-refused:${issues.join(',')}`);
600
+ const copy = structuredClone(ledger);
601
+ copy.usedNonces.push(waiver.nonce);
602
+ copy.waivers ??= [];
603
+ copy.waivers.push(waiver);
604
+ for (const id of waiver.obligationIds) {
605
+ const o = copy.obligations.find(item => item.id === id);
606
+ o.status = waiver.permit === 'phase-transition' ? 'waived-for-phase-transition' : 'waived-for-debt';
607
+ }
608
+ return copy;
609
+ }
610
+ export class EchoWindowLedgerStore {
611
+ file;
612
+ constructor(agentHome) { this.file = path.join(agentHome, 'window-lifecycle', 'ledger.json'); }
613
+ load(agentId, scope) { assertEchoScope(agentId, scope); if (!fs.existsSync(this.file))
614
+ return null; const parsed = JSON.parse(fs.readFileSync(this.file, 'utf8')); this.validate(parsed); return parsed; }
615
+ save(ledger) { this.validate(ledger); fs.mkdirSync(path.dirname(this.file), { recursive: true, mode: 0o700 }); const tmp = `${this.file}.${process.pid}.tmp`; fs.writeFileSync(tmp, `${JSON.stringify(ledger, null, 2)}\n`, { mode: 0o600 }); fs.renameSync(tmp, this.file); }
616
+ appendNativeEvaluation(agentId, scope, record) { const ledger = this.load(agentId, scope); if (!ledger)
617
+ throw new Error('ledger-not-found'); if (record.agentId !== ledger.agentId || record.scope !== ledger.scope || record.windowId !== ledger.windowId)
618
+ throw new Error('foreign-native-evaluation'); if (ledger.usedNonces.includes(record.nonce))
619
+ throw new Error('nonce-replay'); ledger.usedNonces.push(record.nonce); ledger.nativeEvaluations.push(record); ledger.admission = undefined; this.save(ledger); return ledger; }
620
+ rollback(agentId, scope, request, operatorEvidence, authority, at = new Date().toISOString()) { assertEchoScope(agentId, scope); const ledger = this.load(agentId, scope); if (!ledger)
621
+ throw new Error('ledger-not-found'); const { digest: _digest, approvalCoordinates: _coords, ...payload } = request; if (request.windowId !== ledger.windowId || request.digest !== rollbackDigest(payload) || ledger.usedNonces.includes(request.nonce) || !validTime(request.createdAt) || Date.parse(request.createdAt) >= Date.parse(at))
622
+ throw new Error('invalid-rollback-payload'); const verified = authority.requery(operatorEvidence); if (!verified || verified.authority !== 'verified-operator-approval' || !verified.producer || verified.agentId !== ledger.agentId || verified.scope !== ledger.scope || verified.windowId !== ledger.windowId || verified.nativeCoordinates?.topicId !== request.approvalCoordinates.topicId || verified.nativeCoordinates?.messageId !== request.approvalCoordinates.messageId || verified.verifiedPayload?.trim() !== `approve rollback ${request.digest}`)
623
+ throw new Error('verified-operator-rollback-required'); ledger.usedNonces.push(request.nonce); ledger.state = 'rolled_back'; ledger.rollback = { at, reason: request.reason, operatorEvidence: verified, operatorPrincipalId: verified.producer, windowId: ledger.windowId, scope: ledger.scope, enforcementDisabled: true, auditReadOnly: true, manualRitualRequired: true }; ledger.admission = undefined; this.save(ledger); return ledger; }
624
+ reenable(agentId, scope, faultFixedEvidencePath, dryRunRunner, at = new Date().toISOString()) { assertEchoScope(agentId, scope); const ledger = this.load(agentId, scope); if (!ledger || ledger.state !== 'rolled_back' || !ledger.rollback)
625
+ throw new Error('rollback-not-active'); let fixedHash; try {
626
+ fixedHash = hash(fs.readFileSync(faultFixedEvidencePath));
627
+ }
628
+ catch {
629
+ throw new Error('fault-fixed-evidence-required');
630
+ } if (!dryRunRunner)
631
+ throw new Error('dry-run-runner-required'); let suite; try {
632
+ suite = dryRunRunner();
633
+ }
634
+ catch {
635
+ throw new Error('dry-run-suite-required');
636
+ } if (suite.passed !== true || !validTime(suite.completedAt) || Date.parse(suite.completedAt) < Date.parse(ledger.rollback.at) || !/^[a-f0-9]{64}$/.test(suite.outputHash) || !WINDOW_DRY_RUN_INVENTORY.every(file => suite.testInventory.includes(file)))
637
+ throw new Error('dry-run-suite-required'); const suitePath = path.join(path.dirname(this.file), 'dry-run-suite.json'); fs.writeFileSync(suitePath, `${JSON.stringify({ ...suite, sourceHashes: ledger.sourceHashes }, null, 2)}\n`, { mode: 0o600 }); ledger.rollback.reenabledAt = at; ledger.rollback.faultFixedEvidence = `${faultFixedEvidencePath}#sha256:${fixedHash}`; ledger.rollback.dryRunSuitePassed = true; ledger.state = 'pre_start_gate'; this.save(ledger); return ledger; }
638
+ validate(ledger) { assertEchoScope(ledger.agentId, ledger.scope); if (!/^[0-9a-f-]{36}$/.test(ledger.lifecycleRunId))
639
+ throw new Error('lifecycle-run-id-missing'); if (!Array.isArray(ledger.compiledObligationIds))
640
+ throw new Error('compiled-census-missing'); for (const n of ledger.nativeEvaluations)
641
+ if (n.agentId !== ledger.agentId || n.scope !== ledger.scope || n.windowId !== ledger.windowId)
642
+ throw new Error('foreign-native-evaluation'); for (const o of ledger.obligations) {
643
+ assertEchoScope(o.agentId, o.scope);
644
+ if (o.windowId !== ledger.windowId)
645
+ throw new Error('cross-window-obligation');
646
+ for (const e of o.evidence) {
647
+ assertEchoScope(e.agentId, e.scope);
648
+ if (e.windowId !== ledger.windowId || e.obligationId !== o.id)
649
+ throw new Error('foreign-evidence-binding');
650
+ }
651
+ } }
652
+ }
653
+ export function runNativeAdmissionAdapter(input) {
654
+ assertEchoScope(input.agentId, input.scope);
655
+ if ((input.installedContractVersion ?? BETWEEN_WINDOW_ADMISSION_CONTRACT_VERSION) !== BETWEEN_WINDOW_ADMISSION_CONTRACT_VERSION)
656
+ throw new Error('native-evaluator-incompatible');
657
+ if (!/^[A-Za-z0-9_-]{8,128}$/.test(input.nonce))
658
+ throw new Error('invalid-native-evaluation-nonce');
659
+ const inputBytes = JSON.stringify(input.package);
660
+ const selectedStore = input.storePath ?? path.join(input.stateDir, 'telegram-messages.jsonl');
661
+ const output = evaluateBetweenWindowAdmission({ stateDir: input.stateDir, package: input.package, storePath: input.storePath });
662
+ const storeBytes = fs.readFileSync(selectedStore);
663
+ return { agentId: 'echo', scope: 'echo-window-lifecycle', windowId: input.windowId, inputHash: hash(inputBytes), inputBytes, storePath: selectedStore, storeHash: hash(storeBytes), storeBytesLength: storeBytes.length, evaluatorVersion: NATIVE_ADMISSION_COMPATIBILITY, output, evaluatedAt: input.evaluatedAt ?? new Date().toISOString(), nonce: input.nonce, mapping: { admitted: 'preground.native-structural-preflight' } };
664
+ }
665
+ export function admissionSnapshotDigest(ledger) {
666
+ return hash(JSON.stringify({ sourceHashes: ledger.sourceHashes, ids: ledger.compiledObligationIds, obligations: ledger.obligations.map(o => ({ id: o.id, status: o.status, binding: o.executorBinding, evidence: o.evidence.map(e => ({ authority: e.authority, nonce: e.nonce, hash: e.canonicalPayloadHash, verifierPassed: e.verifierPassed })) })), native: ledger.nativeEvaluations.at(-1) ?? null }));
667
+ }
668
+ export function sourceFreshnessIssues(ledger) {
669
+ const issues = [];
670
+ for (const [source, expected] of Object.entries(ledger.sourceHashes)) {
671
+ try {
672
+ if (hash(fs.readFileSync(source)) !== expected)
673
+ issues.push(`stale-source:${source}`);
674
+ }
675
+ catch {
676
+ issues.push(`source-unavailable:${source}`);
677
+ }
678
+ }
679
+ return issues;
680
+ }
681
+ export function deriveFailureRemediation(obligation) {
682
+ const action = obligation.failureAction;
683
+ const escalation = action === 'block-new-scope-and-escalate';
684
+ const scopeReview = action === 'scope-timebox-review';
685
+ return {
686
+ obligationId: obligation.id, action,
687
+ blockPhase: action === 'block-phase' || action === 'fail-close' || action === 'block-and-surface',
688
+ blockNewScope: action.includes('block-new-scope') || scopeReview,
689
+ blockSend: action === 'block-send',
690
+ blockCompletion: action === 'block-completion-claim' || action === 'fail-close',
691
+ observerTopicId: action === 'repost-visibly' || action === 'block-and-surface' ? 43003 : escalation || scopeReview || action.includes('register-debt') || action.startsWith('correct-') ? 36966 : undefined,
692
+ debtRequired: action.includes('register-debt'), repostRequired: action === 'repost-visibly',
693
+ correctionRequired: action.startsWith('correct-'), scopeReviewRequired: scopeReview,
694
+ };
695
+ }
696
+ export function evaluateLifecycleGuard(ledger, action) {
697
+ if (ledger.state === 'rolled_back')
698
+ return action === 'telegram-send' ? { allowed: false, reasons: ['rollback:operator-account-send-disabled'] } : { allowed: true, reasons: [] };
699
+ if (ledger.state === 'closed_clean' || ledger.state === 'closed_with_operator_waiver')
700
+ return { allowed: true, reasons: [] };
701
+ const unresolved = ledger.obligations.filter(o => ['pending', 'unknown', 'open-unexecuted', 'failed', 'blocked', 'expired'].includes(o.status));
702
+ const selected = unresolved.filter(o => {
703
+ const remediation = deriveFailureRemediation(o);
704
+ return action === 'new-scope' ? remediation.blockNewScope
705
+ : action === 'telegram-send' ? remediation.blockSend
706
+ : remediation.blockCompletion || o.id.includes('no-done-without-effect') || o.id.includes('terminal-census');
707
+ });
708
+ return { allowed: selected.length === 0, reasons: selected.map(o => `${o.id}:${o.status}:${o.failureAction}`) };
709
+ }
710
+ export function runWindowLifecyclePostLiveCheck(ledger) {
711
+ const probe = structuredClone(ledger);
712
+ const target = probe.obligations.find(o => o.id === 'close.no-done-without-effect') ?? probe.obligations.find(o => o.failureAction.includes('block-completion'));
713
+ if (!target)
714
+ return { passed: false, inputHash: hash(canonical({ ids: probe.compiledObligationIds })), outputHash: hash('missing-probe-duty'), reasons: ['missing-completion-probe-duty'] };
715
+ // Normalize unrelated lifecycle state so this replay is stable as later
716
+ // evidence lands. The consumer under test is the real guard/closure pair,
717
+ // and the single controlled fault is the compiled completion-blocking duty.
718
+ // A persisted closed ledger is a valid input to this replay, but closed
719
+ // states intentionally short-circuit the live guard. Re-enter the exact
720
+ // pre-closure consumer state so the controlled omission exercises the
721
+ // production completion guard instead of inheriting that terminal bypass.
722
+ probe.state = 'delivered_pending_post_live';
723
+ for (const obligation of probe.obligations) {
724
+ obligation.status = 'satisfied';
725
+ obligation.evidence = [];
726
+ }
727
+ target.status = 'open-unexecuted';
728
+ target.evidence = [];
729
+ const guard = evaluateLifecycleGuard(probe, 'completion-claim');
730
+ const closure = evaluateClosure(probe);
731
+ const result = { guard, closureState: closure.state, closureIssues: closure.issues };
732
+ return { passed: !guard.allowed && closure.state === 'close_blocked' && closure.issues.some(issue => issue.includes(target.id)), inputHash: hash(canonical({ ids: probe.compiledObligationIds, target: target.id })), outputHash: hash(canonical(result)), reasons: [...guard.reasons, ...closure.issues] };
733
+ }
734
+ //# sourceMappingURL=WindowLifecycleObligationLedger.js.map