instar 1.3.461 → 1.3.463
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/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +7 -24
- package/dist/commands/server.js.map +1 -1
- package/dist/feedback-factory/cutoverReadiness.d.ts +34 -0
- package/dist/feedback-factory/cutoverReadiness.d.ts.map +1 -1
- package/dist/feedback-factory/cutoverReadiness.js +37 -0
- package/dist/feedback-factory/cutoverReadiness.js.map +1 -1
- package/dist/feedback-factory/migration/PersistedShadowImportTarget.d.ts +54 -0
- package/dist/feedback-factory/migration/PersistedShadowImportTarget.d.ts.map +1 -0
- package/dist/feedback-factory/migration/PersistedShadowImportTarget.js +121 -0
- package/dist/feedback-factory/migration/PersistedShadowImportTarget.js.map +1 -0
- package/dist/feedback-factory/migration/integrityPassRunner.d.ts +26 -0
- package/dist/feedback-factory/migration/integrityPassRunner.d.ts.map +1 -0
- package/dist/feedback-factory/migration/integrityPassRunner.js +83 -0
- package/dist/feedback-factory/migration/integrityPassRunner.js.map +1 -0
- package/dist/monitoring/sentinelConsolidatedSend.d.ts +26 -0
- package/dist/monitoring/sentinelConsolidatedSend.d.ts.map +1 -0
- package/dist/monitoring/sentinelConsolidatedSend.js +80 -0
- package/dist/monitoring/sentinelConsolidatedSend.js.map +1 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +49 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +1 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +3 -0
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +28 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +47 -47
- package/upgrades/1.3.462.md +83 -0
- package/upgrades/1.3.463.md +27 -0
- package/upgrades/eli16/integrity-leg-shadow-import.md +27 -0
- package/upgrades/side-effects/integrity-leg-shadow-import.md +35 -0
- package/upgrades/side-effects/sentinel-escalation-selfheal.md +65 -0
|
@@ -97,6 +97,21 @@ export type ImportDryRunOutcome = {
|
|
|
97
97
|
ok: false;
|
|
98
98
|
reason: string;
|
|
99
99
|
};
|
|
100
|
+
/**
|
|
101
|
+
* Outcome of the REAL pre-click integrity pass. Unlike the dry-run, a passing (or
|
|
102
|
+
* failing) report is recorded to the CANONICAL integrity path via recordIntegrityReport
|
|
103
|
+
* — so this outcome IS load-bearing on `ready`. `recorded` says whether a report landed
|
|
104
|
+
* (false only on a pre-import abort, where there is no report to record).
|
|
105
|
+
*/
|
|
106
|
+
export type ImportIntegrityPassOutcome = {
|
|
107
|
+
ok: true;
|
|
108
|
+
result: ImportRunResult;
|
|
109
|
+
generatedAt: string;
|
|
110
|
+
recorded: boolean;
|
|
111
|
+
} | {
|
|
112
|
+
ok: false;
|
|
113
|
+
reason: string;
|
|
114
|
+
};
|
|
100
115
|
export interface CutoverReadinessDeps {
|
|
101
116
|
parityMonitor: DurableParityMonitor;
|
|
102
117
|
/** Where the import tooling's IntegrityReport persists (JSON envelope). */
|
|
@@ -113,6 +128,13 @@ export interface CutoverReadinessDeps {
|
|
|
113
128
|
/** SERVER-SIDE import rehearsal (live source fetch → in-memory AS-IS import →
|
|
114
129
|
* integrity gate over readback). Null/absent when no source is configured. */
|
|
115
130
|
runImportDryRun?: (() => Promise<ImportRunResult>) | null;
|
|
131
|
+
/** SERVER-SIDE REAL pre-click integrity pass (live source fetch → AS-IS import into
|
|
132
|
+
* a PERSISTED shadow → integrity gate over readback). Distinct from the dry-run:
|
|
133
|
+
* its passing report greens the canonical integrity condition via
|
|
134
|
+
* recordIntegrityReport. Run OFF the event loop (AgentServer spawns a child process)
|
|
135
|
+
* because the full-corpus fetch+import cannot settle in-process (#948). Null/absent
|
|
136
|
+
* when no source is configured. */
|
|
137
|
+
runIntegrityImport?: (() => Promise<ImportRunResult>) | null;
|
|
116
138
|
/** Freshness bound for the parity window (default 6h). */
|
|
117
139
|
maxPassStalenessMs?: number;
|
|
118
140
|
/**
|
|
@@ -175,6 +197,18 @@ export declare class CutoverReadiness {
|
|
|
175
197
|
* integrity report is structurally out of reach of this method.
|
|
176
198
|
*/
|
|
177
199
|
runImportDryRunPass(): Promise<ImportDryRunOutcome>;
|
|
200
|
+
/**
|
|
201
|
+
* TRIGGER the REAL pre-click integrity pass (T7: the agent may ask; the server
|
|
202
|
+
* computes — live source fetch → AS-IS import into a PERSISTED shadow → integrity
|
|
203
|
+
* gate over the readback). UNLIKE the dry-run, the resulting report is recorded to
|
|
204
|
+
* the CANONICAL integrity path via recordIntegrityReport: a PASSING report greens
|
|
205
|
+
* `ready`, a FAILING report flips it closed (the door always reflects the LATEST
|
|
206
|
+
* real verdict — never a stale green). A pre-import abort (fingerprint collision →
|
|
207
|
+
* null report) records nothing and leaves prior state; the abort is returned for the
|
|
208
|
+
* operator to resolve. A FAILED fetch records nothing (absence of evidence, not a
|
|
209
|
+
* verdict). Shares the single-flight live-fetch guard + the max-hold backstop.
|
|
210
|
+
*/
|
|
211
|
+
runIntegrityPass(): Promise<ImportIntegrityPassOutcome>;
|
|
178
212
|
importDryRunStatus(): ImportDryRunStatus;
|
|
179
213
|
/** The everything-up-to-the-door readiness signal. Read-only; never fires anything. */
|
|
180
214
|
status(): CutoverReadinessStatus;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cutoverReadiness.d.ts","sourceRoot":"","sources":["../../src/feedback-factory/cutoverReadiness.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cutoverReadiness.d.ts","sourceRoot":"","sources":["../../src/feedback-factory/cutoverReadiness.ts"],"names":[],"mappings":"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"}
|
|
@@ -186,6 +186,43 @@ export class CutoverReadiness {
|
|
|
186
186
|
fs.writeFileSync(this.d.importDryRunReportPath, JSON.stringify(envelope, null, 2));
|
|
187
187
|
return { ok: true, result, generatedAt };
|
|
188
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* TRIGGER the REAL pre-click integrity pass (T7: the agent may ask; the server
|
|
191
|
+
* computes — live source fetch → AS-IS import into a PERSISTED shadow → integrity
|
|
192
|
+
* gate over the readback). UNLIKE the dry-run, the resulting report is recorded to
|
|
193
|
+
* the CANONICAL integrity path via recordIntegrityReport: a PASSING report greens
|
|
194
|
+
* `ready`, a FAILING report flips it closed (the door always reflects the LATEST
|
|
195
|
+
* real verdict — never a stale green). A pre-import abort (fingerprint collision →
|
|
196
|
+
* null report) records nothing and leaves prior state; the abort is returned for the
|
|
197
|
+
* operator to resolve. A FAILED fetch records nothing (absence of evidence, not a
|
|
198
|
+
* verdict). Shares the single-flight live-fetch guard + the max-hold backstop.
|
|
199
|
+
*/
|
|
200
|
+
async runIntegrityPass() {
|
|
201
|
+
if (!this.d.runIntegrityImport) {
|
|
202
|
+
return { ok: false, reason: 'no import source configured (feedbackMigration.paritySource) — cannot run a real integrity pass' };
|
|
203
|
+
}
|
|
204
|
+
if (this.liveFetchInFlight) {
|
|
205
|
+
return { ok: false, reason: `live source fetch already in flight (${this.liveFetchInFlight.op} started ${this.liveFetchInFlight.startedAt}) — one at a time; retry after it completes` };
|
|
206
|
+
}
|
|
207
|
+
this.liveFetchInFlight = { op: 'integrity-pass', startedAt: new Date(this.nowMs()).toISOString() };
|
|
208
|
+
let result;
|
|
209
|
+
try {
|
|
210
|
+
result = await this.withMaxHold(this.d.runIntegrityImport(), 'integrity-pass');
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
return { ok: false, reason: `integrity pass failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
this.liveFetchInFlight = null;
|
|
217
|
+
}
|
|
218
|
+
const generatedAt = new Date(this.nowMs()).toISOString();
|
|
219
|
+
let recorded = false;
|
|
220
|
+
if (result.report) {
|
|
221
|
+
this.recordIntegrityReport(result.report, generatedAt);
|
|
222
|
+
recorded = true;
|
|
223
|
+
}
|
|
224
|
+
return { ok: true, result, generatedAt, recorded };
|
|
225
|
+
}
|
|
189
226
|
importDryRunStatus() {
|
|
190
227
|
const empty = { ran: false, passed: false, generatedAt: null, imported: null, abortedPreImport: null };
|
|
191
228
|
if (!this.d.importDryRunReportPath)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cutoverReadiness.js","sourceRoot":"","sources":["../../src/feedback-factory/cutoverReadiness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cutoverReadiness.js","sourceRoot":"","sources":["../../src/feedback-factory/cutoverReadiness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAqH7B,MAAM,6BAA6B,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACzD;gFACgF;AAChF,MAAM,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAajD,MAAM,OAAO,gBAAgB;IACV,CAAC,CAAuB;IACxB,UAAU,CAAS;IACnB,cAAc,CAAS;IACxC;;;;;;;;;OASG;IACK,iBAAiB,GAA0F,IAAI,CAAC;IAExH,YAAY,IAA0B;QACpC,IAAI,IAAI,CAAC,sBAAsB,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC;YACxH,sEAAsE;YACtE,6EAA6E;YAC7E,MAAM,IAAI,KAAK,CAAC,6GAA6G,CAAC,CAAC;QACjI,CAAC;QACD,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QACd,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,kBAAkB,IAAI,6BAA6B,CAAC;QAC3E,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,yBAAyB,CAAC;IACzE,CAAC;IAEO,KAAK;QACX,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IAChD,CAAC;IAED;;;;;;;+DAO2D;IACnD,KAAK,CAAC,WAAW,CAAI,IAAgB,EAAE,EAAU;QACvD,IAAI,KAAgD,CAAC;QACrD,MAAM,KAAK,GAAG,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;YACpD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBACtB,MAAM,CAAC,IAAI,KAAK,CACd,QAAQ,EAAE,wCAAwC,IAAI,CAAC,cAAc,iIAAiI,CACvM,CAAC,CAAC;YACL,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;YACxB,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,UAAU;gBAAE,KAAK,CAAC,KAAK,EAAE,CAAC;QACvD,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QAC3C,CAAC;gBAAS,CAAC;YACT,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,qBAAqB,CAAC,MAAuB,EAAE,WAAoB;QACjE,MAAM,QAAQ,GAA+B;YAC3C,WAAW,EAAE,WAAW,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,WAAW,EAAE;YAChE,MAAM;SACP,CAAC;QACF,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,mBAAmB,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5E,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,mBAAmB,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAClF,CAAC;IAED,eAAe;QACb,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAA+B,CAAC;YAC1G,MAAM,CAAC,GAAG,GAAG,EAAE,MAAM,CAAC;YACtB,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBACxC,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;YACzE,CAAC;YACD,OAAO;gBACL,GAAG,EAAE,IAAI;gBACT,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,WAAW,EAAE,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;gBACzE,OAAO,EAAE;oBACP,qBAAqB,EAAE,CAAC,CAAC,qBAAqB,EAAE,MAAM,IAAI,CAAC;oBAC3D,iBAAiB,EAAE,CAAC,CAAC,iBAAiB,EAAE,MAAM,IAAI,CAAC;oBACnD,kBAAkB,EAAE,CAAC,CAAC,kBAAkB,EAAE,MAAM,IAAI,CAAC;oBACrD,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,MAAM,IAAI,CAAC;iBAC1C;aACF,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC,CAAC,4FAA4F;YACpG,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACzE,CAAC;IACH,CAAC;IAED,YAAY;QACV,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACpD,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC;QAC3C,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3E,MAAM,KAAK,GAAG,UAAU,KAAK,IAAI;YAC/B,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC;QAC5D,OAAO,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IACxC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,aAAa;QACjB,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC;YAC3B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wFAAwF,EAAE,CAAC;QACzH,CAAC;QACD,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wCAAwC,IAAI,CAAC,iBAAiB,CAAC,EAAE,YAAY,IAAI,CAAC,iBAAiB,CAAC,SAAS,6CAA6C,EAAE,CAAC;QAC3L,CAAC;QACD,IAAI,CAAC,iBAAiB,GAAG,EAAE,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QAChG,IAAI,MAAoB,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,EAAE,EAAE,aAAa,CAAC,CAAC;QAC1E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wBAAwB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QAC3G,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAChC,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAChD,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC9C,MAAM,WAAW,GAAG,MAAM,CAAC,sBAAsB,CAAC,MAAM,GAAG,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC;QAC5F,OAAO;YACL,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,EAAE,EAAE,EAAE,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,GAAG,CAAC,EAAE;YAChG,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;SACpC,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,mBAAmB;QACvB,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,sBAAsB,EAAE,CAAC;YAC9D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,6FAA6F,EAAE,CAAC;QAC9H,CAAC;QACD,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wCAAwC,IAAI,CAAC,iBAAiB,CAAC,EAAE,YAAY,IAAI,CAAC,iBAAiB,CAAC,SAAS,6CAA6C,EAAE,CAAC;QAC3L,CAAC;QACD,IAAI,CAAC,iBAAiB,GAAG,EAAE,EAAE,EAAE,gBAAgB,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QACnG,IAAI,MAAuB,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,EAAE,EAAE,gBAAgB,CAAC,CAAC;QAC9E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,0BAA0B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QAC7G,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAChC,CAAC;QACD,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACzD,MAAM,QAAQ,GAA4B,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QACnF,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,sBAAsB,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/E,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,sBAAsB,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACnF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAC3C,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,gBAAgB;QACpB,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,EAAE,CAAC;YAC/B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,iGAAiG,EAAE,CAAC;QAClI,CAAC;QACD,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wCAAwC,IAAI,CAAC,iBAAiB,CAAC,EAAE,YAAY,IAAI,CAAC,iBAAiB,CAAC,SAAS,6CAA6C,EAAE,CAAC;QAC3L,CAAC;QACD,IAAI,CAAC,iBAAiB,GAAG,EAAE,EAAE,EAAE,gBAAgB,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QACnG,IAAI,MAAuB,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,EAAE,EAAE,gBAAgB,CAAC,CAAC;QACjF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,0BAA0B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QAC7G,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAChC,CAAC;QACD,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACzD,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;YACvD,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;IACrD,CAAC;IAED,kBAAkB;QAChB,MAAM,KAAK,GAAuB,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;QAC3H,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,sBAAsB;YAAE,OAAO,KAAK,CAAC;QACjD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAA4B,CAAC;YAC1G,MAAM,CAAC,GAAG,GAAG,EAAE,MAAM,CAAC;YACtB,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAC;YACtD,OAAO;gBACL,GAAG,EAAE,IAAI;gBACT,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,WAAW,EAAE,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;gBACzE,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,IAAI;gBAC5B,gBAAgB,EAAE,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;aACxE,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC,CAAC,8GAA8G;YACtH,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,uFAAuF;IACvF,MAAM;QACJ,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACnC,OAAO;YACL,0FAA0F;YAC1F,KAAK,EAAE,SAAS,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK;YAC1D,IAAI,EAAE,uBAAuB;YAC7B,SAAS;YACT,MAAM;YACN,YAAY,EAAE,IAAI,CAAC,kBAAkB,EAAE;SACxC,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PersistedShadowImportTarget.ts — a DURABLE, on-disk AS-IS import sink for the
|
|
3
|
+
* pre-click cutover integrity gate (spec §2.4; feedback-factory-migration.md §2.5).
|
|
4
|
+
*
|
|
5
|
+
* Why this exists (and why it is NOT `InMemoryImportTarget`):
|
|
6
|
+
*
|
|
7
|
+
* `cutoverReadiness.ts` hard-gates the operator's IRREVERSIBLE cutover click on
|
|
8
|
+
* `ready = integrity.passed && parity.cleared && !parity.stale`. The integrity leg
|
|
9
|
+
* greens ONLY when `recordIntegrityReport(passingReport)` is called. The existing
|
|
10
|
+
* import-DRYRUN route is deliberately walled off from that path (its report goes to
|
|
11
|
+
* `importDryRunReportPath`, never `integrityReportPath`) precisely so an EPHEMERAL,
|
|
12
|
+
* in-memory rehearsal can never quietly green an irreversible gate.
|
|
13
|
+
*
|
|
14
|
+
* The pre-click integrity gate therefore needs a target that is a step more real than
|
|
15
|
+
* the in-memory dry-run: a **persisted** shadow the import physically lands in and is
|
|
16
|
+
* read back FROM DISK, so the recorded integrity report corresponds to a durable,
|
|
17
|
+
* inspectable artifact — not an in-process Map that vanishes with the process. The
|
|
18
|
+
* shadow is a VERIFICATION target, never canonical: the canonical production import
|
|
19
|
+
* (Portal's apply-clusters / the real Prisma write) happens AFTER the click. This
|
|
20
|
+
* mirrors `InMemoryImportTarget`'s contract exactly (same `ImportTarget` interface,
|
|
21
|
+
* same dup-PK refusal, same `schemaDescriptor() === null` → the runner derives the
|
|
22
|
+
* dry-run schema), differing ONLY in where the rows live.
|
|
23
|
+
*
|
|
24
|
+
* Backing store: two newline-delimited JSON files (`clusters.jsonl`, `feedback.jsonl`)
|
|
25
|
+
* under a caller-supplied directory. JSONL (not SQLite) keeps the target dependency-free
|
|
26
|
+
* so the out-of-process integrity-pass runner bundles cleanly (no native module). Each
|
|
27
|
+
* AS-IS import APPENDS one row; `readBack*` parses the file. Duplicate PKs are refused
|
|
28
|
+
* before the append (mirrors the DB @id constraint the real adapter would hit). The
|
|
29
|
+
* shadow directory is disposable — `dispose()` removes it after the gate has run.
|
|
30
|
+
*/
|
|
31
|
+
import { type ImportTarget, type RawRow } from './importRunner.js';
|
|
32
|
+
import type { SchemaDescriptor } from './importIntegrity.js';
|
|
33
|
+
export declare class PersistedShadowImportTarget implements ImportTarget {
|
|
34
|
+
private readonly dir;
|
|
35
|
+
private readonly clustersPath;
|
|
36
|
+
private readonly feedbackPath;
|
|
37
|
+
private readonly clusterIds;
|
|
38
|
+
private readonly feedbackIds;
|
|
39
|
+
constructor(dir: string);
|
|
40
|
+
private readJsonl;
|
|
41
|
+
importClusterAsIs(row: RawRow): void;
|
|
42
|
+
importFeedbackAsIs(row: RawRow): void;
|
|
43
|
+
readBackClusters(): RawRow[];
|
|
44
|
+
readBackFeedback(): RawRow[];
|
|
45
|
+
/**
|
|
46
|
+
* No real schema descriptor — like the in-memory dry-run target, the runner derives
|
|
47
|
+
* the target schema from readback + the canonical accepted-status contract. (A future
|
|
48
|
+
* real Prisma adapter would return its actual descriptor here.)
|
|
49
|
+
*/
|
|
50
|
+
schemaDescriptor(): SchemaDescriptor | null;
|
|
51
|
+
/** Remove the on-disk shadow. Call after the integrity gate has run + the report is recorded. */
|
|
52
|
+
dispose(): void;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=PersistedShadowImportTarget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PersistedShadowImportTarget.d.ts","sourceRoot":"","sources":["../../../src/feedback-factory/migration/PersistedShadowImportTarget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,MAAM,EAEZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAa7D,qBAAa,2BAA4B,YAAW,YAAY;IASlD,OAAO,CAAC,QAAQ,CAAC,GAAG;IARhC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IAItC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;gBAEpB,GAAG,EAAE,MAAM;IAexC,OAAO,CAAC,SAAS;IAWjB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAQpC,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAQrC,gBAAgB,IAAI,MAAM,EAAE;IAI5B,gBAAgB,IAAI,MAAM,EAAE;IAI5B;;;;OAIG;IACH,gBAAgB,IAAI,gBAAgB,GAAG,IAAI;IAI3C,iGAAiG;IACjG,OAAO,IAAI,IAAI;CAGhB"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PersistedShadowImportTarget.ts — a DURABLE, on-disk AS-IS import sink for the
|
|
3
|
+
* pre-click cutover integrity gate (spec §2.4; feedback-factory-migration.md §2.5).
|
|
4
|
+
*
|
|
5
|
+
* Why this exists (and why it is NOT `InMemoryImportTarget`):
|
|
6
|
+
*
|
|
7
|
+
* `cutoverReadiness.ts` hard-gates the operator's IRREVERSIBLE cutover click on
|
|
8
|
+
* `ready = integrity.passed && parity.cleared && !parity.stale`. The integrity leg
|
|
9
|
+
* greens ONLY when `recordIntegrityReport(passingReport)` is called. The existing
|
|
10
|
+
* import-DRYRUN route is deliberately walled off from that path (its report goes to
|
|
11
|
+
* `importDryRunReportPath`, never `integrityReportPath`) precisely so an EPHEMERAL,
|
|
12
|
+
* in-memory rehearsal can never quietly green an irreversible gate.
|
|
13
|
+
*
|
|
14
|
+
* The pre-click integrity gate therefore needs a target that is a step more real than
|
|
15
|
+
* the in-memory dry-run: a **persisted** shadow the import physically lands in and is
|
|
16
|
+
* read back FROM DISK, so the recorded integrity report corresponds to a durable,
|
|
17
|
+
* inspectable artifact — not an in-process Map that vanishes with the process. The
|
|
18
|
+
* shadow is a VERIFICATION target, never canonical: the canonical production import
|
|
19
|
+
* (Portal's apply-clusters / the real Prisma write) happens AFTER the click. This
|
|
20
|
+
* mirrors `InMemoryImportTarget`'s contract exactly (same `ImportTarget` interface,
|
|
21
|
+
* same dup-PK refusal, same `schemaDescriptor() === null` → the runner derives the
|
|
22
|
+
* dry-run schema), differing ONLY in where the rows live.
|
|
23
|
+
*
|
|
24
|
+
* Backing store: two newline-delimited JSON files (`clusters.jsonl`, `feedback.jsonl`)
|
|
25
|
+
* under a caller-supplied directory. JSONL (not SQLite) keeps the target dependency-free
|
|
26
|
+
* so the out-of-process integrity-pass runner bundles cleanly (no native module). Each
|
|
27
|
+
* AS-IS import APPENDS one row; `readBack*` parses the file. Duplicate PKs are refused
|
|
28
|
+
* before the append (mirrors the DB @id constraint the real adapter would hit). The
|
|
29
|
+
* shadow directory is disposable — `dispose()` removes it after the gate has run.
|
|
30
|
+
*/
|
|
31
|
+
import { appendFileSync, mkdirSync, readFileSync, existsSync } from 'node:fs';
|
|
32
|
+
import { join } from 'node:path';
|
|
33
|
+
import { DuplicateImportIdError, } from './importRunner.js';
|
|
34
|
+
import { SafeFsExecutor } from '../../core/SafeFsExecutor.js';
|
|
35
|
+
/** Resolve a row's primary-key id from the AS-IS field aliases (mirrors importRunner's pickId). */
|
|
36
|
+
function pickId(row, ...keys) {
|
|
37
|
+
for (const k of keys) {
|
|
38
|
+
const v = row[k];
|
|
39
|
+
if (typeof v === 'string' && v.length > 0)
|
|
40
|
+
return v;
|
|
41
|
+
if (typeof v === 'number')
|
|
42
|
+
return String(v);
|
|
43
|
+
}
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
export class PersistedShadowImportTarget {
|
|
47
|
+
dir;
|
|
48
|
+
clustersPath;
|
|
49
|
+
feedbackPath;
|
|
50
|
+
// In-memory PK sets enforce the @id constraint at import time (the durable readback
|
|
51
|
+
// is still the file). Sets are rebuilt from disk on construction so a reused dir is
|
|
52
|
+
// duplicate-safe across processes.
|
|
53
|
+
clusterIds = new Set();
|
|
54
|
+
feedbackIds = new Set();
|
|
55
|
+
constructor(dir) {
|
|
56
|
+
this.dir = dir;
|
|
57
|
+
mkdirSync(dir, { recursive: true });
|
|
58
|
+
this.clustersPath = join(dir, 'clusters.jsonl');
|
|
59
|
+
this.feedbackPath = join(dir, 'feedback.jsonl');
|
|
60
|
+
// Rebuild PK sets from any rows already on disk (idempotent reuse / crash recovery).
|
|
61
|
+
for (const r of this.readJsonl(this.clustersPath)) {
|
|
62
|
+
const id = pickId(r, 'clusterId', 'cluster_id', 'id');
|
|
63
|
+
if (id)
|
|
64
|
+
this.clusterIds.add(id);
|
|
65
|
+
}
|
|
66
|
+
for (const r of this.readJsonl(this.feedbackPath)) {
|
|
67
|
+
const id = pickId(r, 'feedbackId', 'feedback_id', 'id');
|
|
68
|
+
if (id)
|
|
69
|
+
this.feedbackIds.add(id);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
readJsonl(path) {
|
|
73
|
+
if (!existsSync(path))
|
|
74
|
+
return [];
|
|
75
|
+
const txt = readFileSync(path, 'utf8');
|
|
76
|
+
const out = [];
|
|
77
|
+
for (const line of txt.split('\n')) {
|
|
78
|
+
if (!line)
|
|
79
|
+
continue;
|
|
80
|
+
out.push(JSON.parse(line));
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
importClusterAsIs(row) {
|
|
85
|
+
const id = pickId(row, 'clusterId', 'cluster_id', 'id');
|
|
86
|
+
if (!id)
|
|
87
|
+
throw new Error('cluster row has no resolvable id (clusterId/cluster_id/id) — cannot import');
|
|
88
|
+
if (this.clusterIds.has(id))
|
|
89
|
+
throw new DuplicateImportIdError('cluster', id);
|
|
90
|
+
this.clusterIds.add(id);
|
|
91
|
+
appendFileSync(this.clustersPath, JSON.stringify(row) + '\n');
|
|
92
|
+
}
|
|
93
|
+
importFeedbackAsIs(row) {
|
|
94
|
+
const id = pickId(row, 'feedbackId', 'feedback_id', 'id');
|
|
95
|
+
if (!id)
|
|
96
|
+
throw new Error('feedback row has no resolvable id (feedbackId/feedback_id/id) — cannot import');
|
|
97
|
+
if (this.feedbackIds.has(id))
|
|
98
|
+
throw new DuplicateImportIdError('feedback', id);
|
|
99
|
+
this.feedbackIds.add(id);
|
|
100
|
+
appendFileSync(this.feedbackPath, JSON.stringify(row) + '\n');
|
|
101
|
+
}
|
|
102
|
+
readBackClusters() {
|
|
103
|
+
return this.readJsonl(this.clustersPath);
|
|
104
|
+
}
|
|
105
|
+
readBackFeedback() {
|
|
106
|
+
return this.readJsonl(this.feedbackPath);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* No real schema descriptor — like the in-memory dry-run target, the runner derives
|
|
110
|
+
* the target schema from readback + the canonical accepted-status contract. (A future
|
|
111
|
+
* real Prisma adapter would return its actual descriptor here.)
|
|
112
|
+
*/
|
|
113
|
+
schemaDescriptor() {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
/** Remove the on-disk shadow. Call after the integrity gate has run + the report is recorded. */
|
|
117
|
+
dispose() {
|
|
118
|
+
SafeFsExecutor.safeRmSync(this.dir, { recursive: true, force: true, operation: 'PersistedShadowImportTarget.dispose' });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=PersistedShadowImportTarget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PersistedShadowImportTarget.js","sourceRoot":"","sources":["../../../src/feedback-factory/migration/PersistedShadowImportTarget.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ImportRunResult } from './importRunner.js';
|
|
2
|
+
/** What the pass returns (and prints as stdout JSON in CLI mode). */
|
|
3
|
+
export interface IntegrityPassResult {
|
|
4
|
+
passed: boolean;
|
|
5
|
+
imported: ImportRunResult['imported'];
|
|
6
|
+
abortedPreImport: ImportRunResult['abortedPreImport'];
|
|
7
|
+
report: ImportRunResult['report'];
|
|
8
|
+
fetchSeconds: number;
|
|
9
|
+
importSeconds: number;
|
|
10
|
+
}
|
|
11
|
+
/** The minimal source contract the pass consumes — lets tests inject a fake. */
|
|
12
|
+
export interface RawCorpusSource {
|
|
13
|
+
prepare(): Promise<void>;
|
|
14
|
+
readRawClusters(): Record<string, unknown>[];
|
|
15
|
+
readRawFeedback(): Record<string, unknown>[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Run the AS-IS import of `source`'s corpus into a persisted shadow under `shadowDir`,
|
|
19
|
+
* then the integrity gate over the readback. The shadow dir is removed afterward unless
|
|
20
|
+
* `keepShadow`. Pure w.r.t. canonical state — the shadow is a verification target only.
|
|
21
|
+
*/
|
|
22
|
+
export declare function runIntegrityPass(source: RawCorpusSource, shadowDir: string, opts?: {
|
|
23
|
+
keepShadow?: boolean;
|
|
24
|
+
now?: () => number;
|
|
25
|
+
}): Promise<IntegrityPassResult>;
|
|
26
|
+
//# sourceMappingURL=integrityPassRunner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrityPassRunner.d.ts","sourceRoot":"","sources":["../../../src/feedback-factory/migration/integrityPassRunner.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpE,qEAAqE;AACrE,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC;IACtC,gBAAgB,EAAE,eAAe,CAAC,kBAAkB,CAAC,CAAC;IACtD,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC7C,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CAC9C;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GACtD,OAAO,CAAC,mBAAmB,CAAC,CAsB9B"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* integrityPassRunner.ts — the REAL pre-click import-integrity pass that greens the
|
|
3
|
+
* cutover door's INTEGRITY leg (spec §2.4; feedback-factory-migration.md §2.5).
|
|
4
|
+
*
|
|
5
|
+
* Runs the canonical pipeline — HttpParitySource captureRaw (read-only GET
|
|
6
|
+
* /api/instar/read) → runImport into a PERSISTED shadow target → the pure integrity
|
|
7
|
+
* gate over the readback — and returns the IntegrityReport verdict. The caller
|
|
8
|
+
* (AgentServer) feeds a PASSING report to `CutoverReadiness.recordIntegrityReport()`.
|
|
9
|
+
*
|
|
10
|
+
* Why this is a standalone module with a CLI entry (not an in-process closure like
|
|
11
|
+
* runParityCheck): the full 145K-row captureRaw fetch + import cannot settle inside
|
|
12
|
+
* the server's single-flight budget when run IN the event loop — that is exactly why
|
|
13
|
+
* the in-process import-dryrun budget-fails at 720s (#948), and why the parity-pass
|
|
14
|
+
* had to drop to clustersOnly (#1007). The heavy pass belongs OFF the event loop, so
|
|
15
|
+
* AgentServer spawns this module as a child `node` process. Exporting the logic as a
|
|
16
|
+
* function ALSO keeps it unit-testable against an injected source.
|
|
17
|
+
*/
|
|
18
|
+
import { tmpdir } from 'node:os';
|
|
19
|
+
import { join, resolve } from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import { runImport } from './importRunner.js';
|
|
22
|
+
import { PersistedShadowImportTarget } from './PersistedShadowImportTarget.js';
|
|
23
|
+
import { HttpParitySource } from '../dryrun/HttpParitySource.js';
|
|
24
|
+
/**
|
|
25
|
+
* Run the AS-IS import of `source`'s corpus into a persisted shadow under `shadowDir`,
|
|
26
|
+
* then the integrity gate over the readback. The shadow dir is removed afterward unless
|
|
27
|
+
* `keepShadow`. Pure w.r.t. canonical state — the shadow is a verification target only.
|
|
28
|
+
*/
|
|
29
|
+
export async function runIntegrityPass(source, shadowDir, opts = {}) {
|
|
30
|
+
const now = opts.now ?? (() => Date.now());
|
|
31
|
+
const t0 = now();
|
|
32
|
+
const target = new PersistedShadowImportTarget(shadowDir);
|
|
33
|
+
try {
|
|
34
|
+
await source.prepare();
|
|
35
|
+
const clusters = source.readRawClusters();
|
|
36
|
+
const feedback = source.readRawFeedback();
|
|
37
|
+
const tFetch = now();
|
|
38
|
+
const result = runImport({ clusters, feedback }, target);
|
|
39
|
+
const tDone = now();
|
|
40
|
+
return {
|
|
41
|
+
passed: result.passed,
|
|
42
|
+
imported: result.imported,
|
|
43
|
+
abortedPreImport: result.abortedPreImport,
|
|
44
|
+
report: result.report,
|
|
45
|
+
fetchSeconds: Number(((tFetch - t0) / 1000).toFixed(1)),
|
|
46
|
+
importSeconds: Number(((tDone - tFetch) / 1000).toFixed(1)),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
if (!opts.keepShadow)
|
|
51
|
+
target.dispose();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** CLI entry: env-driven, prints the verdict JSON to stdout. Used by AgentServer's child spawn. */
|
|
55
|
+
async function main() {
|
|
56
|
+
const token = process.env.TOKEN;
|
|
57
|
+
if (!token) {
|
|
58
|
+
console.error('FATAL: TOKEN env required (instar:read scope)');
|
|
59
|
+
process.exit(2);
|
|
60
|
+
}
|
|
61
|
+
const baseUrl = process.env.BASE_URL || 'https://dawn.bot-me.ai';
|
|
62
|
+
const shadowDir = process.env.SHADOW_DIR || join(tmpdir(), `cutover-integrity-shadow-${process.pid}`);
|
|
63
|
+
const keepShadow = process.env.KEEP_SHADOW === '1';
|
|
64
|
+
const source = new HttpParitySource({
|
|
65
|
+
baseUrl,
|
|
66
|
+
token,
|
|
67
|
+
captureRaw: true,
|
|
68
|
+
pageTimeoutMs: 120_000,
|
|
69
|
+
// Stay UNDER CutoverReadiness's 12-min single-flight max-hold backstop so the
|
|
70
|
+
// child self-exits before the in-process guard fires (measured pass ~2.7min).
|
|
71
|
+
totalTimeoutMs: 600_000,
|
|
72
|
+
});
|
|
73
|
+
console.error(`[integrity-pass] fetching corpus from ${baseUrl}/api/instar/read (captureRaw) → shadow ${shadowDir} ...`);
|
|
74
|
+
const out = await runIntegrityPass(source, shadowDir, { keepShadow });
|
|
75
|
+
console.error(`[integrity-pass] fetch ${out.fetchSeconds}s + import/integrity ${out.importSeconds}s → passed=${out.passed}`);
|
|
76
|
+
console.log(JSON.stringify(out));
|
|
77
|
+
process.exit(out.passed ? 0 : 1);
|
|
78
|
+
}
|
|
79
|
+
// Direct-run detection (ESM): run main() only when invoked as a script, not when imported.
|
|
80
|
+
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
81
|
+
void main();
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=integrityPassRunner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrityPassRunner.js","sourceRoot":"","sources":["../../../src/feedback-factory/migration/integrityPassRunner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Robust consolidated-escalation send for SentinelNotifier.
|
|
3
|
+
*
|
|
4
|
+
* Background (incident 2026-06-09): the sentinel's `sendConsolidated` closures
|
|
5
|
+
* did `try { sendToTopic(lifelineTopicId) } catch { return false }`. The
|
|
6
|
+
* configured lifeline/system topic had been DELETED on the Telegram side, so
|
|
7
|
+
* every send returned `400: message thread not found` — and the bare `catch`
|
|
8
|
+
* black-holed it. 41 stall escalations in one day failed silently; the user got
|
|
9
|
+
* pure silence and could not tell a stalled session from a working one.
|
|
10
|
+
*
|
|
11
|
+
* `TelegramAdapter.ensureLifelineTopic()` already knows how to recreate a
|
|
12
|
+
* deleted lifeline topic and persist the new id — the send path just never
|
|
13
|
+
* invoked it. This helper closes that gap: send to the lifeline topic; on
|
|
14
|
+
* failure, self-heal via ensureLifelineTopic() and retry once; and NEVER
|
|
15
|
+
* swallow the error silently (it is always logged).
|
|
16
|
+
*
|
|
17
|
+
* Pure + adapter-injected so it is unit-testable without a live Telegram.
|
|
18
|
+
*/
|
|
19
|
+
export interface ConsolidatedSendTelegram {
|
|
20
|
+
getLifelineTopicId(): number | undefined | null;
|
|
21
|
+
sendToTopic(topicId: number, text: string): Promise<unknown>;
|
|
22
|
+
/** Recreates the lifeline/system topic if deleted, persists the new id, returns it (or null). */
|
|
23
|
+
ensureLifelineTopic(): Promise<number | null>;
|
|
24
|
+
}
|
|
25
|
+
export declare function sendConsolidatedWithSelfHeal(tg: ConsolidatedSendTelegram, text: string, log: (line: string) => void): Promise<boolean>;
|
|
26
|
+
//# sourceMappingURL=sentinelConsolidatedSend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentinelConsolidatedSend.d.ts","sourceRoot":"","sources":["../../src/monitoring/sentinelConsolidatedSend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,wBAAwB;IACvC,kBAAkB,IAAI,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAChD,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,iGAAiG;IACjG,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC/C;AAkBD,wBAAsB,4BAA4B,CAChD,EAAE,EAAE,wBAAwB,EAC5B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,OAAO,CAAC,CA6ClB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Robust consolidated-escalation send for SentinelNotifier.
|
|
3
|
+
*
|
|
4
|
+
* Background (incident 2026-06-09): the sentinel's `sendConsolidated` closures
|
|
5
|
+
* did `try { sendToTopic(lifelineTopicId) } catch { return false }`. The
|
|
6
|
+
* configured lifeline/system topic had been DELETED on the Telegram side, so
|
|
7
|
+
* every send returned `400: message thread not found` — and the bare `catch`
|
|
8
|
+
* black-holed it. 41 stall escalations in one day failed silently; the user got
|
|
9
|
+
* pure silence and could not tell a stalled session from a working one.
|
|
10
|
+
*
|
|
11
|
+
* `TelegramAdapter.ensureLifelineTopic()` already knows how to recreate a
|
|
12
|
+
* deleted lifeline topic and persist the new id — the send path just never
|
|
13
|
+
* invoked it. This helper closes that gap: send to the lifeline topic; on
|
|
14
|
+
* failure, self-heal via ensureLifelineTopic() and retry once; and NEVER
|
|
15
|
+
* swallow the error silently (it is always logged).
|
|
16
|
+
*
|
|
17
|
+
* Pure + adapter-injected so it is unit-testable without a live Telegram.
|
|
18
|
+
*/
|
|
19
|
+
function errMsg(err) {
|
|
20
|
+
return err instanceof Error ? err.message : String(err);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* True when the send error means the target topic is GONE (deleted/closed) — the
|
|
24
|
+
* only case where the message definitely did NOT land, so recreating + retrying
|
|
25
|
+
* is safe. We deliberately do NOT retry other (transient/network) errors: those
|
|
26
|
+
* may have landed at Telegram before the response was lost, and a blind retry
|
|
27
|
+
* would double-post the escalation (this path bypasses the /telegram/reply
|
|
28
|
+
* content-dedup). A transient failure is recovered by the sentinel's next sweep.
|
|
29
|
+
*/
|
|
30
|
+
function isTopicGone(msg) {
|
|
31
|
+
return /message thread not found|thread not found|topic[ _-]?deleted|topic[ _-]?closed|chat not found/i.test(msg);
|
|
32
|
+
}
|
|
33
|
+
export async function sendConsolidatedWithSelfHeal(tg, text, log) {
|
|
34
|
+
const topicId = tg.getLifelineTopicId();
|
|
35
|
+
// Fast path: send to the currently-configured lifeline topic.
|
|
36
|
+
if (topicId) {
|
|
37
|
+
try {
|
|
38
|
+
await tg.sendToTopic(topicId, text);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
// De-swallow (the original `catch { return false }` black-holed this).
|
|
43
|
+
const msg = errMsg(err);
|
|
44
|
+
if (!isTopicGone(msg)) {
|
|
45
|
+
// Not a deleted-topic error — could be transient and may have partially
|
|
46
|
+
// landed. Do NOT retry (avoid a duplicate escalation); the sentinel
|
|
47
|
+
// re-escalates on its next sweep if the condition persists.
|
|
48
|
+
log(`escalation send to lifeline topic ${topicId} failed (${msg}) — transient/non-topic-gone, not retrying`);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
log(`escalation send to lifeline topic ${topicId} failed (${msg}: topic gone) — self-healing`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
log('escalation has no lifeline topic configured — establishing one');
|
|
56
|
+
}
|
|
57
|
+
// Self-heal: (re)establish the lifeline/system topic (recreates if deleted,
|
|
58
|
+
// persists the new id) and retry the send once.
|
|
59
|
+
let healed;
|
|
60
|
+
try {
|
|
61
|
+
healed = await tg.ensureLifelineTopic();
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
log(`escalation self-heal (ensureLifelineTopic) threw (${errMsg(err)}) — alert NOT delivered`);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (!healed) {
|
|
68
|
+
log('escalation self-heal could not establish a lifeline topic — alert NOT delivered');
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
await tg.sendToTopic(healed, text);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
log(`escalation retry to healed lifeline topic ${healed} failed (${errMsg(err)}) — alert NOT delivered`);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=sentinelConsolidatedSend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentinelConsolidatedSend.js","sourceRoot":"","sources":["../../src/monitoring/sentinelConsolidatedSend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AASH,SAAS,MAAM,CAAC,GAAY;IAC1B,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,gGAAgG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,EAA4B,EAC5B,IAAY,EACZ,GAA2B;IAE3B,MAAM,OAAO,GAAG,EAAE,CAAC,kBAAkB,EAAE,CAAC;IAExC,8DAA8D;IAC9D,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACpC,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,uEAAuE;YACvE,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,wEAAwE;gBACxE,oEAAoE;gBACpE,4DAA4D;gBAC5D,GAAG,CAAC,qCAAqC,OAAO,YAAY,GAAG,4CAA4C,CAAC,CAAC;gBAC7G,OAAO,KAAK,CAAC;YACf,CAAC;YACD,GAAG,CAAC,qCAAqC,OAAO,YAAY,GAAG,8BAA8B,CAAC,CAAC;QACjG,CAAC;IACH,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,gEAAgE,CAAC,CAAC;IACxE,CAAC;IAED,4EAA4E;IAC5E,gDAAgD;IAChD,IAAI,MAAqB,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,EAAE,CAAC,mBAAmB,EAAE,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,qDAAqD,MAAM,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QAC/F,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,GAAG,CAAC,iFAAiF,CAAC,CAAC;QACvF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,6CAA6C,MAAM,YAAY,MAAM,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QACzG,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|