@mnmal-ai/cortext 1.0.0-alpha.1 → 1.0.0-alpha.11

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/dist/bootstrap.js CHANGED
@@ -1,5 +1,5 @@
1
- import { E as isMonotonicAuth, t as createCortextPlugin, w as DEFAULT_AUTH } from "./src-BKaGTnRw.js";
2
- import { D as ARCHITECTURE_NAMESPACE, k as CORTEXT_NAMESPACE } from "./plugin-27UpMnoE.js";
1
+ import { D as isMonotonicAuth, T as DEFAULT_AUTH, t as createCortextPlugin } from "./src-DJA6hyG-.js";
2
+ import { A as CORTEXT_NAMESPACE, O as ARCHITECTURE_NAMESPACE } from "./plugin-BtBVkZpZ.js";
3
3
  import "./kernel.js";
4
4
  import { defineBootstrap } from "@mnmal-ai/substrate/sdk";
5
5
  import { z } from "zod";
package/dist/index.d.ts CHANGED
@@ -26,7 +26,7 @@ import type { CortextPluginConfig } from './config.js';
26
26
  export { buildAuthPolicies, DEFAULT_AUTH, isMonotonicAuth } from './auth-policy.js';
27
27
  export { type CompactResult, registerCompactRoute, runCompact } from './compact.js';
28
28
  export type { CortextAuthConfig, CortextAuthMode, CortextPluginConfig } from './config.js';
29
- export { AGENT_MESSAGE_STATUS_VOCAB, AGENT_RULE_CATEGORY_VOCAB, AGENT_RULE_SEVERITY_ALIASES, AGENT_RULE_SEVERITY_VOCAB, ARCHITECTURE_TYPE_NAMES, buildArchitectureNamespace, buildClaudeNamespace, CLAUDE_TYPE_NAMES, DECISION_STATUS_ALIASES, DECISION_STATUS_VOCAB, HOST_INSTALL_CHECK_STATES, HOST_INSTALL_FRAGMENT_STATES, TODO_RESOLUTION_ALIASES, TODO_STATUS_ALIASES, TODO_STATUS_IMPLIES } from './namespace.js';
29
+ export { AGENT_MESSAGE_STATUS_VOCAB, AGENT_RULE_CATEGORY_VOCAB, AGENT_RULE_SEVERITY_ALIASES, AGENT_RULE_SEVERITY_VOCAB, ARCHITECTURE_TYPE_NAMES, buildArchitectureNamespace, buildClaudeNamespace, CLAUDE_TYPE_NAMES, DECISION_STATUS_ALIASES, DECISION_STATUS_VOCAB, HOST_INSTALL_CHECK_STATES, HOST_INSTALL_FRAGMENT_STATES, TASK_STATUS_VOCAB, TODO_RESOLUTION_ALIASES, TODO_STATUS_ALIASES, TODO_STATUS_IMPLIES } from './namespace.js';
30
30
  export { registerOnboardRoute } from './onboard.js';
31
31
  export { buildReleaseSurface, bumpClass, classifySurfaceChange, classSatisfies, type ReleaseClass, type ReleaseSurface, type SurfaceChange, type SurfaceField } from './release-surface.js';
32
32
  export { type ClosedTodoText, inferTodoResolution, type TodoResolution } from './todo-resolution.js';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { C as runCompact, E as isMonotonicAuth, S as registerCompactRoute, T as buildAuthPolicies, _ as TODO_RESOLUTION_ALIASES, a as classSatisfies, b as buildArchitectureNamespace, c as AGENT_RULE_CATEGORY_VOCAB, d as ARCHITECTURE_TYPE_NAMES, f as CLAUDE_TYPE_NAMES, g as HOST_INSTALL_FRAGMENT_STATES, h as HOST_INSTALL_CHECK_STATES, i as bumpClass, l as AGENT_RULE_SEVERITY_ALIASES, m as DECISION_STATUS_VOCAB, n as inferTodoResolution, o as classifySurfaceChange, p as DECISION_STATUS_ALIASES, r as buildReleaseSurface, s as registerOnboardRoute, t as createCortextPlugin, u as AGENT_RULE_SEVERITY_VOCAB, v as TODO_STATUS_ALIASES, w as DEFAULT_AUTH, x as buildClaudeNamespace, y as TODO_STATUS_IMPLIES } from "./src-BKaGTnRw.js";
2
- import { g as AGENT_MESSAGE_STATUS_VOCAB } from "./plugin-27UpMnoE.js";
1
+ import { C as registerCompactRoute, D as isMonotonicAuth, E as buildAuthPolicies, S as buildClaudeNamespace, T as DEFAULT_AUTH, _ as TASK_STATUS_VOCAB, a as classSatisfies, b as TODO_STATUS_IMPLIES, c as AGENT_RULE_CATEGORY_VOCAB, d as ARCHITECTURE_TYPE_NAMES, f as CLAUDE_TYPE_NAMES, g as HOST_INSTALL_FRAGMENT_STATES, h as HOST_INSTALL_CHECK_STATES, i as bumpClass, l as AGENT_RULE_SEVERITY_ALIASES, m as DECISION_STATUS_VOCAB, n as inferTodoResolution, o as classifySurfaceChange, p as DECISION_STATUS_ALIASES, r as buildReleaseSurface, s as registerOnboardRoute, t as createCortextPlugin, u as AGENT_RULE_SEVERITY_VOCAB, v as TODO_RESOLUTION_ALIASES, w as runCompact, x as buildArchitectureNamespace, y as TODO_STATUS_ALIASES } from "./src-DJA6hyG-.js";
2
+ import { _ as AGENT_MESSAGE_STATUS_VOCAB } from "./plugin-BtBVkZpZ.js";
3
3
  import "./kernel.js";
4
4
 
5
- export { AGENT_MESSAGE_STATUS_VOCAB, AGENT_RULE_CATEGORY_VOCAB, AGENT_RULE_SEVERITY_ALIASES, AGENT_RULE_SEVERITY_VOCAB, ARCHITECTURE_TYPE_NAMES, CLAUDE_TYPE_NAMES, DECISION_STATUS_ALIASES, DECISION_STATUS_VOCAB, DEFAULT_AUTH, HOST_INSTALL_CHECK_STATES, HOST_INSTALL_FRAGMENT_STATES, TODO_RESOLUTION_ALIASES, TODO_STATUS_ALIASES, TODO_STATUS_IMPLIES, buildArchitectureNamespace, buildAuthPolicies, buildClaudeNamespace, buildReleaseSurface, bumpClass, classSatisfies, classifySurfaceChange, createCortextPlugin, inferTodoResolution, isMonotonicAuth, registerCompactRoute, registerOnboardRoute, runCompact };
5
+ export { AGENT_MESSAGE_STATUS_VOCAB, AGENT_RULE_CATEGORY_VOCAB, AGENT_RULE_SEVERITY_ALIASES, AGENT_RULE_SEVERITY_VOCAB, ARCHITECTURE_TYPE_NAMES, CLAUDE_TYPE_NAMES, DECISION_STATUS_ALIASES, DECISION_STATUS_VOCAB, DEFAULT_AUTH, HOST_INSTALL_CHECK_STATES, HOST_INSTALL_FRAGMENT_STATES, TASK_STATUS_VOCAB, TODO_RESOLUTION_ALIASES, TODO_STATUS_ALIASES, TODO_STATUS_IMPLIES, buildArchitectureNamespace, buildAuthPolicies, buildClaudeNamespace, buildReleaseSurface, bumpClass, classSatisfies, classifySurfaceChange, createCortextPlugin, inferTodoResolution, isMonotonicAuth, registerCompactRoute, registerOnboardRoute, runCompact };
@@ -45,12 +45,12 @@
45
45
  export declare const CORTEXT_NAMESPACE = "cortext";
46
46
  /**
47
47
  * The namespace the architecture-findings surface registers under, and which
48
- * migration 0002 scopes its body normalization to.
48
+ * the pre-baseline body-normalization migration was scoped to.
49
49
  *
50
50
  * Separate from CORTEXT_NAMESPACE because the two have separate operator
51
51
  * knobs and diverging lifecycle policies (compaction, retention, audit) — but
52
52
  * subject to exactly the same gap, which is why it is stated here rather than
53
- * left as a bare literal inside 0002.
53
+ * left as a bare literal inside that migration.
54
54
  */
55
55
  export declare const ARCHITECTURE_NAMESPACE = "architecture";
56
56
  /**
@@ -8,8 +8,9 @@
8
8
  export { ARCHITECTURE_NAMESPACE, CORTEXT_COORDINATOR_NAME, CORTEXT_NAMESPACE } from './cortext-namespace.js';
9
9
  export { auditNamespaceCoverage, auditObjects, type Finding, findingMessage, INVARIANT_OBJECTS, type InvariantObject, type PluginNamespace, reportInvariantAudit, resolveCortextNamespaces, scopedNamespace, type UntargetableReason } from './invariant-audit.js';
10
10
  export { cortextBaselineMigration } from './migrations/0001_baseline.js';
11
+ export { cortextDropEngineOwnedIndexesMigration, ENGINE_OWNED_INDEXES } from './migrations/0002_drop_engine_owned_indexes.js';
11
12
  export { createCortextKernelPlugin } from './plugin.js';
12
13
  export { REPAIR_ENV_VAR, type RepairOutcome, repairIfRequested, repairInvariants, repairOutcomeMessage, repairRequested } from './repair.js';
13
- export { countDuplicateHostInstallKeys, countDuplicateKids, rescopeHostInstallKeyIndex, rescopeKidUniqueIndex, rescopeRecipientIndex, rescopeVocabConstraint, VOCAB_CONSTRAINTS, type VocabConstraint } from './rescope.js';
14
+ export { countDuplicateHostInstallKeys, countDuplicateKids, rescopeHostInstallKeyIndex, rescopeKidUniqueIndex, rescopeVocabConstraint, VOCAB_CONSTRAINTS, type VocabConstraint } from './rescope.js';
14
15
  export { SUBSTRATE_CONTRACT_RANGE } from './substrate-contract.js';
15
16
  export { AGENT_MESSAGE_STATUS_VOCAB, TODO_LIFECYCLE_VOCAB, TODO_PRIORITY_VOCAB, TODO_RESOLUTION_VOCAB } from './todo-vocab.js';
@@ -51,8 +51,8 @@ export interface InvariantObject {
51
51
  * if this list and the migrations disagree — which is the check that actually
52
52
  * holds them together.
53
53
  *
54
- * Note there is no `architecture/*` entry, and that is correct: migration 0002
55
- * is a one-time body normalisation, not a standing invariant. A drifted
54
+ * Note there is no `architecture/*` entry, and that is correct: the
55
+ * pre-baseline architecture migration was a one-time body normalisation, not a standing invariant. A drifted
56
56
  * architecture namespace means that normalisation never ran for those rows —
57
57
  * real, but a different and lesser thing than "kid uniqueness is not held".
58
58
  */
@@ -89,6 +89,7 @@ export type Finding =
89
89
  readonly kind: 'object_misscoped';
90
90
  readonly object: InvariantObject;
91
91
  readonly scopedTo: string | null;
92
+ readonly expected: string;
92
93
  }
93
94
  /** Present and correctly scoped, but NOT VALID — new writes only. */
94
95
  | {
@@ -148,7 +149,7 @@ export declare function scopedNamespace(definition: string): string | null;
148
149
  * gains a filter, an early return or a per-object skip, the completion metric
149
150
  * reports what was actually inspected instead of a promise.
150
151
  */
151
- export declare function auditObjects(sql: unknown): Promise<{
152
+ export declare function auditObjects(sql: unknown, target?: string): Promise<{
152
153
  findings: Finding[];
153
154
  checked: number;
154
155
  }>;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * How the store keeps rows: one shared `rows` table (`legacy`), `rows`
3
+ * partitioned by namespace and type (`partitioned`), or one table per type
4
+ * listed in `substrate_type_tables` (`tables`, the only layout substrate
5
+ * 1.0.0-alpha.12 and later boots).
6
+ *
7
+ * cortext's raw DDL targets `rows` on `legacy` only. On `partitioned`,
8
+ * Postgres refuses a UNIQUE index that lacks the partition keys and clones
9
+ * every parent-level index and CHECK onto other plugins' partitions; on
10
+ * `tables` there is no `rows` at all. On both, the engine builds cortext's
11
+ * declared indexes, `indexed: true` fields and enum CHECKs per type.
12
+ */
13
+ export type StoreLayout = 'legacy' | 'partitioned' | 'tables';
14
+ /** Whether `layout` has a shared `rows` table for cortext's raw DDL to target. */
15
+ export declare const hasRowsTable: (layout: unknown) => boolean;
16
+ /**
17
+ * The layout as the catalog reports it. Migrations read `ctx.layout` instead;
18
+ * this is for boot-time code, whose lifecycle context does not carry it.
19
+ * A schema with neither table reads as `legacy`, whose audit then reports
20
+ * every object missing.
21
+ */
22
+ export declare function storeLayout(sql: unknown): Promise<StoreLayout>;
@@ -3,6 +3,10 @@
3
3
  * body (see `CORTEXT_PROMOTED_COLUMNS`), their indexes, and the invariants
4
4
  * on cortext's own types.
5
5
  *
6
+ * Only a store with a shared `rows` table gets any of it; on per-type tables
7
+ * the engine builds the columns, indexes and enum CHECKs from cortext's
8
+ * namespace definition, so both `apply` and `rollback` return first.
9
+ *
6
10
  * The kernel runner does not wrap `apply` in a transaction, so each statement
7
11
  * commits on its own and a run that dies part-way re-applies cleanly: every
8
12
  * statement is idempotent.
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Drops the hand-made indexes 0001 used to build on a legacy `rows`, now that
3
+ * the engine serves the same reads: the derived recipient index for the
4
+ * inbox, and the `indexed: true` indexes on project and status.
5
+ *
6
+ * A partitioned `rows` never had them, and a per-type tables store has no
7
+ * `rows` at all. Idempotent, like 0001: the runner does
8
+ * not wrap `apply` in a transaction.
9
+ */
10
+ import type { KernelMigration } from '@mnmal-ai/substrate/sdk';
11
+ export declare const ENGINE_OWNED_INDEXES: readonly ['rows_agent_message_recipient_idx', 'rows_ns_type_project_idx', 'rows_ns_type_status_idx', 'rows_ns_type_priority_idx', 'rows_cold_start_idx'];
12
+ export declare const cortextDropEngineOwnedIndexesMigration: KernelMigration;
@@ -41,14 +41,6 @@ export declare const VOCAB_CONSTRAINTS: readonly VocabConstraint[];
41
41
  * enough to accumulate bad rows.
42
42
  */
43
43
  export declare function rescopeVocabConstraint(s: Sql, metric: Metric, { constraint, type, column, vocab }: VocabConstraint, namespace: string): Promise<number>;
44
- /**
45
- * Re-scope the mailbox inbox index (0003) to `namespace`.
46
- *
47
- * Dropped and rebuilt rather than left alongside a correctly-scoped sibling:
48
- * two partial indexes over the same expression is one the planner can never
49
- * use, plus write amplification on every AgentMessage insert.
50
- */
51
- export declare function rescopeRecipientIndex(s: Sql, namespace: string): Promise<void>;
52
44
  /**
53
45
  * How many kids would collide if the unique index were scoped to `namespace`.
54
46
  *
@@ -16,4 +16,4 @@
16
16
  * by reflex: widen it only after reading what the new major changed.
17
17
  * `substrate-contract-parity.test.ts` holds the manifest to this literal.
18
18
  */
19
- export declare const SUBSTRATE_CONTRACT_RANGE = ">=1.0.0-alpha.0 <2.0.0";
19
+ export declare const SUBSTRATE_CONTRACT_RANGE = ">=1.0.0-alpha.12 <2.0.0";
@@ -1,4 +1,4 @@
1
- import { t as createCortextKernelPlugin } from "./plugin-27UpMnoE.js";
1
+ import { t as createCortextKernelPlugin } from "./plugin-BtBVkZpZ.js";
2
2
  import { defineBootstrap } from "@mnmal-ai/substrate/sdk";
3
3
  import { z } from "zod";
4
4
 
package/dist/kernel.js CHANGED
@@ -1,3 +1,3 @@
1
- import { C as findingMessage, D as ARCHITECTURE_NAMESPACE, E as scopedNamespace, O as CORTEXT_COORDINATOR_NAME, S as auditObjects, T as resolveCortextNamespaces, _ as TODO_LIFECYCLE_VOCAB, a as repairInvariants, b as INVARIANT_OBJECTS, c as cortextBaselineMigration, d as countDuplicateKids, f as rescopeHostInstallKeyIndex, g as AGENT_MESSAGE_STATUS_VOCAB, h as rescopeVocabConstraint, i as repairIfRequested, k as CORTEXT_NAMESPACE, l as VOCAB_CONSTRAINTS, m as rescopeRecipientIndex, n as SUBSTRATE_CONTRACT_RANGE, o as repairOutcomeMessage, p as rescopeKidUniqueIndex, r as REPAIR_ENV_VAR, s as repairRequested, t as createCortextKernelPlugin, u as countDuplicateHostInstallKeys, v as TODO_PRIORITY_VOCAB, w as reportInvariantAudit, x as auditNamespaceCoverage, y as TODO_RESOLUTION_VOCAB } from "./plugin-27UpMnoE.js";
1
+ import { A as CORTEXT_NAMESPACE, C as auditObjects, D as scopedNamespace, E as resolveCortextNamespaces, O as ARCHITECTURE_NAMESPACE, S as auditNamespaceCoverage, T as reportInvariantAudit, _ as AGENT_MESSAGE_STATUS_VOCAB, a as repairInvariants, b as TODO_RESOLUTION_VOCAB, c as ENGINE_OWNED_INDEXES, d as VOCAB_CONSTRAINTS, f as countDuplicateHostInstallKeys, g as rescopeVocabConstraint, h as rescopeKidUniqueIndex, i as repairIfRequested, k as CORTEXT_COORDINATOR_NAME, l as cortextDropEngineOwnedIndexesMigration, m as rescopeHostInstallKeyIndex, n as SUBSTRATE_CONTRACT_RANGE, o as repairOutcomeMessage, p as countDuplicateKids, r as REPAIR_ENV_VAR, s as repairRequested, t as createCortextKernelPlugin, u as cortextBaselineMigration, v as TODO_LIFECYCLE_VOCAB, w as findingMessage, x as INVARIANT_OBJECTS, y as TODO_PRIORITY_VOCAB } from "./plugin-BtBVkZpZ.js";
2
2
 
3
- export { AGENT_MESSAGE_STATUS_VOCAB, ARCHITECTURE_NAMESPACE, CORTEXT_COORDINATOR_NAME, CORTEXT_NAMESPACE, INVARIANT_OBJECTS, REPAIR_ENV_VAR, SUBSTRATE_CONTRACT_RANGE, TODO_LIFECYCLE_VOCAB, TODO_PRIORITY_VOCAB, TODO_RESOLUTION_VOCAB, VOCAB_CONSTRAINTS, auditNamespaceCoverage, auditObjects, cortextBaselineMigration, countDuplicateHostInstallKeys, countDuplicateKids, createCortextKernelPlugin, findingMessage, repairIfRequested, repairInvariants, repairOutcomeMessage, repairRequested, reportInvariantAudit, rescopeHostInstallKeyIndex, rescopeKidUniqueIndex, rescopeRecipientIndex, rescopeVocabConstraint, resolveCortextNamespaces, scopedNamespace };
3
+ export { AGENT_MESSAGE_STATUS_VOCAB, ARCHITECTURE_NAMESPACE, CORTEXT_COORDINATOR_NAME, CORTEXT_NAMESPACE, ENGINE_OWNED_INDEXES, INVARIANT_OBJECTS, REPAIR_ENV_VAR, SUBSTRATE_CONTRACT_RANGE, TODO_LIFECYCLE_VOCAB, TODO_PRIORITY_VOCAB, TODO_RESOLUTION_VOCAB, VOCAB_CONSTRAINTS, auditNamespaceCoverage, auditObjects, cortextBaselineMigration, cortextDropEngineOwnedIndexesMigration, countDuplicateHostInstallKeys, countDuplicateKids, createCortextKernelPlugin, findingMessage, repairIfRequested, repairInvariants, repairOutcomeMessage, repairRequested, reportInvariantAudit, rescopeHostInstallKeyIndex, rescopeKidUniqueIndex, rescopeVocabConstraint, resolveCortextNamespaces, scopedNamespace };
@@ -141,9 +141,10 @@ export { AGENT_MESSAGE_STATUS_VOCAB } from './kernel/index.js';
141
141
  export declare const HOST_INSTALL_FRAGMENT_STATES: readonly ['current', 'stale', 'missing', 'undigested', 'uncomparable', 'source', 'absent'];
142
142
  /** HostInstall's per-check verdicts: the worst finding each check produced. */
143
143
  export declare const HOST_INSTALL_CHECK_STATES: readonly ['ok', 'warn', 'unknown'];
144
+ export declare const TASK_STATUS_VOCAB: readonly ['active', 'paused', 'done'];
144
145
  export declare function buildClaudeNamespace(ns: string): NamespaceDefinition;
145
146
  /** The canonical ordered list of type names registered by this namespace. */
146
- export declare const CLAUDE_TYPE_NAMES: readonly ['SessionLog', 'Decision', 'Todo', 'AgentRule', 'CouncilSession', 'CouncilAdvisor', 'Memory', 'AnchorIntent', 'Concept', 'ConceptLink', 'Skill', 'AgentIdentity', 'AgentMessage', 'HostInstall'];
147
+ export declare const CLAUDE_TYPE_NAMES: readonly ['SessionLog', 'Decision', 'Todo', 'AgentRule', 'CouncilSession', 'CouncilAdvisor', 'Memory', 'AnchorIntent', 'Concept', 'ConceptLink', 'Skill', 'AgentIdentity', 'AgentMessage', 'HostInstall', 'Task', 'ExecutionCheckpoint'];
147
148
  /**
148
149
  * Architecture namespace — Claude-lineage code-review findings. Kept separate
149
150
  * from the claude context namespace so lifecycle (compaction, retention,
@@ -21,12 +21,26 @@ export declare function isOperator(ctx: RequestContext): boolean;
21
21
  * neither is a bystander's call.
22
22
  */
23
23
  export declare function messageParticipantsOnly(installCtx: NamespaceInstallContext, name: string, inner: RegistryEntryResolver): RegistryEntryResolver;
24
+ /**
25
+ * AgentIdentity create: a non-operator may not file a row whose `host` is a
26
+ * machine other than the one its kid names. The fleet audit trusts `host`
27
+ * (and `roaming` on that host) when it decides who may write a host's
28
+ * HostInstall rows.
29
+ */
30
+ export declare function identityCreateGuard(installCtx: NamespaceInstallContext, name: string, inner: RegistryEntryResolver): RegistryEntryResolver;
24
31
  /**
25
32
  * AgentIdentity update: the identity itself (the caller's kid is the row's
26
33
  * kid) or an operator. `kid` is the row's identity, so changing it is an
27
34
  * operator act even on your own row: otherwise a row could be renamed onto a
28
- * kid whose mail it would then receive.
35
+ * kid whose mail it would then receive. `host` must stay the kid's machine,
36
+ * and reactivating a retired row is an operator act.
29
37
  */
30
38
  export declare function identitySelfOnly(installCtx: NamespaceInstallContext, name: string, inner: RegistryEntryResolver): RegistryEntryResolver;
39
+ /**
40
+ * Todo create/update: `resolution` says why a CLOSED Todo stopped, so a live
41
+ * Todo carries none. A resolution with a live resulting status is refused,
42
+ * and moving a Todo back to a live status clears the one it had.
43
+ */
44
+ export declare function todoResolutionGuard(installCtx: NamespaceInstallContext, name: string, inner: RegistryEntryResolver, statusAliases: Readonly<Record<string, string>>): RegistryEntryResolver;
31
45
  /** AgentIdentity delete: operators only. Mail hangs off these rows. */
32
46
  export declare function operatorOnly(name: string, inner: RegistryEntryResolver): RegistryEntryResolver;
@@ -46,12 +46,12 @@
46
46
  const CORTEXT_NAMESPACE = "cortext";
47
47
  /**
48
48
  * The namespace the architecture-findings surface registers under, and which
49
- * migration 0002 scopes its body normalization to.
49
+ * the pre-baseline body-normalization migration was scoped to.
50
50
  *
51
51
  * Separate from CORTEXT_NAMESPACE because the two have separate operator
52
52
  * knobs and diverging lifecycle policies (compaction, retention, audit) — but
53
53
  * subject to exactly the same gap, which is why it is stated here rather than
54
- * left as a bare literal inside 0002.
54
+ * left as a bare literal inside that migration.
55
55
  */
56
56
  const ARCHITECTURE_NAMESPACE = "architecture";
57
57
  /**
@@ -96,6 +96,26 @@ const CORTEXT_PROMOTED_COLUMNS = {
96
96
  }
97
97
  };
98
98
 
99
+ //#endregion
100
+ //#region src/kernel/layout.ts
101
+ /** Whether `layout` has a shared `rows` table for cortext's raw DDL to target. */
102
+ const hasRowsTable = (layout) => layout === "legacy" || layout === "partitioned";
103
+ /**
104
+ * The layout as the catalog reports it. Migrations read `ctx.layout` instead;
105
+ * this is for boot-time code, whose lifecycle context does not carry it.
106
+ * A schema with neither table reads as `legacy`, whose audit then reports
107
+ * every object missing.
108
+ */
109
+ async function storeLayout(sql) {
110
+ const [row] = await sql`
111
+ SELECT (SELECT c.relkind FROM pg_class c WHERE c.oid = to_regclass('rows')) AS kind,
112
+ to_regclass('substrate_type_tables') IS NOT NULL AS tables
113
+ `;
114
+ if (row?.kind === "p") return "partitioned";
115
+ if (row?.kind == null && row?.tables === true) return "tables";
116
+ return "legacy";
117
+ }
118
+
99
119
  //#endregion
100
120
  //#region src/kernel/invariant-audit.ts
101
121
  /**
@@ -141,8 +161,8 @@ const CORTEXT_PROMOTED_COLUMNS = {
141
161
  * if this list and the migrations disagree — which is the check that actually
142
162
  * holds them together.
143
163
  *
144
- * Note there is no `architecture/*` entry, and that is correct: migration 0002
145
- * is a one-time body normalisation, not a standing invariant. A drifted
164
+ * Note there is no `architecture/*` entry, and that is correct: the
165
+ * pre-baseline architecture migration was a one-time body normalisation, not a standing invariant. A drifted
146
166
  * architecture namespace means that normalisation never ran for those rows —
147
167
  * real, but a different and lesser thing than "kid uniqueness is not held".
148
168
  */
@@ -159,12 +179,6 @@ const INVARIANT_OBJECTS = [
159
179
  migration: "0001_baseline",
160
180
  guarantee: "Todo.priority is restricted to its controlled vocabulary"
161
181
  },
162
- {
163
- name: "rows_agent_message_recipient_idx",
164
- kind: "index",
165
- migration: "0001_baseline",
166
- guarantee: "mailbox inbox reads are index-backed rather than heap scans"
167
- },
168
182
  {
169
183
  name: "rows_agent_identity_kid_unique",
170
184
  kind: "index",
@@ -280,7 +294,7 @@ async function readCatalog(sql) {
280
294
  * gains a filter, an early return or a per-object skip, the completion metric
281
295
  * reports what was actually inspected instead of a promise.
282
296
  */
283
- async function auditObjects(sql) {
297
+ async function auditObjects(sql, target = CORTEXT_NAMESPACE) {
284
298
  const catalog = await readCatalog(sql);
285
299
  const findings = [];
286
300
  let checked = 0;
@@ -295,11 +309,12 @@ async function auditObjects(sql) {
295
309
  continue;
296
310
  }
297
311
  const scope = scopedNamespace(row.definition);
298
- if (scope !== "cortext") {
312
+ if (scope !== target) {
299
313
  findings.push({
300
314
  kind: "object_misscoped",
301
315
  object,
302
- scopedTo: scope
316
+ scopedTo: scope,
317
+ expected: target
303
318
  });
304
319
  continue;
305
320
  }
@@ -320,7 +335,7 @@ function findingMessage(finding) {
320
335
  case "namespace_drifted": return `${head} the cortext coordinator is configured for namespace '${finding.configured}', but every kernel constraint and index is scoped to '${finding.scopedTo}'. So all of them are INACTIVE for '${finding.configured}': Todo/AgentMessage vocabularies unenforced, the mailbox inbox index covering no rows, and AgentIdentity.kid uniqueness NOT held. Writes succeed and queries answer, so nothing else will report this. Use '${finding.scopedTo}', or accept the loss deliberately.`;
321
336
  case "namespace_uncovered": return `${head} invariants are scoped to namespace '${finding.scopedTo}', which no loaded coordinator declares (declared: ${finding.declared.join(", ") || "none"}). Every kernel guarantee below is therefore INACTIVE, and nothing else will report it — a constraint scoped to a namespace with no rows passes trivially. ` + (finding.reason === "host_does_not_attribute" ? "This substrate does not attribute namespaces to their plugin (added in 5.1.0), so which namespace REPLACED it cannot be determined — upgrade the binary to get a named target rather than this list." : "This substrate does attribute, and the cortext coordinator declared no namespace at all — it is not loaded. The objects are orphaned rather than misscoped.") + " Set SYNOPTIC_CORTEXT_REPAIR=1 for one restart to re-scope.";
322
337
  case "object_missing": return `${head} ${finding.object.kind} '${finding.object.name}' (${finding.object.migration}) is ABSENT. Lost guarantee: ${finding.object.guarantee}. Migrations are recorded once by id, so this will NOT be reinstalled by a restart.`;
323
- case "object_misscoped": return `${head} ${finding.object.kind} '${finding.object.name}' is scoped to ${finding.scopedTo === null ? "no namespace" : `'${finding.scopedTo}'`}, not '${CORTEXT_NAMESPACE}'. Lost guarantee: ${finding.object.guarantee}. It exists, so nothing errors; it simply matches no rows.`;
338
+ case "object_misscoped": return `${head} ${finding.object.kind} '${finding.object.name}' is scoped to ${finding.scopedTo === null ? "no namespace" : `'${finding.scopedTo}'`}, not '${finding.expected}'. Lost guarantee: ${finding.object.guarantee}. It exists, so nothing errors; it simply matches no rows.`;
324
339
  case "object_not_validated": return `${head} ${finding.object.kind} '${finding.object.name}' exists but is NOT VALID — enforced for new writes only, and existing rows were never checked against it. Guarantee held only going forward: ${finding.object.guarantee}.`;
325
340
  }
326
341
  }
@@ -352,11 +367,14 @@ function findingMessage(finding) {
352
367
  * structured channel this belongs in.
353
368
  */
354
369
  async function reportInvariantAudit(ctx, warn = console.warn) {
355
- const findings = [...auditNamespaceCoverage(ctx.coordinatorNamespaces, ctx.coordinatorNamespacesByPlugin)];
356
370
  const catalogRead = ctx.sql !== null && ctx.sql !== void 0;
371
+ const layout = catalogRead ? await storeLayout(ctx.sql).catch(() => "legacy") : null;
372
+ let findings = layout !== null && layout !== "legacy" ? [] : auditNamespaceCoverage(ctx.coordinatorNamespaces, ctx.coordinatorNamespacesByPlugin);
373
+ const target = ctx.coordinatorNamespaces.includes("cortext") ? CORTEXT_NAMESPACE : resolveCortextNamespaces(ctx.coordinatorNamespacesByPlugin)?.[0] ?? "cortext";
357
374
  let objectsChecked = 0;
358
- if (catalogRead) try {
359
- const result = await auditObjects(ctx.sql);
375
+ if (catalogRead && layout === "legacy") try {
376
+ const result = await auditObjects(ctx.sql, target);
377
+ if (target !== "cortext" && result.findings.length === 0) findings = findings.filter((f) => f.kind !== "namespace_drifted");
360
378
  findings.push(...result.findings);
361
379
  objectsChecked = result.checked;
362
380
  } catch (error) {
@@ -378,7 +396,8 @@ async function reportInvariantAudit(ctx, warn = console.warn) {
378
396
  ctx.metric("cortext_kernel.invariant_audit_complete", {
379
397
  checked: objectsChecked,
380
398
  findings: findings.length,
381
- catalogRead
399
+ catalogRead,
400
+ layout
382
401
  });
383
402
  return findings;
384
403
  }
@@ -505,24 +524,6 @@ async function rescopeVocabConstraint(s, metric, { constraint, type, column, voc
505
524
  return violations;
506
525
  }
507
526
  /**
508
- * Re-scope the mailbox inbox index (0003) to `namespace`.
509
- *
510
- * Dropped and rebuilt rather than left alongside a correctly-scoped sibling:
511
- * two partial indexes over the same expression is one the planner can never
512
- * use, plus write amplification on every AgentMessage insert.
513
- */
514
- async function rescopeRecipientIndex(s, namespace) {
515
- await s.unsafe("DROP INDEX IF EXISTS rows_agent_message_recipient_idx");
516
- await s.unsafe(`
517
- CREATE INDEX IF NOT EXISTS rows_agent_message_recipient_idx
518
- ON rows ((body->>'recipient'), status)
519
- WHERE namespace = '${namespace}'
520
- AND type = 'AgentMessage'
521
- AND deleted_at IS NULL
522
- AND archived IS NOT TRUE
523
- `);
524
- }
525
- /**
526
527
  * How many kids would collide if the unique index were scoped to `namespace`.
527
528
  *
528
529
  * Exposed separately because a UNIQUE index has no `NOT VALID` equivalent —
@@ -606,8 +607,9 @@ async function rescopeHostInstallKeyIndex(s, namespace) {
606
607
  //#region src/kernel/migrations/0001_baseline.ts
607
608
  const cortextBaselineMigration = {
608
609
  id: "0001_baseline",
609
- apply: async ({ sql, metric }) => {
610
- if (sql === null) return;
610
+ apply: async (ctx) => {
611
+ const { sql, metric } = ctx;
612
+ if (sql === null || !hasRowsTable(ctx.layout)) return;
611
613
  const s = sql;
612
614
  await s.unsafe(`
613
615
  ALTER TABLE rows
@@ -616,27 +618,17 @@ const cortextBaselineMigration = {
616
618
  ADD COLUMN IF NOT EXISTS status text NULL,
617
619
  ADD COLUMN IF NOT EXISTS priority text NULL
618
620
  `);
619
- for (const column of [
620
- "archived",
621
- "project",
622
- "status",
623
- "priority"
624
- ]) await s.unsafe(`
625
- CREATE INDEX IF NOT EXISTS rows_ns_type_${column}_idx
626
- ON rows (namespace, type, ${column}) WHERE deleted_at IS NULL
627
- `);
621
+ if (ctx.layout === "partitioned") return;
628
622
  await s.unsafe(`
629
- CREATE INDEX IF NOT EXISTS rows_cold_start_idx
630
- ON rows (namespace, type, project)
631
- WHERE deleted_at IS NULL AND archived IS NOT TRUE
623
+ CREATE INDEX IF NOT EXISTS rows_ns_type_archived_idx
624
+ ON rows (namespace, type, archived) WHERE deleted_at IS NULL
632
625
  `);
633
626
  for (const spec of VOCAB_CONSTRAINTS) await rescopeVocabConstraint(s, metric, spec, CORTEXT_NAMESPACE);
634
- await rescopeRecipientIndex(s, CORTEXT_NAMESPACE);
635
627
  await rescopeKidUniqueIndex(s, CORTEXT_NAMESPACE);
636
628
  await rescopeHostInstallKeyIndex(s, CORTEXT_NAMESPACE);
637
629
  },
638
- rollback: async ({ sql }) => {
639
- if (sql === null) return;
630
+ rollback: async ({ sql, layout }) => {
631
+ if (sql === null || !hasRowsTable(layout)) return;
640
632
  const s = sql;
641
633
  for (const { constraint } of VOCAB_CONSTRAINTS) await s.unsafe(`ALTER TABLE rows DROP CONSTRAINT IF EXISTS ${constraint}`);
642
634
  await s.unsafe("DROP INDEX IF EXISTS rows_agent_identity_kid_unique, rows_host_install_key_unique");
@@ -644,6 +636,24 @@ const cortextBaselineMigration = {
644
636
  }
645
637
  };
646
638
 
639
+ //#endregion
640
+ //#region src/kernel/migrations/0002_drop_engine_owned_indexes.ts
641
+ const ENGINE_OWNED_INDEXES = [
642
+ "rows_agent_message_recipient_idx",
643
+ "rows_ns_type_project_idx",
644
+ "rows_ns_type_status_idx",
645
+ "rows_ns_type_priority_idx",
646
+ "rows_cold_start_idx"
647
+ ];
648
+ const cortextDropEngineOwnedIndexesMigration = {
649
+ id: "0002_drop_engine_owned_indexes",
650
+ apply: async ({ sql, layout }) => {
651
+ if (sql === null || layout !== "legacy") return;
652
+ await sql.unsafe(`DROP INDEX IF EXISTS ${ENGINE_OWNED_INDEXES.join(", ")}`);
653
+ },
654
+ rollback: async () => {}
655
+ };
656
+
647
657
  //#endregion
648
658
  //#region src/kernel/repair.ts
649
659
  /** The env var that opts a deployment in, for one restart. */
@@ -681,11 +691,6 @@ async function repairInvariants(sql, metric, namespace) {
681
691
  object: spec.constraint
682
692
  });
683
693
  }
684
- await rescopeRecipientIndex(s, namespace);
685
- outcomes.push({
686
- kind: "repaired",
687
- object: "rows_agent_message_recipient_idx"
688
- });
689
694
  const duplicates = await countDuplicateKids(s, namespace);
690
695
  if (duplicates > 0) outcomes.push({
691
696
  kind: "skipped",
@@ -742,6 +747,12 @@ async function repairIfRequested(ctx, warn = console.warn) {
742
747
  ctx.metric("cortext_kernel.repair_declined", { reason: "target_unknown" });
743
748
  return null;
744
749
  }
750
+ const layout = await storeLayout(ctx.sql).catch(() => "legacy");
751
+ if (layout !== "legacy") {
752
+ warn(`[cortext-kernel] ${REPAIR_ENV_VAR} is set, but the store layout is ${layout}: the engine owns cortext's indexes and CHECKs there, and there are no objects on rows to repair. Nothing to do.`);
753
+ ctx.metric("cortext_kernel.repair_declined", { reason: layout });
754
+ return null;
755
+ }
745
756
  warn(`[cortext-kernel] ${REPAIR_ENV_VAR} is set — re-scoping kernel invariants to '${ctx.namespace}'. This takes ACCESS EXCLUSIVE on rows and briefly blocks writes.` + (ctx.namespace === "cortext" ? " Target matches the built-in scope literal, so this is a no-op rebuild." : ""));
746
757
  try {
747
758
  const outcomes = await repairInvariants(ctx.sql, ctx.metric, ctx.namespace);
@@ -786,7 +797,7 @@ async function repairIfRequested(ctx, warn = console.warn) {
786
797
  * by reflex: widen it only after reading what the new major changed.
787
798
  * `substrate-contract-parity.test.ts` holds the manifest to this literal.
788
799
  */
789
- const SUBSTRATE_CONTRACT_RANGE = ">=1.0.0-alpha.0 <2.0.0";
800
+ const SUBSTRATE_CONTRACT_RANGE = ">=1.0.0-alpha.12 <2.0.0";
790
801
 
791
802
  //#endregion
792
803
  //#region src/kernel/plugin.ts
@@ -796,6 +807,7 @@ function createCortextKernelPlugin() {
796
807
  contractVersion: SUBSTRATE_CONTRACT_RANGE,
797
808
  onKernelReady: async (ctx) => {
798
809
  await reportInvariantAudit(ctx);
810
+ if (!ctx.background) return;
799
811
  await repairIfRequested({
800
812
  sql: ctx.sql,
801
813
  namespace: resolveCortextNamespaces(ctx.coordinatorNamespacesByPlugin)?.[0] ?? null,
@@ -803,9 +815,9 @@ function createCortextKernelPlugin() {
803
815
  metric: ctx.metric
804
816
  });
805
817
  },
806
- migrations: [cortextBaselineMigration]
818
+ migrations: [cortextBaselineMigration, cortextDropEngineOwnedIndexesMigration]
807
819
  };
808
820
  }
809
821
 
810
822
  //#endregion
811
- export { CORTEXT_PROMOTED_COLUMNS as A, findingMessage as C, ARCHITECTURE_NAMESPACE as D, scopedNamespace as E, CORTEXT_COORDINATOR_NAME as O, auditObjects as S, resolveCortextNamespaces as T, TODO_LIFECYCLE_VOCAB as _, repairInvariants as a, INVARIANT_OBJECTS as b, cortextBaselineMigration as c, countDuplicateKids as d, rescopeHostInstallKeyIndex as f, AGENT_MESSAGE_STATUS_VOCAB as g, rescopeVocabConstraint as h, repairIfRequested as i, CORTEXT_NAMESPACE as k, VOCAB_CONSTRAINTS as l, rescopeRecipientIndex as m, SUBSTRATE_CONTRACT_RANGE as n, repairOutcomeMessage as o, rescopeKidUniqueIndex as p, REPAIR_ENV_VAR as r, repairRequested as s, createCortextKernelPlugin as t, countDuplicateHostInstallKeys as u, TODO_PRIORITY_VOCAB as v, reportInvariantAudit as w, auditNamespaceCoverage as x, TODO_RESOLUTION_VOCAB as y };
823
+ export { CORTEXT_NAMESPACE as A, auditObjects as C, scopedNamespace as D, resolveCortextNamespaces as E, ARCHITECTURE_NAMESPACE as O, auditNamespaceCoverage as S, reportInvariantAudit as T, AGENT_MESSAGE_STATUS_VOCAB as _, repairInvariants as a, TODO_RESOLUTION_VOCAB as b, ENGINE_OWNED_INDEXES as c, VOCAB_CONSTRAINTS as d, countDuplicateHostInstallKeys as f, rescopeVocabConstraint as g, rescopeKidUniqueIndex as h, repairIfRequested as i, CORTEXT_PROMOTED_COLUMNS as j, CORTEXT_COORDINATOR_NAME as k, cortextDropEngineOwnedIndexesMigration as l, rescopeHostInstallKeyIndex as m, SUBSTRATE_CONTRACT_RANGE as n, repairOutcomeMessage as o, countDuplicateKids as p, REPAIR_ENV_VAR as r, repairRequested as s, createCortextKernelPlugin as t, cortextBaselineMigration as u, TODO_LIFECYCLE_VOCAB as v, findingMessage as w, INVARIANT_OBJECTS as x, TODO_PRIORITY_VOCAB as y };
@@ -33,10 +33,9 @@
33
33
  * enum, or moving the substrate contract range are all breaks for someone holding
34
34
  * data or code against this schema.
35
35
  *
36
- * `indexed` is included and it is not cosmetic: un-indexing a field turns a
37
- * working `where` filter into an `unindexed_field` rejection. That is a
38
- * runtime break for a caller who changed nothing, and it would otherwise be
39
- * invisible to a version-bump decision.
36
+ * `indexed` is included as a review item, not a break: every field filters,
37
+ * so un-indexing one turns an index-served `where` into a scan of the type.
38
+ * The caller's query still works, but slower, and a human should weigh it.
40
39
  */
41
40
  /** One field, reduced to the properties a consumer can break against. */
42
41
  export interface SurfaceField {
@@ -1,5 +1,5 @@
1
- import { A as CORTEXT_PROMOTED_COLUMNS, D as ARCHITECTURE_NAMESPACE, O as CORTEXT_COORDINATOR_NAME, _ as TODO_LIFECYCLE_VOCAB, g as AGENT_MESSAGE_STATUS_VOCAB, k as CORTEXT_NAMESPACE, n as SUBSTRATE_CONTRACT_RANGE, v as TODO_PRIORITY_VOCAB, y as TODO_RESOLUTION_VOCAB } from "./plugin-27UpMnoE.js";
2
- import { JSON_HEADERS, RESERVED_AUTOCRUD_FIELDS, ResolverFrameError, and, applyUpdatePatch, createResolverWithEdges, defineNamespace, deleteResolver, denyAll, denyVerbs, field, formatQualifiedKey, getResolver, grantOf, grantedIn, jsonResponse, listResolver, makeCtx, or, queryAll, requireIdentity, resolveEntityRow, resolveIdentity, selectAll, updateResolver } from "@mnmal-ai/substrate/sdk";
1
+ import { A as CORTEXT_NAMESPACE, O as ARCHITECTURE_NAMESPACE, _ as AGENT_MESSAGE_STATUS_VOCAB, b as TODO_RESOLUTION_VOCAB, j as CORTEXT_PROMOTED_COLUMNS, k as CORTEXT_COORDINATOR_NAME, n as SUBSTRATE_CONTRACT_RANGE, v as TODO_LIFECYCLE_VOCAB, y as TODO_PRIORITY_VOCAB } from "./plugin-BtBVkZpZ.js";
2
+ import { JSON_HEADERS, RESERVED_AUTOCRUD_FIELDS, ResolverFrameError, and, createResolverWithEdges, defineNamespace, deleteResolver, denyAll, denyVerbs, field, formatQualifiedKey, getResolver, grantOf, grantedIn, jsonResponse, listResolver, makeCtx, or, queryAll, requireIdentity, resolveEntityRow, resolveIdentity, selectAll, updateResolver } from "@mnmal-ai/substrate/sdk";
3
3
 
4
4
  //#region src/auth-policy.ts
5
5
  /**
@@ -163,8 +163,12 @@ function parseBody(req) {
163
163
  } catch (err) {
164
164
  return { error: err instanceof Error ? err.message : "invalid JSON" };
165
165
  }
166
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return { dryRun: false };
167
- return { dryRun: parsed.dryRun === true };
166
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return { error: "body must be a JSON object, e.g. {\"dryRun\": true}" };
167
+ const unknown = Object.keys(parsed).filter((k) => k !== "dryRun");
168
+ if (unknown.length > 0) return { error: `unknown field(s): ${unknown.join(", ")}` };
169
+ const dryRun = parsed.dryRun;
170
+ if (dryRun !== void 0 && typeof dryRun !== "boolean") return { error: "\"dryRun\" must be true or false" };
171
+ return { dryRun: dryRun === true };
168
172
  }
169
173
  function registerCompactRoute(router, ctx, retention, namespace, auth = DEFAULT_AUTH) {
170
174
  router.post("/compact", async (req) => {
@@ -295,10 +299,44 @@ function messageParticipantsOnly(installCtx, name, inner) {
295
299
  };
296
300
  }
297
301
  /**
302
+ * Whether `host` names the machine in `kid`'s `@host` part, ignoring case.
303
+ * A kid with no `@` names no machine, so it may carry no host.
304
+ */
305
+ function hostMatchesKid(host, kid) {
306
+ if (host === void 0 || host === null) return true;
307
+ if (typeof kid !== "string" || !kid.includes("@")) return false;
308
+ return String(host).toLowerCase() === kid.slice(kid.lastIndexOf("@") + 1).toLowerCase();
309
+ }
310
+ function refuseForeignHost(name, host, kid) {
311
+ if (hostMatchesKid(host, kid)) return;
312
+ throw new ResolverFrameError("operator_only", `${name}: 'host' must be the machine in the kid ('${String(kid)}'); only an operator may set another`, { field: "host" });
313
+ }
314
+ /**
315
+ * AgentIdentity create: a non-operator may not file a row whose `host` is a
316
+ * machine other than the one its kid names. The fleet audit trusts `host`
317
+ * (and `roaming` on that host) when it decides who may write a host's
318
+ * HostInstall rows.
319
+ */
320
+ function identityCreateGuard(installCtx, name, inner) {
321
+ return async (ctx, args) => {
322
+ const params = args.params ?? {};
323
+ if (!isOperator(ctx)) refuseForeignHost(name, params.host, params.kid);
324
+ if (typeof params.kid === "string") {
325
+ const { rows } = await (args.dataset ?? installCtx.dataset).read("AgentIdentity", {
326
+ where: { kid: { eq: params.kid } },
327
+ limit: 1
328
+ });
329
+ if (rows.length > 0) throw new ResolverFrameError("duplicate_kid", `${name}: an AgentIdentity row for '${params.kid}' already exists; update it instead`, { field: "kid" });
330
+ }
331
+ return inner(ctx, args);
332
+ };
333
+ }
334
+ /**
298
335
  * AgentIdentity update: the identity itself (the caller's kid is the row's
299
336
  * kid) or an operator. `kid` is the row's identity, so changing it is an
300
337
  * operator act even on your own row: otherwise a row could be renamed onto a
301
- * kid whose mail it would then receive.
338
+ * kid whose mail it would then receive. `host` must stay the kid's machine,
339
+ * and reactivating a retired row is an operator act.
302
340
  */
303
341
  function identitySelfOnly(installCtx, name, inner) {
304
342
  return async (ctx, args) => {
@@ -309,10 +347,45 @@ function identitySelfOnly(installCtx, name, inner) {
309
347
  if (row === null) return inner(ctx, args);
310
348
  const params = args.params ?? {};
311
349
  if ("kid" in params && params.kid !== row.kid) throw new ResolverFrameError("operator_only", `${name}: 'kid' is the row's identity and only an operator may change it`, { field: "kid" });
312
- if (callerKid(ctx) === row.kid) return inner(ctx, args);
350
+ if (callerKid(ctx) === row.kid) {
351
+ if ("host" in params || "roaming" in params || "project" in params) refuseForeignHost(name, "host" in params ? params.host : row.host, row.kid);
352
+ if (params.active === true && row.active === false) throw new ResolverFrameError("operator_only", `${name}: a retired identity may only be reactivated by an operator`, { field: "active" });
353
+ return inner(ctx, args);
354
+ }
313
355
  throw new ResolverFrameError("not_self", `${name}: only '${String(row.kid)}' itself, or an operator, may edit this identity row`, { id: row.id });
314
356
  };
315
357
  }
358
+ /**
359
+ * Todo create/update: `resolution` says why a CLOSED Todo stopped, so a live
360
+ * Todo carries none. A resolution with a live resulting status is refused,
361
+ * and moving a Todo back to a live status clears the one it had.
362
+ */
363
+ function todoResolutionGuard(installCtx, name, inner, statusAliases) {
364
+ const canon = (v) => {
365
+ const s = String(v).trim().toLowerCase();
366
+ return statusAliases[s] ?? s;
367
+ };
368
+ return async (ctx, args) => {
369
+ const params = { ...args.params ?? {} };
370
+ const statusGiven = params.status !== void 0 && params.status !== null;
371
+ let resulting = statusGiven ? canon(params.status) : null;
372
+ let current = null;
373
+ const id = rowId(args);
374
+ if (id !== void 0) {
375
+ const ds = args.dataset ?? installCtx.dataset;
376
+ current = await resolveEntityRow(ds, "Todo", id);
377
+ if (resulting === null && current !== null) resulting = canon(current.status);
378
+ }
379
+ if (resulting === null) resulting = "open";
380
+ if (resulting === "closed") return inner(ctx, args);
381
+ if (params.resolution !== void 0 && params.resolution !== null) throw new ResolverFrameError("resolution_on_live_todo", `${name}: 'resolution' is for closed Todos; this one would be '${resulting}'`, { field: "resolution" });
382
+ if (statusGiven && current !== null && current.resolution != null && !("resolution" in params)) params.resolution = null;
383
+ return inner(ctx, {
384
+ ...args,
385
+ params
386
+ });
387
+ };
388
+ }
316
389
  /** AgentIdentity delete: operators only. Mail hangs off these rows. */
317
390
  function operatorOnly(name, inner) {
318
391
  return async (ctx, args) => {
@@ -541,12 +614,27 @@ function transitionResolver(installCtx, target) {
541
614
  if (identityRow === void 0) throw new ResolverFrameError("no_recipient", `${name}: the recipient identity row no longer exists`, { id: message.id });
542
615
  const actor = resolveIdentity(ctx);
543
616
  if (actor.id !== identityRow.kid) throw new ResolverFrameError("not_recipient", `${name}: only the recipient ('${String(identityRow.kid)}') may transition this message`, { recipient: identityRow.kid });
544
- if (isNoOp(message.status)) return message;
545
- const at = ctx.now.toISOString();
546
- return applyUpdatePatch(ds, TYPE, message.id, {
547
- status: target,
548
- ...stamp(actor, at)
549
- }, ctx, args.onWrite);
617
+ let current = message;
618
+ for (let attempt = 0; attempt < 3 && current !== null; attempt++) {
619
+ if (isNoOp(current.status)) return current;
620
+ const { matched, row } = await ds.conditionalUpdate(TYPE, current.id, { status: current.status }, {
621
+ status: target,
622
+ ...stamp(actor, ctx.now.toISOString())
623
+ }, actor, ctx.now);
624
+ if (matched && row !== null) {
625
+ args.onWrite?.({
626
+ ns: ds.namespace,
627
+ type: TYPE,
628
+ id: row.id,
629
+ operation: "update",
630
+ row
631
+ });
632
+ return row;
633
+ }
634
+ current = await resolveEntityRow(ds, TYPE, message.id);
635
+ }
636
+ if (current === null) return null;
637
+ throw new ResolverFrameError("conflict", `${name}: the message kept changing while it was being transitioned; retry`, { id: message.id });
550
638
  };
551
639
  }
552
640
  /**
@@ -844,6 +932,11 @@ const HOST_INSTALL_CHECK_STATES = [
844
932
  "warn",
845
933
  "unknown"
846
934
  ];
935
+ const TASK_STATUS_VOCAB = [
936
+ "active",
937
+ "paused",
938
+ "done"
939
+ ];
847
940
  const CONCEPT_NAME_TTL_MS = 6e4;
848
941
  /** Long-form length past which `embed.source` appends the long form to the synopsis. */
849
942
  const LONGFORM_EMBED_CHARS = 1200;
@@ -989,7 +1082,8 @@ function buildClaudeNamespace(ns) {
989
1082
  area: field.string(),
990
1083
  status: field.vocab(TODO_LIFECYCLE_VOCAB, {
991
1084
  aliases: TODO_STATUS_ALIASES,
992
- implies: TODO_STATUS_IMPLIES
1085
+ implies: TODO_STATUS_IMPLIES,
1086
+ indexed: true
993
1087
  }),
994
1088
  resolution: field.vocab(TODO_RESOLUTION_VOCAB, {
995
1089
  aliases: TODO_RESOLUTION_ALIASES,
@@ -1026,7 +1120,10 @@ function buildClaudeNamespace(ns) {
1026
1120
  nullable: true,
1027
1121
  onDelete: "set_null"
1028
1122
  }),
1029
- project: field.string({ nullable: true })
1123
+ project: field.string({
1124
+ nullable: true,
1125
+ indexed: true
1126
+ })
1030
1127
  } },
1031
1128
  AgentRule: {
1032
1129
  fields: {
@@ -1045,7 +1142,10 @@ function buildClaudeNamespace(ns) {
1045
1142
  onDelete: "set_null"
1046
1143
  }),
1047
1144
  archived: field.boolean({ default: false }),
1048
- project: field.string({ nullable: true })
1145
+ project: field.string({
1146
+ nullable: true,
1147
+ indexed: true
1148
+ })
1049
1149
  },
1050
1150
  embed: { source: (r) => {
1051
1151
  return `${embedText(r.synopsis, typeof r.rule === "string" ? r.rule : "")}${typeof r.category === "string" && r.category.length > 0 ? ` [${r.category}]` : ""}`;
@@ -1098,7 +1198,10 @@ function buildClaudeNamespace(ns) {
1098
1198
  body: field.string({ indexed: false }),
1099
1199
  synopsis: field.string({ nullable: true }),
1100
1200
  archived: field.boolean({ default: false }),
1101
- project: field.string({ nullable: true })
1201
+ project: field.string({
1202
+ nullable: true,
1203
+ indexed: true
1204
+ })
1102
1205
  },
1103
1206
  embed: { source: (r) => {
1104
1207
  const desc = typeof r.description === "string" ? r.description : "";
@@ -1115,7 +1218,10 @@ function buildClaudeNamespace(ns) {
1115
1218
  indexed: false
1116
1219
  }),
1117
1220
  authoredBy: field.struct({ kind: field.string() }),
1118
- project: field.string({ nullable: true }),
1221
+ project: field.string({
1222
+ nullable: true,
1223
+ indexed: true
1224
+ }),
1119
1225
  archived: field.boolean({ default: false })
1120
1226
  },
1121
1227
  embed: { source: (r) => embedText(r.synopsis, typeof r.body === "string" ? r.body : "") },
@@ -1157,19 +1263,27 @@ function buildClaudeNamespace(ns) {
1157
1263
  version: field.string(),
1158
1264
  active: field.boolean()
1159
1265
  } },
1160
- AgentIdentity: { fields: {
1161
- kid: field.string(),
1162
- project: field.string({ nullable: true }),
1163
- host: field.string(),
1164
- displayName: field.string({ nullable: true }),
1165
- description: field.string({
1166
- nullable: true,
1167
- indexed: false
1168
- }),
1169
- active: field.boolean({ default: true }),
1170
- broadcastEligible: field.boolean({ default: true }),
1171
- roaming: field.boolean({ default: false })
1172
- } },
1266
+ AgentIdentity: {
1267
+ fields: {
1268
+ kid: field.string(),
1269
+ project: field.string({ nullable: true }),
1270
+ host: field.string(),
1271
+ displayName: field.string({ nullable: true }),
1272
+ description: field.string({
1273
+ nullable: true,
1274
+ indexed: false
1275
+ }),
1276
+ active: field.boolean({ default: true }),
1277
+ broadcastEligible: field.boolean({ default: true }),
1278
+ roaming: field.boolean({ default: false })
1279
+ },
1280
+ indexes: [{
1281
+ fields: ["kid"],
1282
+ unique: true,
1283
+ where: "live",
1284
+ name: "rows_agent_identity_kid_unique"
1285
+ }]
1286
+ },
1173
1287
  AgentMessage: {
1174
1288
  autoCrud: false,
1175
1289
  fields: {
@@ -1199,40 +1313,87 @@ function buildClaudeNamespace(ns) {
1199
1313
  archived: field.boolean({ default: false })
1200
1314
  }
1201
1315
  },
1202
- HostInstall: { fields: {
1203
- key: field.string(),
1204
- host: field.string(),
1205
- hostReported: field.string(),
1206
- project: field.string(),
1207
- kid: field.string({ nullable: true }),
1208
- harness: field.string(),
1209
- pluginVersion: field.string({ nullable: true }),
1210
- fragmentVersion: field.string({ nullable: true }),
1211
- fragmentDigest: field.string({ nullable: true }),
1212
- fragmentState: field.enum(HOST_INSTALL_FRAGMENT_STATES),
1213
- refreshedAt: field.date(),
1214
- pinState: field.enum(HOST_INSTALL_CHECK_STATES, { nullable: true }),
1215
- pinNote: field.string({
1216
- nullable: true,
1217
- indexed: false
1218
- }),
1219
- mcpJsonState: field.enum(HOST_INSTALL_CHECK_STATES, { nullable: true }),
1220
- mcpJsonNote: field.string({
1221
- nullable: true,
1222
- indexed: false
1223
- }),
1224
- authorityState: field.enum(HOST_INSTALL_CHECK_STATES, { nullable: true }),
1225
- authorityNote: field.string({
1316
+ HostInstall: {
1317
+ fields: {
1318
+ key: field.string(),
1319
+ host: field.string(),
1320
+ hostReported: field.string(),
1321
+ project: field.string(),
1322
+ kid: field.string({ nullable: true }),
1323
+ harness: field.string(),
1324
+ pluginVersion: field.string({ nullable: true }),
1325
+ fragmentVersion: field.string({ nullable: true }),
1326
+ fragmentDigest: field.string({ nullable: true }),
1327
+ fragmentState: field.enum(HOST_INSTALL_FRAGMENT_STATES),
1328
+ refreshedAt: field.date(),
1329
+ pinState: field.enum(HOST_INSTALL_CHECK_STATES, { nullable: true }),
1330
+ pinNote: field.string({
1331
+ nullable: true,
1332
+ indexed: false
1333
+ }),
1334
+ mcpJsonState: field.enum(HOST_INSTALL_CHECK_STATES, { nullable: true }),
1335
+ mcpJsonNote: field.string({
1336
+ nullable: true,
1337
+ indexed: false
1338
+ }),
1339
+ authorityState: field.enum(HOST_INSTALL_CHECK_STATES, { nullable: true }),
1340
+ authorityNote: field.string({
1341
+ nullable: true,
1342
+ indexed: false
1343
+ }),
1344
+ settingsState: field.enum(HOST_INSTALL_CHECK_STATES, { nullable: true }),
1345
+ settingsNote: field.string({
1346
+ nullable: true,
1347
+ indexed: false
1348
+ }),
1349
+ settingsEnv: field.json({ nullable: true })
1350
+ },
1351
+ indexes: [{
1352
+ fields: ["key"],
1353
+ unique: true,
1354
+ where: "live",
1355
+ name: "rows_host_install_key_unique"
1356
+ }]
1357
+ },
1358
+ Task: { fields: {
1359
+ title: field.string(),
1360
+ status: field.enum(TASK_STATUS_VOCAB, { default: "active" }),
1361
+ todo: field.crossRef(ns, "Todo", {
1226
1362
  nullable: true,
1227
- indexed: false
1363
+ onDelete: "set_null"
1228
1364
  }),
1229
- settingsState: field.enum(HOST_INSTALL_CHECK_STATES, { nullable: true }),
1230
- settingsNote: field.string({
1365
+ archived: field.boolean({ default: false }),
1366
+ project: field.string({
1231
1367
  nullable: true,
1232
- indexed: false
1233
- }),
1234
- settingsEnv: field.json({ nullable: true })
1235
- } }
1368
+ indexed: true
1369
+ })
1370
+ } },
1371
+ ExecutionCheckpoint: {
1372
+ fields: {
1373
+ task: field.crossRef(ns, "Task", { onDelete: "cascade" }),
1374
+ revision: field.number(),
1375
+ completed: field.string({ indexed: false }),
1376
+ nextAction: field.string({ indexed: false }),
1377
+ blockers: field.string({
1378
+ nullable: true,
1379
+ indexed: false
1380
+ }),
1381
+ refs: field.list(field.string(), { nullable: true }),
1382
+ project: field.string({
1383
+ nullable: true,
1384
+ indexed: true
1385
+ })
1386
+ },
1387
+ indexes: [{
1388
+ fields: ["task", {
1389
+ field: "revision",
1390
+ dir: "desc"
1391
+ }],
1392
+ unique: true,
1393
+ where: "live",
1394
+ name: "rows_execution_checkpoint_task_revision_unique"
1395
+ }]
1396
+ }
1236
1397
  };
1237
1398
  const definition = defineNamespace(ns, types, {
1238
1399
  annotations: { nl: CORTEXT_NL_RULINGS },
@@ -1244,7 +1405,13 @@ function buildClaudeNamespace(ns) {
1244
1405
  install(ctx) {
1245
1406
  baseInstall(ctx);
1246
1407
  installMailboxMutations(ctx, definition.types.AgentMessage.fields);
1247
- for (const [name, wrap] of [["updateAgentIdentity", (r) => identitySelfOnly(ctx, "updateAgentIdentity", r)], ["deleteAgentIdentity", (r) => operatorOnly("deleteAgentIdentity", r)]]) {
1408
+ for (const [name, wrap] of [
1409
+ ["createAgentIdentity", (r) => identityCreateGuard(ctx, "createAgentIdentity", r)],
1410
+ ["updateAgentIdentity", (r) => identitySelfOnly(ctx, "updateAgentIdentity", r)],
1411
+ ["deleteAgentIdentity", (r) => operatorOnly("deleteAgentIdentity", r)],
1412
+ ["createTodo", (r) => todoResolutionGuard(ctx, "createTodo", r, TODO_STATUS_ALIASES)],
1413
+ ["updateTodo", (r) => todoResolutionGuard(ctx, "updateTodo", r, TODO_STATUS_ALIASES)]
1414
+ ]) {
1248
1415
  const entry = ctx.registry.get(name);
1249
1416
  if (entry === void 0) throw new Error(`cortext: autoCRUD did not register ${name}`);
1250
1417
  ctx.registry.replace(name, {
@@ -1270,7 +1437,9 @@ const CLAUDE_TYPE_NAMES = [
1270
1437
  "Skill",
1271
1438
  "AgentIdentity",
1272
1439
  "AgentMessage",
1273
- "HostInstall"
1440
+ "HostInstall",
1441
+ "Task",
1442
+ "ExecutionCheckpoint"
1274
1443
  ];
1275
1444
  /**
1276
1445
  * Architecture namespace — Claude-lineage code-review findings. Kept separate
@@ -1567,6 +1736,32 @@ function buildBody(namespace, retention) {
1567
1736
  settingsEnv: "json — allowlisted non-secret switch values, plus whether the identity keys are set (nullable)"
1568
1737
  },
1569
1738
  writeWhen: ["Written by the cold-start of every session with an agent identity; agents read it, they do not write it"]
1739
+ },
1740
+ Task: {
1741
+ purpose: "A unit of work in flight: the stable identity a fresh session resumes by after a reset, independent of session id. Not a backlog ticket; link one via `todo`",
1742
+ fields: {
1743
+ id: "auto-generated",
1744
+ title: "what the work is",
1745
+ status: `${vocabUnion(TASK_STATUS_VOCAB)} (default active)`,
1746
+ todo: "UUID crossRef to the Todo this work implements, set_null on delete (nullable)",
1747
+ archived: "true if retired",
1748
+ project: "repo basename; null = cross-cutting"
1749
+ },
1750
+ writeWhen: ["When starting work that may outlive the context window", "Flip status to done when the work is finished, paused when set aside"]
1751
+ },
1752
+ ExecutionCheckpoint: {
1753
+ purpose: "One acknowledged step of a Task: what was completed and the next action. The latest is the Task's highest revision",
1754
+ fields: {
1755
+ id: "auto-generated",
1756
+ task: "UUID crossRef to the Task, cascade on delete",
1757
+ revision: "one higher than the Task's latest; unique per Task, so a second write at the same revision is refused",
1758
+ completed: "what this step finished (not indexed)",
1759
+ nextAction: "the exact next operation (not indexed)",
1760
+ blockers: "what stands in the way (not indexed, nullable)",
1761
+ refs: "file paths, #shortIds, PR or commit references (nullable)",
1762
+ project: "repo basename; null = cross-cutting"
1763
+ },
1764
+ writeWhen: ["After a completed subtask, a blocker, or before a reset; point at files and rows, never retell them", "Treat the step as done only once the write is acknowledged; on a refused revision, re-read the latest and retry"]
1570
1765
  }
1571
1766
  },
1572
1767
  mutations: buildMutations(),
@@ -1592,7 +1787,11 @@ function buildBody(namespace, retention) {
1592
1787
  description: "Load recent context at the start of each session",
1593
1788
  query: {
1594
1789
  SessionLog: {
1595
- params: { limit: 3 },
1790
+ params: {
1791
+ where: { archived: { eq: false } },
1792
+ orderBy: [{ createdAt: "desc" }],
1793
+ limit: 3
1794
+ },
1596
1795
  fields: {
1597
1796
  id: true,
1598
1797
  branch: true,
@@ -1602,7 +1801,11 @@ function buildBody(namespace, retention) {
1602
1801
  }
1603
1802
  },
1604
1803
  Decision: {
1605
- params: { limit: 80 },
1804
+ params: {
1805
+ where: { archived: { eq: false } },
1806
+ orderBy: [{ createdAt: "desc" }],
1807
+ limit: 80
1808
+ },
1606
1809
  fields: {
1607
1810
  id: true,
1608
1811
  topic: true,
@@ -1615,7 +1818,18 @@ function buildBody(namespace, retention) {
1615
1818
  }
1616
1819
  },
1617
1820
  Todo: {
1618
- params: { limit: 50 },
1821
+ params: {
1822
+ where: {
1823
+ archived: { eq: false },
1824
+ status: { in: [
1825
+ "open",
1826
+ "in_progress",
1827
+ "blocked"
1828
+ ] }
1829
+ },
1830
+ orderBy: [{ createdAt: "desc" }],
1831
+ limit: 50
1832
+ },
1619
1833
  fields: {
1620
1834
  id: true,
1621
1835
  title: true,
@@ -1631,7 +1845,7 @@ function buildBody(namespace, retention) {
1631
1845
  surfaceToUser: [
1632
1846
  "The 3 most recent non-archived SessionLog entries",
1633
1847
  "Non-archived Decisions",
1634
- "Todos with status 'open' or 'blocked'",
1848
+ "Todos with status 'open', 'in_progress' or 'blocked'",
1635
1849
  "Skip archived records silently"
1636
1850
  ]
1637
1851
  } },
@@ -1697,10 +1911,9 @@ function registerOnboardRoute(router, _ctx, retention, namespace) {
1697
1911
  * enum, or moving the substrate contract range are all breaks for someone holding
1698
1912
  * data or code against this schema.
1699
1913
  *
1700
- * `indexed` is included and it is not cosmetic: un-indexing a field turns a
1701
- * working `where` filter into an `unindexed_field` rejection. That is a
1702
- * runtime break for a caller who changed nothing, and it would otherwise be
1703
- * invisible to a version-bump decision.
1914
+ * `indexed` is included as a review item, not a break: every field filters,
1915
+ * so un-indexing one turns an index-served `where` into a scan of the type.
1916
+ * The caller's query still works, but slower, and a human should weigh it.
1704
1917
  */
1705
1918
  function reduceField(raw) {
1706
1919
  const nullable = raw.nullable === true;
@@ -1791,7 +2004,7 @@ function classifySurfaceChange(before, after) {
1791
2004
  if (!bf.required && af.required) breaking.push(`field became REQUIRED: ${at}`);
1792
2005
  if (bf.required && !af.required) additive.push(`field relaxed to optional: ${at}`);
1793
2006
  if (bf.nullable && !af.nullable) breaking.push(`field no longer nullable: ${at}`);
1794
- if (bf.indexed && !af.indexed) breaking.push(`field no longer indexed: ${at}`);
2007
+ if (bf.indexed && !af.indexed) review.push(`field no longer indexed: ${at}`);
1795
2008
  if (!bf.indexed && af.indexed) additive.push(`field now indexed: ${at}`);
1796
2009
  const bEnum = bf.enum ?? null;
1797
2010
  const aEnum = af.enum ?? null;
@@ -1928,4 +2141,4 @@ function createCortextPlugin(cfg) {
1928
2141
  }
1929
2142
 
1930
2143
  //#endregion
1931
- export { runCompact as C, isMonotonicAuth as E, registerCompactRoute as S, buildAuthPolicies as T, TODO_RESOLUTION_ALIASES as _, classSatisfies as a, buildArchitectureNamespace as b, AGENT_RULE_CATEGORY_VOCAB as c, ARCHITECTURE_TYPE_NAMES as d, CLAUDE_TYPE_NAMES as f, HOST_INSTALL_FRAGMENT_STATES as g, HOST_INSTALL_CHECK_STATES as h, bumpClass as i, AGENT_RULE_SEVERITY_ALIASES as l, DECISION_STATUS_VOCAB as m, inferTodoResolution as n, classifySurfaceChange as o, DECISION_STATUS_ALIASES as p, buildReleaseSurface as r, registerOnboardRoute as s, createCortextPlugin as t, AGENT_RULE_SEVERITY_VOCAB as u, TODO_STATUS_ALIASES as v, DEFAULT_AUTH as w, buildClaudeNamespace as x, TODO_STATUS_IMPLIES as y };
2144
+ export { registerCompactRoute as C, isMonotonicAuth as D, buildAuthPolicies as E, buildClaudeNamespace as S, DEFAULT_AUTH as T, TASK_STATUS_VOCAB as _, classSatisfies as a, TODO_STATUS_IMPLIES as b, AGENT_RULE_CATEGORY_VOCAB as c, ARCHITECTURE_TYPE_NAMES as d, CLAUDE_TYPE_NAMES as f, HOST_INSTALL_FRAGMENT_STATES as g, HOST_INSTALL_CHECK_STATES as h, bumpClass as i, AGENT_RULE_SEVERITY_ALIASES as l, DECISION_STATUS_VOCAB as m, inferTodoResolution as n, classifySurfaceChange as o, DECISION_STATUS_ALIASES as p, buildReleaseSurface as r, registerOnboardRoute as s, createCortextPlugin as t, AGENT_RULE_SEVERITY_VOCAB as u, TODO_RESOLUTION_ALIASES as v, runCompact as w, buildArchitectureNamespace as x, TODO_STATUS_ALIASES as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mnmal-ai/cortext",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.0.0-alpha.11",
4
4
  "description": "Cortext: the agent-neutral coordination context store for Substrate (Todos, Decisions, SessionLogs, AgentRules, Memory, Concepts, Councils, …) plus architecture findings. One package carrying two plugins: the coordinator that registers the namespaces, and the kernel plugin that owns their Postgres invariants.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -62,10 +62,10 @@
62
62
  "zod": "^4.6.5"
63
63
  },
64
64
  "peerDependencies": {
65
- "@mnmal-ai/substrate": ">=1.0.0-alpha.0 <2.0.0"
65
+ "@mnmal-ai/substrate": ">=1.0.0-alpha.12 <2.0.0"
66
66
  },
67
67
  "devDependencies": {
68
- "@mnmal-ai/substrate": "1.0.0-alpha.0",
68
+ "@mnmal-ai/substrate": "1.0.0-alpha.12",
69
69
  "@types/node": "^26.6.4",
70
70
  "tsdown": "^0.23.0",
71
71
  "typescript": "^7.0.2",