@inerrata-corporation/errata 2.0.0-dev.68 → 2.0.0-dev.70

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.
@@ -14,7 +14,7 @@ var __export = (target, all) => {
14
14
  function isCausalProtected(type) {
15
15
  return CAUSAL_PROTECT_EDGES.includes(type);
16
16
  }
17
- var SEMANTIC_NODE_LABELS, 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, TAXONOMY_EDGES, TRIAGE_EDGES, GIT_EDGES, ALL_EDGE_TYPES, EDGE_WEIGHT, CAUSAL_PROTECT_EDGES, VALIDATION_SOURCES, ABSTRACTION_LEVEL, PROBLEM_RESOLUTION, DRIFT_KIND;
17
+ var SEMANTIC_NODE_LABELS, 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, TAXONOMY_EDGES, TRIAGE_EDGES, GIT_EDGES, ALL_EDGE_TYPES, EDGE_WEIGHT, CAUSAL_PROTECT_EDGES, VALIDATION_SOURCES, ABSTRACTION_LEVEL, PROBLEM_RESOLUTION, DRIFT_KIND;
18
18
  var init_castalia = __esm({
19
19
  "../../packages/shared/src/castalia.ts"() {
20
20
  "use strict";
@@ -172,6 +172,7 @@ var init_castalia = __esm({
172
172
  ABSTRACTION_EDGES = ["GENERALIZES", "DERIVED_FROM"];
173
173
  DECOMPOSITION_EDGES = ["SPLIT_INTO"];
174
174
  TRANSFER_EDGES = ["MAY_RESOLVE"];
175
+ SOLUTION_STRUCTURE_EDGES = ["BUILDS_ON", "SUPERSEDES", "ALTERNATIVE_TO"];
175
176
  TAXONOMY_EDGES = ["IS_A"];
176
177
  TRIAGE_EDGES = ["TRIAGED_BY", "CONFIRMS", "INDICATES", "ROUTES_TO"];
177
178
  GIT_EDGES = ["POINTS_AT", "PARENT", "AUTHORED_BY"];
@@ -179,6 +180,7 @@ var init_castalia = __esm({
179
180
  ...STRUCTURAL_EDGES,
180
181
  ...CAUSAL_EDGES,
181
182
  ...RESOLUTION_EDGES,
183
+ ...SOLUTION_STRUCTURE_EDGES,
182
184
  ...CONCEPTUAL_EDGES,
183
185
  ...EVIDENCE_EDGES,
184
186
  ...CODE_EDGES,
@@ -288,6 +290,13 @@ var init_castalia = __esm({
288
290
  // INDICATES: real signal, but a candidate must not form a hub; promotion mints the
289
291
  // full-weight SOLVED_BY/MITIGATES instead of upgrading this edge.
290
292
  MAY_RESOLVE: 0.3,
293
+ // Solution-structure (SS-structure) — witnessed inter-solution relationships.
294
+ // BUILDS_ON flows (extension → base) so a foundational solution accrues centrality,
295
+ // discounted like MAY_RESOLVE so one witness isn't a hub; SUPERSEDES/ALTERNATIVE_TO
296
+ // are weightless gradients (PageRank-excluded below).
297
+ BUILDS_ON: 0.4,
298
+ SUPERSEDES: 0,
299
+ ALTERNATIVE_TO: 0,
291
300
  // Git — topology/authorship, weightless (not domain signal-flow)
292
301
  POINTS_AT: 0,
293
302
  PARENT: 0,
package/errata.mjs CHANGED
@@ -64,7 +64,7 @@ function toCloudAttrs(attrs) {
64
64
  }
65
65
  return rest2;
66
66
  }
67
- var SEMANTIC_NODE_LABELS, 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, 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;
67
+ var SEMANTIC_NODE_LABELS, 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, 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;
68
68
  var init_castalia = __esm({
69
69
  "../../packages/shared/src/castalia.ts"() {
70
70
  "use strict";
@@ -222,6 +222,7 @@ var init_castalia = __esm({
222
222
  ABSTRACTION_EDGES = ["GENERALIZES", "DERIVED_FROM"];
223
223
  DECOMPOSITION_EDGES = ["SPLIT_INTO"];
224
224
  TRANSFER_EDGES = ["MAY_RESOLVE"];
225
+ SOLUTION_STRUCTURE_EDGES = ["BUILDS_ON", "SUPERSEDES", "ALTERNATIVE_TO"];
225
226
  TAXONOMY_EDGES = ["IS_A"];
226
227
  TRIAGE_EDGES = ["TRIAGED_BY", "CONFIRMS", "INDICATES", "ROUTES_TO"];
227
228
  GIT_EDGES = ["POINTS_AT", "PARENT", "AUTHORED_BY"];
@@ -229,6 +230,7 @@ var init_castalia = __esm({
229
230
  ...STRUCTURAL_EDGES,
230
231
  ...CAUSAL_EDGES,
231
232
  ...RESOLUTION_EDGES,
233
+ ...SOLUTION_STRUCTURE_EDGES,
232
234
  ...CONCEPTUAL_EDGES,
233
235
  ...EVIDENCE_EDGES,
234
236
  ...CODE_EDGES,
@@ -273,8 +275,13 @@ var init_castalia = __esm({
273
275
  // git topology, not signal-flow
274
276
  "AUTHORED_BY",
275
277
  // git authorship, not signal-flow
276
- "CONTRIBUTED"
278
+ "CONTRIBUTED",
277
279
  // agent attribution (Agent → knowledge), not signal-flow
280
+ "SUPERSEDES",
281
+ // "prefer newer" gradient between solutions, not signal-flow
282
+ "ALTERNATIVE_TO"
283
+ // symmetric peer marker — weight would let alternatives inflate each other
284
+ // NB: BUILDS_ON is NOT excluded — it flows extension→base so foundational solutions rank up.
278
285
  ]);
279
286
  EDGE_WEIGHT = {
280
287
  // Causal — high signal
@@ -371,6 +378,13 @@ var init_castalia = __esm({
371
378
  // INDICATES: real signal, but a candidate must not form a hub; promotion mints the
372
379
  // full-weight SOLVED_BY/MITIGATES instead of upgrading this edge.
373
380
  MAY_RESOLVE: 0.3,
381
+ // Solution-structure (SS-structure) — witnessed inter-solution relationships.
382
+ // BUILDS_ON flows (extension → base) so a foundational solution accrues centrality,
383
+ // discounted like MAY_RESOLVE so one witness isn't a hub; SUPERSEDES/ALTERNATIVE_TO
384
+ // are weightless gradients (PageRank-excluded below).
385
+ BUILDS_ON: 0.4,
386
+ SUPERSEDES: 0,
387
+ ALTERNATIVE_TO: 0,
374
388
  // Git — topology/authorship, weightless (not domain signal-flow)
375
389
  POINTS_AT: 0,
376
390
  PARENT: 0,
@@ -15628,6 +15642,7 @@ __export(src_exports, {
15628
15642
  RESOLUTION_EDGES: () => RESOLUTION_EDGES,
15629
15643
  RouteContextCountWireSchema: () => RouteContextCountWireSchema,
15630
15644
  SEMANTIC_NODE_LABELS: () => SEMANTIC_NODE_LABELS,
15645
+ SOLUTION_STRUCTURE_EDGES: () => SOLUTION_STRUCTURE_EDGES,
15631
15646
  STRUCTURAL_EDGES: () => STRUCTURAL_EDGES,
15632
15647
  SYMBOL_SUMMARY_KINDS: () => SYMBOL_SUMMARY_KINDS,
15633
15648
  SymbolSummarySidecarSchema: () => SymbolSummarySidecarSchema,
@@ -17857,15 +17872,21 @@ function priorsForFile(store, relPath) {
17857
17872
  related.set(n.id, n);
17858
17873
  }
17859
17874
  }
17875
+ const solutionsByProblem = /* @__PURE__ */ new Map();
17860
17876
  for (const p of openProblems) {
17861
17877
  for (const e of store.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
17862
17878
  const n = store.getNode(e.to);
17863
17879
  if (n && CITABLE_PRIOR_LABELS.has(n.label)) related.set(n.id, n);
17880
+ if (n && n.label === "Solution" && e.type === "SOLVED_BY") {
17881
+ const list = solutionsByProblem.get(p.id) ?? [];
17882
+ if (!list.some((s) => s.id === n.id)) list.push(n);
17883
+ solutionsByProblem.set(p.id, list);
17884
+ }
17864
17885
  }
17865
17886
  }
17866
17887
  if (openProblems.length === 0 && related.size === 0) return null;
17867
17888
  openProblems.sort((a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0));
17868
- return { file: file2, openProblems, related: [...related.values()] };
17889
+ return { file: file2, openProblems, related: [...related.values()], solutionsByProblem };
17869
17890
  }
17870
17891
  function anchorProblemToDiff(store, problemId, changedPaths, workspaceId2, t, opts = {}) {
17871
17892
  return anchorNodeToDiff(store, problemId, "Problem", changedPaths, workspaceId2, t, opts);
@@ -20212,7 +20233,13 @@ function sliceForFile(store, relPath) {
20212
20233
  const fp = priorsForFile(store, relPath);
20213
20234
  const priors = fp ? {
20214
20235
  openProblems: fp.openProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
20215
- related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description }))
20236
+ related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
20237
+ solutionsByProblem: Object.fromEntries(
20238
+ fp.openProblems.slice(0, 3).map((p) => [
20239
+ p.id,
20240
+ (fp.solutionsByProblem.get(p.id) ?? []).slice(0, 4).map((s) => ({ id: s.id, description: s.description }))
20241
+ ])
20242
+ )
20216
20243
  } : void 0;
20217
20244
  return {
20218
20245
  relPath,
@@ -20378,6 +20405,12 @@ function renderSnapshot(s) {
20378
20405
  );
20379
20406
  for (const p of w.priors.openProblems) {
20380
20407
  lines.push(` - ${p.description} \u2192 \`(fix:[${p.id}])\` \xB7 \`([${p.id}])\``);
20408
+ const sols = w.priors.solutionsByProblem[p.id] ?? [];
20409
+ for (const s2 of sols) {
20410
+ lines.push(
20411
+ ` - already solved by: ${s2.description} \u2014 if your fix builds on / supersedes / is an alternative to this, say so: \`(builds-on:[${s2.id}])\` \xB7 \`(supersedes:[${s2.id}])\` \xB7 \`(alternative:[${s2.id}])\``
20412
+ );
20413
+ }
20381
20414
  }
20382
20415
  }
20383
20416
  if (w.priors?.related.length) {
@@ -20914,6 +20947,17 @@ function chunkArray(items, size) {
20914
20947
  for (let i2 = 0; i2 < items.length; i2 += size) out2.push(items.slice(i2, i2 + size));
20915
20948
  return out2;
20916
20949
  }
20950
+ function provenanceHeaders(provenance) {
20951
+ return {
20952
+ ...provenance.clientProduct ? { "x-inerrata-client-product": provenance.clientProduct } : {},
20953
+ ...provenance.clientKind ? { "x-inerrata-client-kind": provenance.clientKind } : {},
20954
+ ...provenance.clientVersion ? { "x-inerrata-client-version": provenance.clientVersion } : {},
20955
+ ...provenance.hostHarness ? { "x-inerrata-host-harness": provenance.hostHarness } : {},
20956
+ ...provenance.runtime ? { "x-inerrata-runtime": provenance.runtime } : {},
20957
+ ...provenance.agentProvider ? { "x-inerrata-agent-provider": provenance.agentProvider } : {},
20958
+ ...provenance.agentModel ? { "x-inerrata-agent-model": provenance.agentModel } : {}
20959
+ };
20960
+ }
20917
20961
  var asWireCount, INGEST_NODE_CHUNK, CloudClient, CloudError;
20918
20962
  var init_client = __esm({
20919
20963
  "../../packages/cloud-client/src/client.ts"() {
@@ -20935,6 +20979,7 @@ var init_client = __esm({
20935
20979
  timeoutMs;
20936
20980
  daemonVersion;
20937
20981
  daemonChannel;
20982
+ provenance;
20938
20983
  constructor(opts) {
20939
20984
  this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
20940
20985
  if (opts.apiKey !== void 0) this.apiKey = opts.apiKey;
@@ -20948,6 +20993,12 @@ var init_client = __esm({
20948
20993
  this.timeoutMs = opts.timeoutMs ?? 15e3;
20949
20994
  this.daemonVersion = opts.daemonVersion;
20950
20995
  this.daemonChannel = opts.daemonChannel;
20996
+ this.provenance = opts.provenance ?? {
20997
+ clientProduct: "inerrata_cloud_client",
20998
+ clientKind: "sdk",
20999
+ runtime: "unknown",
21000
+ hostHarness: "unknown"
21001
+ };
20951
21002
  }
20952
21003
  /**
20953
21004
  * The bearer token to send: a fresh OAuth access token (refreshing it first if
@@ -21323,7 +21374,8 @@ var init_client = __esm({
21323
21374
  const url2 = `${this.baseUrl}${path2}`;
21324
21375
  const headers = {
21325
21376
  "content-type": "application/json",
21326
- accept: "application/json"
21377
+ accept: "application/json",
21378
+ ...provenanceHeaders(this.provenance)
21327
21379
  };
21328
21380
  if (!opts?.skipAuth) {
21329
21381
  const token = await this.authToken();
@@ -21813,6 +21865,21 @@ var init_cloud_endpoint_policy = __esm({
21813
21865
  function oauthClientId() {
21814
21866
  return process.env["ERRATA_OAUTH_CLIENT_ID"] ?? "errata-daemon";
21815
21867
  }
21868
+ function daemonProvenance(env2 = process.env) {
21869
+ const explicit = env2["ERRATA_HOST_HARNESS"];
21870
+ const hostHarness = explicit === "claude_code" || explicit === "codex" || explicit === "cursor" || explicit === "gemini_cli" || explicit === "openclaw" || explicit === "custom" ? explicit : env2["CODEX_HOME"] || env2["CODEX_THREAD_ID"] ? "codex" : env2["CLAUDE_PROJECT_DIR"] || env2["CLAUDECODE"] ? "claude_code" : env2["CURSOR_TRACE_ID"] ? "cursor" : "unknown";
21871
+ const provider = env2["ERRATA_AGENT_PROVIDER"];
21872
+ const agentProvider = provider === "anthropic" || provider === "openai" || provider === "google" || provider === "aws" || provider === "azure" || provider === "custom" ? provider : "unknown";
21873
+ return {
21874
+ clientProduct: "inerrata_daemon",
21875
+ clientKind: "sidecar",
21876
+ clientVersion: "2.0.0-alpha.0",
21877
+ hostHarness,
21878
+ runtime: "local",
21879
+ agentProvider,
21880
+ ...env2["ERRATA_AGENT_MODEL"] ? { agentModel: env2["ERRATA_AGENT_MODEL"] } : {}
21881
+ };
21882
+ }
21816
21883
  function authedCloudClient(cfg, opts = {}) {
21817
21884
  const baseUrl = normalizeCloudUrl(cfg.cloudUrl);
21818
21885
  const fetchFn = guardCloudFetch(baseUrl, opts.fetchFn ?? fetch);
@@ -21825,6 +21892,7 @@ function authedCloudClient(cfg, opts = {}) {
21825
21892
  ...cfg.refreshToken ? { refreshToken: cfg.refreshToken } : {},
21826
21893
  fetchFn,
21827
21894
  ...versionOpts,
21895
+ provenance: daemonProvenance(),
21828
21896
  ...opts.timeoutMs ? { timeoutMs: opts.timeoutMs } : {},
21829
21897
  ...tokenEndpoint && cfg.refreshToken ? {
21830
21898
  refreshFn: (rt) => refreshAccessToken(opts.fetchFn ?? fetch, tokenEndpoint, {
@@ -21846,6 +21914,7 @@ function authedCloudClient(cfg, opts = {}) {
21846
21914
  ...cfg.apiKey ? { apiKey: cfg.apiKey } : {},
21847
21915
  fetchFn,
21848
21916
  ...versionOpts,
21917
+ provenance: daemonProvenance(),
21849
21918
  ...opts.timeoutMs ? { timeoutMs: opts.timeoutMs } : {}
21850
21919
  });
21851
21920
  }
@@ -24105,7 +24174,11 @@ function linkBullet(ref) {
24105
24174
  " queue growth under backpressure)) \u2014 this works with NO code anchor, and two agents naming",
24106
24175
  " the same pattern converge on one node. Cite a shown Pattern by handle: (pattern:[handle]).",
24107
24176
  ` \xB7 (aids:[${ref}],\u2026) \u2014 your FIX could also help these other, even unrelated, problems.`,
24108
- " A hypothesis, not a claim \u2014 it's recorded as may-resolve and checked by whoever tries it."
24177
+ " A hypothesis, not a claim \u2014 it's recorded as may-resolve and checked by whoever tries it.",
24178
+ ` \xB7 when your fix relates to a PRIOR solution you were primed with (a problem's`,
24179
+ ` "already solved by" line): (builds-on:[${ref}]) if you layer on it \xB7 (supersedes:[${ref}])`,
24180
+ " if you replace it \xB7 (alternative:[h]) if it's a different approach \u2014 pick one. This is the",
24181
+ " only way N solutions on one problem stop being a flat bag."
24109
24182
  ].join("\n");
24110
24183
  }
24111
24184
  function attemptBullet() {
@@ -41100,6 +41173,9 @@ var CAUSE_PREFIX = /^\s*cause:(?:#([a-z][\w-]{0,63}))?\s*/i;
41100
41173
  var CAUSE_ARROW = /\s+(?:<-|←)\s+/;
41101
41174
  var ATTEMPT_RE = /\(\s*(tried|failed):(?:#([a-z][\w-]{0,63}))?\s*((?:[^()\n]|\([^()\n]*\)){8,}?)\s*\)/gi;
41102
41175
  var AIDS_PREFIX = /^\s*aids:(?:#([a-z][\w-]{0,63}))?\s*/i;
41176
+ var BUILDS_ON_PREFIX = /^\s*builds-on:(?:#([a-z][\w-]{0,63}))?\s*/i;
41177
+ var SUPERSEDES_PREFIX = /^\s*supersedes:(?:#([a-z][\w-]{0,63}))?\s*/i;
41178
+ var ALTERNATIVE_PREFIX = /^\s*alternative:(?:#([a-z][\w-]{0,63}))?\s*/i;
41103
41179
  var INSTANCE_PREFIX = /^\s*instance:\s*/i;
41104
41180
  var PATTERN_RE = /\(\s*pattern:\s*([^()\n]{3,}?)\s*\)/gi;
41105
41181
  var PATTERN_TEXT_MAX = 120;
@@ -41143,10 +41219,13 @@ function parseInlineTags(text) {
41143
41219
  const causeM = fixM ? null : CAUSE_PREFIX.exec(content);
41144
41220
  const aidsM = fixM || causeM ? null : AIDS_PREFIX.exec(content);
41145
41221
  const instM = fixM || causeM || aidsM ? null : INSTANCE_PREFIX.exec(content);
41222
+ const ssM = fixM || causeM || aidsM || instM ? null : BUILDS_ON_PREFIX.exec(content) && { re: BUILDS_ON_PREFIX, kind: "builds-on" } || SUPERSEDES_PREFIX.exec(content) && { re: SUPERSEDES_PREFIX, kind: "supersedes" } || ALTERNATIVE_PREFIX.exec(content) && { re: ALTERNATIVE_PREFIX, kind: "alternative" };
41146
41223
  const isFix = fixM !== null;
41147
41224
  const isCause = causeM !== null;
41148
- if (aidsM || instM) {
41149
- const body2 = content.replace(aidsM ? AIDS_PREFIX : INSTANCE_PREFIX, "");
41225
+ if (aidsM || instM || ssM) {
41226
+ const re = aidsM ? AIDS_PREFIX : instM ? INSTANCE_PREFIX : ssM.re;
41227
+ const threadMatch = ssM ? ssM.re.exec(content) : null;
41228
+ const body2 = content.replace(re, "");
41150
41229
  const handles = [];
41151
41230
  HANDLE_RE.lastIndex = 0;
41152
41231
  let hh;
@@ -41155,11 +41234,13 @@ function parseInlineTags(text) {
41155
41234
  if (handle2 && !handles.includes(handle2)) handles.push(handle2);
41156
41235
  }
41157
41236
  if (handles.length > 0) {
41237
+ const kind = aidsM ? "transfer" : instM ? "instance" : ssM.kind;
41238
+ const threadId = aidsM?.[1] ?? threadMatch?.[1] ?? void 0;
41158
41239
  out2.push({
41159
- kind: aidsM ? "transfer" : "instance",
41240
+ kind,
41160
41241
  handles,
41161
41242
  sentence: sfield,
41162
- ...aidsM?.[1] ? { threadId: aidsM[1] } : {}
41243
+ ...threadId ? { threadId } : {}
41163
41244
  });
41164
41245
  }
41165
41246
  continue;
@@ -41409,7 +41490,7 @@ function harvestInlineTags(store, text, opts) {
41409
41490
  const source = opts.sourceId ? store.getNode(opts.sourceId) : null;
41410
41491
  const mintPriors = opts.mintPriors ?? true;
41411
41492
  const touched = opts.sessionTouchedIds ?? /* @__PURE__ */ new Set();
41412
- const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], instances: [], patterns: [], refutes: [], corroborations: [] };
41493
+ const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], refutes: [], corroborations: [] };
41413
41494
  const tags = parseInlineTags(text);
41414
41495
  const symptomSeqs = tags.filter((t) => (t.kind === "problem" || t.kind === "todo" || t.kind === "constraint") && t.statement).map((t) => ({ seq: t.seq ?? -1, statement: t.statement, threadId: t.threadId })).sort((a, b) => a.seq - b.seq);
41415
41496
  const bindSymptom = (seq, threadId) => {
@@ -41494,6 +41575,22 @@ function harvestInlineTags(store, text, opts) {
41494
41575
  ...b.statement ? { boundStatement: b.statement } : {}
41495
41576
  });
41496
41577
  }
41578
+ } else if (tag.kind === "builds-on" || tag.kind === "supersedes" || tag.kind === "alternative") {
41579
+ const targetIds = [];
41580
+ for (const h of tag.handles ?? []) {
41581
+ const id = resolveHandle(store, h, opts.handleMap);
41582
+ if (id && !targetIds.includes(id)) targetIds.push(id);
41583
+ }
41584
+ if (targetIds.length > 0) {
41585
+ const b = bindSymptom(tag.seq, tag.threadId);
41586
+ const relation = tag.kind === "builds-on" ? "BUILDS_ON" : tag.kind === "supersedes" ? "SUPERSEDES" : "ALTERNATIVE_TO";
41587
+ plan.solutionLinks.push({
41588
+ relation,
41589
+ targetIds,
41590
+ ...tag.threadId ? { threadId: tag.threadId } : {},
41591
+ ...b.statement ? { boundStatement: b.statement } : {}
41592
+ });
41593
+ }
41497
41594
  } else if (tag.kind === "instance") {
41498
41595
  const targetIds = [];
41499
41596
  for (const h of tag.handles ?? []) {
@@ -42940,7 +43037,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
42940
43037
  }
42941
43038
 
42942
43039
  // src/engine.ts
42943
- var DAEMON_VERSION = true ? "2.0.0-dev.68" : "2.0.0-alpha.0";
43040
+ var DAEMON_VERSION = true ? "2.0.0-dev.70" : "2.0.0-alpha.0";
42944
43041
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
42945
43042
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
42946
43043
  var GIT_OP_MUTE_MS = 4e3;
@@ -43755,6 +43852,20 @@ function createWorkspaceEngine(opts) {
43755
43852
  mintCiteEdge(solId, targetId, "MAY_RESOLVE", 0.3, { transfer: true, hypothesis: true });
43756
43853
  }
43757
43854
  }
43855
+ for (const sl of plan.solutionLinks) {
43856
+ const pid = bindPid(sl);
43857
+ const solId = pid ? solutionForProblem(store, pid) : void 0;
43858
+ if (!solId) {
43859
+ console.warn(`[errata] ${sl.relation} tag dropped: no solution in scope \u2014 state it beside its (fix:\u2026)`);
43860
+ continue;
43861
+ }
43862
+ for (const targetId of sl.targetIds) {
43863
+ if (targetId === solId) continue;
43864
+ const target = store.getNode(targetId);
43865
+ if (!target || target.label !== "Solution") continue;
43866
+ mintCiteEdge(solId, targetId, sl.relation, 0.4, { solutionStructure: true, hypothesis: true });
43867
+ }
43868
+ }
43758
43869
  if (plan.refutes.length > 0 && typeof cloud.reportContradictions === "function") {
43759
43870
  void cloud.reportContradictions({ daemonVersion: DAEMON_VERSION, projectId: profile.id, items: plan.refutes }).then((r) => {
43760
43871
  if (r?.recorded) console.log(`[errata] refute: ${r.recorded} contradiction(s) recorded`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.0-dev.68",
3
+ "version": "2.0.0-dev.70",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -25,7 +25,7 @@ function isTestRelPath(relPath) {
25
25
  function isTestScopeQname(qname) {
26
26
  return TEST_SCOPE_RE.test(qname);
27
27
  }
28
- var SEMANTIC_NODE_LABELS, 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, TAXONOMY_EDGES, TRIAGE_EDGES, GIT_EDGES, ALL_EDGE_TYPES, PAGERANK_EXCLUSIONS, EDGE_WEIGHT, CAUSAL_PROTECT_EDGES, VALIDATION_SOURCES, PROBLEM_RESOLUTION, DRIFT_KIND, TEST_PATH_RE, TEST_SCOPE_RE;
28
+ var SEMANTIC_NODE_LABELS, 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, TAXONOMY_EDGES, TRIAGE_EDGES, GIT_EDGES, ALL_EDGE_TYPES, PAGERANK_EXCLUSIONS, EDGE_WEIGHT, CAUSAL_PROTECT_EDGES, VALIDATION_SOURCES, PROBLEM_RESOLUTION, DRIFT_KIND, TEST_PATH_RE, TEST_SCOPE_RE;
29
29
  var init_castalia = __esm({
30
30
  "../../packages/shared/src/castalia.ts"() {
31
31
  "use strict";
@@ -183,6 +183,7 @@ var init_castalia = __esm({
183
183
  ABSTRACTION_EDGES = ["GENERALIZES", "DERIVED_FROM"];
184
184
  DECOMPOSITION_EDGES = ["SPLIT_INTO"];
185
185
  TRANSFER_EDGES = ["MAY_RESOLVE"];
186
+ SOLUTION_STRUCTURE_EDGES = ["BUILDS_ON", "SUPERSEDES", "ALTERNATIVE_TO"];
186
187
  TAXONOMY_EDGES = ["IS_A"];
187
188
  TRIAGE_EDGES = ["TRIAGED_BY", "CONFIRMS", "INDICATES", "ROUTES_TO"];
188
189
  GIT_EDGES = ["POINTS_AT", "PARENT", "AUTHORED_BY"];
@@ -190,6 +191,7 @@ var init_castalia = __esm({
190
191
  ...STRUCTURAL_EDGES,
191
192
  ...CAUSAL_EDGES,
192
193
  ...RESOLUTION_EDGES,
194
+ ...SOLUTION_STRUCTURE_EDGES,
193
195
  ...CONCEPTUAL_EDGES,
194
196
  ...EVIDENCE_EDGES,
195
197
  ...CODE_EDGES,
@@ -234,8 +236,13 @@ var init_castalia = __esm({
234
236
  // git topology, not signal-flow
235
237
  "AUTHORED_BY",
236
238
  // git authorship, not signal-flow
237
- "CONTRIBUTED"
239
+ "CONTRIBUTED",
238
240
  // agent attribution (Agent → knowledge), not signal-flow
241
+ "SUPERSEDES",
242
+ // "prefer newer" gradient between solutions, not signal-flow
243
+ "ALTERNATIVE_TO"
244
+ // symmetric peer marker — weight would let alternatives inflate each other
245
+ // NB: BUILDS_ON is NOT excluded — it flows extension→base so foundational solutions rank up.
239
246
  ]);
240
247
  EDGE_WEIGHT = {
241
248
  // Causal — high signal
@@ -332,6 +339,13 @@ var init_castalia = __esm({
332
339
  // INDICATES: real signal, but a candidate must not form a hub; promotion mints the
333
340
  // full-weight SOLVED_BY/MITIGATES instead of upgrading this edge.
334
341
  MAY_RESOLVE: 0.3,
342
+ // Solution-structure (SS-structure) — witnessed inter-solution relationships.
343
+ // BUILDS_ON flows (extension → base) so a foundational solution accrues centrality,
344
+ // discounted like MAY_RESOLVE so one witness isn't a hub; SUPERSEDES/ALTERNATIVE_TO
345
+ // are weightless gradients (PageRank-excluded below).
346
+ BUILDS_ON: 0.4,
347
+ SUPERSEDES: 0,
348
+ ALTERNATIVE_TO: 0,
335
349
  // Git — topology/authorship, weightless (not domain signal-flow)
336
350
  POINTS_AT: 0,
337
351
  PARENT: 0,
@@ -24960,15 +24974,21 @@ function priorsForFile(store2, relPath) {
24960
24974
  related.set(n.id, n);
24961
24975
  }
24962
24976
  }
24977
+ const solutionsByProblem = /* @__PURE__ */ new Map();
24963
24978
  for (const p of openProblems) {
24964
24979
  for (const e of store2.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
24965
24980
  const n = store2.getNode(e.to);
24966
24981
  if (n && CITABLE_PRIOR_LABELS.has(n.label)) related.set(n.id, n);
24982
+ if (n && n.label === "Solution" && e.type === "SOLVED_BY") {
24983
+ const list = solutionsByProblem.get(p.id) ?? [];
24984
+ if (!list.some((s) => s.id === n.id)) list.push(n);
24985
+ solutionsByProblem.set(p.id, list);
24986
+ }
24967
24987
  }
24968
24988
  }
24969
24989
  if (openProblems.length === 0 && related.size === 0) return null;
24970
24990
  openProblems.sort((a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0));
24971
- return { file: file2, openProblems, related: [...related.values()] };
24991
+ return { file: file2, openProblems, related: [...related.values()], solutionsByProblem };
24972
24992
  }
24973
24993
  function resolveDesignProblems(store2, t) {
24974
24994
  let resolved = 0;
@@ -25819,7 +25839,13 @@ function sliceForFile(store2, relPath) {
25819
25839
  const fp = priorsForFile(store2, relPath);
25820
25840
  const priors = fp ? {
25821
25841
  openProblems: fp.openProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
25822
- related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description }))
25842
+ related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
25843
+ solutionsByProblem: Object.fromEntries(
25844
+ fp.openProblems.slice(0, 3).map((p) => [
25845
+ p.id,
25846
+ (fp.solutionsByProblem.get(p.id) ?? []).slice(0, 4).map((s) => ({ id: s.id, description: s.description }))
25847
+ ])
25848
+ )
25823
25849
  } : void 0;
25824
25850
  return {
25825
25851
  relPath,