@inerrata-corporation/errata 2.0.0-dev.69 → 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) {
@@ -24141,7 +24174,11 @@ function linkBullet(ref) {
24141
24174
  " queue growth under backpressure)) \u2014 this works with NO code anchor, and two agents naming",
24142
24175
  " the same pattern converge on one node. Cite a shown Pattern by handle: (pattern:[handle]).",
24143
24176
  ` \xB7 (aids:[${ref}],\u2026) \u2014 your FIX could also help these other, even unrelated, problems.`,
24144
- " 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."
24145
24182
  ].join("\n");
24146
24183
  }
24147
24184
  function attemptBullet() {
@@ -41136,6 +41173,9 @@ var CAUSE_PREFIX = /^\s*cause:(?:#([a-z][\w-]{0,63}))?\s*/i;
41136
41173
  var CAUSE_ARROW = /\s+(?:<-|←)\s+/;
41137
41174
  var ATTEMPT_RE = /\(\s*(tried|failed):(?:#([a-z][\w-]{0,63}))?\s*((?:[^()\n]|\([^()\n]*\)){8,}?)\s*\)/gi;
41138
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;
41139
41179
  var INSTANCE_PREFIX = /^\s*instance:\s*/i;
41140
41180
  var PATTERN_RE = /\(\s*pattern:\s*([^()\n]{3,}?)\s*\)/gi;
41141
41181
  var PATTERN_TEXT_MAX = 120;
@@ -41179,10 +41219,13 @@ function parseInlineTags(text) {
41179
41219
  const causeM = fixM ? null : CAUSE_PREFIX.exec(content);
41180
41220
  const aidsM = fixM || causeM ? null : AIDS_PREFIX.exec(content);
41181
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" };
41182
41223
  const isFix = fixM !== null;
41183
41224
  const isCause = causeM !== null;
41184
- if (aidsM || instM) {
41185
- 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, "");
41186
41229
  const handles = [];
41187
41230
  HANDLE_RE.lastIndex = 0;
41188
41231
  let hh;
@@ -41191,11 +41234,13 @@ function parseInlineTags(text) {
41191
41234
  if (handle2 && !handles.includes(handle2)) handles.push(handle2);
41192
41235
  }
41193
41236
  if (handles.length > 0) {
41237
+ const kind = aidsM ? "transfer" : instM ? "instance" : ssM.kind;
41238
+ const threadId = aidsM?.[1] ?? threadMatch?.[1] ?? void 0;
41194
41239
  out2.push({
41195
- kind: aidsM ? "transfer" : "instance",
41240
+ kind,
41196
41241
  handles,
41197
41242
  sentence: sfield,
41198
- ...aidsM?.[1] ? { threadId: aidsM[1] } : {}
41243
+ ...threadId ? { threadId } : {}
41199
41244
  });
41200
41245
  }
41201
41246
  continue;
@@ -41445,7 +41490,7 @@ function harvestInlineTags(store, text, opts) {
41445
41490
  const source = opts.sourceId ? store.getNode(opts.sourceId) : null;
41446
41491
  const mintPriors = opts.mintPriors ?? true;
41447
41492
  const touched = opts.sessionTouchedIds ?? /* @__PURE__ */ new Set();
41448
- 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: [] };
41449
41494
  const tags = parseInlineTags(text);
41450
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);
41451
41496
  const bindSymptom = (seq, threadId) => {
@@ -41530,6 +41575,22 @@ function harvestInlineTags(store, text, opts) {
41530
41575
  ...b.statement ? { boundStatement: b.statement } : {}
41531
41576
  });
41532
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
+ }
41533
41594
  } else if (tag.kind === "instance") {
41534
41595
  const targetIds = [];
41535
41596
  for (const h of tag.handles ?? []) {
@@ -42976,7 +43037,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
42976
43037
  }
42977
43038
 
42978
43039
  // src/engine.ts
42979
- var DAEMON_VERSION = true ? "2.0.0-dev.69" : "2.0.0-alpha.0";
43040
+ var DAEMON_VERSION = true ? "2.0.0-dev.70" : "2.0.0-alpha.0";
42980
43041
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
42981
43042
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
42982
43043
  var GIT_OP_MUTE_MS = 4e3;
@@ -43791,6 +43852,20 @@ function createWorkspaceEngine(opts) {
43791
43852
  mintCiteEdge(solId, targetId, "MAY_RESOLVE", 0.3, { transfer: true, hypothesis: true });
43792
43853
  }
43793
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
+ }
43794
43869
  if (plan.refutes.length > 0 && typeof cloud.reportContradictions === "function") {
43795
43870
  void cloud.reportContradictions({ daemonVersion: DAEMON_VERSION, projectId: profile.id, items: plan.refutes }).then((r) => {
43796
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.69",
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,