@inerrata-corporation/errata 2.0.0-dev.77 → 2.0.0-dev.78

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.
Files changed (2) hide show
  1. package/errata.mjs +77 -2
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -21353,6 +21353,16 @@ var init_client = __esm({
21353
21353
  if (q.seed?.length) qs.set("seed", q.seed.join(","));
21354
21354
  if (q.q) qs.set("q", q.q);
21355
21355
  const res = await this.json("GET", `/v2/search?${qs.toString()}`);
21356
+ const { nodes, edges } = this.toCastalia(res);
21357
+ return { nodes, edges, generatedAt: res.generatedAt };
21358
+ }
21359
+ /**
21360
+ * The castalia wire shape → local-mergeable graph. Extracted so the priming fetch
21361
+ * (`getProjectPriors`) lands nodes through the EXACT same mapper as `search`: a
21362
+ * second mapper would be a second convergence-key convention to keep in sync, and
21363
+ * an id-keying drift between the two is a silent no-op, not a crash.
21364
+ */
21365
+ toCastalia(res) {
21356
21366
  const now = Date.now();
21357
21367
  const nodes = res.nodes.map((n) => ({
21358
21368
  id: n.canonicalId,
@@ -21386,7 +21396,25 @@ var init_client = __esm({
21386
21396
  navFailures: 0,
21387
21397
  attrs: { ...e.attrs, source: "cloud" }
21388
21398
  }));
21389
- return { nodes, edges, generatedAt: res.generatedAt };
21399
+ return { nodes, edges };
21400
+ }
21401
+ /**
21402
+ * PJ-hydrate: the one-time seeded prime a fresh clone pulls at attach.
21403
+ *
21404
+ * A clone can already READ its project stratum (query-time federation, PJ-read) —
21405
+ * what it can't do is PRIME, because the passive block builds from the local store
21406
+ * and the remote blend is seeded from local problems a new checkout doesn't have.
21407
+ * This lands the top of the stratum locally so both have something to stand on.
21408
+ *
21409
+ * Membership-gated server-side by the same fail-closed check as ingest: a caller
21410
+ * who couldn't write this project's stratum can't prime from it.
21411
+ */
21412
+ async getProjectPriors(projectId, limit = 50) {
21413
+ const res = await this.json(
21414
+ "GET",
21415
+ `/v2/projects/${encodeURIComponent(projectId)}/priors?limit=${String(limit)}`
21416
+ );
21417
+ return this.toCastalia(res);
21390
21418
  }
21391
21419
  /** Pull cloud-induced skills + the session-bootstrap prime payload. `seed` is the
21392
21420
  * canonical ids of the agent's recent problems — the cloud ranks the skills whose
@@ -43293,7 +43321,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
43293
43321
  }
43294
43322
 
43295
43323
  // src/engine.ts
43296
- var DAEMON_VERSION = true ? "2.0.0-dev.77" : "2.0.0-alpha.0";
43324
+ var DAEMON_VERSION = true ? "2.0.0-dev.78" : "2.0.0-alpha.0";
43297
43325
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
43298
43326
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
43299
43327
  var GIT_OP_MUTE_MS = 4e3;
@@ -45463,6 +45491,39 @@ async function ensureProjectLink(root, profile, client) {
45463
45491
  }
45464
45492
  }
45465
45493
 
45494
+ // src/project-hydrate.ts
45495
+ var DEFAULT_LIMIT = 50;
45496
+ async function hydrateProject(opts) {
45497
+ const { root, profile, store, cloud } = opts;
45498
+ if (!profile.projectId) return { hydrated: false, reason: "no-project" };
45499
+ if (profile.projectHydratedAt) return { hydrated: false, reason: "already-hydrated" };
45500
+ try {
45501
+ const res = await cloud.getProjectPriors(profile.projectId, opts.limit ?? DEFAULT_LIMIT);
45502
+ let landedNodes = 0;
45503
+ for (const n of res.nodes) {
45504
+ if (store.getNode(n.id)) continue;
45505
+ store.mergeNode(n);
45506
+ landedNodes++;
45507
+ }
45508
+ let landedEdges = 0;
45509
+ for (const e of res.edges) {
45510
+ if (!store.getNode(e.from) || !store.getNode(e.to)) continue;
45511
+ if (store.getEdge(e.id)) continue;
45512
+ store.mergeEdge(e);
45513
+ landedEdges++;
45514
+ }
45515
+ profile.projectHydratedAt = Date.now();
45516
+ saveProfile(root, profile);
45517
+ return { hydrated: true, nodes: landedNodes, edges: landedEdges };
45518
+ } catch (err2) {
45519
+ return {
45520
+ hydrated: false,
45521
+ reason: "error",
45522
+ detail: err2 instanceof Error ? err2.message : String(err2)
45523
+ };
45524
+ }
45525
+ }
45526
+
45466
45527
  // src/adopt.ts
45467
45528
  import { existsSync as existsSync22 } from "node:fs";
45468
45529
  import { dirname as dirname8, join as join25 } from "node:path";
@@ -45904,6 +45965,20 @@ async function startMultiDaemon(opts = {}) {
45904
45965
  } else if (out2.reason === "error") {
45905
45966
  console.warn(`[errata] project link failed for ${r.engine.profile.name}: ${out2.detail}`);
45906
45967
  }
45968
+ const hyd = await hydrateProject({
45969
+ root: r.root,
45970
+ profile: r.engine.profile,
45971
+ store: r.engine.store,
45972
+ cloud: client
45973
+ });
45974
+ if (hyd.hydrated) {
45975
+ r.engine.markContextDirty();
45976
+ console.log(
45977
+ `[errata] primed ${r.engine.profile.name} from project \u2014 ${hyd.nodes} priors, ${hyd.edges} links`
45978
+ );
45979
+ } else if (hyd.reason === "error") {
45980
+ console.warn(`[errata] project prime failed for ${r.engine.profile.name}: ${hyd.detail}`);
45981
+ }
45907
45982
  }
45908
45983
  };
45909
45984
  void ambientLinkAll();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.0-dev.77",
3
+ "version": "2.0.0-dev.78",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {