@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 +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/kernel/cortext-namespace.d.ts +2 -2
- package/dist/kernel/index.d.ts +2 -1
- package/dist/kernel/invariant-audit.d.ts +4 -3
- package/dist/kernel/layout.d.ts +22 -0
- package/dist/kernel/migrations/0001_baseline.d.ts +4 -0
- package/dist/kernel/migrations/0002_drop_engine_owned_indexes.d.ts +12 -0
- package/dist/kernel/rescope.d.ts +0 -8
- package/dist/kernel/substrate-contract.d.ts +1 -1
- package/dist/kernel-bootstrap.js +1 -1
- package/dist/kernel.js +2 -2
- package/dist/namespace.d.ts +2 -1
- package/dist/participants.d.ts +15 -1
- package/dist/{plugin-27UpMnoE.js → plugin-BtBVkZpZ.js} +73 -61
- package/dist/release-surface.d.ts +3 -4
- package/dist/{src-BKaGTnRw.js → src-DJA6hyG-.js} +286 -73
- package/package.json +3 -3
package/dist/bootstrap.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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
|
|
2
|
-
import {
|
|
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
|
-
*
|
|
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
|
|
53
|
+
* left as a bare literal inside that migration.
|
|
54
54
|
*/
|
|
55
55
|
export declare const ARCHITECTURE_NAMESPACE = "architecture";
|
|
56
56
|
/**
|
package/dist/kernel/index.d.ts
CHANGED
|
@@ -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,
|
|
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:
|
|
55
|
-
*
|
|
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;
|
package/dist/kernel/rescope.d.ts
CHANGED
|
@@ -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.
|
|
19
|
+
export declare const SUBSTRATE_CONTRACT_RANGE = ">=1.0.0-alpha.12 <2.0.0";
|
package/dist/kernel-bootstrap.js
CHANGED
package/dist/kernel.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as
|
|
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,
|
|
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 };
|
package/dist/namespace.d.ts
CHANGED
|
@@ -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,
|
package/dist/participants.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
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:
|
|
145
|
-
*
|
|
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 !==
|
|
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 '${
|
|
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 (
|
|
610
|
-
|
|
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
|
-
|
|
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
|
|
630
|
-
ON rows (namespace, type,
|
|
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.
|
|
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 {
|
|
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
|
|
37
|
-
*
|
|
38
|
-
*
|
|
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
|
|
2
|
-
import { JSON_HEADERS, RESERVED_AUTOCRUD_FIELDS, ResolverFrameError, and,
|
|
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:
|
|
167
|
-
|
|
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)
|
|
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
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
status:
|
|
548
|
-
|
|
549
|
-
|
|
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({
|
|
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({
|
|
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({
|
|
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({
|
|
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: {
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
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: {
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
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
|
-
|
|
1363
|
+
onDelete: "set_null"
|
|
1228
1364
|
}),
|
|
1229
|
-
|
|
1230
|
-
|
|
1365
|
+
archived: field.boolean({ default: false }),
|
|
1366
|
+
project: field.string({
|
|
1231
1367
|
nullable: true,
|
|
1232
|
-
indexed:
|
|
1233
|
-
})
|
|
1234
|
-
|
|
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 [
|
|
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: {
|
|
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: {
|
|
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: {
|
|
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
|
|
1701
|
-
*
|
|
1702
|
-
*
|
|
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)
|
|
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 {
|
|
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.
|
|
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.
|
|
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.
|
|
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",
|