@cleocode/core 2026.4.58 → 2026.4.60

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 (32) hide show
  1. package/dist/index.js +488 -11
  2. package/dist/index.js.map +3 -3
  3. package/dist/internal.js +497 -13
  4. package/dist/internal.js.map +3 -3
  5. package/dist/memory/graph-queries.d.ts.map +1 -1
  6. package/dist/store/agent-registry-accessor.d.ts +1 -1
  7. package/dist/store/brain-accessor.d.ts +57 -1
  8. package/dist/store/brain-accessor.d.ts.map +1 -1
  9. package/dist/store/brain-schema.d.ts +738 -0
  10. package/dist/store/brain-schema.d.ts.map +1 -1
  11. package/dist/store/brain-sqlite.d.ts.map +1 -1
  12. package/dist/store/nexus-schema.d.ts +1 -1
  13. package/dist/system/health.d.ts.map +1 -1
  14. package/dist/validation/doctor/checks.d.ts +7 -0
  15. package/dist/validation/doctor/checks.d.ts.map +1 -1
  16. package/migrations/drizzle-brain/20260416000001_t673-retrieval-log-plasticity-columns/migration.sql +57 -0
  17. package/migrations/drizzle-brain/20260416000002_t673-plasticity-events-expand/migration.sql +44 -0
  18. package/migrations/drizzle-brain/20260416000003_t673-page-edges-plasticity-columns/migration.sql +44 -0
  19. package/migrations/drizzle-brain/20260416000004_t673-new-plasticity-tables/migration.sql +73 -0
  20. package/package.json +8 -8
  21. package/src/memory/__tests__/brain-retrieval-m1.test.ts +250 -0
  22. package/src/memory/__tests__/brain-schema-m2-m3.test.ts +418 -0
  23. package/src/memory/__tests__/brain-schema-m4.test.ts +494 -0
  24. package/src/memory/brain-retrieval.ts +1 -1
  25. package/src/memory/graph-queries.ts +14 -0
  26. package/src/store/agent-registry-accessor.ts +1 -1
  27. package/src/store/brain-accessor.ts +120 -0
  28. package/src/store/brain-schema.ts +373 -1
  29. package/src/store/brain-sqlite.ts +123 -0
  30. package/src/system/health.ts +4 -1
  31. package/src/validation/doctor/checks.ts +107 -0
  32. package/src/validation/protocols/protocols-markdown/research.md +1 -1
package/dist/internal.js CHANGED
@@ -4,10 +4,16 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __getProtoOf = Object.getPrototypeOf;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b2) => (typeof require !== "undefined" ? require : a)[b2]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
7
13
  var __esm = (fn2, res) => function __init() {
8
14
  return fn2 && (res = (0, fn2[__getOwnPropNames(fn2)[0]])(fn2 = 0)), res;
9
15
  };
10
- var __commonJS = (cb, mod) => function __require() {
16
+ var __commonJS = (cb, mod) => function __require2() {
11
17
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
12
18
  };
13
19
  var __export = (target, all) => {
@@ -9062,9 +9068,11 @@ __export(brain_schema_exports, {
9062
9068
  BRAIN_STICKY_COLORS: () => BRAIN_STICKY_COLORS,
9063
9069
  BRAIN_STICKY_PRIORITIES: () => BRAIN_STICKY_PRIORITIES,
9064
9070
  BRAIN_STICKY_STATUSES: () => BRAIN_STICKY_STATUSES,
9071
+ brainConsolidationEvents: () => brainConsolidationEvents,
9065
9072
  brainDecisions: () => brainDecisions,
9066
9073
  brainLearnings: () => brainLearnings,
9067
9074
  brainMemoryLinks: () => brainMemoryLinks,
9075
+ brainModulators: () => brainModulators,
9068
9076
  brainObservations: () => brainObservations,
9069
9077
  brainPageEdges: () => brainPageEdges,
9070
9078
  brainPageNodes: () => brainPageNodes,
@@ -9072,10 +9080,11 @@ __export(brain_schema_exports, {
9072
9080
  brainPlasticityEvents: () => brainPlasticityEvents,
9073
9081
  brainRetrievalLog: () => brainRetrievalLog,
9074
9082
  brainSchemaMeta: () => brainSchemaMeta,
9075
- brainStickyNotes: () => brainStickyNotes
9083
+ brainStickyNotes: () => brainStickyNotes,
9084
+ brainWeightHistory: () => brainWeightHistory
9076
9085
  });
9077
9086
  import { sql as sql2 } from "drizzle-orm";
9078
- var BRAIN_MEMORY_TIERS, BRAIN_COGNITIVE_TYPES, BRAIN_SOURCE_CONFIDENCE, BRAIN_DECISION_TYPES, BRAIN_CONFIDENCE_LEVELS, BRAIN_OUTCOME_TYPES, BRAIN_PATTERN_TYPES, BRAIN_IMPACT_LEVELS, BRAIN_LINK_TYPES, BRAIN_OBSERVATION_TYPES2, BRAIN_OBSERVATION_SOURCE_TYPES, BRAIN_MEMORY_TYPES, BRAIN_STICKY_STATUSES, BRAIN_STICKY_COLORS, BRAIN_STICKY_PRIORITIES, brainDecisions, brainPatterns, brainLearnings, brainObservations, brainStickyNotes, brainMemoryLinks, brainSchemaMeta, BRAIN_NODE_TYPES, BRAIN_EDGE_TYPES, brainPageNodes, brainPageEdges, brainRetrievalLog, brainPlasticityEvents;
9087
+ var BRAIN_MEMORY_TIERS, BRAIN_COGNITIVE_TYPES, BRAIN_SOURCE_CONFIDENCE, BRAIN_DECISION_TYPES, BRAIN_CONFIDENCE_LEVELS, BRAIN_OUTCOME_TYPES, BRAIN_PATTERN_TYPES, BRAIN_IMPACT_LEVELS, BRAIN_LINK_TYPES, BRAIN_OBSERVATION_TYPES2, BRAIN_OBSERVATION_SOURCE_TYPES, BRAIN_MEMORY_TYPES, BRAIN_STICKY_STATUSES, BRAIN_STICKY_COLORS, BRAIN_STICKY_PRIORITIES, brainDecisions, brainPatterns, brainLearnings, brainObservations, brainStickyNotes, brainMemoryLinks, brainSchemaMeta, BRAIN_NODE_TYPES, BRAIN_EDGE_TYPES, brainPageNodes, brainPageEdges, brainRetrievalLog, brainPlasticityEvents, brainWeightHistory, brainModulators, brainConsolidationEvents;
9079
9088
  var init_brain_schema = __esm({
9080
9089
  "packages/core/src/store/brain-schema.ts"() {
9081
9090
  "use strict";
@@ -9581,13 +9590,75 @@ var init_brain_schema = __esm({
9581
9590
  * 'auto:contradiction-detected' | 'auto:consolidation' | 'manual'
9582
9591
  */
9583
9592
  provenance: text("provenance"),
9584
- createdAt: text("created_at").notNull().default(sql2`(datetime('now'))`)
9593
+ createdAt: text("created_at").notNull().default(sql2`(datetime('now'))`),
9594
+ // === T673-M3: Plasticity tracking columns ===
9595
+ /**
9596
+ * ISO 8601 timestamp of the last LTP event applied to this edge.
9597
+ * Used by the decay pass: edges with (now - last_reinforced_at) > decay_threshold_days
9598
+ * receive a per-day weight decay. Null = never reinforced (structural/semantic edges).
9599
+ * Only populated when plasticity_class IN ('hebbian', 'stdp').
9600
+ *
9601
+ * @task T706
9602
+ */
9603
+ lastReinforcedAt: text("last_reinforced_at"),
9604
+ /**
9605
+ * Count of LTP (potentiation) events applied to this edge lifetime.
9606
+ * Incremented on every LTP write. Used to compute stability_score.
9607
+ *
9608
+ * @task T706
9609
+ */
9610
+ reinforcementCount: integer("reinforcement_count").notNull().default(0),
9611
+ /**
9612
+ * Plasticity class governing which algorithm(s) write to this edge.
9613
+ *
9614
+ * - 'static': Non-plastic edge (structural, semantic, etc.). Immune to decay.
9615
+ * - 'hebbian': Written by strengthenCoRetrievedEdges. Subject to decay.
9616
+ * - 'stdp': Written or refined by applyStdpPlasticity. Subject to decay + LTD.
9617
+ *
9618
+ * Edges start 'static' for all non-co_retrieved types.
9619
+ * co_retrieved edges start 'hebbian' (seeded by M3 migration), can upgrade to 'stdp'.
9620
+ *
9621
+ * @task T706
9622
+ */
9623
+ plasticityClass: text("plasticity_class", {
9624
+ enum: ["static", "hebbian", "stdp"]
9625
+ }).notNull().default("static"),
9626
+ /**
9627
+ * ISO 8601 timestamp of the last LTD (depression) event on this edge.
9628
+ * Null = never depressed. Used for debugging and Studio viz animation.
9629
+ *
9630
+ * @task T706
9631
+ */
9632
+ lastDepressedAt: text("last_depressed_at"),
9633
+ /**
9634
+ * Count of LTD (depression) events applied to this edge lifetime.
9635
+ * Enables analysis of edges that are persistently weakened.
9636
+ *
9637
+ * @task T706
9638
+ */
9639
+ depressionCount: integer("depression_count").notNull().default(0),
9640
+ /**
9641
+ * Biological-analog stability score: 0.0 (unstable) – 1.0 (consolidated).
9642
+ *
9643
+ * Computed by runConsolidation decay pass as:
9644
+ * stability = tanh(reinforcement_count / 10) × exp(-(days_since_reinforced / 30))
9645
+ *
9646
+ * Null = not yet computed (new edges). Enables fast filtering in decay pass:
9647
+ * edges with stability > 0.9 skip the full decay recalculation.
9648
+ * Updated at session-end consolidation, NOT per-event.
9649
+ *
9650
+ * @task T706
9651
+ */
9652
+ stabilityScore: real("stability_score")
9585
9653
  },
9586
9654
  (table) => [
9587
9655
  primaryKey({ columns: [table.fromId, table.toId, table.edgeType] }),
9588
9656
  index("idx_brain_edges_from").on(table.fromId),
9589
9657
  index("idx_brain_edges_to").on(table.toId),
9590
- index("idx_brain_edges_type").on(table.edgeType)
9658
+ index("idx_brain_edges_type").on(table.edgeType),
9659
+ index("idx_brain_edges_last_reinforced").on(table.lastReinforcedAt),
9660
+ index("idx_brain_edges_plasticity_class").on(table.plasticityClass),
9661
+ index("idx_brain_edges_stability").on(table.stabilityScore)
9591
9662
  ]
9592
9663
  );
9593
9664
  brainRetrievalLog = sqliteTable(
@@ -9596,7 +9667,12 @@ var init_brain_schema = __esm({
9596
9667
  id: integer("id").primaryKey({ autoIncrement: true }),
9597
9668
  /** The search query or fetch IDs that triggered this retrieval. */
9598
9669
  query: text("query").notNull(),
9599
- /** Comma-separated list of entry IDs returned in this retrieval. */
9670
+ /**
9671
+ * JSON array of entry IDs returned in this retrieval.
9672
+ * Stored as JSON array string: '["obs:A","obs:B"]'.
9673
+ * Always write with JSON.stringify() — NEVER join(',').
9674
+ * Readers call JSON.parse(). Migration M1 converts any pre-existing CSV rows.
9675
+ */
9600
9676
  entryIds: text("entry_ids").notNull(),
9601
9677
  /** Number of entries returned. */
9602
9678
  entryCount: integer("entry_count").notNull(),
@@ -9606,12 +9682,26 @@ var init_brain_schema = __esm({
9606
9682
  tokensUsed: integer("tokens_used"),
9607
9683
  /** Session ID (soft FK to tasks.db sessions). Enables grouping retrievals by session for STDP analysis. */
9608
9684
  sessionId: text("session_id"),
9609
- createdAt: text("created_at").notNull().default(sql2`(datetime('now'))`)
9685
+ createdAt: text("created_at").notNull().default(sql2`(datetime('now'))`),
9686
+ // === T673-M1: STDP plasticity columns ===
9687
+ /** Sequence position of this retrieval within a batch query (0-based). */
9688
+ retrievalOrder: integer("retrieval_order"),
9689
+ /** Wall-clock ms since the previous retrieval row in the same batch. */
9690
+ deltaMs: integer("delta_ms"),
9691
+ /**
9692
+ * R-STDP reward signal: scalar [-1.0, +1.0], null = unlabeled.
9693
+ * Populated by backfillRewardSignals() at session end (Step 9a).
9694
+ * +1.0 = task verified and passed | +0.5 = done (unverified) | -0.5 = cancelled.
9695
+ * Per D-BRAIN-VIZ-13. backfillRewardSignals MUST skip rows where
9696
+ * session_id LIKE 'ses_backfill_%' (synthetic historical sessions, no task correlation).
9697
+ */
9698
+ rewardSignal: real("reward_signal")
9610
9699
  },
9611
9700
  (table) => [
9612
9701
  index("idx_retrieval_log_created").on(table.createdAt),
9613
9702
  index("idx_retrieval_log_source").on(table.source),
9614
- index("idx_retrieval_log_session").on(table.sessionId)
9703
+ index("idx_retrieval_log_session").on(table.sessionId),
9704
+ index("idx_retrieval_log_reward").on(table.rewardSignal)
9615
9705
  ]
9616
9706
  );
9617
9707
  brainPlasticityEvents = sqliteTable(
@@ -9635,14 +9725,182 @@ var init_brain_schema = __esm({
9635
9725
  /** ISO 8601 timestamp when this event was applied. */
9636
9726
  timestamp: text("timestamp").notNull().default(sql2`(datetime('now'))`),
9637
9727
  /** Session ID that triggered the STDP pass, if available. */
9638
- sessionId: text("session_id")
9728
+ sessionId: text("session_id"),
9729
+ // === T673-M2: Observability columns ===
9730
+ /**
9731
+ * Edge weight immediately BEFORE this plasticity event was applied.
9732
+ * Null on the first LTP event that inserts a new edge (edge didn't exist).
9733
+ * Enables "show learning history" in Studio viz without querying brain_weight_history.
9734
+ *
9735
+ * @task T696
9736
+ */
9737
+ weightBefore: real("weight_before"),
9738
+ /**
9739
+ * Edge weight immediately AFTER this plasticity event was applied.
9740
+ * Computed as CLAMP(weight_before + delta_w, 0.0, 1.0).
9741
+ * Redundant with delta_w but enables fast before/after display without arithmetic.
9742
+ *
9743
+ * @task T696
9744
+ */
9745
+ weightAfter: real("weight_after"),
9746
+ /**
9747
+ * Soft FK to brain_retrieval_log.id — the retrieval row that triggered this pair.
9748
+ * Null for externally-triggered or legacy events.
9749
+ * Enables: "which memory retrieval caused this edge to strengthen?"
9750
+ *
9751
+ * @task T696
9752
+ */
9753
+ retrievalLogId: integer("retrieval_log_id"),
9754
+ /**
9755
+ * R-STDP reward signal active when this event fired.
9756
+ * Copied from the retrieval_log row's reward_signal at time of plasticity pass.
9757
+ * Null = unmodulated. Denormalized for fast filtering without a JOIN.
9758
+ *
9759
+ * @task T696
9760
+ */
9761
+ rewardSignal: real("reward_signal"),
9762
+ /**
9763
+ * Wall-clock milliseconds between the two spikes that generated this event.
9764
+ * Pre-computed at INSERT time — avoids re-deriving from retrieval timestamps.
9765
+ * Enables analysis of STDP window distribution.
9766
+ *
9767
+ * @task T696
9768
+ */
9769
+ deltaTMs: integer("delta_t_ms")
9639
9770
  },
9640
9771
  (table) => [
9641
9772
  index("idx_plasticity_source").on(table.sourceNode),
9642
9773
  index("idx_plasticity_target").on(table.targetNode),
9643
9774
  index("idx_plasticity_timestamp").on(table.timestamp),
9644
9775
  index("idx_plasticity_session").on(table.sessionId),
9645
- index("idx_plasticity_kind").on(table.kind)
9776
+ index("idx_plasticity_kind").on(table.kind),
9777
+ index("idx_plasticity_retrieval_log").on(table.retrievalLogId),
9778
+ index("idx_plasticity_reward").on(table.rewardSignal)
9779
+ ]
9780
+ );
9781
+ brainWeightHistory = sqliteTable(
9782
+ "brain_weight_history",
9783
+ {
9784
+ id: integer("id").primaryKey({ autoIncrement: true }),
9785
+ /** from_id of the affected brain_page_edges row. */
9786
+ edgeFromId: text("edge_from_id").notNull(),
9787
+ /** to_id of the affected brain_page_edges row. */
9788
+ edgeToId: text("edge_to_id").notNull(),
9789
+ /** Edge type of the affected brain_page_edges row (e.g. 'co_retrieved'). */
9790
+ edgeType: text("edge_type").notNull(),
9791
+ /** Edge weight immediately before this event. Null if the edge was just created. */
9792
+ weightBefore: real("weight_before"),
9793
+ /** Edge weight after this event. CLAMP(weightBefore + deltaWeight, 0, 1). NOT NULL. */
9794
+ weightAfter: real("weight_after").notNull(),
9795
+ /**
9796
+ * Signed weight delta applied to the edge.
9797
+ * Positive = potentiation (LTP/Hebbian), negative = depression (LTD).
9798
+ * Prune events record the final weight that triggered deletion (negative).
9799
+ */
9800
+ deltaWeight: real("delta_weight").notNull(),
9801
+ /**
9802
+ * Plasticity event kind.
9803
+ * 'ltp' — Long-Term Potentiation (STDP pre-before-post)
9804
+ * 'ltd' — Long-Term Depression (STDP post-before-pre)
9805
+ * 'hebbian' — Co-retrieval Hebbian strengthening
9806
+ * 'decay' — Temporal decay (only prune-triggering decays written here)
9807
+ * 'prune' — Edge deleted (weight fell below min_weight threshold)
9808
+ * 'external' — Manually-applied external weight change
9809
+ */
9810
+ eventKind: text("event_kind").notNull(),
9811
+ /** Soft FK to brain_plasticity_events.id — the STDP event that caused this. */
9812
+ sourcePlasticityEventId: integer("source_plasticity_event_id"),
9813
+ /** Soft FK to brain_retrieval_log.id — the retrieval batch that triggered this. */
9814
+ retrievalLogId: integer("retrieval_log_id"),
9815
+ /** R-STDP reward signal at time of event (copied from retrieval_log.reward_signal). */
9816
+ rewardSignal: real("reward_signal"),
9817
+ /** ISO 8601 timestamp when this weight change was applied. */
9818
+ changedAt: text("changed_at").notNull().default(sql2`(datetime('now'))`)
9819
+ },
9820
+ (table) => [
9821
+ index("idx_weight_history_edge").on(table.edgeFromId, table.edgeToId, table.edgeType),
9822
+ index("idx_weight_history_from").on(table.edgeFromId),
9823
+ index("idx_weight_history_to").on(table.edgeToId),
9824
+ index("idx_weight_history_changed_at").on(table.changedAt),
9825
+ index("idx_weight_history_event_kind").on(table.eventKind),
9826
+ index("idx_weight_history_plasticity_event").on(table.sourcePlasticityEventId)
9827
+ ]
9828
+ );
9829
+ brainModulators = sqliteTable(
9830
+ "brain_modulators",
9831
+ {
9832
+ id: integer("id").primaryKey({ autoIncrement: true }),
9833
+ /**
9834
+ * Modulator event type. String (not enum constraint) for extensibility.
9835
+ * Expected values: 'task_verified'|'task_completed'|'task_cancelled'|
9836
+ * 'owner_verify'|'session_success'|'session_blocker'|'external'
9837
+ */
9838
+ modulatorType: text("modulator_type").notNull(),
9839
+ /**
9840
+ * Reward valence in range [-1.0, +1.0].
9841
+ * +1.0 = strong reward (verified correct task)
9842
+ * +0.5 = moderate reward (done, unverified)
9843
+ * -0.5 = mild correction (cancelled task)
9844
+ * -1.0 = strong correction (explicit invalidation)
9845
+ * 0.0 = neutral signal
9846
+ */
9847
+ valence: real("valence").notNull(),
9848
+ /**
9849
+ * Magnitude 0.0–1.0 confidence scaling.
9850
+ * Effective reward = valence × magnitude.
9851
+ * Defaults to 1.0 (full confidence).
9852
+ */
9853
+ magnitude: real("magnitude").notNull().default(1),
9854
+ /** Polymorphic source event ID — task ID, memory entry ID, or other string ref. */
9855
+ sourceEventId: text("source_event_id"),
9856
+ /** Session ID (soft FK to tasks.db sessions). */
9857
+ sessionId: text("session_id"),
9858
+ /** Human-readable description of why this modulator was emitted. */
9859
+ description: text("description"),
9860
+ /** ISO 8601 timestamp when this modulator event was recorded. */
9861
+ createdAt: text("created_at").notNull().default(sql2`(datetime('now'))`)
9862
+ },
9863
+ (table) => [
9864
+ index("idx_modulators_type").on(table.modulatorType),
9865
+ index("idx_modulators_session").on(table.sessionId),
9866
+ index("idx_modulators_created_at").on(table.createdAt),
9867
+ index("idx_modulators_source_event").on(table.sourceEventId),
9868
+ index("idx_modulators_valence").on(table.valence)
9869
+ ]
9870
+ );
9871
+ brainConsolidationEvents = sqliteTable(
9872
+ "brain_consolidation_events",
9873
+ {
9874
+ id: integer("id").primaryKey({ autoIncrement: true }),
9875
+ /**
9876
+ * What triggered this consolidation run. String (not enum constraint) for
9877
+ * forward compatibility with T628 scheduler.
9878
+ * Expected values: 'session_end' | 'maintenance' | 'scheduled' | 'manual'
9879
+ */
9880
+ trigger: text("trigger").notNull(),
9881
+ /** Session ID that initiated this consolidation (soft FK to tasks.db sessions). */
9882
+ sessionId: text("session_id"),
9883
+ /**
9884
+ * JSON-serialized ConsolidationResult — all per-step counts and metrics.
9885
+ * Shape: { [stepName: string]: { count: number, durationMs?: number } }
9886
+ * Required NOT NULL — every run must record its results for T628 scheduling.
9887
+ */
9888
+ stepResultsJson: text("step_results_json").notNull(),
9889
+ /** Wall-clock milliseconds from start to completion. Null if run did not complete. */
9890
+ durationMs: integer("duration_ms"),
9891
+ /**
9892
+ * Whether the run succeeded.
9893
+ * Stored as integer(boolean) per Drizzle SQLite boolean convention.
9894
+ * true = completed without unhandled error, false = partial or error.
9895
+ */
9896
+ succeeded: integer("succeeded", { mode: "boolean" }).notNull().default(true),
9897
+ /** ISO 8601 timestamp when this consolidation run started. */
9898
+ startedAt: text("started_at").notNull().default(sql2`(datetime('now'))`)
9899
+ },
9900
+ (table) => [
9901
+ index("idx_consolidation_events_started_at").on(table.startedAt),
9902
+ index("idx_consolidation_events_trigger").on(table.trigger),
9903
+ index("idx_consolidation_events_session").on(table.sessionId)
9646
9904
  ]
9647
9905
  );
9648
9906
  }
@@ -15061,6 +15319,108 @@ function runBrainMigrations(nativeDb, db) {
15061
15319
  AND provenance LIKE 'consolidation:%'`
15062
15320
  ).run();
15063
15321
  }
15322
+ if (tableExists(nativeDb, "brain_retrieval_log")) {
15323
+ ensureColumns(
15324
+ nativeDb,
15325
+ "brain_retrieval_log",
15326
+ [
15327
+ { name: "session_id", ddl: "text" },
15328
+ { name: "reward_signal", ddl: "real" },
15329
+ { name: "retrieval_order", ddl: "integer" },
15330
+ { name: "delta_ms", ddl: "integer" }
15331
+ ],
15332
+ "brain"
15333
+ );
15334
+ }
15335
+ if (tableExists(nativeDb, "brain_plasticity_events")) {
15336
+ ensureColumns(
15337
+ nativeDb,
15338
+ "brain_plasticity_events",
15339
+ [
15340
+ { name: "session_id", ddl: "text" },
15341
+ { name: "weight_before", ddl: "real" },
15342
+ { name: "weight_after", ddl: "real" },
15343
+ { name: "retrieval_log_id", ddl: "integer" },
15344
+ { name: "reward_signal", ddl: "real" },
15345
+ { name: "delta_t_ms", ddl: "integer" }
15346
+ ],
15347
+ "brain"
15348
+ );
15349
+ }
15350
+ ensureColumns(
15351
+ nativeDb,
15352
+ "brain_page_edges",
15353
+ [
15354
+ { name: "last_reinforced_at", ddl: "text" },
15355
+ { name: "reinforcement_count", ddl: "integer NOT NULL DEFAULT 0" },
15356
+ { name: "plasticity_class", ddl: "text NOT NULL DEFAULT 'static'" },
15357
+ { name: "last_depressed_at", ddl: "text" },
15358
+ { name: "depression_count", ddl: "integer NOT NULL DEFAULT 0" },
15359
+ { name: "stability_score", ddl: "real" }
15360
+ ],
15361
+ "brain"
15362
+ );
15363
+ if (tableExists(nativeDb, "brain_page_edges")) {
15364
+ nativeDb.prepare(
15365
+ `UPDATE brain_page_edges SET plasticity_class = 'hebbian'
15366
+ WHERE edge_type = 'co_retrieved' AND plasticity_class = 'static'`
15367
+ ).run();
15368
+ }
15369
+ nativeDb.exec(
15370
+ `CREATE TABLE IF NOT EXISTS brain_weight_history (
15371
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
15372
+ edge_from_id TEXT NOT NULL,
15373
+ edge_to_id TEXT NOT NULL,
15374
+ edge_type TEXT NOT NULL,
15375
+ weight_before REAL,
15376
+ weight_after REAL NOT NULL,
15377
+ delta_weight REAL NOT NULL,
15378
+ event_kind TEXT NOT NULL,
15379
+ source_plasticity_event_id INTEGER,
15380
+ retrieval_log_id INTEGER,
15381
+ reward_signal REAL,
15382
+ changed_at TEXT NOT NULL DEFAULT (datetime('now'))
15383
+ )`
15384
+ );
15385
+ nativeDb.exec(
15386
+ `CREATE INDEX IF NOT EXISTS idx_weight_history_edge
15387
+ ON brain_weight_history (edge_from_id, edge_to_id, edge_type)`
15388
+ );
15389
+ nativeDb.exec(
15390
+ `CREATE INDEX IF NOT EXISTS idx_weight_history_changed_at
15391
+ ON brain_weight_history (changed_at)`
15392
+ );
15393
+ nativeDb.exec(
15394
+ `CREATE TABLE IF NOT EXISTS brain_modulators (
15395
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
15396
+ modulator_type TEXT NOT NULL,
15397
+ valence REAL NOT NULL,
15398
+ magnitude REAL NOT NULL DEFAULT 1.0,
15399
+ source_event_id TEXT,
15400
+ session_id TEXT,
15401
+ description TEXT,
15402
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
15403
+ )`
15404
+ );
15405
+ nativeDb.exec(
15406
+ `CREATE INDEX IF NOT EXISTS idx_modulators_session
15407
+ ON brain_modulators (session_id)`
15408
+ );
15409
+ nativeDb.exec(
15410
+ `CREATE TABLE IF NOT EXISTS brain_consolidation_events (
15411
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
15412
+ trigger TEXT NOT NULL,
15413
+ session_id TEXT,
15414
+ step_results_json TEXT NOT NULL,
15415
+ duration_ms INTEGER,
15416
+ succeeded INTEGER NOT NULL DEFAULT 1,
15417
+ started_at TEXT NOT NULL DEFAULT (datetime('now'))
15418
+ )`
15419
+ );
15420
+ nativeDb.exec(
15421
+ `CREATE INDEX IF NOT EXISTS idx_consolidation_events_started_at
15422
+ ON brain_consolidation_events (started_at)`
15423
+ );
15064
15424
  }
15065
15425
  function loadBrainVecExtension(nativeDb) {
15066
15426
  try {
@@ -17392,13 +17752,48 @@ var init_pagination = __esm({
17392
17752
  var brain_accessor_exports = {};
17393
17753
  __export(brain_accessor_exports, {
17394
17754
  BrainDataAccessor: () => BrainDataAccessor,
17395
- getBrainAccessor: () => getBrainAccessor
17755
+ getBrainAccessor: () => getBrainAccessor,
17756
+ insertModulatorRow: () => insertModulatorRow,
17757
+ insertWeightHistoryRow: () => insertWeightHistoryRow,
17758
+ logConsolidationComplete: () => logConsolidationComplete,
17759
+ logConsolidationStart: () => logConsolidationStart
17396
17760
  });
17397
17761
  import { and as and5, asc as asc2, desc as desc2, eq as eq7, gte as gte2, or as or4 } from "drizzle-orm";
17398
17762
  async function getBrainAccessor(cwd) {
17399
17763
  const db = await getBrainDb(cwd);
17400
17764
  return new BrainDataAccessor(db);
17401
17765
  }
17766
+ async function insertWeightHistoryRow(cwd, input) {
17767
+ const db = await getBrainDb(cwd);
17768
+ const result = await db.insert(brainWeightHistory).values(input).returning();
17769
+ return result[0];
17770
+ }
17771
+ async function insertModulatorRow(cwd, input) {
17772
+ const db = await getBrainDb(cwd);
17773
+ const result = await db.insert(brainModulators).values(input).returning();
17774
+ return result[0];
17775
+ }
17776
+ async function logConsolidationStart(cwd, trigger, sessionId) {
17777
+ const db = await getBrainDb(cwd);
17778
+ const result = await db.insert(brainConsolidationEvents).values({
17779
+ trigger,
17780
+ sessionId: sessionId ?? null,
17781
+ // stepResultsJson is required NOT NULL — use empty object as placeholder
17782
+ // until logConsolidationComplete updates it with final step results.
17783
+ stepResultsJson: "{}",
17784
+ succeeded: true
17785
+ }).returning({ id: brainConsolidationEvents.id });
17786
+ return result[0].id;
17787
+ }
17788
+ async function logConsolidationComplete(cwd, id, stats2, durationMs, succeeded = true) {
17789
+ const db = await getBrainDb(cwd);
17790
+ const result = await db.update(brainConsolidationEvents).set({
17791
+ stepResultsJson: JSON.stringify(stats2),
17792
+ durationMs,
17793
+ succeeded
17794
+ }).where(eq7(brainConsolidationEvents.id, id)).returning();
17795
+ return result[0];
17796
+ }
17402
17797
  var BrainDataAccessor;
17403
17798
  var init_brain_accessor = __esm({
17404
17799
  "packages/core/src/store/brain-accessor.ts"() {
@@ -49591,7 +49986,7 @@ async function logRetrieval(projectRoot, query, entryIds, source, tokensUsed, se
49591
49986
  "INSERT INTO brain_retrieval_log (query, entry_ids, entry_count, source, tokens_used, session_id) VALUES (?, ?, ?, ?, ?, ?)"
49592
49987
  ).run(
49593
49988
  query,
49594
- entryIds.join(","),
49989
+ JSON.stringify(entryIds),
49595
49990
  entryIds.length,
49596
49991
  source,
49597
49992
  tokensUsed ?? null,
@@ -58547,7 +58942,14 @@ function mapEdge(raw) {
58547
58942
  edgeType: raw.edge_type,
58548
58943
  weight: raw.weight,
58549
58944
  provenance: raw.provenance,
58550
- createdAt: raw.created_at
58945
+ createdAt: raw.created_at,
58946
+ // T673-M3 plasticity columns (default to neutral values if absent from SELECT)
58947
+ lastReinforcedAt: raw.last_reinforced_at ?? null,
58948
+ reinforcementCount: raw.reinforcement_count ?? 0,
58949
+ plasticityClass: raw.plasticity_class ?? "static",
58950
+ lastDepressedAt: raw.last_depressed_at ?? null,
58951
+ depressionCount: raw.depression_count ?? 0,
58952
+ stabilityScore: raw.stability_score ?? null
58551
58953
  };
58552
58954
  }
58553
58955
  async function traceBrainGraph(projectRoot, nodeId, maxDepth = 3) {
@@ -88774,6 +89176,85 @@ function checkLegacyAgentOutputs(projectRoot) {
88774
89176
  fix: null
88775
89177
  };
88776
89178
  }
89179
+ function checkCanonicalRcasdPaths(projectRoot) {
89180
+ const root = projectRoot ?? process.cwd();
89181
+ const cleoDir = join92(root, ".cleo");
89182
+ const failures = [];
89183
+ const deprecatedDirs = ["research", "consensus", "specs", "decomposition"];
89184
+ for (const dir of deprecatedDirs) {
89185
+ const dirPath = join92(cleoDir, dir);
89186
+ if (existsSync91(dirPath)) {
89187
+ try {
89188
+ const entries = __require("node:fs").readdirSync(dirPath).filter(
89189
+ (e) => !e.startsWith(".")
89190
+ );
89191
+ if (entries.length > 0) {
89192
+ failures.push(
89193
+ `deprecated .cleo/${dir}/ contains files (should migrate to .cleo/rcasd/{epicId}/${dir}/)`
89194
+ );
89195
+ }
89196
+ } catch {
89197
+ }
89198
+ }
89199
+ }
89200
+ const rcasdPath = join92(cleoDir, "rcasd");
89201
+ if (existsSync91(rcasdPath)) {
89202
+ try {
89203
+ const rootFiles = __require("node:fs").readdirSync(rcasdPath).filter(
89204
+ (e) => e.endsWith(".md")
89205
+ );
89206
+ if (rootFiles.length > 0) {
89207
+ failures.push(
89208
+ `misplaced .md files in .cleo/rcasd/ root (audit-*.md, etc. should be in .cleo/agent-outputs/)`
89209
+ );
89210
+ }
89211
+ } catch {
89212
+ }
89213
+ }
89214
+ const claudedocsPath = join92(root, "claudedocs");
89215
+ if (existsSync91(claudedocsPath)) {
89216
+ try {
89217
+ const agentOutputs = join92(claudedocsPath, "agent-outputs");
89218
+ if (existsSync91(agentOutputs)) {
89219
+ failures.push(
89220
+ `legacy claudedocs/agent-outputs/ directory exists (should migrate to .cleo/agent-outputs/)`
89221
+ );
89222
+ }
89223
+ } catch {
89224
+ }
89225
+ }
89226
+ if (failures.length > 0) {
89227
+ return {
89228
+ id: "canonical_rcasd_paths",
89229
+ category: "configuration",
89230
+ status: "warning",
89231
+ message: `Canonical path drift detected (ADR-045): ${failures.join("; ")}`,
89232
+ details: {
89233
+ issues: failures,
89234
+ canonical: {
89235
+ rcasdStages: ".cleo/rcasd/{epicId}/{stage}/{epicId}-{stage}.md",
89236
+ agentOutputs: ".cleo/agent-outputs/{taskId}-{slug}.md",
89237
+ publishedSpecs: "docs/specs/SPEC-NAME.md"
89238
+ }
89239
+ },
89240
+ fix: "cleo upgrade (migrates old paths) or manually move files per ADR-045"
89241
+ };
89242
+ }
89243
+ return {
89244
+ id: "canonical_rcasd_paths",
89245
+ category: "configuration",
89246
+ status: "passed",
89247
+ message: "All artifacts at canonical RCASD paths (ADR-045 compliant)",
89248
+ details: {
89249
+ canonical: {
89250
+ rcasdStages: ".cleo/rcasd/{epicId}/{stage}/{epicId}-{stage}.md",
89251
+ agentOutputs: ".cleo/agent-outputs/{taskId}-{slug}.md",
89252
+ publishedSpecs: "docs/specs/SPEC-NAME.md"
89253
+ }
89254
+ },
89255
+ fix: null
89256
+ };
89257
+ }
88777
89258
  function checkCaampMarkerIntegrity(projectRoot) {
88778
89259
  const root = projectRoot ?? process.cwd();
88779
89260
  const files = ["CLAUDE.md", "AGENTS.md"];
@@ -89086,6 +89567,8 @@ function runAllGlobalChecks(cleoHome, projectRoot) {
89086
89567
  checkCoreFilesNotIgnored(projectRoot),
89087
89568
  checkSqliteNotTracked(projectRoot),
89088
89569
  checkLegacyAgentOutputs(projectRoot),
89570
+ // ADR-045 canonical paths check (T708)
89571
+ checkCanonicalRcasdPaths(projectRoot),
89089
89572
  // Injection chain checks (T5153)
89090
89573
  checkCaampMarkerIntegrity(projectRoot),
89091
89574
  checkAtReferenceTargetExists(projectRoot),
@@ -89646,6 +90129,7 @@ async function coreDoctorReport(projectRoot) {
89646
90129
  checks.push(mapCheckResult(checkVitalFilesTracked(projectRoot)));
89647
90130
  checks.push(mapCheckResult(checkCoreFilesNotIgnored(projectRoot)));
89648
90131
  checks.push(mapCheckResult(checkLegacyAgentOutputs(projectRoot)));
90132
+ checks.push(mapCheckResult(checkCanonicalRcasdPaths(projectRoot)));
89649
90133
  const cleoGitHeadExists = existsSync92(join93(cleoDir, ".git", "HEAD"));
89650
90134
  checks.push({
89651
90135
  check: "cleo_git_repo",