@inerrata-corporation/errata 2.0.9 → 2.0.10

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.
@@ -15351,7 +15351,7 @@ function anchorLabelAttrs(attrs) {
15351
15351
  ...typeof line === "number" && Number.isInteger(line) && line >= 1 && line <= 1e7 ? { labelLine: line } : {}
15352
15352
  };
15353
15353
  }
15354
- var CLOUD_NODE_LABELS, CLOUD_WRITABLE_NODE_LABELS, PROJECT_ONLY_NODE_LABELS, PROJECT_ONLY_NODE_LABEL_SET, PROJECT_ONLY_EDGE_TYPES, PROJECT_ONLY_EDGE_TYPE_SET, CLOUD_WRITABLE_NODE_LABEL_SET, CLOUD_EDGE_TYPES, INGEST_SOURCES, INGEST_EXTRACTION_SOURCES, CastaliaIngestNodeSchema, RouteContextCountWireSchema, ANCHOR_COMMIT_SHA_RE, ANCHOR_BODY_HASH_RE, ANCHOR_SYMBOL_KINDS, ANCHOR_LABEL_KINDS, FILE_ANCHOR_QNAME, ANCHOR_LABEL_PATH_RE, ANCHOR_LABEL_QNAME_RE, CastaliaIngestEdgeSchema, SymbolSummarySidecarSchema, CastaliaIngestPayloadSchema;
15354
+ var CLOUD_NODE_LABELS, CLOUD_WRITABLE_NODE_LABELS, PROJECT_ONLY_NODE_LABELS, PROJECT_ONLY_NODE_LABEL_SET, PROJECT_ONLY_EDGE_TYPES, PROJECT_ONLY_EDGE_TYPE_SET, CLOUD_WRITABLE_NODE_LABEL_SET, CLOUD_EDGE_TYPES, INGEST_SOURCES, INGEST_EXTRACTION_SOURCES, CastaliaIngestNodeSchema, RouteContextCountWireSchema, ANCHOR_COMMIT_SHA_RE, ANCHOR_BODY_HASH_RE, ANCHOR_SYMBOL_KINDS, ANCHOR_LABEL_KINDS, FILE_ANCHOR_QNAME, ANCHOR_LABEL_PATH_RE, ANCHOR_LABEL_QNAME_RE, TRIAGE_DISCRIMINATOR_MAX_LENGTH, CastaliaIngestEdgeSchema, SymbolSummarySidecarSchema, CastaliaIngestPayloadSchema;
15355
15355
  var init_wire = __esm({
15356
15356
  "../../packages/shared/src/wire.ts"() {
15357
15357
  "use strict";
@@ -15470,6 +15470,7 @@ var init_wire = __esm({
15470
15470
  FILE_ANCHOR_QNAME = "";
15471
15471
  ANCHOR_LABEL_PATH_RE = /^(?![/])(?![A-Za-z]:)(?!(?:.*\/)?\.\.(?:\/|$))[^\x00-\x1f\x7f\\`]{1,512}$/;
15472
15472
  ANCHOR_LABEL_QNAME_RE = /^[^\x00-\x1f\x7f`]{1,256}$/;
15473
+ TRIAGE_DISCRIMINATOR_MAX_LENGTH = 2e3;
15473
15474
  CastaliaIngestEdgeSchema = external_exports.object({
15474
15475
  fromCanonicalId: external_exports.string().min(6).max(160),
15475
15476
  toCanonicalId: external_exports.string().min(6).max(160),
@@ -15480,6 +15481,8 @@ var init_wire = __esm({
15480
15481
  /** Triage route per-context counts (T3 induction). Carried as structured data
15481
15482
  * here; stored JSON-encoded on the Neo4j rel (`perContextJson`). */
15482
15483
  perContext: external_exports.record(external_exports.string(), RouteContextCountWireSchema).optional(),
15484
+ /** Scrubbed diagnostic check for a triage route, re-scanned at the cloud door. */
15485
+ discriminator: external_exports.string().trim().min(1).max(TRIAGE_DISCRIMINATOR_MAX_LENGTH).optional(),
15483
15486
  /** PJ-anchor-version: WHICH version of a symbol an `ANCHORED_AT` was
15484
15487
  * witnessed against. Both opaque — the observing checkout's HEAD sha and
15485
15488
  * the indexer's 16-hex sha256 of the symbol body. Neither is reversible
@@ -18216,6 +18219,15 @@ function generalize(text, opts = {}) {
18216
18219
  // src/instance-ingest.ts
18217
18220
  init_src();
18218
18221
 
18222
+ // src/instance-receipts.ts
18223
+ function receiptMap(attrs) {
18224
+ const value = attrs["instanceReceipts"];
18225
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
18226
+ }
18227
+ function instanceReceipt(n, destination) {
18228
+ return destination ? receiptMap(n.attrs)[destination] ?? {} : n.attrs;
18229
+ }
18230
+
18219
18231
  // src/contribution-ready.ts
18220
18232
  function cosine2(a, b) {
18221
18233
  if (a.length === 0 || a.length !== b.length) return 0;
@@ -19085,8 +19097,8 @@ function stripCodebaseScope(scope) {
19085
19097
  delete s["codebase"];
19086
19098
  return s;
19087
19099
  }
19088
- function contributedWireId(n) {
19089
- const cloudId = n.attrs["cloudNodeId"];
19100
+ function contributedWireId(n, destinationKey) {
19101
+ const cloudId = instanceReceipt(n, destinationKey)["cloudNodeId"];
19090
19102
  return isMembraneSaltedId(cloudId) ? cloudId : n.id;
19091
19103
  }
19092
19104
  var WIRE_CANONICAL_ID_MIN = 6;
@@ -19230,10 +19242,26 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
19230
19242
  fileRefsSubstituted: 0,
19231
19243
  fileRefLexiconMisses: 0
19232
19244
  };
19245
+ const sharedCauses = /* @__PURE__ */ new Map();
19246
+ const causalQueue = store.findNodesByLabel("Problem").filter(
19247
+ (n) => n.attrs["source"] !== "cloud" && n.attrs["resolvedAs"] !== PROBLEM_RESOLUTION.FALSE_POSITIVE && !ignored(n.description)
19248
+ ).map((n) => n.id);
19249
+ const causalSeen = new Set(causalQueue);
19250
+ for (let i = 0; i < causalQueue.length; i++) {
19251
+ const id = causalQueue[i];
19252
+ for (const e of [...store.outEdges(id, ["CAUSED_BY"]), ...opts.sharedStore?.outEdges(id, ["CAUSED_BY"]) ?? []]) {
19253
+ if (causalSeen.has(e.to)) continue;
19254
+ causalSeen.add(e.to);
19255
+ const cause = nodeOf(e.to);
19256
+ if (!cause || cause.label !== "RootCause" || cause.attrs["source"] === "cloud" || ignored(cause.description)) continue;
19257
+ sharedCauses.set(cause.id, cause);
19258
+ causalQueue.push(cause.id);
19259
+ }
19260
+ }
19233
19261
  for (const label of INSTANCE_LABELS) {
19234
19262
  const ref = [];
19235
19263
  includedByLabel.set(label, ref);
19236
- const population = label === "RootCause" && opts.sharedStore ? [...store.findNodesByLabel(label), ...opts.sharedStore.findNodesByLabel(label)] : store.findNodesByLabel(label);
19264
+ const population = label === "RootCause" && opts.sharedStore ? [...store.findNodesByLabel(label), ...sharedCauses.values()] : store.findNodesByLabel(label);
19237
19265
  for (const n of population) {
19238
19266
  if (seen.has(n.id)) continue;
19239
19267
  if (walked.has(n.id)) continue;
@@ -19251,18 +19279,18 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
19251
19279
  composition.ignored++;
19252
19280
  continue;
19253
19281
  }
19254
- const contributedAtSeq = n.attrs["contributedAtSeq"];
19282
+ const contributedAtSeq = instanceReceipt(n, opts.destinationKey)["contributedAtSeq"];
19255
19283
  if (typeof contributedAtSeq === "number" && (n.lastReinforcedAtSeq ?? 0) <= contributedAtSeq) {
19256
19284
  composition.watermarked++;
19257
19285
  anchorSources.add(n.id);
19258
- semanticEndpoints.set(n.id, contributedWireId(n));
19286
+ semanticEndpoints.set(n.id, contributedWireId(n, opts.destinationKey));
19259
19287
  continue;
19260
19288
  }
19261
19289
  if (label === "Solution" && n.description.startsWith(AUTO_MINT_PREFIX)) {
19262
19290
  composition.autoMint++;
19263
19291
  continue;
19264
19292
  }
19265
- if (isTerminallyRefused(n)) {
19293
+ if (isTerminallyRefused({ ...n, attrs: instanceReceipt(n, opts.destinationKey) })) {
19266
19294
  composition.terminalRefused++;
19267
19295
  continue;
19268
19296
  }
@@ -19321,9 +19349,9 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
19321
19349
  }
19322
19350
  const dg = digest(targets.map(({ e, wireId }) => `${e.type}>${wireId}`).sort());
19323
19351
  const sourceNode = nodeOf(sourceId);
19324
- if (sourceNode?.attrs["anchorsContributedDigest"] === dg) continue;
19352
+ if ((sourceNode && instanceReceipt(sourceNode, opts.destinationKey)["anchorsContributedDigest"]) === dg) continue;
19325
19353
  anchorDigests[sourceId] = dg;
19326
- const wireFrom = seen.has(sourceId) || !sourceNode ? sourceId : contributedWireId(sourceNode);
19354
+ const wireFrom = seen.has(sourceId) || !sourceNode ? sourceId : contributedWireId(sourceNode, opts.destinationKey);
19327
19355
  for (const { e, t, wireId } of targets) {
19328
19356
  const reject = unshippableReason(wireId, t.label);
19329
19357
  if (reject) {
@@ -19352,7 +19380,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
19352
19380
  for (const sourceId of anchorSources) {
19353
19381
  const source = nodeOf(sourceId);
19354
19382
  if (!source) continue;
19355
- const wireFrom = seen.has(sourceId) ? sourceId : contributedWireId(source);
19383
+ const wireFrom = seen.has(sourceId) ? sourceId : contributedWireId(source, opts.destinationKey);
19356
19384
  const planned = [];
19357
19385
  for (const e of store.outEdges(sourceId, ["ANCHORED_AT"])) {
19358
19386
  const target = nodeOf(e.to);
@@ -19387,7 +19415,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
19387
19415
  const dg = digest(
19388
19416
  planned.map(({ symId, wireAttrs }) => `ANCHORED_AT>${symId}>${JSON.stringify(wireAttrs)}`).sort()
19389
19417
  );
19390
- if (source.attrs["projectAnchorsContributedDigest"] === dg) continue;
19418
+ if (instanceReceipt(source, opts.destinationKey)["projectAnchorsContributedDigest"] === dg) continue;
19391
19419
  projectAnchorDigests[sourceId] = dg;
19392
19420
  for (const { e, target, symId, wireAttrs } of planned) {
19393
19421
  if (!contextSeen.has(symId)) {
@@ -19433,7 +19461,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
19433
19461
  if (outs.length === 0) continue;
19434
19462
  const dg = digest(outs.map(({ e, wireTo }) => `${e.type}>${wireTo}`).sort());
19435
19463
  const owner = nodeOf(sourceId);
19436
- const digestSkip = owner?.attrs["semanticEdgesContributedDigest"] === dg;
19464
+ const digestSkip = (owner && instanceReceipt(owner, opts.destinationKey)["semanticEdgesContributedDigest"]) === dg;
19437
19465
  if (!digestSkip) semanticEdgeDigests[sourceId] = dg;
19438
19466
  for (const { e, wireTo } of outs) {
19439
19467
  if (digestSkip && !shippedById.has(e.to)) continue;
@@ -19490,7 +19518,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
19490
19518
  for (const wn of nodes) {
19491
19519
  if (wn.label !== "RootCause" && wn.label !== "Solution") continue;
19492
19520
  const src = nodeOf(wn.id);
19493
- if (typeof src?.attrs["contributedAtSeq"] === "number") continue;
19521
+ if (src && typeof instanceReceipt(src, opts.destinationKey)["contributedAtSeq"] === "number") continue;
19494
19522
  if (bindingEndpoints.has(wn.id)) continue;
19495
19523
  heldOrphans.add(wn.id);
19496
19524
  }
@@ -19568,6 +19596,7 @@ function buildDrainInstances(store, sharedStore, input) {
19568
19596
  ...input.includePackages !== void 0 ? { includePackages: input.includePackages } : {},
19569
19597
  workspaceRoot: input.workspaceRoot,
19570
19598
  sharedStore,
19599
+ destinationKey: input.destinationKey,
19571
19600
  onComposition: (c) => {
19572
19601
  composition = c;
19573
19602
  },
package/errata.mjs CHANGED
@@ -15732,6 +15732,13 @@ function isSeedProvenance(source) {
15732
15732
  function isAnchorLabelKind(kind) {
15733
15733
  return typeof kind === "string" && ANCHOR_LABEL_KINDS.includes(kind);
15734
15734
  }
15735
+ function triageDiscriminatorAttrs(type, attrs) {
15736
+ if (!["CONFIRMS", "INDICATES", "ROUTES_TO"].includes(type)) return {};
15737
+ const value = attrs?.["discriminator"];
15738
+ if (typeof value !== "string") return {};
15739
+ const test = value.trim();
15740
+ return test && test.length <= TRIAGE_DISCRIMINATOR_MAX_LENGTH && !(test.startsWith("<") && test.endsWith(">")) ? { discriminator: test } : {};
15741
+ }
15735
15742
  function anchorVersionAttrs(attrs) {
15736
15743
  if (!attrs) return {};
15737
15744
  const commit = attrs["observedAtCommit"];
@@ -15792,7 +15799,7 @@ function summarizeIngestResult(r) {
15792
15799
  ...r.patternReconciliation ? { patternReconciliation: r.patternReconciliation } : {}
15793
15800
  };
15794
15801
  }
15795
- var CLOUD_NODE_LABELS, CLOUD_WRITABLE_NODE_LABELS, PROJECT_ONLY_NODE_LABELS, PROJECT_ONLY_NODE_LABEL_SET, PROJECT_ONLY_EDGE_TYPES, PROJECT_ONLY_EDGE_TYPE_SET, CLOUD_WRITABLE_NODE_LABEL_SET, CLOUD_EDGE_TYPES, INGEST_SOURCES, INGEST_EXTRACTION_SOURCES, QUARANTINE_EXTRACTION_SOURCE_PREFIX, MAX_NODES_PER_PAYLOAD, MAX_EDGES_PER_PAYLOAD, CastaliaIngestNodeSchema, RouteContextCountWireSchema, ANCHOR_COMMIT_SHA_RE, ANCHOR_BODY_HASH_RE, ANCHOR_SYMBOL_KINDS, ANCHOR_LABEL_KINDS, FILE_ANCHOR_QNAME, ANCHOR_LABEL_PATH_RE, ANCHOR_LABEL_QNAME_RE, ANCHOR_LABEL_KEYS, CastaliaIngestEdgeSchema, SymbolSummarySidecarSchema, CastaliaIngestPayloadSchema;
15802
+ var CLOUD_NODE_LABELS, CLOUD_WRITABLE_NODE_LABELS, PROJECT_ONLY_NODE_LABELS, PROJECT_ONLY_NODE_LABEL_SET, PROJECT_ONLY_EDGE_TYPES, PROJECT_ONLY_EDGE_TYPE_SET, CLOUD_WRITABLE_NODE_LABEL_SET, CLOUD_EDGE_TYPES, INGEST_SOURCES, INGEST_EXTRACTION_SOURCES, QUARANTINE_EXTRACTION_SOURCE_PREFIX, MAX_NODES_PER_PAYLOAD, MAX_EDGES_PER_PAYLOAD, CastaliaIngestNodeSchema, RouteContextCountWireSchema, ANCHOR_COMMIT_SHA_RE, ANCHOR_BODY_HASH_RE, ANCHOR_SYMBOL_KINDS, ANCHOR_LABEL_KINDS, FILE_ANCHOR_QNAME, ANCHOR_LABEL_PATH_RE, ANCHOR_LABEL_QNAME_RE, ANCHOR_LABEL_KEYS, TRIAGE_DISCRIMINATOR_MAX_LENGTH, CastaliaIngestEdgeSchema, SymbolSummarySidecarSchema, CastaliaIngestPayloadSchema;
15796
15803
  var init_wire = __esm({
15797
15804
  "../../packages/shared/src/wire.ts"() {
15798
15805
  "use strict";
@@ -15915,6 +15922,7 @@ var init_wire = __esm({
15915
15922
  ANCHOR_LABEL_PATH_RE = /^(?![/])(?![A-Za-z]:)(?!(?:.*\/)?\.\.(?:\/|$))[^\x00-\x1f\x7f\\`]{1,512}$/;
15916
15923
  ANCHOR_LABEL_QNAME_RE = /^[^\x00-\x1f\x7f`]{1,256}$/;
15917
15924
  ANCHOR_LABEL_KEYS = ["labelRelPath", "labelQname", "labelKind", "labelLine"];
15925
+ TRIAGE_DISCRIMINATOR_MAX_LENGTH = 2e3;
15918
15926
  CastaliaIngestEdgeSchema = external_exports.object({
15919
15927
  fromCanonicalId: external_exports.string().min(6).max(160),
15920
15928
  toCanonicalId: external_exports.string().min(6).max(160),
@@ -15925,6 +15933,8 @@ var init_wire = __esm({
15925
15933
  /** Triage route per-context counts (T3 induction). Carried as structured data
15926
15934
  * here; stored JSON-encoded on the Neo4j rel (`perContextJson`). */
15927
15935
  perContext: external_exports.record(external_exports.string(), RouteContextCountWireSchema).optional(),
15936
+ /** Scrubbed diagnostic check for a triage route, re-scanned at the cloud door. */
15937
+ discriminator: external_exports.string().trim().min(1).max(TRIAGE_DISCRIMINATOR_MAX_LENGTH).optional(),
15928
15938
  /** PJ-anchor-version: WHICH version of a symbol an `ANCHORED_AT` was
15929
15939
  * witnessed against. Both opaque — the observing checkout's HEAD sha and
15930
15940
  * the indexer's 16-hex sha256 of the symbol body. Neither is reversible
@@ -16827,6 +16837,7 @@ __export(src_exports, {
16827
16837
  TAXONOMY_EDGES: () => TAXONOMY_EDGES,
16828
16838
  TOOL_KINDS: () => TOOL_KINDS,
16829
16839
  TRANSFER_EDGES: () => TRANSFER_EDGES,
16840
+ TRIAGE_DISCRIMINATOR_MAX_LENGTH: () => TRIAGE_DISCRIMINATOR_MAX_LENGTH,
16830
16841
  TRIAGE_EDGES: () => TRIAGE_EDGES,
16831
16842
  TRUTH_KIND: () => TRUTH_KIND,
16832
16843
  UnregisteredGraphEventError: () => UnregisteredGraphEventError,
@@ -16910,6 +16921,7 @@ __export(src_exports, {
16910
16921
  summaryRejectionReason: () => summaryRejectionReason,
16911
16922
  toCloudAttrs: () => toCloudAttrs,
16912
16923
  toolCanonicalId: () => toolCanonicalId,
16924
+ triageDiscriminatorAttrs: () => triageDiscriminatorAttrs,
16913
16925
  unregisteredGraphEventKeys: () => unregisteredGraphEventKeys,
16914
16926
  validateCastaliaPayload: () => validateCastaliaPayload,
16915
16927
  versionlessPurl: () => versionlessPurl,
@@ -22620,7 +22632,8 @@ function pendingDiscriminators(l2, opts = {}) {
22620
22632
  const perContext = e.attrs["perContext"] ?? {};
22621
22633
  const global2 = perContext[TRIAGE_GLOBAL_BUCKET] ?? {};
22622
22634
  const observations = Number(global2.confirmed ?? 0);
22623
- const sources = new Set(global2.contributors ?? []).size;
22635
+ const inferred = new Set(global2.inferredContributors ?? []);
22636
+ const sources = new Set((global2.contributors ?? []).filter((id) => !inferred.has(id))).size;
22624
22637
  if (sources < minSources || observations < minObservations) continue;
22625
22638
  out2.push({ route: e.id, presenting: triage.description, cause: "", sources, observations, causeId: e.to });
22626
22639
  }
@@ -22638,7 +22651,7 @@ function markDiscriminatorAsked(l2, routeIds, ts) {
22638
22651
  }
22639
22652
  }
22640
22653
  function promoteRouteWithDiscriminator(l2, routeId, discriminator, ts, source) {
22641
- const test = discriminator.trim();
22654
+ const test = triageDiscriminatorAttrs("CONFIRMS", { discriminator }).discriminator;
22642
22655
  if (!test) return false;
22643
22656
  if (isUnfilledPlaceholder(test)) return false;
22644
22657
  const existing = l2.getEdge(routeId);
@@ -22740,9 +22753,12 @@ function parseTriageFences(text) {
22740
22753
  return out2;
22741
22754
  }
22742
22755
  function harvestTriageFences(l2, text, opts) {
22756
+ const seen = [...text.matchAll(/```errata-triage[^\n]*\n([\s\S]*?)```/g)].length;
22743
22757
  let recorded = 0;
22744
22758
  let promoted = 0;
22745
22759
  for (const f of parseTriageFences(text)) {
22760
+ if ([f.presenting, f.cause, f.test, f.fix, f.route].some((value) => value && isUnfilledPlaceholder(value))) continue;
22761
+ if (f.test && !triageDiscriminatorAttrs("CONFIRMS", { discriminator: f.test }).discriminator) continue;
22746
22762
  if (f.route && f.test) {
22747
22763
  if (promoteRouteWithDiscriminator(l2, f.route.trim(), f.test, opts.ts, opts.contributor)) promoted++;
22748
22764
  continue;
@@ -22778,7 +22794,7 @@ function harvestTriageFences(l2, text, opts) {
22778
22794
  }
22779
22795
  recorded++;
22780
22796
  }
22781
- return { recorded, promoted };
22797
+ return { seen, recorded, promoted, rejected: seen - recorded - promoted };
22782
22798
  }
22783
22799
  var COOCCUR_BONUS, routeEdgeId, ROUTE_EDGE_TYPES, routeTypeFor, isRouterRoute, INDUCTED_MARK, DRIFT_VALUES;
22784
22800
  var init_triage2 = __esm({
@@ -24396,7 +24412,7 @@ function renderSnapshot(s) {
24396
24412
  lines.push("");
24397
24413
  lines.push("### \u270D\uFE0F CAPTURE PROTOCOL \u2014 tags are the record");
24398
24414
  lines.push(
24399
- "errata records ONLY inline tags: `[!one line]` for every problem you state, `(fix:[id])` when your edit fixed a shown problem, and to JUDGE a prior shown below \u2014 `(confirm:[id])` if it held, `(refute:[id] why)` if it's wrong or didn't hold. Untagged prose is silently lost \u2014 tag as you write (no fences), we filter downstream."
24415
+ "For problems and fixes, errata records inline tags: `[!one line]` for every problem you state, `(fix:[id])` when your edit fixed a shown problem, and to JUDGE a prior shown below \u2014 `(confirm:[id])` if it held, `(refute:[id] why)` if it's wrong or didn't hold. Untagged prose is silently lost \u2014 tag as you write. Requested errata-* answer blocks keep their fences; put them in your turn-final reply."
24400
24416
  );
24401
24417
  lines.push("");
24402
24418
  lines.push(SEARCH_IMPERATIVE_HEADER);
@@ -24682,7 +24698,7 @@ function renderSnapshot(s) {
24682
24698
  for (const item of reviewItems) {
24683
24699
  if (item.kind === "discriminator") {
24684
24700
  lines.push(
24685
- `- **A recurring diagnosis is one cheap check from promoting** \u2014 "${item.presenting}" usually turns out to be: ${item.cause}. If (and only if) you KNOW a cheap ante-hoc check that confirms it, answer in your reply:`
24701
+ `- **A recurring diagnosis is one cheap check from promoting** \u2014 "${item.presenting}" usually turns out to be: ${item.cause}. If (and only if) you KNOW a cheap ante-hoc check that confirms it, fill this fenced block in your turn-final reply:`
24686
24702
  );
24687
24703
  lines.push(" ```errata-triage");
24688
24704
  lines.push(` route: ${item.route}`);
@@ -24690,7 +24706,7 @@ function renderSnapshot(s) {
24690
24706
  lines.push(" ```");
24691
24707
  } else {
24692
24708
  lines.push(
24693
- `- **Name the principle this cluster of your beliefs generalizes** \u2014 it ships to the collective only once named, and the prose must cover ALL members in one sentence:`
24709
+ `- **Name the principle this cluster of your beliefs generalizes** \u2014 it ships to the collective only once named. Cover ALL members in one sentence; fill this fenced block in your turn-final reply:`
24694
24710
  );
24695
24711
  for (const m of item.members) lines.push(` - ${m.description}`);
24696
24712
  lines.push(" ```errata-abstraction");
@@ -25196,6 +25212,7 @@ function toWirePayload(batch, runId) {
25196
25212
  attrs: {
25197
25213
  ...typeof e.confidence === "number" ? { confidence: clamp012(e.confidence) } : {},
25198
25214
  ...perContext ? { perContext } : {},
25215
+ ...triageDiscriminatorAttrs(e.type, e.attrs),
25199
25216
  // PJ-anchor-version: the two opaque version stamps ride on anchors only.
25200
25217
  ...e.type === "ANCHORED_AT" ? anchorVersionAttrs(e.attrs) : {},
25201
25218
  // PJ-anchor-labels: readable labels ride only when the producer set the
@@ -25802,6 +25819,7 @@ var init_client = __esm({
25802
25819
  if (q.seed?.length) qs.set("seed", q.seed.join(","));
25803
25820
  if (q.q) qs.set("q", q.q);
25804
25821
  if (q.channel) qs.set("channel", q.channel);
25822
+ if (q.includeTriage) qs.set("includeTriage", "true");
25805
25823
  const publicCtx = process.env["ERRATA_PRIMING_PUBLIC_CONTEXT"];
25806
25824
  const usePublicCtx = Boolean(publicCtx && publicCtx !== "0" && q.channel === "priming");
25807
25825
  const UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -29016,7 +29034,7 @@ function buildAgentInstruction(opts = {}) {
29016
29034
  const signals = opts.signals ?? ["prior", "problem", "domain", "fix", "constraint", "triage", "attempt", "link"];
29017
29035
  const ref = opts.referent ?? "its-handle";
29018
29036
  const token = (s) => s === "problem" ? TAG_EXAMPLE.problem() : s === "constraint" ? TAG_EXAMPLE.constraint() : s === "attempt" ? TAG_EXAMPLE.tried() : s === "domain" ? TAG_EXAMPLE.domain() : s === "link" ? TAG_EXAMPLE.pattern() : s === "verdict" ? TAG_EXAMPLE.confirm(ref) : TAG_EXAMPLE[s](ref);
29019
- const head2 = `We tag the priors we show you with a short handle like [${opts.handleExample ?? "chokidar-glob"}]. Tags are the ONLY signal recorded \u2014 anything you state but don't tag is silently lost. Weave each tag inline in your prose as you state it (no fences or backticks, no extra calls); tag everything, we filter downstream:`;
29037
+ const head2 = `We tag the priors we show you with a short handle like [${opts.handleExample ?? "chokidar-glob"}]. Untagged prose is silently lost. Put each tag inline in prose (no fences or backticks). Requested errata-* answer blocks are the exception: keep their fences in your turn-final reply. Tag everything, we filter downstream:`;
29020
29038
  const tail = "Mid-turn text (between tool calls) can be dropped by the harness \u2014 RESTATE every tag in your turn-final message; the final message is what reliably survives \u2014 EXCEPT one already shown above with an id: that is captured, so cite `[its-handle]` rather than retyping it (drift mints a duplicate).";
29021
29039
  return [
29022
29040
  head2,
@@ -29506,6 +29524,18 @@ function resolveWorkspaceIdentity(cfg, entry, env2 = process.env) {
29506
29524
  function identityKey(id) {
29507
29525
  return id.profileName ?? "@default";
29508
29526
  }
29527
+ function publicWorkspaceIdentity(identity) {
29528
+ return {
29529
+ profileName: identity.profileName,
29530
+ rule: identity.rule,
29531
+ invalid: identity.invalid,
29532
+ reason: identity.reason,
29533
+ profile: identity.profile ? {
29534
+ installationId: identity.profile.installationId,
29535
+ cloudUrl: identity.profile.cloudUrl
29536
+ } : null
29537
+ };
29538
+ }
29509
29539
  function blockedCloudClient(reason) {
29510
29540
  return new Proxy(
29511
29541
  {},
@@ -41888,7 +41918,7 @@ function takeDiscriminatorNudge(path2) {
41888
41918
  return [
41889
41919
  `\u26A1 errata \u2014 a known wrong-door is one field from ranking: "${top.presenting.slice(0, 90)}"`,
41890
41920
  ` usually turns out to be: ${top.cause.slice(0, 90)} (seen ${top.observations}x)`,
41891
- " Know a CHEAP check that confirms it? Answer in-flow \u2014 it promotes the route so the",
41921
+ " Know a CHEAP check that confirms it? Fill this fenced block in your turn-final reply so the",
41892
41922
  " next agent gets it first. Only if you actually know one: a guessed test ranks first",
41893
41923
  " and misroutes everyone after you.",
41894
41924
  " ```errata-triage",
@@ -43405,7 +43435,7 @@ var init_mcp = __esm({
43405
43435
  pending,
43406
43436
  ...pending.length > 0 ? {
43407
43437
  howToAnswer: [
43408
- "For each route you know a cheap test for, emit an errata-triage fence",
43438
+ "For each route you know a cheap test for, emit an errata-triage fence in your turn-final reply",
43409
43439
  "carrying its `route` id and the `test` \u2014 no presenting/cause needed, the",
43410
43440
  "route id identifies both endpoints. Attaching a test does NOT count as",
43411
43441
  "another sighting, so it cannot inflate the route's evidence.",
@@ -74132,18 +74162,21 @@ function readTailWithLosses(path2, maxBytes) {
74132
74162
  try {
74133
74163
  fd = openSync(path2, "r");
74134
74164
  } catch {
74135
- return { text: "", bytesUnreachable: 0, ioFailed: true };
74165
+ return { text: "", bytesUnreachable: 0, parseStartByte: 0, ioFailed: true };
74136
74166
  }
74137
74167
  try {
74138
74168
  const size = fstatSync(fd).size;
74139
74169
  const start2 = Math.max(0, size - maxBytes);
74140
74170
  const len = size - start2;
74141
- if (len <= 0) return { text: "", bytesUnreachable: 0, ioFailed: false };
74171
+ if (len <= 0) return { text: "", bytesUnreachable: 0, parseStartByte: 0, ioFailed: false };
74142
74172
  const buf = Buffer.allocUnsafe(len);
74143
74173
  readSync(fd, buf, 0, len, start2);
74144
- return { text: buf.toString("utf8"), bytesUnreachable: start2, ioFailed: false };
74174
+ const text = buf.toString("utf8");
74175
+ const firstNewline = buf.indexOf(10);
74176
+ const parseStartByte = firstNewline < 0 ? start2 : start2 + firstNewline + 1 - Buffer.byteLength(buf.subarray(0, firstNewline + 1).toString("utf8"));
74177
+ return { text, bytesUnreachable: start2, parseStartByte, ioFailed: false };
74145
74178
  } catch {
74146
- return { text: "", bytesUnreachable: 0, ioFailed: true };
74179
+ return { text: "", bytesUnreachable: 0, parseStartByte: 0, ioFailed: true };
74147
74180
  } finally {
74148
74181
  closeSync(fd);
74149
74182
  }
@@ -74207,7 +74240,7 @@ function isUserTurnBoundary(obj) {
74207
74240
  }
74208
74241
  function readAssistantTurnsWithLosses(transcriptPath, includeThinking = true, maxBytes = 2e6) {
74209
74242
  const tail = readTailWithLosses(transcriptPath, maxBytes);
74210
- const parsed = parseAssistantTurns(tail.text, includeThinking, false);
74243
+ const parsed = parseAssistantTurns(tail.text, includeThinking, false, tail.parseStartByte);
74211
74244
  return {
74212
74245
  turns: parsed.turns,
74213
74246
  losses: {
@@ -74219,7 +74252,8 @@ function readAssistantTurnsWithLosses(transcriptPath, includeThinking = true, ma
74219
74252
  }
74220
74253
  function readAssistantTurnsFrom(transcriptPath, fromByte, includeThinking = true, maxBytes) {
74221
74254
  const { text, nextOffset, more, ioFailed } = readFrom(transcriptPath, fromByte, maxBytes);
74222
- const parsed = parseAssistantTurns(text, includeThinking, fromByte > 0);
74255
+ const startByte = fromByte > 0 && fromByte <= nextOffset ? fromByte : 0;
74256
+ const parsed = parseAssistantTurns(text, includeThinking, fromByte > 0, startByte);
74223
74257
  return {
74224
74258
  turns: parsed.turns,
74225
74259
  nextOffset,
@@ -74255,10 +74289,10 @@ function detectHostHarness(raw2) {
74255
74289
  }
74256
74290
  return "claude_code";
74257
74291
  }
74258
- function parseAssistantTurns(raw2, includeThinking, skipFirstLineParseFailure = false) {
74292
+ function parseAssistantTurns(raw2, includeThinking, skipFirstLineParseFailure = false, startByte = 0) {
74259
74293
  if (!raw2) return { turns: [], parseFailures: 0 };
74260
74294
  const harness = detectHostHarness(raw2);
74261
- if (harness === "codex") return parseCodexAssistantTurns(raw2, skipFirstLineParseFailure);
74295
+ if (harness === "codex") return parseCodexAssistantTurns(raw2, skipFirstLineParseFailure, startByte);
74262
74296
  if (harness === "gemini") return parseGeminiAssistantTurns(raw2, skipFirstLineParseFailure);
74263
74297
  if (harness === "opencode") return parseOpencodeAssistantTurns(raw2, skipFirstLineParseFailure);
74264
74298
  return parseClaudeAssistantTurns(raw2, includeThinking, skipFirstLineParseFailure);
@@ -74349,13 +74383,16 @@ function parseGeminiAssistantTurns(raw2, skipFirstLineParseFailure = false) {
74349
74383
  const turns = [...byId.entries()].sort((a, b) => a[1].order - b[1].order).map(([uuid3, v]) => ({ uuid: uuid3, text: v.text, hostHarness: "gemini" }));
74350
74384
  return { turns, parseFailures };
74351
74385
  }
74352
- function parseCodexAssistantTurns(raw2, skipFirstLineParseFailure = false) {
74386
+ function parseCodexAssistantTurns(raw2, skipFirstLineParseFailure = false, startByte = 0) {
74353
74387
  const turns = [];
74354
- const lines = raw2.split(/\r?\n/);
74388
+ const lines = raw2.split("\n");
74355
74389
  let parseFailures = 0;
74390
+ let nextByte = startByte;
74356
74391
  for (let i2 = 0; i2 < lines.length; i2++) {
74357
74392
  const line = lines[i2];
74358
- if (!line) continue;
74393
+ const lineByte = nextByte;
74394
+ nextByte += Buffer.byteLength(line ?? "", "utf8") + 1;
74395
+ if (!line?.trim()) continue;
74359
74396
  let obj;
74360
74397
  try {
74361
74398
  obj = JSON.parse(line);
@@ -74374,7 +74411,9 @@ function parseCodexAssistantTurns(raw2, skipFirstLineParseFailure = false) {
74374
74411
  }
74375
74412
  if (parts2.length === 0) continue;
74376
74413
  turns.push({
74377
- uuid: typeof payload["id"] === "string" ? payload["id"] : `codex-${i2}`,
74414
+ // Slice-local line numbers repeat on every Stop. Position distinguishes
74415
+ // identical messages; the digest distinguishes a replaced row at that position.
74416
+ uuid: typeof payload["id"] === "string" && payload["id"].length > 0 ? payload["id"] : `codex-${lineByte}-${createHash14("sha256").update(line.trimEnd()).digest("hex")}`,
74378
74417
  text: parts2.join("\n\n"),
74379
74418
  hostHarness: "codex"
74380
74419
  });
@@ -76946,21 +76985,31 @@ async function pullCloudTriage(shared, cloud, profile, limit = 50) {
76946
76985
  stack: profile.stack,
76947
76986
  domains: profile.domains,
76948
76987
  kinds: ["Problem"],
76988
+ includeTriage: true,
76949
76989
  limit
76950
76990
  });
76951
76991
  const byId = new Map(res.nodes.map((n) => [n.id, n]));
76952
76992
  const now = Date.now();
76953
76993
  let merged = 0;
76994
+ const landedTriages = /* @__PURE__ */ new Set();
76954
76995
  const landNode = (id) => {
76955
- if (shared.getNode(id)) return true;
76996
+ const existing = shared.getNode(id);
76956
76997
  const n = byId.get(id);
76998
+ if (existing) {
76999
+ if (n && existing.attrs["source"] === "cloud") {
77000
+ shared.updateNode(id, { attrs: { ...existing.attrs, ...n.attrs, source: "cloud" }, lastUpdatedAt: now });
77001
+ }
77002
+ return true;
77003
+ }
76957
77004
  if (!n) return false;
76958
77005
  shared.mergeNode({ ...n, extractionSource: "bko-inferred", embedding: [], attrs: { ...n.attrs, source: "cloud" } });
76959
77006
  return true;
76960
77007
  };
76961
77008
  for (const edge2 of res.edges) {
76962
77009
  if (edge2.type !== "CONFIRMS" && edge2.type !== "ROUTES_TO") continue;
77010
+ if (!triageDiscriminatorAttrs(edge2.type, edge2.attrs).discriminator) continue;
76963
77011
  if (!landNode(edge2.from) || !landNode(edge2.to)) continue;
77012
+ landedTriages.add(edge2.from);
76964
77013
  const cloudAttrs = { ...edge2.attrs, source: "cloud", provisional: false };
76965
77014
  const existing = shared.getEdge(edge2.id);
76966
77015
  if (existing && existing.type === "CONFIRMS") {
@@ -76971,6 +77020,11 @@ async function pullCloudTriage(shared, cloud, profile, limit = 50) {
76971
77020
  }
76972
77021
  merged++;
76973
77022
  }
77023
+ for (const edge2 of res.edges) {
77024
+ if (edge2.type !== "TRIAGED_BY" || !landedTriages.has(edge2.to)) continue;
77025
+ if (!landNode(edge2.from)) continue;
77026
+ shared.mergeEdge({ ...edge2, attrs: { ...edge2.attrs, source: "cloud" } });
77027
+ }
76974
77028
  return merged;
76975
77029
  }
76976
77030
 
@@ -80267,7 +80321,7 @@ function createWatchBreaker(deps) {
80267
80321
  }
80268
80322
 
80269
80323
  // src/engine.ts
80270
- var DAEMON_VERSION = true ? "2.0.9" : "2.0.0-alpha.0";
80324
+ var DAEMON_VERSION = true ? "2.0.10" : "2.0.0-alpha.0";
80271
80325
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
80272
80326
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
80273
80327
  var GIT_OP_MUTE_MS = 4e3;
@@ -81153,6 +81207,9 @@ function createWorkspaceEngine(opts) {
81153
81207
  let resolved = 0;
81154
81208
  let abstracted = 0;
81155
81209
  let triaged = 0;
81210
+ let triageFencesSeen = 0;
81211
+ let triagePromoted = 0;
81212
+ let triageFencesRejected = 0;
81156
81213
  let priorEdges = 0;
81157
81214
  let exposureShown = 0;
81158
81215
  let exposureEvicted = 0;
@@ -81216,11 +81273,16 @@ function createWorkspaceEngine(opts) {
81216
81273
  console.warn("[errata] abstraction harvest failed:", err2);
81217
81274
  }
81218
81275
  try {
81219
- triaged += harvestTriageFences(opts.sharedStore, turn.text, {
81276
+ const triage = harvestTriageFences(opts.sharedStore, turn.text, {
81220
81277
  context: { stack: profile.stack, domain: profile.domains[0] },
81221
81278
  contributor: sessionId,
81222
81279
  ts: t
81223
- }).recorded;
81280
+ });
81281
+ triaged += triage.recorded;
81282
+ triageFencesSeen += triage.seen;
81283
+ triagePromoted += triage.promoted;
81284
+ triageFencesRejected += triage.rejected;
81285
+ if (triage.recorded > 0 || triage.promoted > 0) contextDirty = true;
81224
81286
  } catch (err2) {
81225
81287
  console.warn("[errata] triage harvest failed:", err2);
81226
81288
  }
@@ -81896,8 +81958,8 @@ function createWorkspaceEngine(opts) {
81896
81958
  if (abstracted > 0) {
81897
81959
  console.log(`[errata] abstraction: ${abstracted} principle(s) distilled from the conversation`);
81898
81960
  }
81899
- if (triaged > 0) {
81900
- console.log(`[errata] triage: ${triaged} wrong-door(s) captured from the conversation`);
81961
+ if (triaged > 0 || triagePromoted > 0 || triageFencesRejected > 0) {
81962
+ console.log(`[errata] triage: ${triaged} diagnosis(es), ${triagePromoted} route promotion(s), ${triageFencesRejected} rejected fence(s)`);
81901
81963
  }
81902
81964
  if (processedTurns > 0) {
81903
81965
  appendPassLedger(paths.configDir, "capture", Date.now() - t, {
@@ -81912,6 +81974,9 @@ function createWorkspaceEngine(opts) {
81912
81974
  tagsRestated,
81913
81975
  resolved,
81914
81976
  triaged,
81977
+ triageFencesSeen,
81978
+ triagePromoted,
81979
+ triageFencesRejected,
81915
81980
  priorEdges,
81916
81981
  corroboratedEdges,
81917
81982
  // RG-mark: refute witnesses stamped onto local nodes (the local half of
@@ -82713,6 +82778,40 @@ async function listenServer(fetchFn, port) {
82713
82778
  init_registry();
82714
82779
  init_identity2();
82715
82780
 
82781
+ // src/instance-receipts.ts
82782
+ import { createHash as createHash16 } from "node:crypto";
82783
+ var FIELDS = ["contributedAtSeq", "cloudNodeId", "anchorsContributedDigest", "projectAnchorsContributedDigest", "semanticEdgesContributedDigest", "shipRefusedSinceSeq", "shipRefusedCount", "shipRefusedReason", "shipRefusedFingerprint"];
82784
+ function instanceDestinationKey(input) {
82785
+ return createHash16("sha256").update(JSON.stringify([
82786
+ input.workspace,
82787
+ input.cloudUrl.replace(/\/+$/, ""),
82788
+ input.installationId ?? input.profileName ?? "default",
82789
+ input.projectId ?? null,
82790
+ input.writeTarget?.visibility ?? null,
82791
+ input.writeTarget?.org_id ?? null,
82792
+ input.writeTarget?.team_id ?? null
82793
+ ])).digest("hex");
82794
+ }
82795
+ function receiptMap(attrs) {
82796
+ const value = attrs["instanceReceipts"];
82797
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
82798
+ }
82799
+ function instanceReceipt(n, destination) {
82800
+ return destination ? receiptMap(n.attrs)[destination] ?? {} : n.attrs;
82801
+ }
82802
+ function updateInstanceReceipt(n, destination, patch, mirrorLegacy) {
82803
+ const receipt = { ...instanceReceipt(n, destination), ...patch };
82804
+ const attrs = { ...n.attrs, instanceReceipts: { ...receiptMap(n.attrs), [destination]: receipt } };
82805
+ if (mirrorLegacy) for (const field of FIELDS) {
82806
+ if (field in receipt) attrs[field] = receipt[field];
82807
+ else delete attrs[field];
82808
+ }
82809
+ return attrs;
82810
+ }
82811
+ function instanceReceiptSequence(n) {
82812
+ return Math.max(n.lastReinforcedAtSeq ?? 0, n.createdAtSeq ?? 0);
82813
+ }
82814
+
82716
82815
  // src/multi.ts
82717
82816
  init_dist();
82718
82817
  init_src5();
@@ -83117,8 +83216,8 @@ function compositionCounters(c) {
83117
83216
  ...Object.fromEntries(buckets.map((b) => [`sidecarDrop_${b}`, sidecarDropReasons[b] ?? 0]))
83118
83217
  };
83119
83218
  }
83120
- function contributedWireId(n) {
83121
- const cloudId = n.attrs["cloudNodeId"];
83219
+ function contributedWireId(n, destinationKey) {
83220
+ const cloudId = instanceReceipt(n, destinationKey)["cloudNodeId"];
83122
83221
  return isMembraneSaltedId(cloudId) ? cloudId : n.id;
83123
83222
  }
83124
83223
  var WIRE_CANONICAL_ID_MIN = 6;
@@ -83262,10 +83361,26 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
83262
83361
  fileRefsSubstituted: 0,
83263
83362
  fileRefLexiconMisses: 0
83264
83363
  };
83364
+ const sharedCauses = /* @__PURE__ */ new Map();
83365
+ const causalQueue = store.findNodesByLabel("Problem").filter(
83366
+ (n) => n.attrs["source"] !== "cloud" && n.attrs["resolvedAs"] !== PROBLEM_RESOLUTION.FALSE_POSITIVE && !ignored(n.description)
83367
+ ).map((n) => n.id);
83368
+ const causalSeen = new Set(causalQueue);
83369
+ for (let i2 = 0; i2 < causalQueue.length; i2++) {
83370
+ const id = causalQueue[i2];
83371
+ for (const e of [...store.outEdges(id, ["CAUSED_BY"]), ...opts.sharedStore?.outEdges(id, ["CAUSED_BY"]) ?? []]) {
83372
+ if (causalSeen.has(e.to)) continue;
83373
+ causalSeen.add(e.to);
83374
+ const cause = nodeOf(e.to);
83375
+ if (!cause || cause.label !== "RootCause" || cause.attrs["source"] === "cloud" || ignored(cause.description)) continue;
83376
+ sharedCauses.set(cause.id, cause);
83377
+ causalQueue.push(cause.id);
83378
+ }
83379
+ }
83265
83380
  for (const label of INSTANCE_LABELS) {
83266
83381
  const ref = [];
83267
83382
  includedByLabel.set(label, ref);
83268
- const population = label === "RootCause" && opts.sharedStore ? [...store.findNodesByLabel(label), ...opts.sharedStore.findNodesByLabel(label)] : store.findNodesByLabel(label);
83383
+ const population = label === "RootCause" && opts.sharedStore ? [...store.findNodesByLabel(label), ...sharedCauses.values()] : store.findNodesByLabel(label);
83269
83384
  for (const n of population) {
83270
83385
  if (seen.has(n.id)) continue;
83271
83386
  if (walked.has(n.id)) continue;
@@ -83283,18 +83398,18 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
83283
83398
  composition.ignored++;
83284
83399
  continue;
83285
83400
  }
83286
- const contributedAtSeq = n.attrs["contributedAtSeq"];
83401
+ const contributedAtSeq = instanceReceipt(n, opts.destinationKey)["contributedAtSeq"];
83287
83402
  if (typeof contributedAtSeq === "number" && (n.lastReinforcedAtSeq ?? 0) <= contributedAtSeq) {
83288
83403
  composition.watermarked++;
83289
83404
  anchorSources.add(n.id);
83290
- semanticEndpoints.set(n.id, contributedWireId(n));
83405
+ semanticEndpoints.set(n.id, contributedWireId(n, opts.destinationKey));
83291
83406
  continue;
83292
83407
  }
83293
83408
  if (label === "Solution" && n.description.startsWith(AUTO_MINT_PREFIX)) {
83294
83409
  composition.autoMint++;
83295
83410
  continue;
83296
83411
  }
83297
- if (isTerminallyRefused(n)) {
83412
+ if (isTerminallyRefused({ ...n, attrs: instanceReceipt(n, opts.destinationKey) })) {
83298
83413
  composition.terminalRefused++;
83299
83414
  continue;
83300
83415
  }
@@ -83353,9 +83468,9 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
83353
83468
  }
83354
83469
  const dg = digest(targets.map(({ e, wireId }) => `${e.type}>${wireId}`).sort());
83355
83470
  const sourceNode = nodeOf(sourceId);
83356
- if (sourceNode?.attrs["anchorsContributedDigest"] === dg) continue;
83471
+ if ((sourceNode && instanceReceipt(sourceNode, opts.destinationKey)["anchorsContributedDigest"]) === dg) continue;
83357
83472
  anchorDigests[sourceId] = dg;
83358
- const wireFrom = seen.has(sourceId) || !sourceNode ? sourceId : contributedWireId(sourceNode);
83473
+ const wireFrom = seen.has(sourceId) || !sourceNode ? sourceId : contributedWireId(sourceNode, opts.destinationKey);
83359
83474
  for (const { e, t, wireId } of targets) {
83360
83475
  const reject = unshippableReason(wireId, t.label);
83361
83476
  if (reject) {
@@ -83384,7 +83499,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
83384
83499
  for (const sourceId of anchorSources) {
83385
83500
  const source = nodeOf(sourceId);
83386
83501
  if (!source) continue;
83387
- const wireFrom = seen.has(sourceId) ? sourceId : contributedWireId(source);
83502
+ const wireFrom = seen.has(sourceId) ? sourceId : contributedWireId(source, opts.destinationKey);
83388
83503
  const planned = [];
83389
83504
  for (const e of store.outEdges(sourceId, ["ANCHORED_AT"])) {
83390
83505
  const target = nodeOf(e.to);
@@ -83419,7 +83534,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
83419
83534
  const dg = digest(
83420
83535
  planned.map(({ symId, wireAttrs }) => `ANCHORED_AT>${symId}>${JSON.stringify(wireAttrs)}`).sort()
83421
83536
  );
83422
- if (source.attrs["projectAnchorsContributedDigest"] === dg) continue;
83537
+ if (instanceReceipt(source, opts.destinationKey)["projectAnchorsContributedDigest"] === dg) continue;
83423
83538
  projectAnchorDigests[sourceId] = dg;
83424
83539
  for (const { e, target, symId, wireAttrs } of planned) {
83425
83540
  if (!contextSeen.has(symId)) {
@@ -83465,7 +83580,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
83465
83580
  if (outs.length === 0) continue;
83466
83581
  const dg = digest(outs.map(({ e, wireTo }) => `${e.type}>${wireTo}`).sort());
83467
83582
  const owner = nodeOf(sourceId);
83468
- const digestSkip = owner?.attrs["semanticEdgesContributedDigest"] === dg;
83583
+ const digestSkip = (owner && instanceReceipt(owner, opts.destinationKey)["semanticEdgesContributedDigest"]) === dg;
83469
83584
  if (!digestSkip) semanticEdgeDigests[sourceId] = dg;
83470
83585
  for (const { e, wireTo } of outs) {
83471
83586
  if (digestSkip && !shippedById.has(e.to)) continue;
@@ -83522,7 +83637,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
83522
83637
  for (const wn of nodes) {
83523
83638
  if (wn.label !== "RootCause" && wn.label !== "Solution") continue;
83524
83639
  const src = nodeOf(wn.id);
83525
- if (typeof src?.attrs["contributedAtSeq"] === "number") continue;
83640
+ if (src && typeof instanceReceipt(src, opts.destinationKey)["contributedAtSeq"] === "number") continue;
83526
83641
  if (bindingEndpoints.has(wn.id)) continue;
83527
83642
  heldOrphans.add(wn.id);
83528
83643
  }
@@ -83676,6 +83791,7 @@ function buildDrainInstances(store, sharedStore, input) {
83676
83791
  ...input.includePackages !== void 0 ? { includePackages: input.includePackages } : {},
83677
83792
  workspaceRoot: input.workspaceRoot,
83678
83793
  sharedStore,
83794
+ destinationKey: input.destinationKey,
83679
83795
  onComposition: (c) => {
83680
83796
  composition = c;
83681
83797
  },
@@ -85343,7 +85459,7 @@ async function startHistoryStage(root, input, client, walk11 = {}) {
85343
85459
 
85344
85460
  // src/link-flow.ts
85345
85461
  init_cloud_auth();
85346
- import { createHash as createHash16 } from "node:crypto";
85462
+ import { createHash as createHash17 } from "node:crypto";
85347
85463
  import { execFileSync as execFileSync5 } from "node:child_process";
85348
85464
  import { existsSync as existsSync30, writeFileSync as writeFileSync23 } from "node:fs";
85349
85465
  import { join as join38 } from "node:path";
@@ -85637,7 +85753,7 @@ async function loginOAuthLoopback(opts) {
85637
85753
 
85638
85754
  // src/link-flow.ts
85639
85755
  function remoteFingerprint(locator) {
85640
- return createHash16("sha256").update(locator).digest("hex");
85756
+ return createHash17("sha256").update(locator).digest("hex");
85641
85757
  }
85642
85758
  function isDecision(value) {
85643
85759
  if (!value || typeof value !== "object") return false;
@@ -86944,11 +87060,17 @@ async function startMultiDaemon(opts = {}) {
86944
87060
  const ANCHOR_PULL_INTERVAL_MS = 10 * 60 * 1e3;
86945
87061
  const anchorPullAll = async () => {
86946
87062
  const c = loadConfig();
86947
- if (!hasCloudCredential(c)) return;
86948
- const client = cloudNow();
87063
+ if (registryCorrupt()) return;
86949
87064
  for (const r of records) {
86950
87065
  const projectId = r.engine.profile.projectId;
86951
87066
  if (!projectId || !effectiveConsent(c, r.engine.profile).sync) continue;
87067
+ const identity = identityFor(liveWorkspaceEntry(r.entry));
87068
+ if (identity.invalid) {
87069
+ noteIdentityBlock(r.id, r.entry.name, identity);
87070
+ continue;
87071
+ }
87072
+ if (identity.rule !== "workspace-binding" && !hasCloudCredential(c)) continue;
87073
+ const client = cloudForIdentity(identity);
86952
87074
  try {
86953
87075
  const { salt } = await resolveProjectSymbolSalt(client, projectId);
86954
87076
  await pullProjectAnchors(r, client, salt);
@@ -86962,11 +87084,17 @@ async function startMultiDaemon(opts = {}) {
86962
87084
  }, ANCHOR_PULL_INTERVAL_MS);
86963
87085
  const ambientLinkAll = async () => {
86964
87086
  const c = loadConfig();
86965
- if (!hasCloudCredential(c)) return;
86966
- const client = cloudNow();
87087
+ if (registryCorrupt()) return;
86967
87088
  for (const r of records) {
86968
87089
  const working = loadProfile(r.root) ?? r.engine.profile;
86969
87090
  if (!effectiveConsent(c, working).sync) continue;
87091
+ const identity = identityFor(liveWorkspaceEntry(r.entry));
87092
+ if (identity.invalid) {
87093
+ noteIdentityBlock(r.id, r.entry.name, identity);
87094
+ continue;
87095
+ }
87096
+ if (identity.rule !== "workspace-binding" && !hasCloudCredential(c)) continue;
87097
+ const client = cloudForIdentity(identity);
86970
87098
  const out2 = await ensureProjectLink(r.root, working, client);
86971
87099
  if (working !== r.engine.profile) adoptLinkFields(r.engine.profile, working);
86972
87100
  if (out2.linked) {
@@ -87354,6 +87482,7 @@ async function startMultiDaemon(opts = {}) {
87354
87482
  }
87355
87483
  const ignore2 = loadClaimIgnorePatterns(globalDir());
87356
87484
  const clientPool = /* @__PURE__ */ new Map();
87485
+ const receiptContexts = /* @__PURE__ */ new Map();
87357
87486
  const poolFor = (identity) => {
87358
87487
  const k = identityKey(identity);
87359
87488
  let c = clientPool.get(k);
@@ -87496,7 +87625,26 @@ async function startMultiDaemon(opts = {}) {
87496
87625
  return res.accepted;
87497
87626
  });
87498
87627
  }
87628
+ const receiptProfile = recordIdentity.profile ?? (cfg2.activeInstallationProfile ? cfg2.installationProfiles[cfg2.activeInstallationProfile] : void 0);
87629
+ let receiptContext = receiptContexts.get(identityKey(recordIdentity));
87630
+ if (!receiptContext) {
87631
+ try {
87632
+ receiptContext = await client.context();
87633
+ receiptContexts.set(identityKey(recordIdentity), receiptContext);
87634
+ } catch (err2) {
87635
+ errors.push(`[${projectName}] instance destination unresolved: ${err2 instanceof Error ? err2.message : String(err2)}`);
87636
+ continue;
87637
+ }
87638
+ }
87639
+ const destinationKey = instanceDestinationKey({
87640
+ workspace: r.id,
87641
+ cloudUrl: opts.cloudUrl ?? receiptProfile?.cloudUrl ?? cfg2.cloudUrl,
87642
+ installationId: receiptContext.context.installation_id ?? receiptContext.identity.credential_id,
87643
+ projectId: project?.projectId,
87644
+ writeTarget: project ? null : receiptContext.context.default_write_target
87645
+ });
87499
87646
  const built = await buildInstanceBatch(r, {
87647
+ destinationKey,
87500
87648
  profile: r.engine.profile,
87501
87649
  daemonVersion: DAEMON_VERSION,
87502
87650
  ignorePatterns: [...ignore2],
@@ -87541,7 +87689,6 @@ async function startMultiDaemon(opts = {}) {
87541
87689
  );
87542
87690
  }
87543
87691
  uploaded += res.accepted;
87544
- const seq = store.currentIngestSeq();
87545
87692
  const cloudIdByLocal = new Map(
87546
87693
  res.result.nodes.filter((rn) => rn.nodeId).map((rn) => [rn.canonicalId, rn.nodeId])
87547
87694
  );
@@ -87550,6 +87697,7 @@ async function startMultiDaemon(opts = {}) {
87550
87697
  if (rn.decision === "rejected") rejected.set(rn.canonicalId, rn.reason ?? "no reason given");
87551
87698
  }
87552
87699
  const rejectedEdges = res.result.edges.filter((re) => re.decision === "rejected");
87700
+ const edgeReceiptsComplete = rejectedEdges.length === 0 && res.result.edges.length === instances.edges.length;
87553
87701
  if (rejectedEdges.length > 0) {
87554
87702
  const byReason = /* @__PURE__ */ new Map();
87555
87703
  for (const re of rejectedEdges) {
@@ -87580,17 +87728,17 @@ async function startMultiDaemon(opts = {}) {
87580
87728
  const owner = owningStore(id);
87581
87729
  if (!owner) return;
87582
87730
  const fp = refusalFingerprint(reason, owner.n.description);
87583
- const same = owner.n.attrs["shipRefusedFingerprint"] === fp;
87584
- const count = same ? Number(owner.n.attrs["shipRefusedCount"] ?? 0) + 1 : 1;
87585
- if (owner.n.attrs["shipRefusedSinceSeq"] !== void 0) repeat();
87731
+ const same = instanceReceipt(owner.n, destinationKey)["shipRefusedFingerprint"] === fp;
87732
+ const count = same ? Number(instanceReceipt(owner.n, destinationKey)["shipRefusedCount"] ?? 0) + 1 : 1;
87733
+ if (instanceReceipt(owner.n, destinationKey)["shipRefusedSinceSeq"] !== void 0) repeat();
87586
87734
  owner.s.updateNode(id, {
87587
- attrs: {
87588
- ...owner.n.attrs,
87589
- ...same && owner.n.attrs["shipRefusedSinceSeq"] !== void 0 ? {} : { shipRefusedSinceSeq: seq },
87735
+ attrs: updateInstanceReceipt(owner.n, destinationKey, {
87736
+ ...instanceReceipt(owner.n, destinationKey),
87737
+ ...same && instanceReceipt(owner.n, destinationKey)["shipRefusedSinceSeq"] !== void 0 ? {} : { shipRefusedSinceSeq: owner.s.currentIngestSeq() },
87590
87738
  shipRefusedCount: count,
87591
87739
  shipRefusedReason: reason,
87592
87740
  shipRefusedFingerprint: fp
87593
- }
87741
+ }, owner.s === store)
87594
87742
  });
87595
87743
  if (same && count === SHIP_TERMINAL_REFUSALS && !isRetryableRefusal(reason)) {
87596
87744
  nowHeld.push({ id, reason });
@@ -87607,6 +87755,7 @@ async function startMultiDaemon(opts = {}) {
87607
87755
  const owner = owningStore(n.id);
87608
87756
  if (!owner || !["Problem", "Solution", "RootCause"].includes(owner.n.label)) return;
87609
87757
  if (blocked.has(n.id)) return;
87758
+ if (!cloudIdByLocal.has(n.id)) return;
87610
87759
  if (rejected.has(n.id)) return;
87611
87760
  const cloudNodeId = cloudIdByLocal.get(n.id);
87612
87761
  const {
@@ -87615,37 +87764,41 @@ async function startMultiDaemon(opts = {}) {
87615
87764
  shipRefusedReason: _clearedReason,
87616
87765
  shipRefusedFingerprint: _clearedFp,
87617
87766
  ...carried
87618
- } = owner.n.attrs;
87767
+ } = instanceReceipt(owner.n, destinationKey);
87619
87768
  owner.s.updateNode(n.id, {
87620
- attrs: {
87769
+ attrs: updateInstanceReceipt(owner.n, destinationKey, {
87621
87770
  ...carried,
87622
- contributedAtSeq: seq,
87623
- ...cloudNodeId && cloudNodeId !== n.id ? { cloudNodeId } : {}
87624
- }
87771
+ shipRefusedSinceSeq: void 0,
87772
+ shipRefusedCount: void 0,
87773
+ shipRefusedReason: void 0,
87774
+ shipRefusedFingerprint: void 0,
87775
+ contributedAtSeq: instanceReceiptSequence(n),
87776
+ cloudNodeId
87777
+ }, owner.s === store)
87625
87778
  });
87626
87779
  });
87627
87780
  await stampBatched(Object.entries(instances.anchorDigests), ([localId, dg]) => {
87628
87781
  const owner = owningStore(localId);
87629
87782
  if (!owner) return;
87630
- if (rejected.has(localId) || blocked.has(localId)) return;
87783
+ if (!edgeReceiptsComplete || !instanceReceipt(owner.n, destinationKey)["cloudNodeId"] || rejected.has(localId) || blocked.has(localId)) return;
87631
87784
  owner.s.updateNode(localId, {
87632
- attrs: { ...owner.n.attrs, anchorsContributedDigest: dg }
87785
+ attrs: updateInstanceReceipt(owner.n, destinationKey, { anchorsContributedDigest: dg }, owner.s === store)
87633
87786
  });
87634
87787
  });
87635
87788
  await stampBatched(Object.entries(instances.projectAnchorDigests), ([localId, dg]) => {
87636
87789
  const owner = owningStore(localId);
87637
87790
  if (!owner) return;
87638
- if (rejected.has(localId) || blocked.has(localId)) return;
87791
+ if (!edgeReceiptsComplete || !instanceReceipt(owner.n, destinationKey)["cloudNodeId"] || rejected.has(localId) || blocked.has(localId)) return;
87639
87792
  owner.s.updateNode(localId, {
87640
- attrs: { ...owner.n.attrs, projectAnchorsContributedDigest: dg }
87793
+ attrs: updateInstanceReceipt(owner.n, destinationKey, { projectAnchorsContributedDigest: dg }, owner.s === store)
87641
87794
  });
87642
87795
  });
87643
87796
  await stampBatched(Object.entries(instances.semanticEdgeDigests), ([localId, dg]) => {
87644
87797
  const owner = owningStore(localId);
87645
87798
  if (!owner) return;
87646
- if (rejected.has(localId) || blocked.has(localId)) return;
87799
+ if (!edgeReceiptsComplete || !instanceReceipt(owner.n, destinationKey)["cloudNodeId"] || rejected.has(localId) || blocked.has(localId)) return;
87647
87800
  owner.s.updateNode(localId, {
87648
- attrs: { ...owner.n.attrs, semanticEdgesContributedDigest: dg }
87801
+ attrs: updateInstanceReceipt(owner.n, destinationKey, { semanticEdgesContributedDigest: dg }, owner.s === store)
87649
87802
  });
87650
87803
  });
87651
87804
  appendPassLedger(r.engine.paths.configDir, "ship", Date.now() - shipStartedAt, {
@@ -89539,7 +89692,7 @@ async function cmdWhoami(args2) {
89539
89692
  `note: daemon capture and the MCP server resolve identically; a process with ERRATA_CONFIG_PATH set resolves by rule 1 regardless of binding.`
89540
89693
  ];
89541
89694
  if (args2.includes("--json")) {
89542
- console.log(JSON.stringify({ path: path2, registered: Boolean(entry), ...identity }, null, 2));
89695
+ console.log(JSON.stringify({ path: path2, registered: Boolean(entry), ...publicWorkspaceIdentity(identity) }, null, 2));
89543
89696
  } else {
89544
89697
  for (const l of lines) console.log(l);
89545
89698
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.9",
3
+ "version": "2.0.10",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -15140,7 +15140,7 @@ var init_symbol_intent = __esm({
15140
15140
  });
15141
15141
 
15142
15142
  // ../../packages/shared/src/wire.ts
15143
- var CLOUD_NODE_LABELS, CLOUD_WRITABLE_NODE_LABELS, PROJECT_ONLY_NODE_LABELS, PROJECT_ONLY_NODE_LABEL_SET, PROJECT_ONLY_EDGE_TYPES, PROJECT_ONLY_EDGE_TYPE_SET, CLOUD_WRITABLE_NODE_LABEL_SET, CLOUD_EDGE_TYPES, INGEST_SOURCES, INGEST_EXTRACTION_SOURCES, CastaliaIngestNodeSchema, RouteContextCountWireSchema, ANCHOR_COMMIT_SHA_RE, ANCHOR_BODY_HASH_RE, ANCHOR_SYMBOL_KINDS, ANCHOR_LABEL_KINDS, ANCHOR_LABEL_PATH_RE, ANCHOR_LABEL_QNAME_RE, CastaliaIngestEdgeSchema, SymbolSummarySidecarSchema, CastaliaIngestPayloadSchema;
15143
+ var CLOUD_NODE_LABELS, CLOUD_WRITABLE_NODE_LABELS, PROJECT_ONLY_NODE_LABELS, PROJECT_ONLY_NODE_LABEL_SET, PROJECT_ONLY_EDGE_TYPES, PROJECT_ONLY_EDGE_TYPE_SET, CLOUD_WRITABLE_NODE_LABEL_SET, CLOUD_EDGE_TYPES, INGEST_SOURCES, INGEST_EXTRACTION_SOURCES, CastaliaIngestNodeSchema, RouteContextCountWireSchema, ANCHOR_COMMIT_SHA_RE, ANCHOR_BODY_HASH_RE, ANCHOR_SYMBOL_KINDS, ANCHOR_LABEL_KINDS, ANCHOR_LABEL_PATH_RE, ANCHOR_LABEL_QNAME_RE, TRIAGE_DISCRIMINATOR_MAX_LENGTH, CastaliaIngestEdgeSchema, SymbolSummarySidecarSchema, CastaliaIngestPayloadSchema;
15144
15144
  var init_wire = __esm({
15145
15145
  "../../packages/shared/src/wire.ts"() {
15146
15146
  "use strict";
@@ -15258,6 +15258,7 @@ var init_wire = __esm({
15258
15258
  ANCHOR_LABEL_KINDS = [...ANCHOR_SYMBOL_KINDS, "File"];
15259
15259
  ANCHOR_LABEL_PATH_RE = /^(?![/])(?![A-Za-z]:)(?!(?:.*\/)?\.\.(?:\/|$))[^\x00-\x1f\x7f\\`]{1,512}$/;
15260
15260
  ANCHOR_LABEL_QNAME_RE = /^[^\x00-\x1f\x7f`]{1,256}$/;
15261
+ TRIAGE_DISCRIMINATOR_MAX_LENGTH = 2e3;
15261
15262
  CastaliaIngestEdgeSchema = external_exports.object({
15262
15263
  fromCanonicalId: external_exports.string().min(6).max(160),
15263
15264
  toCanonicalId: external_exports.string().min(6).max(160),
@@ -15268,6 +15269,8 @@ var init_wire = __esm({
15268
15269
  /** Triage route per-context counts (T3 induction). Carried as structured data
15269
15270
  * here; stored JSON-encoded on the Neo4j rel (`perContextJson`). */
15270
15271
  perContext: external_exports.record(external_exports.string(), RouteContextCountWireSchema).optional(),
15272
+ /** Scrubbed diagnostic check for a triage route, re-scanned at the cloud door. */
15273
+ discriminator: external_exports.string().trim().min(1).max(TRIAGE_DISCRIMINATOR_MAX_LENGTH).optional(),
15271
15274
  /** PJ-anchor-version: WHICH version of a symbol an `ANCHORED_AT` was
15272
15275
  * witnessed against. Both opaque — the observing checkout's HEAD sha and
15273
15276
  * the indexer's 16-hex sha256 of the symbol body. Neither is reversible