@inerrata-corporation/errata 2.0.22-dev.4135 → 2.0.22-dev.4164

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/errata.mjs CHANGED
@@ -94,7 +94,7 @@ function toCloudAttrs(attrs) {
94
94
  }
95
95
  return rest2;
96
96
  }
97
- var SEMANTIC_NODE_LABELS, ANCHOR_REFUSAL_REASONS, ROUTING_REASONS, CONTEXT_NODE_LABELS, CODE_NODE_LABELS, ALL_NODE_LABELS, STRUCTURAL_EDGES, CAUSAL_EDGES, RESOLUTION_EDGES, CONCEPTUAL_EDGES, EVIDENCE_EDGES, CODE_EDGES, BRIDGE_EDGES, IDENTITY_EDGES, JUSTIFICATION_EDGES, BELIEF_EDGES, ABSTRACTION_EDGES, DECOMPOSITION_EDGES, TRANSFER_EDGES, SOLUTION_STRUCTURE_EDGES, SUSPICION_EDGES, TAXONOMY_EDGES, TRIAGE_EDGES, GIT_EDGES, ALL_EDGE_TYPES, PAGERANK_EXCLUSIONS, EDGE_WEIGHT, EDGE_CONDUCTANCE_OVERRIDES, CAUSAL_PROTECT_EDGES, VALIDATION_SOURCES, TRUTH_KIND, ABSTRACTION_LEVEL, PROBLEM_RESOLUTION, DRIFT_KIND, TEST_PATH_RE, TEST_SCOPE_RE;
97
+ var SEMANTIC_NODE_LABELS, ANCHOR_REFUSAL_REASONS, ROUTING_REASONS, CONTEXT_NODE_LABELS, CODE_NODE_LABELS, ALL_NODE_LABELS, STRUCTURAL_EDGES, CAUSAL_EDGES, RESOLUTION_EDGES, CONCEPTUAL_EDGES, EVIDENCE_EDGES, CODE_EDGES, BRIDGE_EDGES, IDENTITY_EDGES, JUSTIFICATION_EDGES, BELIEF_EDGES, ABSTRACTION_EDGES, DECOMPOSITION_EDGES, TRANSFER_EDGES, PROJECT_SYNC_EDGE_TYPES, SOLUTION_STRUCTURE_EDGES, SUSPICION_EDGES, TAXONOMY_EDGES, TRIAGE_EDGES, GIT_EDGES, ALL_EDGE_TYPES, PAGERANK_EXCLUSIONS, EDGE_WEIGHT, EDGE_CONDUCTANCE_OVERRIDES, CAUSAL_PROTECT_EDGES, VALIDATION_SOURCES, TRUTH_KIND, ABSTRACTION_LEVEL, PROBLEM_RESOLUTION, DRIFT_KIND, TEST_PATH_RE, TEST_SCOPE_RE;
98
98
  var init_castalia = __esm({
99
99
  "../../packages/shared/src/castalia.ts"() {
100
100
  "use strict";
@@ -321,6 +321,7 @@ var init_castalia = __esm({
321
321
  ABSTRACTION_EDGES = ["GENERALIZES", "DERIVED_FROM"];
322
322
  DECOMPOSITION_EDGES = ["SPLIT_INTO"];
323
323
  TRANSFER_EDGES = ["MAY_RESOLVE"];
324
+ PROJECT_SYNC_EDGE_TYPES = ["SOLVED_BY", "CAUSED_BY", "FIXED_BY", "MAY_RESOLVE"];
324
325
  SOLUTION_STRUCTURE_EDGES = ["BUILDS_ON", "SUPERSEDES", "ALTERNATIVE_TO"];
325
326
  SUSPICION_EDGES = ["SUSPECTED_LINK", "REGRESSION_OF"];
326
327
  TAXONOMY_EDGES = ["IS_A"];
@@ -17625,6 +17626,7 @@ __export(src_exports, {
17625
17626
  PROBLEM_RESOLUTION: () => PROBLEM_RESOLUTION,
17626
17627
  PROJECT_ONLY_EDGE_TYPES: () => PROJECT_ONLY_EDGE_TYPES,
17627
17628
  PROJECT_ONLY_NODE_LABELS: () => PROJECT_ONLY_NODE_LABELS,
17629
+ PROJECT_SYNC_EDGE_TYPES: () => PROJECT_SYNC_EDGE_TYPES,
17628
17630
  QUARANTINE_EXTRACTION_SOURCE_PREFIX: () => QUARANTINE_EXTRACTION_SOURCE_PREFIX,
17629
17631
  REDACTED_CREDENTIAL: () => REDACTED_CREDENTIAL,
17630
17632
  REDACTED_EMAIL: () => REDACTED_EMAIL,
@@ -18205,6 +18207,603 @@ var init_sqlite_adapter = __esm({
18205
18207
  }
18206
18208
  });
18207
18209
 
18210
+ // ../../packages/local-shared/src/tag-grammar.ts
18211
+ function tagGrammarCopy() {
18212
+ const copy = [];
18213
+ for (const ref of ["its-handle", "its-id", "h", "id"]) {
18214
+ for (const build of Object.values(TAG_EXAMPLES)) copy.push(build(ref));
18215
+ }
18216
+ for (const v of TAG_VERBS) {
18217
+ copy.push(v.hint);
18218
+ if (v.taughtBy) copy.push(v.taughtBy);
18219
+ for (const q of v.qualifiers ?? []) copy.push(q.note);
18220
+ for (const l of v.lowering) {
18221
+ if (l.note) copy.push(l.note);
18222
+ if (l.knownViolation) copy.push(l.knownViolation.finding);
18223
+ }
18224
+ }
18225
+ for (const f of TAG_FENCES) copy.push(f.hint);
18226
+ return copy;
18227
+ }
18228
+ function normalizeTemplateBody(body2) {
18229
+ return body2.replace(/…|\.{3}/g, " ").replace(/\s+/g, " ").trim().replace(/^[\s,;:—–-]+|[\s,;:.—–-]+$/g, "").toLowerCase();
18230
+ }
18231
+ function deriveTagTemplates(copy) {
18232
+ const bodies = /* @__PURE__ */ new Set();
18233
+ const handles = new Set([...TAG_PLACEHOLDER_HANDLES].map((h) => h.toLowerCase()));
18234
+ for (const text of copy) {
18235
+ for (const m of text.matchAll(BRACKET_TOKEN)) {
18236
+ const token = m[1].trim().toLowerCase();
18237
+ if (!/^[!?]/.test(token) && !REAL_HANDLE_SHAPE.test(token)) handles.add(token);
18238
+ }
18239
+ }
18240
+ for (const text of copy) {
18241
+ for (const m of text.matchAll(TEMPLATE_VERB_GROUP)) {
18242
+ const body2 = normalizeTemplateBody(m[2]);
18243
+ bodies.add(body2);
18244
+ const unthreaded = normalizeTemplateBody(body2.replace(/^#[a-z][\w-]*/i, ""));
18245
+ bodies.add(unthreaded);
18246
+ const rest2 = normalizeTemplateBody(unthreaded.replace(BRACKET_TOKEN, (t, h) => handles.has(h.trim().toLowerCase()) ? " " : t).replace(/,/g, " "));
18247
+ bodies.add(rest2);
18248
+ }
18249
+ }
18250
+ bodies.delete("");
18251
+ return { bodies, handles };
18252
+ }
18253
+ function isTemplateSegment(verb, text, templates) {
18254
+ const t = normalizeTemplateBody(text);
18255
+ if (t === "" || templates.bodies.has(t)) return true;
18256
+ const unthreaded = normalizeTemplateBody(t.replace(/^#[a-z][\w-]*/i, ""));
18257
+ if (unthreaded === "" || templates.bodies.has(unthreaded)) return true;
18258
+ let real = false;
18259
+ let standIn = false;
18260
+ const rest2 = unthreaded.replace(BRACKET_TOKEN, (token, inner) => {
18261
+ const h = inner.trim().toLowerCase();
18262
+ if (/^[!?]/.test(h)) return token;
18263
+ if (templates.handles.has(h) || /…|\.{3}/.test(h) || !REAL_HANDLE_SHAPE.test(h)) standIn = true;
18264
+ else real = true;
18265
+ return " ";
18266
+ });
18267
+ if (standIn || real) return true;
18268
+ const words = normalizeTemplateBody(rest2.replace(ANY_VERB_WORD, " ").replace(/,/g, " "));
18269
+ if (words === "" || templates.bodies.has(words)) return true;
18270
+ const spec = verb ? TAG_VERB_BY_NAME.get(verb) : void 0;
18271
+ if (!spec || spec.handleRequired) return true;
18272
+ return words.length < (spec.minText ?? 2);
18273
+ }
18274
+ function isTemplateTagBody(verb, body2, templates = TAG_TEMPLATES) {
18275
+ const whole = normalizeTemplateBody(body2);
18276
+ if (whole === "" || templates.bodies.has(whole)) return true;
18277
+ const segments = body2.split(JOINED_VERB);
18278
+ return segments.every((seg, i2) => {
18279
+ const lead2 = i2 === 0 ? null : LEADING_VERB.exec(seg);
18280
+ return isTemplateSegment(lead2 ? lead2[1].toLowerCase() : verb, lead2 ? seg.slice(lead2[0].length) : seg, templates);
18281
+ });
18282
+ }
18283
+ function quotesOnlyTemplateTags(span, templates = TAG_TEMPLATES) {
18284
+ let found = false;
18285
+ for (const m of span.matchAll(QUOTED_TAG_OPENER)) {
18286
+ found = true;
18287
+ if (m.index > 0 && /[\w$.\])]/.test(span[m.index - 1])) continue;
18288
+ const bodyStart = m.index + m[0].length;
18289
+ let depth = 1;
18290
+ let close = -1;
18291
+ for (let i2 = bodyStart; i2 < span.length; i2++) {
18292
+ if (span[i2] === "(") depth++;
18293
+ else if (span[i2] === ")" && --depth === 0) {
18294
+ close = i2;
18295
+ break;
18296
+ }
18297
+ }
18298
+ if (close < 0) continue;
18299
+ if (!isTemplateTagBody(m[1]?.toLowerCase() ?? null, span.slice(bodyStart, close), templates)) return false;
18300
+ }
18301
+ return found;
18302
+ }
18303
+ var TAG_CANONICAL_ID_PREFIXES, TAG_HANDLE_SOURCE, TAG_THREAD_SOURCE, TAG_PLACEHOLDER_HANDLES, TAG_EXAMPLES, P, S, RC, CITE_LOWERING, INSTANCE_LOWERING, TRIAGE_TO_RC, TAG_VERBS, TAG_FENCES, TAG_VERB_BY_NAME, TAG_PAREN_VERBS, TAG_GRAMMAR_OPENERS, TAG_QUOTED_PAREN_SOURCE, TAG_QUOTED_RE, TAG_QUOTED_PAREN_RE, TEMPLATE_VERB_GROUP, BRACKET_TOKEN, REAL_HANDLE_SHAPE, TAG_TEMPLATES, VERB_WORDS_SOURCE, JOINED_VERB, LEADING_VERB, ANY_VERB_WORD, QUOTED_TAG_OPENER, TAG_VERDICT_TOKENS;
18304
+ var init_tag_grammar = __esm({
18305
+ "../../packages/local-shared/src/tag-grammar.ts"() {
18306
+ "use strict";
18307
+ TAG_CANONICAL_ID_PREFIXES = ["pat", "sol", "drc", "dcause", "dfix", "dprob", "dvuln", "prob", "claim", "err"];
18308
+ TAG_HANDLE_SOURCE = String.raw`\[([a-z0-9][\w-]{0,80})\]|((?:${TAG_CANONICAL_ID_PREFIXES.join("|")})_[0-9a-f]{6,})`;
18309
+ TAG_THREAD_SOURCE = String.raw`#([a-z][\w-]{0,63})`;
18310
+ TAG_PLACEHOLDER_HANDLES = /* @__PURE__ */ new Set([
18311
+ "handle",
18312
+ "its-handle",
18313
+ "another",
18314
+ "a-third",
18315
+ "h",
18316
+ "h1",
18317
+ "h2",
18318
+ "id",
18319
+ "its-id",
18320
+ "a",
18321
+ "b",
18322
+ "\u2026",
18323
+ "..."
18324
+ ]);
18325
+ TAG_EXAMPLES = {
18326
+ prior: (ref) => `([${ref}])`,
18327
+ problem: () => `[!one line on what's wrong]`,
18328
+ vulnerability: () => `[!vuln: who can do what; observed basis @ path:line]`,
18329
+ suspectedVulnerability: () => `[?vuln: consequence; missing check]`,
18330
+ vulnerabilityThread: () => `[!#v vuln: finding here]`,
18331
+ fix: (ref) => `(fix:[${ref}])`,
18332
+ // CONSTRAINT — the native capture path for a DIFFUSE problem (a constraint/tension/
18333
+ // conflict that shaped a decision). Free text, attaches to the decision sentence the
18334
+ // agent is already writing. Full rationale in the constraint note below + prior-tags.ts.
18335
+ constraint: () => `(constraint: the limitation that forced it)`,
18336
+ // TRIAGE — routes the problem/symptom in scope to its cause. `(cause:[handle])`
18337
+ // links to a cited RootCause/Solution node; `(cause: free text)` mints a RootCause
18338
+ // from the text. Harvested in prior-tags.ts → engine.ts (see the triage note below).
18339
+ triage: (ref) => `(cause:[${ref}])`,
18340
+ // Unlike a bare cause citation, this handle names the Problem being explained.
18341
+ triageExplain: (ref) => `(cause:[${ref}] the mechanism)`,
18342
+ // The MINT form — free-text cause that mints a RootCause. Prime-time surfacing
18343
+ // (recallForFile) pastes this next to an open Problem that has no cause yet, so the
18344
+ // diagnostic moment is cued when the agent is looking at the problem. Kept here so
18345
+ // the grammar can't drift from parseInlineTags' `(cause: …)` acceptance.
18346
+ triageMint: () => `(cause: why it happens)`,
18347
+ // THREAD forms — the witness-declared symptom↔cause/fix binding. A `#id` on the
18348
+ // flag names the diagnosis thread; the same `#id` on a later verb binds to it
18349
+ // explicitly (across sentences AND turns), replacing proximity guessing. Adjacent
18350
+ // tags never need one — the id is for diagnoses that unfold over distance.
18351
+ problemThread: () => `[!#slug one line on what's wrong]`,
18352
+ causeThread: () => `(cause:#slug the mechanism)`,
18353
+ fixThread: (thread = "slug") => `(fix:#${thread} what you changed)`,
18354
+ // ATTEMPT arc — what's being tried and what didn't work. A failed attempt is a
18355
+ // witnessed NEGATIVE result: it saves the next agent the same dead end.
18356
+ tried: () => `(tried: the approach you're attempting)`,
18357
+ failed: () => `(failed: what didn't work and the dead end it rules out)`,
18358
+ // `(failed:[h] …)` on a shown SOLUTION — the refute-by-failure form the context render teaches.
18359
+ failedCite: (ref) => `(failed:[${ref}] what came back)`,
18360
+ // LINK verbs (AC epic) — bind the problem in scope to CANONICAL priors and
18361
+ // abstractions. Multi-target by design: single links are weak; ~3 links to 3
18362
+ // DIFFERENT relevant priors is the target shape (see linkBullet).
18363
+ instance: (ref) => `(instance:[${ref}],[another],[a-third])`,
18364
+ pattern: () => `(pattern: name the general shape)`,
18365
+ // DOMAIN — the abstract area a problem is about; the concept layer an
18366
+ // anchor-less problem clusters on. Mints/resolves a Domain by name.
18367
+ domain: () => `(domain: The Area)`,
18368
+ // PACKAGE/COMPONENT — agent-named public anchors (OM-agent-anchors): the
18369
+ // package a problem is ABOUT (even when not a dependency) and the
18370
+ // framework/product-level unit it concerns. Public spine anchors → the
18371
+ // knowledge can cross to the collective.
18372
+ package: () => `(package: the-package-name)`,
18373
+ component: () => `(component: The Component)`,
18374
+ aids: (ref) => `(aids:[${ref}])`,
18375
+ // SOLUTION-STRUCTURE (SS-structure) — taught in the link bullet; Solution→Solution.
18376
+ buildsOn: (ref) => `(builds-on:[${ref}])`,
18377
+ supersedes: (ref) => `(supersedes:[${ref}])`,
18378
+ alternative: (ref) => `(alternative:[${ref}])`,
18379
+ // VERDICT (VS-verdict-tags) — a judgment on a SHOWN prior, distinct from leaning on it.
18380
+ // `confirm` rides the corroborate channel (it held); `refute` the contradict channel
18381
+ // (it's wrong / didn't hold) and REQUIRES a reason. This is the inline producer the
18382
+ // evidence channels lacked — refute sat dead (contradictionPressure 0) until now, while
18383
+ // lean-on corroboration lived, because only corroboration had an inline path. Grammar
18384
+ // single-sourced with parseInlineTags' CONFIRM_PREFIX/REFUTE_PREFIX.
18385
+ confirm: (ref) => `(confirm:[${ref}])`,
18386
+ refute: (ref) => `(refute:[${ref}] one line why)`,
18387
+ // RETRACT — "not a real problem": the errata-resolved block with a disposition
18388
+ // (parseDesignResolutions, turn.ts). No inline form exists; the block is the grammar.
18389
+ retract: (id) => ["```errata-resolved", `id: ${id}`, "disposition: false_positive", "reason: why it was never a real problem", "```"].join("\n")
18390
+ };
18391
+ P = "Problem";
18392
+ S = "Solution";
18393
+ RC = "RootCause";
18394
+ CITE_LOWERING = [
18395
+ { target: RC, writes: [{ from: P, edge: "CAUSED_BY", to: RC }] },
18396
+ { target: S, writes: [{ from: P, edge: "SOLVED_BY", to: S }] },
18397
+ { target: "Pattern", writes: [{ from: P, edge: "INSTANCE_OF", to: "Pattern" }, { from: "Pattern", edge: "ANCHORED_AT", to: "File" }], note: "cite-anchor promotion after 2 sessions", decision: "d", knownViolation: { edge: "ANCHORED_AT", finding: "motif cite-anchor promotion writes a refused ANCHORED_AT" } },
18398
+ { target: "Package", writes: [{ from: P, edge: "DEPENDS_ON", to: "Package" }] },
18399
+ { target: "Language", writes: [{ from: P, edge: "OCCURS_IN", to: "Language" }] },
18400
+ { target: "Tool", writes: [{ from: P, edge: "CONCERNS", to: "Tool" }] },
18401
+ { target: "Component", writes: [{ from: P, edge: "CONCERNS", to: "Component" }] },
18402
+ { target: P, writes: [{ from: P, edge: "RELATES_TO", to: P }, { from: P, edge: "ANCHORED_AT", to: "File" }], note: "corroboration witness + cite-anchor promotion" },
18403
+ { target: "AntiPattern", writes: [{ from: P, edge: "RELATES_TO", to: "AntiPattern" }, { from: "AntiPattern", edge: "ANCHORED_AT", to: "File" }], decision: "e", knownViolation: { edge: "ANCHORED_AT", finding: "motif cite-anchor promotion writes a refused ANCHORED_AT" } },
18404
+ { target: "Technique", writes: [{ from: P, edge: "RELATES_TO", to: "Technique" }, { from: "Technique", edge: "ANCHORED_AT", to: "File" }], decision: "e", knownViolation: { edge: "ANCHORED_AT", finding: "motif cite-anchor promotion writes a refused ANCHORED_AT" } },
18405
+ { target: "Weakness", writes: [{ from: P, edge: "RELATES_TO", to: "Weakness" }], decision: "e" },
18406
+ { target: "Domain", writes: [{ from: P, edge: "RELATES_TO", to: "Domain" }], decision: "e" },
18407
+ { target: "Claim", writes: [{ from: P, edge: "RELATES_TO", to: "Claim" }, { from: "Claim", edge: "ANCHORED_AT", to: "File" }] }
18408
+ ];
18409
+ INSTANCE_LOWERING = [
18410
+ { target: "Pattern", writes: [{ from: P, edge: "INSTANCE_OF", to: "Pattern" }] },
18411
+ { target: P, writes: [{ from: P, edge: "RELATES_TO", to: P }] },
18412
+ { target: S, writes: [{ from: P, edge: "SOLVED_BY", to: S }], note: "an instance cite on a Solution claims the problem is solved by it", decision: "a" },
18413
+ { target: RC, writes: [{ from: P, edge: "CAUSED_BY", to: RC }] },
18414
+ { target: "AntiPattern", writes: [{ from: P, edge: "RELATES_TO", to: "AntiPattern" }], decision: "e" },
18415
+ { target: "Technique", writes: [{ from: P, edge: "RELATES_TO", to: "Technique" }], decision: "e" },
18416
+ { target: "Weakness", writes: [{ from: P, edge: "RELATES_TO", to: "Weakness" }], decision: "e" },
18417
+ { target: "Domain", writes: [{ from: P, edge: "RELATES_TO", to: "Domain" }], decision: "e" },
18418
+ { target: "Package", writes: [{ from: P, edge: "DEPENDS_ON", to: "Package" }] },
18419
+ { target: "Component", writes: [{ from: P, edge: "CONCERNS", to: "Component" }] }
18420
+ ];
18421
+ TRIAGE_TO_RC = [
18422
+ { from: P, edge: "TRIAGED_BY", to: "Triage" },
18423
+ { from: "Triage", edge: "INDICATES", to: RC },
18424
+ { from: P, edge: "CAUSED_BY", to: RC }
18425
+ ];
18426
+ TAG_VERBS = [
18427
+ {
18428
+ verb: "cite",
18429
+ form: "citation",
18430
+ opener: "(",
18431
+ kinds: ["prior"],
18432
+ args: "handles",
18433
+ thread: false,
18434
+ handleRequired: true,
18435
+ lowering: CITE_LOWERING,
18436
+ examples: ["prior"],
18437
+ hint: "cite a shown prior you leaned on: ([its-handle]) \u2014 handles only, no prose inside the parens",
18438
+ taught: "taught"
18439
+ },
18440
+ {
18441
+ verb: "!",
18442
+ form: "flag",
18443
+ opener: "[!",
18444
+ kinds: ["problem", "triage", "vulnerability", "fix", "prior"],
18445
+ args: "flag-body",
18446
+ thread: true,
18447
+ minText: 8,
18448
+ qualifiers: [
18449
+ { name: "vuln", taught: true, note: "[!vuln: \u2026] \u2192 Vulnerability (asserted)" },
18450
+ { name: "fixed", taught: false, note: "[!fixed: X] \u2192 read as a self-found fix" },
18451
+ { name: "resolved", taught: false, note: "[!resolved: X] \u2192 read as a self-found fix" }
18452
+ ],
18453
+ lowering: [
18454
+ { target: P, writes: [], note: "mints a Problem; `@ path[:line]` anchors it (ANCHORED_AT to code)" },
18455
+ { target: RC, writes: [...TRIAGE_TO_RC, { from: RC, edge: "CAUSED_BY", to: RC }], note: "`<-` segments: first \u2192 triage route, later \u2192 RootCause chain" },
18456
+ { target: "Vulnerability", writes: [], note: "[!vuln: \u2026]; `<-` causes stay on the finding (vulnerabilityCauses)" }
18457
+ ],
18458
+ examples: ["problem", "problemThread", "vulnerability", "vulnerabilityThread"],
18459
+ hint: "flag a problem on one line: [!what's wrong @ path:42]; fold causes with <- ; a fix goes in (fix: \u2026), not [!fixed: \u2026]",
18460
+ taught: "taught"
18461
+ },
18462
+ {
18463
+ verb: "?",
18464
+ form: "flag",
18465
+ opener: "[?",
18466
+ kinds: ["todo", "vulnerability"],
18467
+ args: "flag-body",
18468
+ thread: true,
18469
+ minText: 8,
18470
+ qualifiers: [{ name: "vuln", taught: true, note: "[?vuln: \u2026] \u2192 Vulnerability (suspected)" }],
18471
+ lowering: [{ target: P, writes: [], note: "mints a todo Problem; a fix can close it today", decision: "g" }],
18472
+ examples: ["suspectedVulnerability"],
18473
+ hint: "[?\u2026] marks an open question / todo; [?vuln: consequence; missing check] a suspected vulnerability",
18474
+ taught: "untaught",
18475
+ taughtBy: "only the [?vuln:] form is taught; the bare [?todo] form is not",
18476
+ decisions: ["g"]
18477
+ },
18478
+ {
18479
+ verb: "fix",
18480
+ form: "verb",
18481
+ opener: "(fix:",
18482
+ kinds: ["fix"],
18483
+ args: "handles-text",
18484
+ thread: true,
18485
+ minText: 12,
18486
+ lowering: [
18487
+ { target: P, writes: [{ from: P, edge: "SOLVED_BY", to: S }], note: "closes the Problem; a note \u226512 chars mints the Solution" },
18488
+ { target: "Vulnerability", writes: [{ from: "Vulnerability", edge: "SOLVED_BY", to: S }] },
18489
+ { target: RC, writes: [], note: "dropped: 'fix tag went nowhere' (not a Problem); no tag writes FIXED_BY", decision: "f" }
18490
+ ],
18491
+ examples: ["fix", "fixThread"],
18492
+ hint: "one fix per tag: (fix:[its-handle]) or (fix:#thread what you changed) \u2014 put the cause in its own (cause: \u2026)",
18493
+ taught: "taught",
18494
+ decisions: ["f"]
18495
+ },
18496
+ {
18497
+ verb: "cause",
18498
+ form: "verb",
18499
+ opener: "(cause:",
18500
+ kinds: ["triage"],
18501
+ args: "handles-text",
18502
+ thread: true,
18503
+ minText: 8,
18504
+ lowering: [
18505
+ { target: RC, writes: TRIAGE_TO_RC },
18506
+ { target: P, writes: [{ from: P, edge: "TRIAGED_BY", to: "Triage" }, { from: "Triage", edge: "INDICATES", to: RC }, { from: P, edge: "CAUSED_BY", to: RC }], note: "(cause:[problem] mechanism) \u2014 the cited Problem is the symptom; the mechanism mints the RootCause" },
18507
+ { target: S, writes: [{ from: P, edge: "TRIAGED_BY", to: "Triage" }, { from: "Triage", edge: "INDICATES", to: S }], decision: "b", knownViolation: { edge: "INDICATES", finding: "(cause:[solution]) writes a refused INDICATES \u2192 Solution" } }
18508
+ ],
18509
+ examples: ["triage", "triageExplain", "triageMint", "causeThread"],
18510
+ hint: "(cause: the mechanism you'd change) or (cause:[root-cause-handle]); bind a distant symptom with (cause:#thread \u2026)",
18511
+ taught: "taught",
18512
+ decisions: ["b"]
18513
+ },
18514
+ {
18515
+ verb: "constraint",
18516
+ form: "verb",
18517
+ opener: "(constraint:",
18518
+ kinds: ["constraint"],
18519
+ args: "text",
18520
+ thread: false,
18521
+ minText: 8,
18522
+ textRequired: true,
18523
+ lowering: [{ target: P, writes: [], note: "mints a constraint-kind Problem" }],
18524
+ examples: ["constraint"],
18525
+ hint: "(constraint: the limitation that forced the decision)",
18526
+ taught: "taught"
18527
+ },
18528
+ {
18529
+ verb: "tried",
18530
+ form: "verb",
18531
+ opener: "(tried:",
18532
+ kinds: ["attempt"],
18533
+ args: "text",
18534
+ thread: true,
18535
+ minText: 8,
18536
+ textRequired: true,
18537
+ lowering: [{ target: P, writes: [], note: "journals an attempt on the bound problem" }],
18538
+ examples: ["tried"],
18539
+ hint: "(tried: the approach) \u2014 bind with (tried:#thread \u2026)",
18540
+ taught: "taught"
18541
+ },
18542
+ {
18543
+ verb: "failed",
18544
+ form: "verb",
18545
+ opener: "(failed:",
18546
+ kinds: ["failure"],
18547
+ args: "handles-text",
18548
+ thread: true,
18549
+ minText: 8,
18550
+ textRequired: true,
18551
+ lowering: [
18552
+ { target: P, writes: [], note: "journals a failed attempt on the bound problem" },
18553
+ { target: S, writes: [], note: "(failed:[h]) refutes the cited prior (contradictionPressure); skips the label gate today", decision: "j" }
18554
+ ],
18555
+ examples: ["failed", "failedCite"],
18556
+ hint: "(failed: what didn't work); (failed:[solution] what came back) refutes a shown fix",
18557
+ taught: "taught",
18558
+ decisions: ["j"]
18559
+ },
18560
+ {
18561
+ verb: "instance",
18562
+ form: "verb",
18563
+ opener: "(instance:",
18564
+ kinds: ["instance"],
18565
+ args: "handles",
18566
+ thread: false,
18567
+ handleRequired: true,
18568
+ lowering: INSTANCE_LOWERING,
18569
+ examples: ["instance"],
18570
+ hint: "(instance:[h1],[h2],[h3]) \u2014 ~3 DIFFERENT shown priors this problem instantiates; handles only",
18571
+ taught: "taught",
18572
+ decisions: ["a", "e"]
18573
+ },
18574
+ {
18575
+ verb: "pattern",
18576
+ form: "verb",
18577
+ opener: "(pattern:",
18578
+ kinds: ["pattern"],
18579
+ args: "handle-or-text",
18580
+ thread: false,
18581
+ minText: 3,
18582
+ lowering: [{ target: "Pattern", writes: [{ from: P, edge: "INSTANCE_OF", to: "Pattern" }], note: "a handle must be a Pattern; free text resolves-or-mints one" }],
18583
+ examples: ["pattern"],
18584
+ hint: "(pattern: name the general shape) or (pattern:[shown-pattern])",
18585
+ taught: "taught"
18586
+ },
18587
+ {
18588
+ verb: "domain",
18589
+ form: "verb",
18590
+ opener: "(domain:",
18591
+ kinds: ["domain"],
18592
+ args: "text",
18593
+ thread: false,
18594
+ minText: 2,
18595
+ textRequired: true,
18596
+ lowering: [{ target: "Domain", writes: [{ from: P, edge: "PERTAIN_TO", to: "Domain" }], note: '(domain:#t X) mints a Domain named "#t X" today', decision: "k" }],
18597
+ examples: ["domain"],
18598
+ hint: "(domain: The Area) \u2014 Title Case, no thread",
18599
+ taught: "taught",
18600
+ decisions: ["k"]
18601
+ },
18602
+ {
18603
+ verb: "package",
18604
+ form: "verb",
18605
+ opener: "(package:",
18606
+ kinds: ["package"],
18607
+ args: "text",
18608
+ thread: false,
18609
+ minText: 2,
18610
+ textRequired: true,
18611
+ lowering: [{ target: "Package", writes: [{ from: P, edge: "DEPENDS_ON", to: "Package" }], note: "means 'concerns', written as DEPENDS_ON", decision: "c" }],
18612
+ examples: ["package"],
18613
+ hint: "(package: the-package-name) \u2014 the public package it's about",
18614
+ taught: "taught",
18615
+ decisions: ["c"]
18616
+ },
18617
+ {
18618
+ verb: "component",
18619
+ form: "verb",
18620
+ opener: "(component:",
18621
+ kinds: ["component"],
18622
+ args: "text",
18623
+ thread: false,
18624
+ minText: 2,
18625
+ textRequired: true,
18626
+ lowering: [{ target: "Component", writes: [{ from: P, edge: "CONCERNS", to: "Component" }] }],
18627
+ examples: ["component"],
18628
+ hint: "(component: The Component) \u2014 the framework/product unit",
18629
+ taught: "taught"
18630
+ },
18631
+ {
18632
+ verb: "aids",
18633
+ form: "verb",
18634
+ opener: "(aids:",
18635
+ kinds: ["transfer"],
18636
+ args: "handles",
18637
+ thread: true,
18638
+ handleRequired: true,
18639
+ lowering: [{ target: P, writes: [{ from: S, edge: "MAY_RESOLVE", to: P }], note: "source = the Solution in scope; non-Problem targets are skipped" }],
18640
+ examples: ["aids"],
18641
+ hint: "(aids:[problem-handle],\u2026) \u2014 other shown problems your fix might also resolve",
18642
+ taught: "taught"
18643
+ },
18644
+ {
18645
+ verb: "builds-on",
18646
+ form: "verb",
18647
+ opener: "(builds-on:",
18648
+ kinds: ["builds-on"],
18649
+ args: "handles",
18650
+ thread: true,
18651
+ handleRequired: true,
18652
+ lowering: [{ target: S, writes: [{ from: S, edge: "BUILDS_ON", to: S }] }],
18653
+ examples: ["buildsOn"],
18654
+ hint: "(builds-on:[shown-solution]) \u2014 your fix layers on it",
18655
+ taught: "taught",
18656
+ taughtBy: "linkBullet instruction text"
18657
+ },
18658
+ {
18659
+ verb: "supersedes",
18660
+ form: "verb",
18661
+ opener: "(supersedes:",
18662
+ kinds: ["supersedes"],
18663
+ args: "handles",
18664
+ thread: true,
18665
+ handleRequired: true,
18666
+ lowering: [{ target: S, writes: [{ from: S, edge: "SUPERSEDES", to: S }] }],
18667
+ examples: ["supersedes"],
18668
+ hint: "(supersedes:[shown-solution]) \u2014 your fix replaces it",
18669
+ taught: "taught",
18670
+ taughtBy: "linkBullet instruction text"
18671
+ },
18672
+ {
18673
+ verb: "alternative",
18674
+ form: "verb",
18675
+ opener: "(alternative:",
18676
+ kinds: ["alternative"],
18677
+ args: "handles",
18678
+ thread: true,
18679
+ handleRequired: true,
18680
+ lowering: [{ target: S, writes: [{ from: S, edge: "ALTERNATIVE_TO", to: S }] }],
18681
+ examples: ["alternative"],
18682
+ hint: "(alternative:[shown-solution]) \u2014 a different valid approach",
18683
+ taught: "taught",
18684
+ taughtBy: "linkBullet instruction text"
18685
+ },
18686
+ {
18687
+ verb: "confirm",
18688
+ form: "verb",
18689
+ opener: "(confirm:",
18690
+ kinds: ["confirm"],
18691
+ args: "handles",
18692
+ thread: false,
18693
+ handleRequired: true,
18694
+ lowering: [{ target: S, writes: [], note: "corroboration witness (corroborationCount), no edge" }],
18695
+ examples: ["confirm"],
18696
+ hint: "(confirm:[shown-prior]) \u2014 it held",
18697
+ taught: "taught",
18698
+ taughtBy: "verdictBullet + context render",
18699
+ decisions: ["j"]
18700
+ },
18701
+ {
18702
+ verb: "refute",
18703
+ form: "verb",
18704
+ opener: "(refute:",
18705
+ kinds: ["refute"],
18706
+ args: "handles-text",
18707
+ thread: false,
18708
+ minText: 4,
18709
+ handleRequired: true,
18710
+ textRequired: true,
18711
+ lowering: [{ target: S, writes: [], note: "contradiction witness (contradictionPressure), no edge; reason stays local" }],
18712
+ examples: ["refute"],
18713
+ hint: "(refute:[shown-prior] one line why) \u2014 the reason is required",
18714
+ taught: "taught",
18715
+ taughtBy: "verdictBullet + context render",
18716
+ decisions: ["j"]
18717
+ }
18718
+ ];
18719
+ TAG_FENCES = [
18720
+ {
18721
+ fence: "errata-problem",
18722
+ fields: ["problem", "cause", "fix", "anchor", "kind"],
18723
+ required: [["problem"]],
18724
+ taught: "untaught",
18725
+ parser: "apps/daemon/src/turn.ts parseDesignFlags",
18726
+ decisions: ["h"],
18727
+ hint: "use an inline [!\u2026] flag instead (errata-problem cause ids never meet inline ids)"
18728
+ },
18729
+ {
18730
+ fence: "errata-resolved",
18731
+ fields: ["id", "problem", "fix", "disposition", "reason"],
18732
+ required: [["id", "problem"]],
18733
+ taught: "taught",
18734
+ taughtBy: "retract form only (TAG_EXAMPLE.retract); the fix form is untaught",
18735
+ parser: "apps/daemon/src/turn.ts parseDesignResolutions",
18736
+ hint: "```errata-resolved with id: and disposition: false_positive | invalid, reason: \u2026"
18737
+ },
18738
+ {
18739
+ fence: "errata-triage",
18740
+ fields: ["presenting", "cause", "test", "fix", "drift", "route"],
18741
+ required: [["presenting", "route"], ["cause", "test"]],
18742
+ taught: "taught",
18743
+ taughtBy: "MCP errata_triage tool copy",
18744
+ parser: "packages/local-graph/src/triage.ts parseTriageFences",
18745
+ hint: "```errata-triage with presenting: and cause: (or route: + test: to answer one)"
18746
+ },
18747
+ {
18748
+ fence: "errata-abstraction",
18749
+ fields: ["candidate", "principle", "covers"],
18750
+ required: [["candidate"], ["principle"], ["covers"]],
18751
+ taught: "taught",
18752
+ taughtBy: "MCP errata_pending_abstractions tool copy",
18753
+ parser: "packages/local-graph/src/abstraction.ts parseAbstractionFences",
18754
+ hint: "```errata-abstraction with candidate:, principle:, covers: (all required)"
18755
+ },
18756
+ {
18757
+ fence: "errata-intent",
18758
+ fields: ["intent"],
18759
+ required: [["intent"]],
18760
+ taught: "taught",
18761
+ parser: "packages/local-graph/src/intent.ts parseIntentFences",
18762
+ hint: "```errata-intent with intent: what you're about to do"
18763
+ },
18764
+ {
18765
+ fence: "errata-share",
18766
+ fields: ["priors"],
18767
+ required: [["priors"]],
18768
+ taught: "taught",
18769
+ taughtBy: "subagent hand-off copy",
18770
+ parser: "packages/local-graph/src/intent.ts parseShareFence",
18771
+ hint: "```errata-share with priors: all | [h1], [h2]"
18772
+ }
18773
+ ];
18774
+ TAG_VERB_BY_NAME = new Map(
18775
+ TAG_VERBS.filter((v) => v.form === "verb").map((v) => [v.verb, v])
18776
+ );
18777
+ TAG_PAREN_VERBS = [...TAG_VERB_BY_NAME.keys()].sort((a, b) => b.length - a.length);
18778
+ TAG_GRAMMAR_OPENERS = [
18779
+ ...new Set(TAG_VERBS.filter((v) => v.form !== "citation").map((v) => v.opener))
18780
+ ];
18781
+ TAG_QUOTED_PAREN_SOURCE = String.raw`\(\s*(?:${TAG_PAREN_VERBS.map((v) => v.replace(/-/g, "\\-")).join("|")})\s*:|\(\[`;
18782
+ TAG_QUOTED_RE = new RegExp(String.raw`\[[!?]|` + TAG_QUOTED_PAREN_SOURCE, "i");
18783
+ TAG_QUOTED_PAREN_RE = new RegExp(TAG_QUOTED_PAREN_SOURCE, "i");
18784
+ TEMPLATE_VERB_GROUP = new RegExp(
18785
+ String.raw`\(\s*(${TAG_PAREN_VERBS.map((v) => v.replace(/-/g, "\\-")).join("|")})\s*:((?:[^()\n]|\([^()\n]*\))*)\)`,
18786
+ "gi"
18787
+ );
18788
+ BRACKET_TOKEN = /\[([^\]\n]{1,80})\]/g;
18789
+ REAL_HANDLE_SHAPE = new RegExp(
18790
+ String.raw`^(?:[a-z0-9]+(?:-[a-z0-9]+)*-(?=[0-9a-f]{0,3}\d)[0-9a-f]{4}|(?:${TAG_CANONICAL_ID_PREFIXES.join("|")})_[0-9a-f]{6,})$`,
18791
+ "i"
18792
+ );
18793
+ TAG_TEMPLATES = deriveTagTemplates(tagGrammarCopy());
18794
+ VERB_WORDS_SOURCE = TAG_PAREN_VERBS.map((v) => v.replace(/-/g, "\\-")).join("|");
18795
+ JOINED_VERB = new RegExp(String.raw`[,;]\s*(?=(?:${VERB_WORDS_SOURCE})\s*:)`, "i");
18796
+ LEADING_VERB = new RegExp(String.raw`^\s*(${VERB_WORDS_SOURCE})\s*:`, "i");
18797
+ ANY_VERB_WORD = new RegExp(String.raw`\b(?:${VERB_WORDS_SOURCE})\s*:`, "gi");
18798
+ QUOTED_TAG_OPENER = new RegExp(String.raw`\(\s*(?:(${VERB_WORDS_SOURCE})\s*:|(?=\[))`, "gi");
18799
+ TAG_VERDICT_TOKENS = {
18800
+ confirm: (id) => TAG_EXAMPLES.confirm(id),
18801
+ refute: (id) => TAG_EXAMPLES.refute(id),
18802
+ failed: (id) => TAG_EXAMPLES.failedCite(id)
18803
+ };
18804
+ }
18805
+ });
18806
+
18208
18807
  // ../../packages/local-shared/src/index.ts
18209
18808
  var init_src2 = __esm({
18210
18809
  "../../packages/local-shared/src/index.ts"() {
@@ -18218,6 +18817,7 @@ var init_src2 = __esm({
18218
18817
  init_review();
18219
18818
  init_anchorable();
18220
18819
  init_sqlite_adapter();
18820
+ init_tag_grammar();
18221
18821
  }
18222
18822
  });
18223
18823
 
@@ -20854,6 +21454,20 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
20854
21454
  if (revived > 0) this.mutations++;
20855
21455
  return revived;
20856
21456
  }
21457
+ incomingEdgeCoverage(label, edgeTypes, cloudOnly) {
21458
+ if (edgeTypes.length === 0) return { count: 0, total: 0 };
21459
+ const r = this.db.prepare(
21460
+ `SELECT COUNT(*) AS total,
21461
+ COALESCE(SUM(CASE WHEN EXISTS (
21462
+ SELECT 1 FROM edges e WHERE e.to_id = n.id AND e.valid_to IS NULL
21463
+ AND e.type IN (${edgeTypes.map(() => "?").join(",")})
21464
+ ) THEN 1 ELSE 0 END), 0) AS count
21465
+ FROM nodes n
21466
+ WHERE n.label = ? AND n.valid_to IS NULL
21467
+ AND (? = 0 OR json_extract(n.attrs_json, '$.source') = 'cloud')`
21468
+ ).get(...edgeTypes, label, cloudOnly ? 1 : 0);
21469
+ return { count: Number(r.count), total: Number(r.total) };
21470
+ }
20857
21471
  recentEdgeAttrCoverage(marker, attr, limit) {
20858
21472
  const r = this.db.prepare(
20859
21473
  `SELECT COUNT(*) AS total,
@@ -25739,6 +26353,14 @@ var init_principle_sync = __esm({
25739
26353
 
25740
26354
  // ../../packages/local-graph/src/mechanism-liveness.ts
25741
26355
  function evaluateFed(store, d) {
26356
+ if (d.fedIncoming) {
26357
+ const { count: count2, total } = store.incomingEdgeCoverage?.(
26358
+ d.fedIncoming.label,
26359
+ d.fedIncoming.edgeTypes,
26360
+ d.fedIncoming.cloudOnly
26361
+ ) ?? { count: 0, total: 0 };
26362
+ return { count: count2, total, fraction: total === 0 ? 0 : count2 / total };
26363
+ }
25742
26364
  if (d.fedEdges) {
25743
26365
  const { count: count2, total } = store.recentEdgeAttrCoverage(
25744
26366
  d.fedEdges.marker,
@@ -25790,7 +26412,7 @@ function evaluateMechanisms(store, invocations = /* @__PURE__ */ new Map(), mech
25790
26412
  if (d.configCounter && inv?.lastTotals?.[d.configCounter] === 0) verdict = "unconfigured";
25791
26413
  else if ((backlog ?? 0) === 0 && (!hasInput && !d.inputOptional || missingRequiredNodes)) verdict = "no-input";
25792
26414
  else if (!inv || inv.runs === 0) verdict = "never-invoked";
25793
- else if (fed.fraction < (d.fedEdges?.minFraction ?? d.fed?.minFraction ?? 0)) verdict = "starved";
26415
+ else if (fed.fraction < minFractionOf(d)) verdict = "starved";
25794
26416
  else if (stalled) verdict = "stalled";
25795
26417
  else if (effectCount === 0) verdict = "no-effect";
25796
26418
  else verdict = "ok";
@@ -25801,7 +26423,7 @@ function evaluateMechanisms(store, invocations = /* @__PURE__ */ new Map(), mech
25801
26423
  fedCount: fed.count,
25802
26424
  fedTotal: fed.total,
25803
26425
  fedFraction: fed.fraction,
25804
- minFraction: d.fedEdges?.minFraction ?? d.fed?.minFraction ?? 0,
26426
+ minFraction: minFractionOf(d),
25805
26427
  ...inv ? { lastInvokedMs: inv.lastTs } : {},
25806
26428
  runs: inv?.runs ?? 0,
25807
26429
  effectCount,
@@ -25813,6 +26435,9 @@ function evaluateMechanisms(store, invocations = /* @__PURE__ */ new Map(), mech
25813
26435
  };
25814
26436
  });
25815
26437
  }
26438
+ function minFractionOf(d) {
26439
+ return d.fedIncoming?.minFraction ?? d.fedEdges?.minFraction ?? d.fed?.minFraction ?? 0;
26440
+ }
25816
26441
  function formatMechanismStatus(rows, nowMs) {
25817
26442
  return rows.map((r) => {
25818
26443
  const pct = (r.fedFraction * 100).toFixed(r.fedFraction < 0.01 ? 2 : 1);
@@ -26099,6 +26724,25 @@ var init_mechanism_liveness = __esm({
26099
26724
  // reading is IDLE — correct, and it declares no backlog on purpose.
26100
26725
  effectCounter: "alarms",
26101
26726
  note: "steady-state IDLE is healthy; the row exists for its invocation age"
26727
+ },
26728
+ {
26729
+ id: "project-causal-sync",
26730
+ what: "lands the cloud project stratum's SOLVED_BY/FIXED_BY edges onto the knowledge the anchor feed mirrors",
26731
+ // PRODUCER: the anchor feed's causal leg — `fetchProjectAnchors` returns the
26732
+ // edges, `syncProjectAnchors` (apps/daemon/src/project-reanchor.ts) lands or
26733
+ // parks them, on the `anchor-pull` beat. CONSUMERS: every local walk that
26734
+ // crosses Problem→Solution — errata_trace/why/burst over the store, and the
26735
+ // passive priming block that renders a problem with its fix. Before
26736
+ // PJ-sync-causal the feed mirrored 5,054 cloud Solutions on the dev checkout
26737
+ // with not one incoming SOLVED_BY: this row would have read 0.0% for months.
26738
+ pass: "anchor-pull",
26739
+ fedIncoming: { label: "Solution", edgeTypes: ["SOLVED_BY", "FIXED_BY"], cloudOnly: true, minFraction: 0.5 },
26740
+ effectCounter: "edgesLanded",
26741
+ // Parked edges waiting on an endpoint — a flat, non-zero backlog is a stratum
26742
+ // whose other ends never arrive here (a cap, or a gate that refuses them).
26743
+ backlogCounter: "edgesPending",
26744
+ requiresNodes: ["Problem"],
26745
+ note: "an orphaned cloud Solution reads as a fix with no problem; the one-time causal re-walk lifts this after the server ships the leg"
26102
26746
  }
26103
26747
  ];
26104
26748
  COLUMN_INPUTS = {
@@ -26743,7 +27387,7 @@ function renderSnapshot(s) {
26743
27387
  lines.push(` - previously resolved as: ${g.closed.description.slice(0, 120)} \u2014 \`${g.closed.id}\``);
26744
27388
  if (g.solution) {
26745
27389
  lines.push(
26746
- ` - the fix on record: ${g.solution.description.slice(0, 120)} \u2014 \`${g.solution.id}\` \xB7 fix still holds \u2192 (confirm:[${g.solution.id}]) \xB7 hitting it again \u2192 flag it (a second independent witness reopens the close)`
27390
+ ` - the fix on record: ${g.solution.description.slice(0, 120)} \u2014 \`${g.solution.id}\` \xB7 fix still holds \u2192 ${TAG_VERDICT_TOKENS.confirm(g.solution.id)} \xB7 hitting it again \u2192 flag it (a second independent witness reopens the close)`
26747
27391
  );
26748
27392
  } else {
26749
27393
  lines.push(
@@ -26840,7 +27484,7 @@ function renderSnapshot(s) {
26840
27484
  ` - still open \u2014 did this work settle it? fixed \u2192 (fix:[${h.id}]) \xB7 not a real problem \u2192 an \`errata-resolved\` block with \`id: ` + h.id + "`, `disposition: false_positive`, `reason: \u2026` \xB7 still open \u2192 say nothing"
26841
27485
  );
26842
27486
  } else if (h.verdict === "foreign") {
26843
- lines.push(` - open in another membrane \u2014 not yours to close: (confirm:[${h.id}]) it held \xB7 (refute:[${h.id}] one line why)`);
27487
+ lines.push(` - open in another membrane \u2014 not yours to close: ${TAG_VERDICT_TOKENS.confirm(h.id)} it held \xB7 ${TAG_VERDICT_TOKENS.refute(h.id)}`);
26844
27488
  }
26845
27489
  }
26846
27490
  }
@@ -26911,7 +27555,7 @@ function renderSnapshot(s) {
26911
27555
  lines.push(` - \u2713 ${p.description}`);
26912
27556
  for (const s2 of w.priors.solutionsByProblem[p.id] ?? []) {
26913
27557
  lines.push(
26914
- ` - fixed by: ${s2.description} \u2014 if that fix did NOT hold, refute it: \`(failed:[${s2.id}] what came back)\``
27558
+ ` - fixed by: ${s2.description} \u2014 if that fix did NOT hold, refute it: \`${TAG_VERDICT_TOKENS.failed(s2.id)}\``
26915
27559
  );
26916
27560
  }
26917
27561
  }
@@ -28396,7 +29040,7 @@ var init_client = __esm({
28396
29040
  "GET",
28397
29041
  `/v2/projects/${encodeURIComponent(projectId)}/anchors?since=${String(Math.max(0, Math.floor(since)))}&limit=${String(limit)}`
28398
29042
  );
28399
- const { nodes } = this.toCastalia({ nodes: res.nodes ?? [], edges: [] });
29043
+ const { nodes, edges } = this.toCastalia({ nodes: res.nodes ?? [], edges: res.edges ?? [] });
28400
29044
  const anchors = (res.anchors ?? []).map((a) => ({
28401
29045
  from: a.fromCanonicalId,
28402
29046
  toSymbolId: a.toSymbolId,
@@ -28404,7 +29048,7 @@ var init_client = __esm({
28404
29048
  attrs: a.attrs ?? {},
28405
29049
  stampedAt: typeof a.stampedAt === "number" ? a.stampedAt : 0
28406
29050
  }));
28407
- return { nodes, anchors, cursor: typeof res.cursor === "number" ? res.cursor : since };
29051
+ return { nodes, edges, causalLeg: Array.isArray(res.edges), anchors, cursor: typeof res.cursor === "number" ? res.cursor : since };
28408
29052
  }
28409
29053
  /** Pull cloud-induced skills + the session-bootstrap prime payload. `seed` is the
28410
29054
  * canonical ids of the agent's recent problems — the cloud ranks the skills whose
@@ -33885,7 +34529,7 @@ function linkBullet(ref) {
33885
34529
  ` \xB7 ${TAG_EXAMPLE.package()} \u2014 the PUBLIC package it's about, dependency or not`,
33886
34530
  ` \xB7 ${TAG_EXAMPLE.component()} \u2014 the framework/product unit`,
33887
34531
  ` \xB7 (aids:[${ref}],\u2026) \u2014 other problems your fix might also resolve (hypothesis)`,
33888
- ` \xB7 your fix vs a shown solution: (builds-on:[${ref}]) \xB7 (supersedes:[${ref}]) \xB7 (alternative:[h]) \u2014 pick one`
34532
+ ` \xB7 your fix vs a shown solution: ${TAG_EXAMPLE.buildsOn(ref)} \xB7 ${TAG_EXAMPLE.supersedes(ref)} \xB7 ${TAG_EXAMPLE.alternative("h")} \u2014 pick one`
33889
34533
  ].join("\n");
33890
34534
  }
33891
34535
  function verdictBullet(ref) {
@@ -33916,75 +34560,13 @@ function buildAgentInstruction(opts = {}) {
33916
34560
  tail
33917
34561
  ].join("\n");
33918
34562
  }
33919
- var TAG_EXAMPLE, TAG_OPENERS, GLOSS;
34563
+ var TAG_EXAMPLE, TAG_OPENERS, GLOSS, ALL_SIGNALS, TAG_TEMPLATES2;
33920
34564
  var init_agent_signals = __esm({
33921
34565
  "src/agent-signals.ts"() {
33922
34566
  "use strict";
33923
- TAG_EXAMPLE = {
33924
- prior: (ref) => `([${ref}])`,
33925
- problem: () => `[!one line on what's wrong]`,
33926
- vulnerability: () => `[!vuln: who can do what; observed basis @ path:line]`,
33927
- suspectedVulnerability: () => `[?vuln: consequence; missing check]`,
33928
- vulnerabilityThread: () => `[!#v vuln: finding here]`,
33929
- fix: (ref) => `(fix:[${ref}])`,
33930
- // CONSTRAINT — the native capture path for a DIFFUSE problem (a constraint/tension/
33931
- // conflict that shaped a decision). Free text, attaches to the decision sentence the
33932
- // agent is already writing. Full rationale in the constraint note below + prior-tags.ts.
33933
- constraint: () => `(constraint: the limitation that forced it)`,
33934
- // TRIAGE — routes the problem/symptom in scope to its cause. `(cause:[handle])`
33935
- // links to a cited RootCause/Solution node; `(cause: free text)` mints a RootCause
33936
- // from the text. Harvested in prior-tags.ts → engine.ts (see the triage note below).
33937
- triage: (ref) => `(cause:[${ref}])`,
33938
- // Unlike a bare cause citation, this handle names the Problem being explained.
33939
- triageExplain: (ref) => `(cause:[${ref}] the mechanism)`,
33940
- // The MINT form — free-text cause that mints a RootCause. Prime-time surfacing
33941
- // (recallForFile) pastes this next to an open Problem that has no cause yet, so the
33942
- // diagnostic moment is cued when the agent is looking at the problem. Kept here so
33943
- // the grammar can't drift from parseInlineTags' `(cause: …)` acceptance.
33944
- triageMint: () => `(cause: why it happens)`,
33945
- // THREAD forms — the witness-declared symptom↔cause/fix binding. A `#id` on the
33946
- // flag names the diagnosis thread; the same `#id` on a later verb binds to it
33947
- // explicitly (across sentences AND turns), replacing proximity guessing. Adjacent
33948
- // tags never need one — the id is for diagnoses that unfold over distance.
33949
- problemThread: () => `[!#slug one line on what's wrong]`,
33950
- causeThread: () => `(cause:#slug the mechanism)`,
33951
- fixThread: (thread = "slug") => `(fix:#${thread} what you changed)`,
33952
- // ATTEMPT arc — what's being tried and what didn't work. A failed attempt is a
33953
- // witnessed NEGATIVE result: it saves the next agent the same dead end.
33954
- tried: () => `(tried: the approach you're attempting)`,
33955
- failed: () => `(failed: what didn't work and the dead end it rules out)`,
33956
- // LINK verbs (AC epic) — bind the problem in scope to CANONICAL priors and
33957
- // abstractions. Multi-target by design: single links are weak; ~3 links to 3
33958
- // DIFFERENT relevant priors is the target shape (see linkBullet).
33959
- instance: (ref) => `(instance:[${ref}],[another],[a-third])`,
33960
- pattern: () => `(pattern: name the general shape)`,
33961
- // DOMAIN — the abstract area a problem is about; the concept layer an
33962
- // anchor-less problem clusters on. Mints/resolves a Domain by name.
33963
- domain: () => `(domain: The Area)`,
33964
- // PACKAGE/COMPONENT — agent-named public anchors (OM-agent-anchors): the
33965
- // package a problem is ABOUT (even when not a dependency) and the
33966
- // framework/product-level unit it concerns. Public spine anchors → the
33967
- // knowledge can cross to the collective.
33968
- package: () => `(package: the-package-name)`,
33969
- component: () => `(component: The Component)`,
33970
- aids: (ref) => `(aids:[${ref}])`,
33971
- // VERDICT (VS-verdict-tags) — a judgment on a SHOWN prior, distinct from leaning on it.
33972
- // `confirm` rides the corroborate channel (it held); `refute` the contradict channel
33973
- // (it's wrong / didn't hold) and REQUIRES a reason. This is the inline producer the
33974
- // evidence channels lacked — refute sat dead (contradictionPressure 0) until now, while
33975
- // lean-on corroboration lived, because only corroboration had an inline path. Grammar
33976
- // single-sourced with parseInlineTags' CONFIRM_PREFIX/REFUTE_PREFIX.
33977
- confirm: (ref) => `(confirm:[${ref}])`,
33978
- refute: (ref) => `(refute:[${ref}] one line why)`,
33979
- // RETRACT — "not a real problem": the errata-resolved block with a disposition
33980
- // (parseDesignResolutions, turn.ts). No inline form exists; the block is the grammar.
33981
- retract: (id) => ["```errata-resolved", `id: ${id}`, "disposition: false_positive", "reason: why it was never a real problem", "```"].join("\n")
33982
- };
33983
- TAG_OPENERS = [
33984
- ...new Set(
33985
- Object.values(TAG_EXAMPLE).map((example) => /^(\([a-z][\w-]*:|\[[!?])/i.exec(example("x"))?.[1]?.toLowerCase()).filter((opener) => opener !== void 0)
33986
- )
33987
- ];
34567
+ init_src2();
34568
+ TAG_EXAMPLE = TAG_EXAMPLES;
34569
+ TAG_OPENERS = TAG_GRAMMAR_OPENERS;
33988
34570
  GLOSS = {
33989
34571
  prior: "a shown prior helped, or you work in a primed language/package",
33990
34572
  // "even in code you just wrote" names the implementation phase explicitly. Field
@@ -34021,6 +34603,12 @@ var init_agent_signals = __esm({
34021
34603
  link: "the problem you flagged is an instance of priors/abstractions you can name \u2014 (instance:[h1],[h2],\u2026) / (pattern: \u2026) / (aids:[h]); rendered whole via linkBullet",
34022
34604
  verdict: "you can judge a prior we SHOWED you \u2014 confirm it if it held, refute it (with why) if it didn't; rendered whole via verdictBullet"
34023
34605
  };
34606
+ ALL_SIGNALS = ["prior", "problem", "vulnerability", "domain", "fix", "constraint", "triage", "attempt", "link", "verdict"];
34607
+ TAG_TEMPLATES2 = deriveTagTemplates([
34608
+ ...tagGrammarCopy(),
34609
+ ...["its-handle", "its-id"].map((referent) => buildAgentInstruction({ signals: ALL_SIGNALS, referent })),
34610
+ buildFileRecallInstruction()
34611
+ ]);
34024
34612
  }
34025
34613
  });
34026
34614
 
@@ -75835,8 +76423,8 @@ var init_report_render = __esm({
75835
76423
 
75836
76424
  // src/cli.ts
75837
76425
  init_src6();
75838
- import { closeSync as closeSync3, existsSync as existsSync40, openSync as openSync3, readFileSync as readFileSync42, renameSync as renameSync7, statSync as statSync13 } from "node:fs";
75839
- import { join as join49, resolve as pathResolve2 } from "node:path";
76426
+ import { closeSync as closeSync3, existsSync as existsSync40, openSync as openSync3, readFileSync as readFileSync43, renameSync as renameSync8, statSync as statSync13 } from "node:fs";
76427
+ import { join as join50, resolve as pathResolve2 } from "node:path";
75840
76428
  import { spawn as spawn5 } from "node:child_process";
75841
76429
 
75842
76430
  // src/daemon.ts
@@ -77301,9 +77889,9 @@ var NodeFsHandler = class {
77301
77889
  if (this.fsw.closed) {
77302
77890
  return;
77303
77891
  }
77304
- const dirname17 = sysPath.dirname(file2);
77892
+ const dirname18 = sysPath.dirname(file2);
77305
77893
  const basename10 = sysPath.basename(file2);
77306
- const parent = this.fsw._getWatchedDir(dirname17);
77894
+ const parent = this.fsw._getWatchedDir(dirname18);
77307
77895
  let prevStats = stats;
77308
77896
  if (parent.has(basename10))
77309
77897
  return;
@@ -77330,7 +77918,7 @@ var NodeFsHandler = class {
77330
77918
  prevStats = newStats2;
77331
77919
  }
77332
77920
  } catch (error48) {
77333
- this.fsw._remove(dirname17, basename10);
77921
+ this.fsw._remove(dirname18, basename10);
77334
77922
  }
77335
77923
  } else if (parent.has(basename10)) {
77336
77924
  const at = newStats.atimeMs;
@@ -79560,26 +80148,14 @@ init_src5();
79560
80148
  import { readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "node:fs";
79561
80149
  var NEG = /n['']t|\b(?:not|never|no|none|neither|nor|unrelated|irrelevant|would|might|could|if)\b/i;
79562
80150
  var CITE_GROUP_RE = /\(((?:[^()\n]|\([^()\n]*\)){1,200})\)/g;
79563
- var HANDLE_RE2 = /\[([a-z0-9][\w-]{0,80})\]|((?:pat|sol|drc|dcause|dfix|dprob|dvuln|prob|claim|err)_[0-9a-f]{6,})/gi;
79564
- var PLACEHOLDER_HANDLES = /* @__PURE__ */ new Set([
79565
- "handle",
79566
- "its-handle",
79567
- "another",
79568
- "a-third",
79569
- "h",
79570
- "h1",
79571
- "h2",
79572
- "id",
79573
- "its-id",
79574
- "a",
79575
- "b"
79576
- ]);
80151
+ var HANDLE_RE2 = new RegExp(TAG_HANDLE_SOURCE, "gi");
80152
+ var PLACEHOLDER_HANDLES = TAG_PLACEHOLDER_HANDLES;
79577
80153
  var isPlaceholderHandle = (h) => PLACEHOLDER_HANDLES.has(h.toLowerCase());
79578
80154
  var FLAG_BODY_HANDLE_RE = /^(?:[a-z0-9]+(?:-[a-z0-9]+){2,}-(?=[0-9a-f]{0,3}\d)[0-9a-f]{4}|(?:pat|sol|drc|dcause|dfix|dprob|dvuln|prob|claim|err)_[0-9a-f]{6,})$/i;
79579
80155
  var FIX_PREFIX = /^\s*fix:(?:#([a-z][\w-]{0,63}))?\s*/i;
79580
- var CONSTRAINT_RE = /\(\s*constraint:\s*([^()\n]{8,}?)\s*\)/gi;
79581
80156
  var CAUSE_PREFIX = /^\s*cause:(?:#([a-z][\w-]{0,63}))?\s*/i;
79582
80157
  var NEST2_UNIT = String.raw`(?:[^()\n]|\((?:[^()\n]|\([^()\n]*\))*\))`;
80158
+ var CONSTRAINT_RE = new RegExp(String.raw`\(\s*constraint:\s*(${NEST2_UNIT}{8,}?)\s*\)`, "gi");
79583
80159
  var FIX_LONG_RE = new RegExp(String.raw`\(\s*fix:(?:#([a-z][\w-]{0,63}))?\s*(${NEST2_UNIT}{12,}?)\s*\)`, "gi");
79584
80160
  var CAUSE_LONG_RE = new RegExp(String.raw`\(\s*cause:(?:#([a-z][\w-]{0,63}))?\s*(${NEST2_UNIT}{8,}?)\s*\)`, "gi");
79585
80161
  var CONFIRM_LONG_RE = new RegExp(String.raw`\(\s*confirm:\s*(${NEST2_UNIT}{4,}?)\s*\)`, "gi");
@@ -79595,7 +80171,7 @@ var BRACKETED_TOKEN_RE = /\[[^\]\n]{1,120}\]/;
79595
80171
  var CONFIRM_PREFIX = /^\s*confirm:\s*/i;
79596
80172
  var REFUTE_PREFIX = /^\s*refute:\s*/i;
79597
80173
  var REFUTE_REASON_MIN = 4;
79598
- var PATTERN_RE = /\(\s*pattern:\s*([^()\n]{3,}?)\s*\)/gi;
80174
+ var PATTERN_RE = new RegExp(String.raw`\(\s*pattern:\s*(${NEST2_UNIT}{3,}?)\s*\)`, "gi");
79599
80175
  var DOMAIN_RE = /\(\s*domain:\s*([^()\n]{2,}?)\s*\)/gi;
79600
80176
  var PACKAGE_RE = /\(\s*package:\s*([^()\n]{2,}?)\s*\)/gi;
79601
80177
  var COMPONENT_RE = /\(\s*component:\s*([^()\n]{2,}?)\s*\)/gi;
@@ -79688,6 +80264,10 @@ function deriveFixRationale(sentence, startIdx, endIdx) {
79688
80264
  if (before.length >= FIX_RATIONALE_MIN) return before;
79689
80265
  return fixRationaleAfter(sentence, endIdx);
79690
80266
  }
80267
+ var TAG_GROUP_OPENER_RE = new RegExp(
80268
+ TAG_OPENERS.filter((o) => o.startsWith("(")).map((o) => String.raw`\(\s*${o.slice(1, -1).replace(/-/g, "\\-")}\s*:`).join("|"),
80269
+ "gi"
80270
+ );
79691
80271
  var LEADING_OPENER_RE = new RegExp(String.raw`^(?:${RESIDUAL_OPENER_RE.source})(?:#[a-z][\w-]{0,63})?\s*`, "i");
79692
80272
  var WRAPPER_PAIRS = [["(", ")"], ["[", "]"]];
79693
80273
  var LEADING_HANDLES_RE = /^(?:\[[a-z0-9][\w-]{0,80}\]\s*,?\s*)+/i;
@@ -79826,19 +80406,41 @@ function addTagDispositions(a, b) {
79826
80406
  function parseInlineTags(text) {
79827
80407
  return parseInlineTagsWithDispositions(text).tags;
79828
80408
  }
80409
+ function tagGroupSpans(line) {
80410
+ const spans = [];
80411
+ let after = 0;
80412
+ for (const m of line.matchAll(TAG_GROUP_OPENER_RE)) {
80413
+ const start2 = m.index;
80414
+ if (start2 < after) continue;
80415
+ let depth = 0;
80416
+ const limit = Math.min(line.length, start2 + 4e3);
80417
+ for (let i2 = start2; i2 < limit; i2++) {
80418
+ if (line[i2] === "(") depth++;
80419
+ else if (line[i2] === ")" && --depth === 0) {
80420
+ spans.push({ start: start2, end: i2 + 1 });
80421
+ after = i2 + 1;
80422
+ break;
80423
+ }
80424
+ }
80425
+ }
80426
+ return spans;
80427
+ }
79829
80428
  function* captureSentences(text) {
79830
80429
  for (const line of text.split(/\n+/)) {
79831
80430
  if (line.length > 2e4) {
79832
80431
  yield line;
79833
80432
  continue;
79834
80433
  }
79835
- const flags2 = [...flagScanView(line).matchAll(new RegExp(FLAG_RE.source, FLAG_RE.flags))].map((m) => ({ start: m.index, end: m.index + m[0].length }));
80434
+ const flags2 = [
80435
+ ...[...flagScanView(line).matchAll(new RegExp(FLAG_RE.source, FLAG_RE.flags))].map((m) => ({ start: m.index, end: m.index + m[0].length })),
80436
+ ...tagGroupSpans(line)
80437
+ ].sort((a, b) => a.start - b.start);
79836
80438
  let flag = 0;
79837
80439
  let start2 = 0;
79838
80440
  for (const boundary of line.matchAll(/(?<=[.!?])\s+(?!\(fix:)/g)) {
79839
80441
  const index = boundary.index;
79840
80442
  while (flags2[flag] && flags2[flag].end <= index) flag++;
79841
- if (flags2[flag] && flags2[flag].start < index) continue;
80443
+ if (flags2.slice(flag).some((f) => f.start < index && index < f.end)) continue;
79842
80444
  yield line.slice(start2, index);
79843
80445
  start2 = index + boundary[0].length;
79844
80446
  }
@@ -79857,22 +80459,28 @@ function parseInlineTagsWithDispositions(text) {
79857
80459
  continue;
79858
80460
  }
79859
80461
  const seqStart = out2.length;
79860
- const sentence = raw2.replace(/`[^`]*`/g, (m) => {
79861
- if (!/\[[!?]|\((?:fix|cause|constraint|tried|failed|confirm|refute):|\(\[/.test(m)) return m;
79862
- if (!/\((?:fix|cause|constraint|tried|failed|confirm|refute):|\(\[/.test(m)) {
80462
+ const original = raw2;
80463
+ const flagView = flagScanView(raw2);
80464
+ const flagSpans = [...flagView.matchAll(new RegExp(FLAG_RE.source, FLAG_RE.flags))].map((m) => [m.index, m.index + m[0].length]);
80465
+ const sentence = raw2.replace(/`[^`]*`/g, (m, at) => {
80466
+ if (!TAG_QUOTED_RE.test(m)) return m;
80467
+ if (!TAG_QUOTED_PAREN_RE.test(m)) {
79863
80468
  dispositions.codeSpanNeutralized++;
79864
80469
  return m;
79865
80470
  }
80471
+ const inFlag = flagSpans.some(([a2, b]) => a2 < at && at < b);
80472
+ if (!inFlag && !/\[[!?]/.test(m) && !quotesOnlyTemplateTags(m, TAG_TEMPLATES2)) return m;
79866
80473
  dispositions.codeSpanNeutralized++;
79867
- return " ";
80474
+ return m.replace(/[()[\]]/g, " ");
79868
80475
  });
79869
- const sfield = sentence.replace(/\s+/g, " ").trim().slice(0, 160);
80476
+ const sfield = raw2.replace(/\s+/g, " ").trim().slice(0, 160);
79870
80477
  const citeMatched = /* @__PURE__ */ new Set();
79871
- const flagSpans = [...flagScanView(sentence).matchAll(new RegExp(FLAG_RE.source, FLAG_RE.flags))].map((m) => [m.index, m.index + m[0].length]);
80478
+ const codeOnly = [...sentence.matchAll(/`[^`]*`/g)].filter((m) => !TAG_QUOTED_PAREN_RE.test(m[0])).map((m) => [m.index, m.index + m[0].length]);
79872
80479
  CITE_GROUP_RE.lastIndex = 0;
79873
80480
  let g;
79874
80481
  while ((g = CITE_GROUP_RE.exec(sentence)) !== null) {
79875
80482
  citeMatched.add(g.index);
80483
+ if (codeOnly.some(([a2, b]) => a2 < g.index && g.index < b)) continue;
79876
80484
  let content = g[1];
79877
80485
  if (/^\s*pattern:/i.test(content)) continue;
79878
80486
  if (/^\s*failed:/i.test(content)) continue;
@@ -79957,6 +80565,7 @@ function parseInlineTagsWithDispositions(text) {
79957
80565
  let h;
79958
80566
  let emittedHandle = false;
79959
80567
  while ((h = HANDLE_RE2.exec(content)) !== null) {
80568
+ if (content[h.index + h[0].length] === "(") continue;
79960
80569
  const handle2 = h[1] ?? h[2];
79961
80570
  if (handle2 && isPlaceholderHandle(handle2)) {
79962
80571
  dispositions.placeholderEcho++;
@@ -80062,10 +80671,9 @@ function parseInlineTagsWithDispositions(text) {
80062
80671
  }
80063
80672
  }
80064
80673
  FLAG_RE.lastIndex = 0;
80065
- const flagView = flagScanView(sentence);
80066
80674
  let f;
80067
80675
  while ((f = FLAG_RE.exec(flagView)) !== null) {
80068
- f[3] = sentence.slice(...f.indices[3]);
80676
+ f[3] = original.slice(...f.indices[3]);
80069
80677
  const flagThread = f[2];
80070
80678
  const raw3 = f[3].trim();
80071
80679
  if (/^vuln\b/i.test(raw3)) {
@@ -85224,7 +85832,7 @@ function createWatchBreaker(deps) {
85224
85832
  }
85225
85833
 
85226
85834
  // src/engine.ts
85227
- var DAEMON_VERSION = true ? "2.0.22-dev.4135" : "2.0.0-alpha.0";
85835
+ var DAEMON_VERSION = true ? "2.0.22-dev.4164" : "2.0.0-alpha.0";
85228
85836
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
85229
85837
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
85230
85838
  var GIT_OP_MUTE_MS = 4e3;
@@ -88350,7 +88958,7 @@ function invalidateInstanceReceipt(current, destination, failedId, mirrorLegacy)
88350
88958
  // src/multi.ts
88351
88959
  init_dist();
88352
88960
  init_src5();
88353
- import { existsSync as existsSync34, writeFileSync as writeFileSync26 } from "node:fs";
88961
+ import { existsSync as existsSync34, writeFileSync as writeFileSync27 } from "node:fs";
88354
88962
  import { resolve as pathResolve } from "node:path";
88355
88963
 
88356
88964
  // src/principle-sync.ts
@@ -89840,7 +90448,6 @@ function liveCloudClient(src) {
89840
90448
  init_identity2();
89841
90449
 
89842
90450
  // src/project-hydrate.ts
89843
- init_src11();
89844
90451
  init_profile2();
89845
90452
 
89846
90453
  // src/project-reanchor.ts
@@ -89915,6 +90522,62 @@ async function syncHistoryCorrections(opts) {
89915
90522
  }
89916
90523
  }
89917
90524
 
90525
+ // src/project-reanchor.ts
90526
+ init_paths();
90527
+
90528
+ // src/project-sync-landing.ts
90529
+ init_src11();
90530
+ function landCloudNode(store, n, isOwnTwin) {
90531
+ if (store.getNode(n.id)) return "present";
90532
+ if (isOwnTwin) return "own";
90533
+ store.mergeNode(n);
90534
+ return "landed";
90535
+ }
90536
+ function landCloudEdge(store, e, from, to) {
90537
+ if (store.outEdges(from, [e.type]).some((x) => x.to === to)) return false;
90538
+ const id = from === e.from && to === e.to ? e.id : edgeId(from, e.type, to);
90539
+ if (store.getEdge(id)) return false;
90540
+ store.mergeEdge({ ...e, id, from, to, attrs: { ...e.attrs, source: "cloud" } });
90541
+ return true;
90542
+ }
90543
+
90544
+ // src/causal-edge-queue.ts
90545
+ import { readFileSync as readFileSync35, renameSync as renameSync5, writeFileSync as writeFileSync25 } from "node:fs";
90546
+ import { dirname as dirname13, join as join39 } from "node:path";
90547
+ var CAUSAL_EDGE_QUEUE_CAP = 5e3;
90548
+ var CAUSAL_EDGE_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
90549
+ function causalEdgeQueuePath(workspaceConfigDir) {
90550
+ return join39(workspaceConfigDir, "causal-edge-queue.json");
90551
+ }
90552
+ function parkedEdgeKey(e) {
90553
+ return `${e.from}|${e.type}|${e.to}`;
90554
+ }
90555
+ function loadCausalEdgeQueue(path3) {
90556
+ let raw2;
90557
+ try {
90558
+ raw2 = JSON.parse(readFileSync35(path3, "utf8"));
90559
+ } catch {
90560
+ return [];
90561
+ }
90562
+ if (!Array.isArray(raw2)) return [];
90563
+ return raw2.filter(
90564
+ (p) => !!p && typeof p === "object" && typeof p.ts === "number" && typeof p.edge?.from === "string" && typeof p.edge?.to === "string" && typeof p.edge?.type === "string"
90565
+ );
90566
+ }
90567
+ function saveCausalEdgeQueue(path3, queue) {
90568
+ try {
90569
+ const tmp = join39(dirname13(path3), `.${Date.now()}.causal-edge-queue.tmp`);
90570
+ writeFileSync25(tmp, JSON.stringify(queue), "utf8");
90571
+ renameSync5(tmp, path3);
90572
+ } catch {
90573
+ }
90574
+ }
90575
+ function pruneCausalEdgeQueue(queue, now) {
90576
+ const live3 = queue.filter((p) => now - p.ts < CAUSAL_EDGE_TTL_MS);
90577
+ if (live3.length <= CAUSAL_EDGE_QUEUE_CAP) return live3;
90578
+ return [...live3].sort((a, b) => a.ts - b.ts).slice(live3.length - CAUSAL_EDGE_QUEUE_CAP);
90579
+ }
90580
+
89918
90581
  // src/project-reanchor.ts
89919
90582
  var DEFAULT_LIMIT = 50;
89920
90583
  var ANCHOR_SYMBOL_KINDS2 = [
@@ -90114,10 +90777,12 @@ async function reanchorProject(opts) {
90114
90777
  }
90115
90778
  var SYNC_PAGE = 200;
90116
90779
  var SYNC_MAX_PAGES = 25;
90780
+ var SYNC_EDGE_TYPES = new Set(PROJECT_SYNC_EDGE_TYPES);
90117
90781
  async function syncProjectAnchors(opts) {
90118
90782
  const { root, profile, store, cloud, salt } = opts;
90119
90783
  if (!profile.projectId) return { synced: false, reason: "no-project" };
90120
90784
  const projectId = profile.projectId;
90785
+ const clock = opts.now ?? Date.now;
90121
90786
  const key = `${projectId}|${salt}`;
90122
90787
  const reverse = cachedMap(
90123
90788
  reverseCache,
@@ -90126,23 +90791,95 @@ async function syncProjectAnchors(opts) {
90126
90791
  () => buildReverseSymbolMap({ store, salt, projectId })
90127
90792
  );
90128
90793
  const own = cachedMap(ownCache, store, salt, () => collectLastWinsYielding(ownKnowledgeEntries({ store, salt })));
90794
+ const resolveKnowledge = async (id) => store.getNode(id) ? id : lookupLive(store, own, id);
90795
+ const queuePath = opts.edgeQueuePath ?? causalEdgeQueuePath(workspacePaths(root).configDir);
90796
+ let parked = pruneCausalEdgeQueue(loadCausalEdgeQueue(queuePath), clock());
90797
+ let parkedDirty = false;
90798
+ const parkedKeys = new Set(parked.map((p) => parkedEdgeKey(p.edge)));
90129
90799
  const limit = opts.limit ?? SYNC_PAGE;
90130
90800
  const maxPages = opts.maxPages ?? SYNC_MAX_PAGES;
90131
90801
  let anchors = 0;
90132
90802
  let updated = 0;
90133
90803
  let dropped = 0;
90134
90804
  let landed = 0;
90135
- let cursor = profile.projectAnchorCursor ?? 0;
90805
+ let edgesLanded = 0;
90806
+ let edgesParked = 0;
90807
+ let edgesReplayed = 0;
90808
+ let persistedCursor = 0;
90809
+ let cursor = 0;
90810
+ let backfillPending = false;
90811
+ let backfillStarted = false;
90812
+ const settleEdges = async (incoming) => {
90813
+ for (const e of incoming) {
90814
+ if (!SYNC_EDGE_TYPES.has(e.type)) continue;
90815
+ const from = await resolveKnowledge(e.from);
90816
+ const to = from ? await resolveKnowledge(e.to) : null;
90817
+ const k = parkedEdgeKey(e);
90818
+ if (from && to) {
90819
+ if (from !== to && landCloudEdge(store, e, from, to)) edgesLanded++;
90820
+ if (parkedKeys.delete(k)) {
90821
+ parked = parked.filter((p) => parkedEdgeKey(p.edge) !== k);
90822
+ parkedDirty = true;
90823
+ }
90824
+ } else if (!parkedKeys.has(k)) {
90825
+ parkedKeys.add(k);
90826
+ parked.push({ edge: e, ts: clock() });
90827
+ parkedDirty = true;
90828
+ edgesParked++;
90829
+ }
90830
+ }
90831
+ if (parked.length === 0) return;
90832
+ const still = [];
90833
+ for (const p of parked) {
90834
+ const from = await resolveKnowledge(p.edge.from);
90835
+ const to = from ? await resolveKnowledge(p.edge.to) : null;
90836
+ if (from && to) {
90837
+ if (from !== to && landCloudEdge(store, p.edge, from, to)) edgesReplayed++;
90838
+ parkedKeys.delete(parkedEdgeKey(p.edge));
90839
+ parkedDirty = true;
90840
+ } else {
90841
+ still.push(p);
90842
+ }
90843
+ }
90844
+ parked = still;
90845
+ };
90846
+ const flushParked = () => {
90847
+ if (!parkedDirty) return;
90848
+ parked = pruneCausalEdgeQueue(parked, clock());
90849
+ saveCausalEdgeQueue(queuePath, parked);
90850
+ parkedDirty = false;
90851
+ };
90136
90852
  try {
90137
90853
  await syncHistoryCorrections({ root, profile, store, cloud, ...opts.correctionMirrors ? { mirrors: opts.correctionMirrors } : {} });
90854
+ persistedCursor = profile.projectAnchorCursor ?? 0;
90855
+ backfillPending = !profile.projectCausalBackfillAt;
90856
+ cursor = backfillPending ? 0 : persistedCursor;
90138
90857
  for (let page = 0; page < maxPages; page++) {
90858
+ if (page > 0) await new Promise((r) => setImmediate(r));
90139
90859
  const res = await cloud.getProjectAnchors(projectId, cursor, limit);
90860
+ if (backfillPending) {
90861
+ backfillPending = false;
90862
+ if (res.causalLeg) {
90863
+ profile.projectCausalBackfillAt = clock();
90864
+ profile.projectAnchorCursor = 0;
90865
+ saveProfile(root, profile);
90866
+ backfillStarted = persistedCursor > 0;
90867
+ } else if (cursor !== persistedCursor) {
90868
+ cursor = persistedCursor;
90869
+ page--;
90870
+ continue;
90871
+ }
90872
+ }
90140
90873
  if (res.anchors.length === 0) break;
90141
90874
  if ((await reverse.get()).size === 0 && (await reverse.refresh()).size === 0) {
90142
90875
  return { synced: false, reason: "no-local-symbols" };
90143
90876
  }
90144
- const byId = new Map(res.nodes.map((n) => [n.id, n]));
90145
- const now = Date.now();
90877
+ for (const node2 of res.nodes) {
90878
+ if (store.getNode(node2.id)) continue;
90879
+ const ownTwin = await lookupLive(store, own, node2.id) !== null;
90880
+ if (landCloudNode(store, node2, ownTwin) === "landed") landed++;
90881
+ }
90882
+ const now = clock();
90146
90883
  for (const a of res.anchors) {
90147
90884
  if (a.type !== "ANCHORED_AT") continue;
90148
90885
  const localTo = await lookupLive(store, reverse, a.toSymbolId);
@@ -90150,16 +90887,10 @@ async function syncProjectAnchors(opts) {
90150
90887
  dropped++;
90151
90888
  continue;
90152
90889
  }
90153
- let from = store.getNode(a.from) ? a.from : await lookupLive(store, own, a.from);
90890
+ const from = await resolveKnowledge(a.from);
90154
90891
  if (!from) {
90155
- const node2 = byId.get(a.from);
90156
- if (!node2) {
90157
- dropped++;
90158
- continue;
90159
- }
90160
- store.mergeNode(node2);
90161
- landed++;
90162
- from = node2.id;
90892
+ dropped++;
90893
+ continue;
90163
90894
  }
90164
90895
  const existing = existingAnchor(store, from, localTo);
90165
90896
  const id = existing?.id ?? edgeId(from, "ANCHORED_AT", localTo);
@@ -90191,6 +90922,8 @@ async function syncProjectAnchors(opts) {
90191
90922
  if (existing) updated++;
90192
90923
  else anchors++;
90193
90924
  }
90925
+ await settleEdges(res.edges ?? []);
90926
+ flushParked();
90194
90927
  const next = typeof res.cursor === "number" && res.cursor > cursor ? res.cursor : cursor;
90195
90928
  if (next !== cursor) {
90196
90929
  cursor = next;
@@ -90199,8 +90932,23 @@ async function syncProjectAnchors(opts) {
90199
90932
  }
90200
90933
  if (res.anchors.length < limit) break;
90201
90934
  }
90202
- return { synced: true, anchors, updated, dropped, landed, cursor };
90935
+ await settleEdges([]);
90936
+ flushParked();
90937
+ return {
90938
+ synced: true,
90939
+ anchors,
90940
+ updated,
90941
+ dropped,
90942
+ landed,
90943
+ cursor,
90944
+ edgesLanded,
90945
+ edgesParked,
90946
+ edgesReplayed,
90947
+ edgesPending: parked.length,
90948
+ backfillStarted
90949
+ };
90203
90950
  } catch (err2) {
90951
+ flushParked();
90204
90952
  return { synced: false, reason: "error", detail: err2 instanceof Error ? err2.message : String(err2) };
90205
90953
  }
90206
90954
  }
@@ -90218,24 +90966,16 @@ async function hydrateProject(opts) {
90218
90966
  let landedNodes = 0;
90219
90967
  let ownNodes = 0;
90220
90968
  for (const n of res.nodes) {
90221
- if (store.getNode(n.id)) continue;
90222
- if (own.has(n.id)) {
90223
- ownNodes++;
90224
- continue;
90225
- }
90226
- store.mergeNode(n);
90227
- landedNodes++;
90969
+ const landing = landCloudNode(store, n, own.has(n.id));
90970
+ if (landing === "own") ownNodes++;
90971
+ else if (landing === "landed") landedNodes++;
90228
90972
  }
90229
90973
  let landedEdges = 0;
90230
90974
  for (const e of res.edges) {
90231
90975
  const from = localId(e.from);
90232
90976
  const to = localId(e.to);
90233
90977
  if (!from || !to) continue;
90234
- if (store.outEdges(from, [e.type]).some((x) => x.to === to)) continue;
90235
- const id = from === e.from && to === e.to ? e.id : edgeId(from, e.type, to);
90236
- if (store.getEdge(id)) continue;
90237
- store.mergeEdge({ ...e, id, from, to });
90238
- landedEdges++;
90978
+ if (landCloudEdge(store, e, from, to)) landedEdges++;
90239
90979
  }
90240
90980
  profile.projectHydratedAt = Date.now();
90241
90981
  saveProfile(root, profile);
@@ -90499,8 +91239,8 @@ async function syncHistoryRevisionPage(opts) {
90499
91239
  init_src7();
90500
91240
  init_src();
90501
91241
  import { execFile as execFile6 } from "node:child_process";
90502
- import { readFileSync as readFileSync35 } from "node:fs";
90503
- import { join as join39 } from "node:path";
91242
+ import { readFileSync as readFileSync36 } from "node:fs";
91243
+ import { join as join40 } from "node:path";
90504
91244
  import { promisify as promisify5 } from "node:util";
90505
91245
 
90506
91246
  // src/history-walk.ts
@@ -91052,7 +91792,7 @@ async function defaultGit(root, args2) {
91052
91792
  function readIgnoreLines(root, profile) {
91053
91793
  let lines = [];
91054
91794
  try {
91055
- lines = readFileSync35(join39(root, ".errataignore"), "utf8").split("\n");
91795
+ lines = readFileSync36(join40(root, ".errataignore"), "utf8").split("\n");
91056
91796
  } catch {
91057
91797
  }
91058
91798
  return [...lines, ...profile.ignoreExtra ?? []].map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
@@ -91628,8 +92368,8 @@ async function startHistoryStage(root, input, client, walk11 = {}) {
91628
92368
  init_cloud_auth();
91629
92369
  import { createHash as createHash19 } from "node:crypto";
91630
92370
  import { execFileSync as execFileSync6 } from "node:child_process";
91631
- import { existsSync as existsSync32, writeFileSync as writeFileSync25 } from "node:fs";
91632
- import { join as join40 } from "node:path";
92371
+ import { existsSync as existsSync32, writeFileSync as writeFileSync26 } from "node:fs";
92372
+ import { join as join41 } from "node:path";
91633
92373
 
91634
92374
  // src/device-poll.ts
91635
92375
  var THROTTLED_POLL_MIN_WAIT_MS = 11e3;
@@ -92088,9 +92828,9 @@ function applyLinkDecision(root, profile, config2, decision, now = Date.now) {
92088
92828
  delete nextProfile.projectLinkHold;
92089
92829
  let wroteIgnore = false;
92090
92830
  if (decision.writeIgnoreStarter !== false) {
92091
- const target = join40(root, ".errataignore");
92831
+ const target = join41(root, ".errataignore");
92092
92832
  if (!existsSync32(target)) {
92093
- writeFileSync25(target, IGNORE_STARTER, "utf8");
92833
+ writeFileSync26(target, IGNORE_STARTER, "utf8");
92094
92834
  wroteIgnore = true;
92095
92835
  }
92096
92836
  }
@@ -92104,12 +92844,12 @@ init_registry();
92104
92844
  // src/adopt.ts
92105
92845
  init_git_remote();
92106
92846
  import { existsSync as existsSync33 } from "node:fs";
92107
- import { dirname as dirname13, join as join41 } from "node:path";
92847
+ import { dirname as dirname14, join as join42 } from "node:path";
92108
92848
  function findGitRoot(absPath) {
92109
92849
  let dir = absPath;
92110
92850
  for (let depth = 0; depth < 64; depth++) {
92111
- if (existsSync33(join41(dir, ".git"))) return dir;
92112
- const parent = dirname13(dir);
92851
+ if (existsSync33(join42(dir, ".git"))) return dir;
92852
+ const parent = dirname14(dir);
92113
92853
  if (parent === dir) return null;
92114
92854
  dir = parent;
92115
92855
  }
@@ -92826,7 +93566,7 @@ async function startMultiDaemon(opts = {}) {
92826
93566
  records.push(rec);
92827
93567
  void ambientLinkAll();
92828
93568
  try {
92829
- writeFileSync26(
93569
+ writeFileSync27(
92830
93570
  rec.engine.paths.daemonLock,
92831
93571
  daemonLockPayload({
92832
93572
  pid: process.pid,
@@ -93205,7 +93945,7 @@ async function startMultiDaemon(opts = {}) {
93205
93945
  baseUrl = `http://127.0.0.1:${port}`;
93206
93946
  try {
93207
93947
  ensureDir(globalDir());
93208
- writeFileSync26(
93948
+ writeFileSync27(
93209
93949
  lockPath,
93210
93950
  daemonLockPayload({ pid: process.pid, webUiUrl: baseUrl, processStartedAt: PROCESS_STARTED_AT, entry: SELF_ENTRY, version: DAEMON_VERSION }),
93211
93951
  "utf8"
@@ -93214,7 +93954,7 @@ async function startMultiDaemon(opts = {}) {
93214
93954
  }
93215
93955
  for (const r of records) {
93216
93956
  try {
93217
- writeFileSync26(
93957
+ writeFileSync27(
93218
93958
  r.engine.paths.daemonLock,
93219
93959
  daemonLockPayload({
93220
93960
  pid: process.pid,
@@ -93256,6 +93996,7 @@ async function startMultiDaemon(opts = {}) {
93256
93996
  const lastAnchorPull = /* @__PURE__ */ new Map();
93257
93997
  const pullProjectAnchors = async (r, client, salt) => {
93258
93998
  const donePass = markPass(`anchor-pull:${r.engine.profile.name}`);
93999
+ const pullStartedAt = Date.now();
93259
94000
  const beforeCorrections = r.engine.profile.projectHistoryCorrectionCursor ?? 0;
93260
94001
  const sync = await syncProjectAnchors({
93261
94002
  root: r.root,
@@ -93277,10 +94018,22 @@ async function startMultiDaemon(opts = {}) {
93277
94018
  console.warn(`[errata] history revision reconciliation failed for ${r.engine.profile.name}: ${err2 instanceof Error ? err2.message : String(err2)}`);
93278
94019
  }
93279
94020
  if (sync.synced) {
93280
- if (sync.anchors + sync.updated > 0) {
94021
+ appendPassLedger(r.engine.paths.configDir, "anchor-pull", Date.now() - pullStartedAt, {
94022
+ anchorsLanded: sync.anchors,
94023
+ anchorsUpdated: sync.updated,
94024
+ nodesLanded: sync.landed,
94025
+ edgesLanded: sync.edgesLanded + sync.edgesReplayed,
94026
+ edgesParked: sync.edgesParked,
94027
+ edgesReplayed: sync.edgesReplayed,
94028
+ edgesPending: sync.edgesPending
94029
+ });
94030
+ if (sync.backfillStarted) {
94031
+ console.log(`[errata] causal re-walk started for ${r.engine.profile.name} \u2014 anchor cursor reset to 0 once`);
94032
+ }
94033
+ if (sync.anchors + sync.updated + sync.landed + sync.edgesLanded + sync.edgesReplayed > 0) {
93281
94034
  r.engine.markContextDirty();
93282
94035
  console.log(
93283
- `[errata] anchors pulled for ${r.engine.profile.name} \u2014 ${sync.anchors} new, ${sync.updated} re-versioned, ${sync.dropped} unresolved (${sync.landed} priors landed, cursor ${sync.cursor})`
94036
+ `[errata] anchors pulled for ${r.engine.profile.name} \u2014 ${sync.anchors} new, ${sync.updated} re-versioned, ${sync.dropped} unresolved (${sync.landed} priors landed, ${sync.edgesLanded + sync.edgesReplayed} causal edges landed, ${sync.edgesPending} parked, cursor ${sync.cursor})`
93284
94037
  );
93285
94038
  }
93286
94039
  } else if (sync.reason === "error") {
@@ -94696,7 +95449,7 @@ import { spawnSync as spawnSync2 } from "node:child_process";
94696
95449
 
94697
95450
  // src/codex-runtime.ts
94698
95451
  import { readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
94699
- import { join as join42 } from "node:path";
95452
+ import { join as join43 } from "node:path";
94700
95453
  function resolveCodexExecutable(options = {}) {
94701
95454
  const platform = options.platform ?? process.platform;
94702
95455
  const env2 = options.env ?? process.env;
@@ -94711,13 +95464,13 @@ function resolveCodexExecutable(options = {}) {
94711
95464
  };
94712
95465
  for (const dir of (env2["PATH"] ?? env2["Path"] ?? "").split(";")) {
94713
95466
  if (!dir.trim()) continue;
94714
- const path3 = join42(dir.trim().replace(/^"|"$/g, ""), "codex.exe");
95467
+ const path3 = join43(dir.trim().replace(/^"|"$/g, ""), "codex.exe");
94715
95468
  if (fileTime(path3) !== null) return path3;
94716
95469
  }
94717
95470
  if (!env2["LOCALAPPDATA"]) return "codex";
94718
- const root = join42(env2["LOCALAPPDATA"], "OpenAI", "Codex", "bin");
95471
+ const root = join43(env2["LOCALAPPDATA"], "OpenAI", "Codex", "bin");
94719
95472
  try {
94720
- const candidates = readdirSync13(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join42(root, entry.name, "codex.exe")).map((path3) => ({ path: path3, mtime: fileTime(path3) })).filter((candidate) => candidate.mtime !== null).sort((a, b) => b.mtime - a.mtime || a.path.localeCompare(b.path));
95473
+ const candidates = readdirSync13(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join43(root, entry.name, "codex.exe")).map((path3) => ({ path: path3, mtime: fileTime(path3) })).filter((candidate) => candidate.mtime !== null).sort((a, b) => b.mtime - a.mtime || a.path.localeCompare(b.path));
94721
95474
  return candidates[0]?.path ?? "codex";
94722
95475
  } catch {
94723
95476
  return "codex";
@@ -94856,14 +95609,14 @@ async function diagnoseInstallation(cfg, dependencies) {
94856
95609
 
94857
95610
  // src/codex-doctor.ts
94858
95611
  import { spawn as spawn4 } from "node:child_process";
94859
- import { readFileSync as readFileSync37, statSync as statSync11 } from "node:fs";
94860
- import { join as join44 } from "node:path";
95612
+ import { readFileSync as readFileSync38, statSync as statSync11 } from "node:fs";
95613
+ import { join as join45 } from "node:path";
94861
95614
  import { createInterface } from "node:readline";
94862
95615
 
94863
95616
  // src/codex-hooks.ts
94864
- import { existsSync as existsSync35, mkdirSync as mkdirSync9, readFileSync as readFileSync36, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync27 } from "node:fs";
95617
+ import { existsSync as existsSync35, mkdirSync as mkdirSync9, readFileSync as readFileSync37, renameSync as renameSync6, unlinkSync as unlinkSync5, writeFileSync as writeFileSync28 } from "node:fs";
94865
95618
  import { homedir as homedir7 } from "node:os";
94866
- import { join as join43 } from "node:path";
95619
+ import { join as join44 } from "node:path";
94867
95620
 
94868
95621
  // src/hook-commands.ts
94869
95622
  function hookCurlCommand(port, path3 = "/api/hook") {
@@ -95067,34 +95820,34 @@ function buildCodexRelaySource(workspaceRoot) {
95067
95820
  init_checkout_root();
95068
95821
  var CODEX_HOOK_REVIEW_HELP = "Codex desktop: Settings > Hooks, select this project, expand each event and Trust the Errata hook. CLI users can use /hooks.";
95069
95822
  function writeRelay(file2, source) {
95070
- if (existsSync35(file2) && readFileSync36(file2, "utf8") === source) return;
95823
+ if (existsSync35(file2) && readFileSync37(file2, "utf8") === source) return;
95071
95824
  const temp = `${file2}.${process.pid}.tmp`;
95072
95825
  try {
95073
- writeFileSync27(temp, source, { encoding: "utf8", mode: 384 });
95074
- renameSync5(temp, file2);
95826
+ writeFileSync28(temp, source, { encoding: "utf8", mode: 384 });
95827
+ renameSync6(temp, file2);
95075
95828
  } finally {
95076
95829
  if (existsSync35(temp)) unlinkSync5(temp);
95077
95830
  }
95078
95831
  }
95079
95832
  function refreshCodexRelay(root) {
95080
- const config2 = join43(root, ".codex", "config.toml");
95833
+ const config2 = join44(root, ".codex", "config.toml");
95081
95834
  if (!existsSync35(config2)) return "absent";
95082
- const existing = readFileSync36(config2, "utf8");
95835
+ const existing = readFileSync37(config2, "utf8");
95083
95836
  if (!existing.includes(CODEX_HOOKS_BEGIN) && !existing.includes(CODEX_HOOKS_END)) return "absent";
95084
95837
  replaceCodexHooks(existing, "");
95085
- const relay = join43(root, ".codex", "hooks", "errata-relay.mjs");
95086
- const prior = existsSync35(relay) ? readFileSync36(relay, "utf8") : "";
95838
+ const relay = join44(root, ".codex", "hooks", "errata-relay.mjs");
95839
+ const prior = existsSync35(relay) ? readFileSync37(relay, "utf8") : "";
95087
95840
  if (prior && !prior.startsWith("// errata-managed: regenerated by errata install-hooks codex")) {
95088
95841
  throw new Error("Codex relay is not Errata-managed; preserved it. Run errata install-hooks codex to explicitly replace it.");
95089
95842
  }
95090
95843
  const source = buildCodexRelaySource(checkoutRootOf(root));
95091
95844
  if (prior === source) return "current";
95092
- mkdirSync9(join43(root, ".codex", "hooks"), { recursive: true });
95845
+ mkdirSync9(join44(root, ".codex", "hooks"), { recursive: true });
95093
95846
  writeRelay(relay, source);
95094
95847
  return "updated";
95095
95848
  }
95096
- function hasCodexConfig(root, codexHome = process.env["CODEX_HOME"] || join43(homedir7(), ".codex")) {
95097
- return existsSync35(join43(root, ".codex")) || existsSync35(codexHome);
95849
+ function hasCodexConfig(root, codexHome = process.env["CODEX_HOME"] || join44(homedir7(), ".codex")) {
95850
+ return existsSync35(join44(root, ".codex")) || existsSync35(codexHome);
95098
95851
  }
95099
95852
  function replaceCodexHooks(existing, block) {
95100
95853
  const begin = existing.indexOf(CODEX_HOOKS_BEGIN);
@@ -95110,14 +95863,14 @@ function replaceCodexHooks(existing, block) {
95110
95863
  return existing.trimEnd() + (existing.trim() ? "\n\n" : "") + block;
95111
95864
  }
95112
95865
  function writeCodexHooks(root, port, nodePath = process.execPath) {
95113
- const dir = join43(root, ".codex");
95114
- const file2 = join43(dir, "config.toml");
95115
- const relay = join43(dir, "hooks", "errata-relay.mjs");
95866
+ const dir = join44(root, ".codex");
95867
+ const file2 = join44(dir, "config.toml");
95868
+ const relay = join44(dir, "hooks", "errata-relay.mjs");
95116
95869
  const block = buildCodexHooksToml(port, { nodePath, relayPath: relay });
95117
- const final = replaceCodexHooks(existsSync35(file2) ? readFileSync36(file2, "utf8") : "", block);
95118
- mkdirSync9(join43(dir, "hooks"), { recursive: true });
95870
+ const final = replaceCodexHooks(existsSync35(file2) ? readFileSync37(file2, "utf8") : "", block);
95871
+ mkdirSync9(join44(dir, "hooks"), { recursive: true });
95119
95872
  writeRelay(relay, buildCodexRelaySource(checkoutRootOf(root)));
95120
- if (!existsSync35(file2) || readFileSync36(file2, "utf8") !== final) writeFileSync27(file2, final, "utf8");
95873
+ if (!existsSync35(file2) || readFileSync37(file2, "utf8") !== final) writeFileSync28(file2, final, "utf8");
95121
95874
  return file2;
95122
95875
  }
95123
95876
 
@@ -95126,7 +95879,7 @@ init_checkout_root();
95126
95879
  function inspectCodexRelay(cwd) {
95127
95880
  const root = nearestGitRoot(cwd) ?? cwd;
95128
95881
  try {
95129
- const source = readFileSync37(join44(root, ".codex", "hooks", "errata-relay.mjs"), "utf8");
95882
+ const source = readFileSync38(join45(root, ".codex", "hooks", "errata-relay.mjs"), "utf8");
95130
95883
  if (source === buildCodexRelaySource(checkoutRootOf(root))) {
95131
95884
  return { id: "codex_relay", status: "pass", detail: "Installed Codex relay matches this Errata version" };
95132
95885
  }
@@ -95137,8 +95890,8 @@ function inspectCodexRelay(cwd) {
95137
95890
  function inspectCodexContext(cwd) {
95138
95891
  const root = checkoutRootOf(nearestGitRoot(cwd) ?? cwd);
95139
95892
  try {
95140
- const file2 = join44(root, ".errata", "context.md");
95141
- if (statSync11(file2).size <= 128 * 1024 && readFileSync37(file2, "utf8").trim()) {
95893
+ const file2 = join45(root, ".errata", "context.md");
95894
+ if (statSync11(file2).size <= 128 * 1024 && readFileSync38(file2, "utf8").trim()) {
95142
95895
  return { id: "codex_context", status: "pass", detail: "Saved workspace context is readable by the relay; this does not prove live daemon freshness" };
95143
95896
  }
95144
95897
  } catch {
@@ -95316,9 +96069,9 @@ function consolidationGapMs(lastPassMs, policy) {
95316
96069
 
95317
96070
  // src/codex-context-plugin.ts
95318
96071
  import { createHash as createHash20 } from "node:crypto";
95319
- import { cpSync as cpSync2, existsSync as existsSync36, mkdirSync as mkdirSync10, readFileSync as readFileSync38, renameSync as renameSync6, writeFileSync as writeFileSync28 } from "node:fs";
96072
+ import { cpSync as cpSync2, existsSync as existsSync36, mkdirSync as mkdirSync10, readFileSync as readFileSync39, renameSync as renameSync7, writeFileSync as writeFileSync29 } from "node:fs";
95320
96073
  import { homedir as homedir8 } from "node:os";
95321
- import { join as join45 } from "node:path";
96074
+ import { join as join46 } from "node:path";
95322
96075
  import { fileURLToPath as fileURLToPath2 } from "node:url";
95323
96076
 
95324
96077
  // ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/error.js
@@ -96016,23 +96769,23 @@ var NAME = "errata-context";
96016
96769
  var PREFIX2 = `./.errata/plugins/${NAME}/`;
96017
96770
  var object2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
96018
96771
  function readText(file2) {
96019
- return existsSync36(file2) ? readFileSync38(file2, "utf8") : "";
96772
+ return existsSync36(file2) ? readFileSync39(file2, "utf8") : "";
96020
96773
  }
96021
96774
  function writeChanged(file2, content) {
96022
96775
  if (readText(file2) === content) return;
96023
96776
  const temp = `${file2}.${process.pid}.tmp`;
96024
- writeFileSync28(temp, content, "utf8");
96025
- renameSync6(temp, file2);
96777
+ writeFileSync29(temp, content, "utf8");
96778
+ renameSync7(temp, file2);
96026
96779
  }
96027
96780
  function bundledContextPlugin() {
96028
96781
  const packaged = fileURLToPath2(new URL("./resources/context-plugin/", import.meta.url));
96029
- if (existsSync36(join45(packaged, ".codex-plugin/plugin.json"))) return packaged;
96782
+ if (existsSync36(join46(packaged, ".codex-plugin/plugin.json"))) return packaged;
96030
96783
  return fileURLToPath2(new URL("../dist/resources/context-plugin/", import.meta.url));
96031
96784
  }
96032
96785
  function refreshCodexContextPlugin(root) {
96033
- const file2 = join45(root, ".agents", "plugins", "marketplace.json");
96786
+ const file2 = join46(root, ".agents", "plugins", "marketplace.json");
96034
96787
  if (!existsSync36(file2)) return false;
96035
- const marketplace = JSON.parse(readFileSync38(file2, "utf8"));
96788
+ const marketplace = JSON.parse(readFileSync39(file2, "utf8"));
96036
96789
  const entries = Array.isArray(marketplace.plugins) ? marketplace.plugins : [];
96037
96790
  const entry = entries.map(object2).find((value) => value.name === NAME && String(object2(value.source).path).startsWith(PREFIX2));
96038
96791
  if (!entry) return false;
@@ -96043,24 +96796,24 @@ function refreshCodexContextPlugin(root) {
96043
96796
  function installCodexContextPlugin(root, options = {}) {
96044
96797
  const bundle = options.bundle ?? bundledContextPlugin();
96045
96798
  const manifest = JSON.parse(
96046
- readFileSync38(join45(bundle, ".codex-plugin/plugin.json"), "utf8")
96799
+ readFileSync39(join46(bundle, ".codex-plugin/plugin.json"), "utf8")
96047
96800
  );
96048
96801
  if (manifest.name !== NAME || typeof manifest.version !== "string" || !/^[\w.-]+$/.test(manifest.version)) {
96049
96802
  throw new Error("Invalid bundled Errata Context manifest.");
96050
96803
  }
96051
- const checksums = readFileSync38(join45(bundle, "SHA256SUMS"), "utf8");
96804
+ const checksums = readFileSync39(join46(bundle, "SHA256SUMS"), "utf8");
96052
96805
  for (const line of checksums.trim().split("\n")) {
96053
96806
  const match2 = /^([a-f0-9]{64}) ([\w./-]+)$/.exec(line);
96054
96807
  if (!match2 || match2[2].startsWith("/") || match2[2].split("/").includes("..")) {
96055
96808
  throw new Error("Invalid bundled Errata Context checksum manifest.");
96056
96809
  }
96057
- const actual = createHash20("sha256").update(readFileSync38(join45(bundle, match2[2]))).digest("hex");
96810
+ const actual = createHash20("sha256").update(readFileSync39(join46(bundle, match2[2]))).digest("hex");
96058
96811
  if (actual !== match2[1])
96059
96812
  throw new Error(`Bundled Errata Context checksum mismatch: ${match2[2]}`);
96060
96813
  }
96061
96814
  const release = `${manifest.version}-${createHash20("sha256").update(checksums).digest("hex").slice(0, 12)}`;
96062
- const plugin = join45(root, ".errata", "plugins", NAME, release);
96063
- const marketplaceFile = join45(root, ".agents", "plugins", "marketplace.json");
96815
+ const plugin = join46(root, ".errata", "plugins", NAME, release);
96816
+ const marketplaceFile = join46(root, ".agents", "plugins", "marketplace.json");
96064
96817
  const priorMarketplace = readText(marketplaceFile);
96065
96818
  const marketplace = priorMarketplace ? JSON.parse(priorMarketplace) : {
96066
96819
  name: "errata-local",
@@ -96081,12 +96834,12 @@ function installCodexContextPlugin(root, options = {}) {
96081
96834
  );
96082
96835
  }
96083
96836
  const key = `${NAME}@${marketplace.name}`;
96084
- const configFile = join45(root, ".codex", "config.toml");
96837
+ const configFile = join46(root, ".codex", "config.toml");
96085
96838
  const config2 = readText(configFile);
96086
96839
  const parsed = parse4(config2);
96087
96840
  const plugins = object2(parsed.plugins);
96088
- const codexHome = options.codexHome ?? process.env["CODEX_HOME"] ?? join45(homedir8(), ".codex");
96089
- const globalPlugins = object2(parse4(readText(join45(codexHome, "config.toml"))).plugins);
96841
+ const codexHome = options.codexHome ?? process.env["CODEX_HOME"] ?? join46(homedir8(), ".codex");
96842
+ const globalPlugins = object2(parse4(readText(join46(codexHome, "config.toml"))).plugins);
96090
96843
  const disabledGlobally = Object.entries(globalPlugins).some(
96091
96844
  ([name2, value]) => (name2 === NAME || name2.startsWith(`${NAME}@`)) && object2(value).enabled === false
96092
96845
  );
@@ -96110,13 +96863,13 @@ enabled = ${enabled}
96110
96863
  mkdirSync10(plugin, { recursive: true });
96111
96864
  cpSync2(bundle, plugin, { recursive: true });
96112
96865
  writeChanged(
96113
- join45(plugin, ".mcp.json"),
96866
+ join46(plugin, ".mcp.json"),
96114
96867
  JSON.stringify(
96115
96868
  {
96116
96869
  mcpServers: {
96117
96870
  errata_context: {
96118
96871
  command: options.nodePath ?? process.execPath,
96119
- args: [join45(plugin, "dist", "mcp-server.mjs")],
96872
+ args: [join46(plugin, "dist", "mcp-server.mjs")],
96120
96873
  env: { ERRATA_WORKSPACE: root, ERRATA_CONTEXT_VERSION: manifest.version }
96121
96874
  }
96122
96875
  }
@@ -96125,8 +96878,8 @@ enabled = ${enabled}
96125
96878
  2
96126
96879
  ) + "\n"
96127
96880
  );
96128
- mkdirSync10(join45(root, ".agents", "plugins"), { recursive: true });
96129
- mkdirSync10(join45(root, ".codex"), { recursive: true });
96881
+ mkdirSync10(join46(root, ".agents", "plugins"), { recursive: true });
96882
+ mkdirSync10(join46(root, ".codex"), { recursive: true });
96130
96883
  writeChanged(marketplaceFile, JSON.stringify(marketplace, null, 2) + "\n");
96131
96884
  writeChanged(configFile, nextConfig);
96132
96885
  return { plugin, enabled, marketplace: marketplaceFile };
@@ -96134,15 +96887,15 @@ enabled = ${enabled}
96134
96887
 
96135
96888
  // src/codex-hook-refresh.ts
96136
96889
  init_checkout_root();
96137
- import { existsSync as existsSync37, readdirSync as readdirSync14, readFileSync as readFileSync39 } from "node:fs";
96138
- import { dirname as dirname14, join as join46, resolve as resolve8 } from "node:path";
96890
+ import { existsSync as existsSync37, readdirSync as readdirSync14, readFileSync as readFileSync40 } from "node:fs";
96891
+ import { dirname as dirname15, join as join47, resolve as resolve8 } from "node:path";
96139
96892
  init_registry();
96140
96893
  function codexHookRoots(roots) {
96141
96894
  const found = new Set(roots.map((root) => resolve8(root)));
96142
96895
  for (const root of [...found]) {
96143
96896
  const main2 = mainCheckoutOfWorktree(root) ?? root;
96144
96897
  found.add(main2);
96145
- const dir = join46(main2, ".git", "worktrees");
96898
+ const dir = join47(main2, ".git", "worktrees");
96146
96899
  if (!existsSync37(dir)) continue;
96147
96900
  let names;
96148
96901
  try {
@@ -96152,7 +96905,7 @@ function codexHookRoots(roots) {
96152
96905
  }
96153
96906
  for (const name2 of names) {
96154
96907
  try {
96155
- const checkout = dirname14(readFileSync39(join46(dir, name2, "gitdir"), "utf8").trim());
96908
+ const checkout = dirname15(readFileSync40(join47(dir, name2, "gitdir"), "utf8").trim());
96156
96909
  if (mainCheckoutOfWorktree(checkout) === main2) found.add(checkout);
96157
96910
  } catch {
96158
96911
  }
@@ -96174,9 +96927,9 @@ function refreshInstalledCodexRelays(roots = listWorkspaceEntries().map((entry)
96174
96927
  }
96175
96928
 
96176
96929
  // src/claude-statusline.ts
96177
- import { existsSync as existsSync38, mkdirSync as mkdirSync11, readFileSync as readFileSync40, writeFileSync as writeFileSync29 } from "node:fs";
96930
+ import { existsSync as existsSync38, mkdirSync as mkdirSync11, readFileSync as readFileSync41, writeFileSync as writeFileSync30 } from "node:fs";
96178
96931
  import { homedir as homedir9 } from "node:os";
96179
- import { dirname as dirname15, join as join47 } from "node:path";
96932
+ import { dirname as dirname16, join as join48 } from "node:path";
96180
96933
  var STATUSLINE_RELAY_SOURCE = String.raw`// errata-managed: regenerated by errata init / install-hooks claude
96181
96934
  import { readFileSync } from 'node:fs';
96182
96935
 
@@ -96224,8 +96977,8 @@ function isSameInstall(value, relay) {
96224
96977
  return command.includes(relay.replace(/\\/g, "/").toLowerCase());
96225
96978
  }
96226
96979
  function userClaudeSettingsPath() {
96227
- const base = process.env["CLAUDE_CONFIG_DIR"] ?? join47(homedir9(), ".claude");
96228
- return join47(base, "settings.json");
96980
+ const base = process.env["CLAUDE_CONFIG_DIR"] ?? join48(homedir9(), ".claude");
96981
+ return join48(base, "settings.json");
96229
96982
  }
96230
96983
  function ensureUserStatusLine(opts) {
96231
96984
  const env2 = opts.env ?? process.env;
@@ -96235,21 +96988,21 @@ function ensureUserStatusLine(opts) {
96235
96988
  let settings = {};
96236
96989
  if (existsSync38(settingsPath)) {
96237
96990
  try {
96238
- const parsed = JSON.parse(readFileSync40(settingsPath, "utf8"));
96991
+ const parsed = JSON.parse(readFileSync41(settingsPath, "utf8"));
96239
96992
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return "unreadable";
96240
96993
  settings = parsed;
96241
96994
  } catch {
96242
96995
  return "unreadable";
96243
96996
  }
96244
96997
  }
96245
- const relay = join47(opts.relayDir, STATUSLINE_RELAY_FILE);
96998
+ const relay = join48(opts.relayDir, STATUSLINE_RELAY_FILE);
96246
96999
  const keep = (value) => value !== void 0 && (!isErrataStatusLine(value) || !isSameInstall(value, relay));
96247
97000
  const ownMain = keep(settings["statusLine"]);
96248
97001
  const ownRows = keep(settings["subagentStatusLine"]);
96249
97002
  if (ownMain && ownRows) return "kept-own";
96250
97003
  mkdirSync11(opts.relayDir, { recursive: true });
96251
- if (!existsSync38(relay) || readFileSync40(relay, "utf8") !== STATUSLINE_RELAY_SOURCE) {
96252
- writeFileSync29(relay, STATUSLINE_RELAY_SOURCE, "utf8");
97004
+ if (!existsSync38(relay) || readFileSync41(relay, "utf8") !== STATUSLINE_RELAY_SOURCE) {
97005
+ writeFileSync30(relay, STATUSLINE_RELAY_SOURCE, "utf8");
96253
97006
  }
96254
97007
  const nodePath = opts.nodePath ?? process.execPath;
96255
97008
  const next = {
@@ -96260,8 +97013,8 @@ function ensureUserStatusLine(opts) {
96260
97013
  return ownMain ? "kept-own" : "unchanged";
96261
97014
  }
96262
97015
  Object.assign(settings, next);
96263
- mkdirSync11(dirname15(settingsPath), { recursive: true });
96264
- writeFileSync29(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
97016
+ mkdirSync11(dirname16(settingsPath), { recursive: true });
97017
+ writeFileSync30(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
96265
97018
  return "installed";
96266
97019
  }
96267
97020
  function describeStatusLineOutcome(outcome) {
@@ -96281,8 +97034,8 @@ function describeStatusLineOutcome(outcome) {
96281
97034
 
96282
97035
  // src/claude-mods.ts
96283
97036
  import { createHash as createHash21 } from "node:crypto";
96284
- import { cpSync as cpSync3, existsSync as existsSync39, mkdirSync as mkdirSync12, readFileSync as readFileSync41, readdirSync as readdirSync15, rmSync as rmSync4, statSync as statSync12, writeFileSync as writeFileSync30 } from "node:fs";
96285
- import { delimiter as delimiter2, dirname as dirname16, join as join48, relative as relative8, resolve as resolve9, sep as sep5 } from "node:path";
97037
+ import { cpSync as cpSync3, existsSync as existsSync39, mkdirSync as mkdirSync12, readFileSync as readFileSync42, readdirSync as readdirSync15, rmSync as rmSync4, statSync as statSync12, writeFileSync as writeFileSync31 } from "node:fs";
97038
+ import { delimiter as delimiter2, dirname as dirname17, join as join49, relative as relative8, resolve as resolve9, sep as sep5 } from "node:path";
96286
97039
  import { fileURLToPath as fileURLToPath3 } from "node:url";
96287
97040
  var CLAUDE_PLUGIN_DIRS_ENV = "CLAUDE_CODE_PLUGIN_DIRS";
96288
97041
  var HASH_FILE = ".errata-mod-hash";
@@ -96297,7 +97050,7 @@ function isEngineOwned(relPath) {
96297
97050
  function listFiles(root, dir = root) {
96298
97051
  const out2 = [];
96299
97052
  for (const name2 of readdirSync15(dir)) {
96300
- const abs = join48(dir, name2);
97053
+ const abs = join49(dir, name2);
96301
97054
  if (isEngineOwned(relative8(root, abs))) continue;
96302
97055
  if (statSync12(abs).isDirectory()) out2.push(...listFiles(root, abs));
96303
97056
  else out2.push(abs);
@@ -96309,14 +97062,14 @@ function modHash(dir) {
96309
97062
  for (const abs of listFiles(dir).sort()) {
96310
97063
  h.update(relative8(dir, abs).split(sep5).join("/"));
96311
97064
  h.update("\0");
96312
- h.update(readFileSync41(abs));
97065
+ h.update(readFileSync42(abs));
96313
97066
  h.update("\0");
96314
97067
  }
96315
97068
  return h.digest("hex");
96316
97069
  }
96317
97070
  function bundledMods(bundleDir) {
96318
97071
  if (!existsSync39(bundleDir)) return [];
96319
- return readdirSync15(bundleDir).filter((name2) => existsSync39(join48(bundleDir, name2, ".claude-plugin", "plugin.json")));
97072
+ return readdirSync15(bundleDir).filter((name2) => existsSync39(join49(bundleDir, name2, ".claude-plugin", "plugin.json")));
96320
97073
  }
96321
97074
  function sameDir(a, b) {
96322
97075
  const norm = (p) => resolve9(p).replace(/[\\/]+$/, "").toLowerCase();
@@ -96333,7 +97086,7 @@ function off(env2) {
96333
97086
  function readSettings(path3) {
96334
97087
  if (!existsSync39(path3)) return {};
96335
97088
  try {
96336
- const parsed = JSON.parse(readFileSync41(path3, "utf8"));
97089
+ const parsed = JSON.parse(readFileSync42(path3, "utf8"));
96337
97090
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
96338
97091
  return parsed;
96339
97092
  } catch {
@@ -96357,13 +97110,13 @@ function currentDirs(settings) {
96357
97110
  return raw2.split(delimiter2).map((s) => s.trim()).filter(Boolean);
96358
97111
  }
96359
97112
  function writeSettings(path3, settings) {
96360
- mkdirSync12(dirname16(path3), { recursive: true });
96361
- writeFileSync30(path3, JSON.stringify(settings, null, 2) + "\n", "utf8");
97113
+ mkdirSync12(dirname17(path3), { recursive: true });
97114
+ writeFileSync31(path3, JSON.stringify(settings, null, 2) + "\n", "utf8");
96362
97115
  }
96363
97116
  function ensureClaudeMods(opts) {
96364
97117
  const env2 = opts.env ?? process.env;
96365
97118
  const settingsPath = opts.userSettingsPath ?? userClaudeSettingsPath();
96366
- const modsRoot = join48(opts.installRoot, "claude-mods");
97119
+ const modsRoot = join49(opts.installRoot, "claude-mods");
96367
97120
  if (off(env2)) {
96368
97121
  const settings2 = readSettings(settingsPath);
96369
97122
  if (!settings2) return { kind: "unreadable" };
@@ -96380,14 +97133,14 @@ function ensureClaudeMods(opts) {
96380
97133
  let changed = false;
96381
97134
  const installed = [];
96382
97135
  for (const name2 of names) {
96383
- const src = join48(bundleDir, name2);
96384
- const dest = join48(modsRoot, name2);
97136
+ const src = join49(bundleDir, name2);
97137
+ const dest = join49(modsRoot, name2);
96385
97138
  const hash2 = modHash(src);
96386
- const hashFile = join48(dest, HASH_FILE);
96387
- if (!existsSync39(hashFile) || readFileSync41(hashFile, "utf8") !== hash2) {
97139
+ const hashFile = join49(dest, HASH_FILE);
97140
+ if (!existsSync39(hashFile) || readFileSync42(hashFile, "utf8") !== hash2) {
96388
97141
  rmSync4(dest, { recursive: true, force: true });
96389
97142
  cpSync3(src, dest, { recursive: true, filter: (p) => !isEngineOwned(relative8(src, p)) });
96390
- writeFileSync30(hashFile, hash2, "utf8");
97143
+ writeFileSync31(hashFile, hash2, "utf8");
96391
97144
  changed = true;
96392
97145
  }
96393
97146
  installed.push(dest);
@@ -96808,9 +97561,9 @@ function resolveHookPort(explicit) {
96808
97561
  }
96809
97562
  }
96810
97563
  }
96811
- const settingsPath = join49(ROOT, ".claude", "settings.json");
97564
+ const settingsPath = join50(ROOT, ".claude", "settings.json");
96812
97565
  try {
96813
- if (existsSync40(settingsPath)) return installedHookPort(readFileSync42(settingsPath, "utf8")) ?? 7891;
97566
+ if (existsSync40(settingsPath)) return installedHookPort(readFileSync43(settingsPath, "utf8")) ?? 7891;
96814
97567
  } catch {
96815
97568
  }
96816
97569
  return 7891;
@@ -96836,13 +97589,13 @@ async function cmdInit() {
96836
97589
  console.log("");
96837
97590
  console.log("installing harness hooks...");
96838
97591
  const { existsSync: existsSync41 } = await import("node:fs");
96839
- const { join: join50 } = await import("node:path");
97592
+ const { join: join51 } = await import("node:path");
96840
97593
  try {
96841
97594
  await installClaudeHooks(port);
96842
97595
  } catch (err2) {
96843
97596
  console.warn(` \u26A0\uFE0F Claude hook install failed: ${err2 instanceof Error ? err2.message : err2}`);
96844
97597
  }
96845
- if (existsSync41(join50(ROOT, ".cursor"))) {
97598
+ if (existsSync41(join51(ROOT, ".cursor"))) {
96846
97599
  try {
96847
97600
  await installCursorMcpConfig();
96848
97601
  } catch (err2) {
@@ -98064,7 +98817,7 @@ async function cmdReview() {
98064
98817
  console.log("(review queue empty)");
98065
98818
  return;
98066
98819
  }
98067
- const queue = JSON.parse(readFileSync42(paths.reviewQueue, "utf8"));
98820
+ const queue = JSON.parse(readFileSync43(paths.reviewQueue, "utf8"));
98068
98821
  if (queue.length === 0) {
98069
98822
  console.log("(review queue empty)");
98070
98823
  return;
@@ -98803,7 +99556,7 @@ async function gatherReportData(generatedAt) {
98803
99556
  };
98804
99557
  }
98805
99558
  async function cmdReport(args2) {
98806
- const { mkdirSync: mkdirSync13, writeFileSync: writeFileSync31 } = await import("node:fs");
99559
+ const { mkdirSync: mkdirSync13, writeFileSync: writeFileSync32 } = await import("node:fs");
98807
99560
  const { renderReport: renderReport2 } = await Promise.resolve().then(() => (init_report_render(), report_render_exports));
98808
99561
  const includeFutureVerbs = args2.includes("--future-verbs");
98809
99562
  const now = /* @__PURE__ */ new Date();
@@ -98816,8 +99569,8 @@ async function cmdReport(args2) {
98816
99569
  const outDir = workspacePaths(ROOT).configDir;
98817
99570
  mkdirSync13(outDir, { recursive: true });
98818
99571
  const files = renderReport2(data, { includeFutureVerbs });
98819
- for (const f of files) writeFileSync31(join49(outDir, f.name), f.html, "utf8");
98820
- const indexPath = join49(outDir, "report.html");
99572
+ for (const f of files) writeFileSync32(join50(outDir, f.name), f.html, "utf8");
99573
+ const indexPath = join50(outDir, "report.html");
98821
99574
  console.log(`report \u2192 ${indexPath}`);
98822
99575
  console.log(
98823
99576
  ` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`
@@ -98873,7 +99626,7 @@ function spawnDaemonDetached(options = {}) {
98873
99626
  try {
98874
99627
  const logPath = daemonLogPath();
98875
99628
  try {
98876
- if (statSync13(logPath).size > 5 * 1024 * 1024) renameSync7(logPath, `${logPath}.1`);
99629
+ if (statSync13(logPath).size > 5 * 1024 * 1024) renameSync8(logPath, `${logPath}.1`);
98877
99630
  } catch {
98878
99631
  }
98879
99632
  out2 = openSync3(logPath, "a");
@@ -98942,24 +99695,24 @@ async function cmdInstallHooks(args2) {
98942
99695
  }
98943
99696
  }
98944
99697
  async function installOpencodePlugin(port) {
98945
- const { mkdirSync: mkdirSync13, existsSync: existsSync41, readFileSync: readFileSync43, writeFileSync: writeFileSync31 } = await import("node:fs");
98946
- const { join: join50 } = await import("node:path");
98947
- const dir = join50(ROOT, ".opencode", "plugin");
99698
+ const { mkdirSync: mkdirSync13, existsSync: existsSync41, readFileSync: readFileSync44, writeFileSync: writeFileSync32 } = await import("node:fs");
99699
+ const { join: join51 } = await import("node:path");
99700
+ const dir = join51(ROOT, ".opencode", "plugin");
98948
99701
  if (!existsSync41(dir)) mkdirSync13(dir, { recursive: true });
98949
- const file2 = join50(dir, "inerrata.js");
99702
+ const file2 = join51(dir, "inerrata.js");
98950
99703
  if (existsSync41(file2)) {
98951
- const head2 = readFileSync43(file2, "utf8").split("\n")[0] ?? "";
99704
+ const head2 = readFileSync44(file2, "utf8").split("\n")[0] ?? "";
98952
99705
  if (head2.trim() !== OPENCODE_PLUGIN_MARKER) {
98953
99706
  console.error(`refusing to overwrite ${file2}: not an errata-managed plugin (line 1 lacks the marker)`);
98954
99707
  process.exit(2);
98955
99708
  }
98956
99709
  }
98957
- writeFileSync31(file2, buildOpencodePluginSource(port, { version: DAEMON_VERSION }), "utf8");
98958
- const cfgFile = join50(ROOT, "opencode.json");
99710
+ writeFileSync32(file2, buildOpencodePluginSource(port, { version: DAEMON_VERSION }), "utf8");
99711
+ const cfgFile = join51(ROOT, "opencode.json");
98959
99712
  let cfg = { $schema: "https://opencode.ai/config.json" };
98960
99713
  if (existsSync41(cfgFile)) {
98961
99714
  try {
98962
- cfg = JSON.parse(readFileSync43(cfgFile, "utf8"));
99715
+ cfg = JSON.parse(readFileSync44(cfgFile, "utf8"));
98963
99716
  } catch {
98964
99717
  console.error(`refusing to touch invalid JSON at ${cfgFile} \u2014 fix it and re-run`);
98965
99718
  process.exit(2);
@@ -98970,7 +99723,7 @@ async function installOpencodePlugin(port) {
98970
99723
  if (!hadErrata) {
98971
99724
  const inv = errataMcpInvocation();
98972
99725
  cfg.mcp["errata"] = { type: "local", command: [inv.command, ...inv.args] };
98973
- writeFileSync31(cfgFile, JSON.stringify(cfg, null, 2) + "\n", "utf8");
99726
+ writeFileSync32(cfgFile, JSON.stringify(cfg, null, 2) + "\n", "utf8");
98974
99727
  }
98975
99728
  console.log(`installed OpenCode plugin \u2192 ${file2}`);
98976
99729
  if (hadErrata) console.log(` mcp.errata already present in ${cfgFile} \u2014 left untouched`);
@@ -99005,15 +99758,15 @@ function installClaudeMods() {
99005
99758
  }
99006
99759
  }
99007
99760
  async function installClaudeHooks(port) {
99008
- const { mkdirSync: mkdirSync13, existsSync: existsSync41, readFileSync: readFileSync43, writeFileSync: writeFileSync31 } = await import("node:fs");
99009
- const { join: join50 } = await import("node:path");
99010
- const dir = join50(ROOT, ".claude");
99761
+ const { mkdirSync: mkdirSync13, existsSync: existsSync41, readFileSync: readFileSync44, writeFileSync: writeFileSync32 } = await import("node:fs");
99762
+ const { join: join51 } = await import("node:path");
99763
+ const dir = join51(ROOT, ".claude");
99011
99764
  if (!existsSync41(dir)) mkdirSync13(dir, { recursive: true });
99012
- const file2 = join50(dir, "settings.json");
99765
+ const file2 = join51(dir, "settings.json");
99013
99766
  let settings = {};
99014
99767
  if (existsSync41(file2)) {
99015
99768
  try {
99016
- settings = JSON.parse(readFileSync43(file2, "utf8"));
99769
+ settings = JSON.parse(readFileSync44(file2, "utf8"));
99017
99770
  } catch {
99018
99771
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
99019
99772
  process.exit(2);
@@ -99062,12 +99815,12 @@ async function installClaudeHooks(port) {
99062
99815
  list.push({ hooks: [{ type: "command", command: injectCmd }] });
99063
99816
  }
99064
99817
  removeProjectStatusLine(settings);
99065
- writeFileSync31(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
99818
+ writeFileSync32(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
99066
99819
  console.log(`installed Claude Code hooks \u2192 ${file2}`);
99067
99820
  console.log(` ${ensureStatusLine(port)}`);
99068
99821
  console.log(` ${installClaudeMods().line}`);
99069
99822
  await installClaudeMcpConfig();
99070
- const claudeMd = join50(ROOT, "CLAUDE.md");
99823
+ const claudeMd = join51(ROOT, "CLAUDE.md");
99071
99824
  const recall = writeManagedBlock(claudeMd, { body: RECALL_FIRST_BLOCK });
99072
99825
  if (recall.kind === "collision") {
99073
99826
  console.warn(
@@ -99079,15 +99832,15 @@ async function installClaudeHooks(port) {
99079
99832
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
99080
99833
  }
99081
99834
  async function installClaudeMcpConfig() {
99082
- const { mkdirSync: mkdirSync13, existsSync: existsSync41, readFileSync: readFileSync43, writeFileSync: writeFileSync31 } = await import("node:fs");
99083
- const { join: join50, dirname: dirname17 } = await import("node:path");
99084
- const file2 = join50(ROOT, ".mcp.json");
99085
- const dir = dirname17(file2);
99835
+ const { mkdirSync: mkdirSync13, existsSync: existsSync41, readFileSync: readFileSync44, writeFileSync: writeFileSync32 } = await import("node:fs");
99836
+ const { join: join51, dirname: dirname18 } = await import("node:path");
99837
+ const file2 = join51(ROOT, ".mcp.json");
99838
+ const dir = dirname18(file2);
99086
99839
  if (!existsSync41(dir)) mkdirSync13(dir, { recursive: true });
99087
99840
  let cfg = {};
99088
99841
  if (existsSync41(file2)) {
99089
99842
  try {
99090
- cfg = JSON.parse(readFileSync43(file2, "utf8"));
99843
+ cfg = JSON.parse(readFileSync44(file2, "utf8"));
99091
99844
  } catch {
99092
99845
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
99093
99846
  process.exit(2);
@@ -99095,21 +99848,21 @@ async function installClaudeMcpConfig() {
99095
99848
  }
99096
99849
  cfg.mcpServers ??= {};
99097
99850
  cfg.mcpServers["errata"] = errataMcpInvocation();
99098
- writeFileSync31(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
99851
+ writeFileSync32(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
99099
99852
  console.log(`installed Claude Code MCP server config \u2192 ${file2}`);
99100
99853
  console.log(` Claude Code will spawn \`errata mcp\` on workspace open.`);
99101
99854
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses / show / similar`);
99102
99855
  }
99103
99856
  async function installCursorMcpConfig() {
99104
- const { mkdirSync: mkdirSync13, existsSync: existsSync41, readFileSync: readFileSync43, writeFileSync: writeFileSync31 } = await import("node:fs");
99105
- const { join: join50 } = await import("node:path");
99106
- const dir = join50(ROOT, ".cursor");
99857
+ const { mkdirSync: mkdirSync13, existsSync: existsSync41, readFileSync: readFileSync44, writeFileSync: writeFileSync32 } = await import("node:fs");
99858
+ const { join: join51 } = await import("node:path");
99859
+ const dir = join51(ROOT, ".cursor");
99107
99860
  if (!existsSync41(dir)) mkdirSync13(dir, { recursive: true });
99108
- const file2 = join50(dir, "mcp.json");
99861
+ const file2 = join51(dir, "mcp.json");
99109
99862
  let cfg = {};
99110
99863
  if (existsSync41(file2)) {
99111
99864
  try {
99112
- cfg = JSON.parse(readFileSync43(file2, "utf8"));
99865
+ cfg = JSON.parse(readFileSync44(file2, "utf8"));
99113
99866
  } catch {
99114
99867
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
99115
99868
  process.exit(2);
@@ -99117,7 +99870,7 @@ async function installCursorMcpConfig() {
99117
99870
  }
99118
99871
  cfg.mcpServers ??= {};
99119
99872
  cfg.mcpServers["errata"] = errataMcpInvocation();
99120
- writeFileSync31(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
99873
+ writeFileSync32(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
99121
99874
  console.log(`installed Cursor MCP server config \u2192 ${file2}`);
99122
99875
  console.log(` Cursor will spawn \`errata mcp\` on workspace open.`);
99123
99876
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses`);
@@ -99128,7 +99881,7 @@ async function installCodexHooks(port) {
99128
99881
  const gitRoot = nearestGitRoot(process.cwd());
99129
99882
  const hookRoot = gitRoot && mainCheckoutOfWorktree(gitRoot) ? gitRoot : ROOT;
99130
99883
  const file2 = writeCodexHooks(hookRoot, port);
99131
- const instructions = writeManagedBlock(join49(hookRoot, "AGENTS.md"), {
99884
+ const instructions = writeManagedBlock(join50(hookRoot, "AGENTS.md"), {
99132
99885
  body: AGENTS_POINTER_BODY,
99133
99886
  stable: true
99134
99887
  });
@@ -99574,9 +100327,9 @@ async function cmdDash(args2) {
99574
100327
  await yieldToLoop2();
99575
100328
  try {
99576
100329
  const items = selectDurableMemory(handle2.sharedStore, r.engine.profile, {
99577
- atCommit: readGitRefState(join49(r.root, ".git")).sha ?? void 0
100330
+ atCommit: readGitRefState(join50(r.root, ".git")).sha ?? void 0
99578
100331
  });
99579
- const res = bleedRules(join49(r.root, ".claude", "rules"), items);
100332
+ const res = bleedRules(join50(r.root, ".claude", "rules"), items);
99580
100333
  if (res.written || res.pruned) {
99581
100334
  console.log(
99582
100335
  `[rules:${r.entry.name}] ${res.written} collective principle(s) \u2192 .claude/rules` + (res.pruned ? `, ${res.pruned} pruned` : "")