@cleocode/core 2026.10.10-canary.2 → 2026.10.10
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/build-provenance.json +2 -2
- package/dist/cloud/auto-sync-command.d.ts +28 -4
- package/dist/cloud/auto-sync-command.d.ts.map +1 -1
- package/dist/cloud/auto-sync-command.js +55 -10
- package/dist/cloud/auto-sync-command.js.map +1 -1
- package/dist/cloud/auto-sync.d.ts +12 -0
- package/dist/cloud/auto-sync.d.ts.map +1 -1
- package/dist/cloud/auto-sync.js +22 -0
- package/dist/cloud/auto-sync.js.map +1 -1
- package/dist/cloud/nexus-cloud-status.d.ts.map +1 -1
- package/dist/cloud/nexus-cloud-status.js +5 -0
- package/dist/cloud/nexus-cloud-status.js.map +1 -1
- package/dist/cloud/nexus-vault.d.ts.map +1 -1
- package/dist/cloud/nexus-vault.js +31 -3
- package/dist/cloud/nexus-vault.js.map +1 -1
- package/dist/cloud/secret-seal.d.ts +69 -0
- package/dist/cloud/secret-seal.d.ts.map +1 -0
- package/dist/cloud/secret-seal.js +148 -0
- package/dist/cloud/secret-seal.js.map +1 -0
- package/dist/docs/fetch-result.d.ts +7 -0
- package/dist/docs/fetch-result.d.ts.map +1 -1
- package/dist/docs/fetch-result.js +2 -1
- package/dist/docs/fetch-result.js.map +1 -1
- package/dist/docs/triage.d.ts.map +1 -1
- package/dist/docs/triage.js +13 -0
- package/dist/docs/triage.js.map +1 -1
- package/dist/memory/engine-compat.d.ts.map +1 -1
- package/dist/memory/engine-compat.js +14 -2
- package/dist/memory/engine-compat.js.map +1 -1
- package/dist/memory/graph-auto-populate.d.ts +22 -2
- package/dist/memory/graph-auto-populate.d.ts.map +1 -1
- package/dist/memory/graph-auto-populate.js +21 -22
- package/dist/memory/graph-auto-populate.js.map +1 -1
- package/dist/paths.d.ts +2 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +12 -1
- package/dist/paths.js.map +1 -1
- package/dist/release/hooks-v1-promotion.d.ts +66 -0
- package/dist/release/hooks-v1-promotion.d.ts.map +1 -0
- package/dist/release/hooks-v1-promotion.js +115 -0
- package/dist/release/hooks-v1-promotion.js.map +1 -0
- package/dist/release/plan.d.ts +5 -0
- package/dist/release/plan.d.ts.map +1 -1
- package/dist/release/plan.js +26 -0
- package/dist/release/plan.js.map +1 -1
- package/dist/release/pr-evidence.d.ts +27 -0
- package/dist/release/pr-evidence.d.ts.map +1 -1
- package/dist/release/pr-evidence.js +75 -3
- package/dist/release/pr-evidence.js.map +1 -1
- package/dist/resources/admission-ledger.d.ts +34 -0
- package/dist/resources/admission-ledger.d.ts.map +1 -1
- package/dist/resources/admission-ledger.js +60 -7
- package/dist/resources/admission-ledger.js.map +1 -1
- package/dist/resources/run-governed.d.ts.map +1 -1
- package/dist/resources/run-governed.js +4 -2
- package/dist/resources/run-governed.js.map +1 -1
- package/dist/store/decision-adr-links.d.ts +152 -0
- package/dist/store/decision-adr-links.d.ts.map +1 -0
- package/dist/store/decision-adr-links.js +258 -0
- package/dist/store/decision-adr-links.js.map +1 -0
- package/dist/store/row-identity-registry.d.ts +4 -4
- package/dist/store/row-identity-registry.d.ts.map +1 -1
- package/dist/store/row-identity-registry.js +50 -8
- package/dist/store/row-identity-registry.js.map +1 -1
- package/dist/store/schema/cleo-global/accounts.d.ts +19 -1
- package/dist/store/schema/cleo-global/accounts.d.ts.map +1 -1
- package/dist/store/schema/cleo-global/accounts.js +7 -2
- package/dist/store/schema/cleo-global/accounts.js.map +1 -1
- package/dist/store/schema/cleo-global/services.d.ts +48 -0
- package/dist/store/schema/cleo-global/services.d.ts.map +1 -1
- package/dist/store/schema/cleo-global/services.js +10 -1
- package/dist/store/schema/cleo-global/services.js.map +1 -1
- package/dist/store/schema/cleo-project/docs.d.ts +162 -0
- package/dist/store/schema/cleo-project/docs.d.ts.map +1 -1
- package/dist/store/schema/cleo-project/docs.js +55 -1
- package/dist/store/schema/cleo-project/docs.js.map +1 -1
- package/dist/store/sync/apply/applier.d.ts +13 -0
- package/dist/store/sync/apply/applier.d.ts.map +1 -1
- package/dist/store/sync/apply/applier.js +43 -1
- package/dist/store/sync/apply/applier.js.map +1 -1
- package/dist/store/sync/apply/parent-delete.d.ts.map +1 -1
- package/dist/store/sync/apply/parent-delete.js +3 -0
- package/dist/store/sync/apply/parent-delete.js.map +1 -1
- package/dist/store/sync/merge/rules.d.ts +2 -1
- package/dist/store/sync/merge/rules.d.ts.map +1 -1
- package/dist/store/sync/merge/rules.js +18 -1
- package/dist/store/sync/merge/rules.js.map +1 -1
- package/dist/store/sync/pull.d.ts +6 -0
- package/dist/store/sync/pull.d.ts.map +1 -1
- package/dist/store/sync/pull.js +2 -0
- package/dist/store/sync/pull.js.map +1 -1
- package/dist/store/sync/push.d.ts +8 -0
- package/dist/store/sync/push.d.ts.map +1 -1
- package/dist/store/sync/push.js +5 -1
- package/dist/store/sync/push.js.map +1 -1
- package/dist/store/sync/sealer.d.ts +46 -0
- package/dist/store/sync/sealer.d.ts.map +1 -1
- package/dist/store/sync/sealer.js +123 -5
- package/dist/store/sync/sealer.js.map +1 -1
- package/dist/store/sync/timestamp-canon.d.ts.map +1 -1
- package/dist/store/sync/timestamp-canon.js +9 -1
- package/dist/store/sync/timestamp-canon.js.map +1 -1
- package/dist/store/table-classification.d.ts.map +1 -1
- package/dist/store/table-classification.js +6 -0
- package/dist/store/table-classification.js.map +1 -1
- package/dist/store/vault-manifest.d.ts.map +1 -1
- package/dist/store/vault-manifest.js +4 -0
- package/dist/store/vault-manifest.js.map +1 -1
- package/dist/store/with-retry.d.ts +3 -1
- package/dist/store/with-retry.d.ts.map +1 -1
- package/dist/store/with-retry.js +19 -5
- package/dist/store/with-retry.js.map +1 -1
- package/dist/tasks/affected-scope.d.ts +20 -0
- package/dist/tasks/affected-scope.d.ts.map +1 -1
- package/dist/tasks/affected-scope.js +37 -2
- package/dist/tasks/affected-scope.js.map +1 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/dist/tasks/complete.js +37 -20
- package/dist/tasks/complete.js.map +1 -1
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +14 -0
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/tasks/gate-runner.d.ts.map +1 -1
- package/dist/tasks/gate-runner.js +127 -32
- package/dist/tasks/gate-runner.js.map +1 -1
- package/dist/tasks/qa-run-binding.d.ts +35 -2
- package/dist/tasks/qa-run-binding.d.ts.map +1 -1
- package/dist/tasks/qa-run-binding.js +42 -19
- package/dist/tasks/qa-run-binding.js.map +1 -1
- package/dist/tasks/test-run-binding.d.ts +2 -1
- package/dist/tasks/test-run-binding.d.ts.map +1 -1
- package/dist/tasks/test-run-binding.js +25 -3
- package/dist/tasks/test-run-binding.js.map +1 -1
- package/dist/tasks/tool-semaphore.d.ts +7 -0
- package/dist/tasks/tool-semaphore.d.ts.map +1 -1
- package/dist/tasks/tool-semaphore.js +1 -1
- package/dist/tasks/tool-semaphore.js.map +1 -1
- package/migrations/drizzle-cleo-global/20261010190000_t13467-credential-uids/migration.sql +31 -0
- package/migrations/drizzle-cleo-project/20261012000000_t13359-decision-adr-links/migration.sql +63 -0
- package/package.json +13 -13
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical accessors for `decision_adr_links` — the two-way link table
|
|
3
|
+
* between `D####` brain ledger decisions and curated ADR docs
|
|
4
|
+
* (T13359 · spec `cleodocs-rearch-design` §4/§10 · D11165).
|
|
5
|
+
*
|
|
6
|
+
* Every write goes through the functions here, on the `openDualScopeDb`
|
|
7
|
+
* chokepoint's drizzle handle ({@link getDb}) — gate 3 (no raw opens) and
|
|
8
|
+
* gate 28 (no raw writes to syncing tables).
|
|
9
|
+
*
|
|
10
|
+
* Semantics implemented here (the settled tiered methodology, spec §4):
|
|
11
|
+
*
|
|
12
|
+
* - The `D####` ledger stays the operational authority; ADR docs are the
|
|
13
|
+
* curated architecture record, authored when a decision graduates.
|
|
14
|
+
* Neither tier is generated from the other — this table is the only
|
|
15
|
+
* cross-reference, and it starts EMPTY (AC4: no ledger rewrite, no
|
|
16
|
+
* backfill of the ~360 existing decisions).
|
|
17
|
+
* - Identity is NATURAL over (decision_id, adr_slug): both devices derive
|
|
18
|
+
* the same uid for one pair, so concurrent identical links coalesce via
|
|
19
|
+
* `ON CONFLICT DO NOTHING` instead of breaking a unique index on apply
|
|
20
|
+
* (spec §10).
|
|
21
|
+
* - APPEND-ONLY: no DELETE exists. An unlink is a one-way monotonic latch
|
|
22
|
+
* flipping `status` to 'revoked' — the row itself is the tombstone and
|
|
23
|
+
* survives sync/restore. A revoked pair is never re-linked (rejection,
|
|
24
|
+
* not resurrection), so the latch can never be observed half-flipped.
|
|
25
|
+
* The latch is enforced at MERGE too (T13597): `SYNC_MERGE_RULES` marks
|
|
26
|
+
* 'revoked' absorbing with no leave op, so a concurrent same-uid link op
|
|
27
|
+
* from another device is refused by the merge engine and cannot
|
|
28
|
+
* resurrect the tombstone by LWW.
|
|
29
|
+
* - Neither column is a DB-level FK: `decision_id` is a cross-DB soft
|
|
30
|
+
* reference (the ledger lives in brain.db) and `adr_slug` is a mutable
|
|
31
|
+
* doc alias (old slugs resolve through `conduit_doc_slug_aliases`).
|
|
32
|
+
*
|
|
33
|
+
* @module
|
|
34
|
+
* @task T13359
|
|
35
|
+
* @epic T13340
|
|
36
|
+
* @saga T13339
|
|
37
|
+
*/
|
|
38
|
+
import type { DecisionAdrLinkRow } from './schema/cleo-project/docs.js';
|
|
39
|
+
/** Link states. The revoke latch only ever moves `linked` → `revoked`. */
|
|
40
|
+
export declare const DECISION_ADR_LINK_STATUSES: readonly ["linked", "revoked"];
|
|
41
|
+
/** A live link state. */
|
|
42
|
+
export type DecisionAdrLinkStatus = (typeof DECISION_ADR_LINK_STATUSES)[number];
|
|
43
|
+
/**
|
|
44
|
+
* The natural row uid of one (decision, ADR) pair. Both devices derive this
|
|
45
|
+
* same value, which is what makes concurrent identical links coalesce.
|
|
46
|
+
*
|
|
47
|
+
* @param decisionId - The `D####` ledger decision id.
|
|
48
|
+
* @param adrSlug - The ADR doc slug.
|
|
49
|
+
* @returns The canonical natural uid.
|
|
50
|
+
*/
|
|
51
|
+
export declare function decisionAdrLinkUid(decisionId: string, adrSlug: string): string;
|
|
52
|
+
/** Input for {@link linkDecisionToAdr}. */
|
|
53
|
+
export interface LinkDecisionToAdrInput {
|
|
54
|
+
/** The `D####` ledger decision id. */
|
|
55
|
+
readonly decisionId: string;
|
|
56
|
+
/** The ADR doc slug (`adr-NNN-…`). */
|
|
57
|
+
readonly adrSlug: string;
|
|
58
|
+
/** Agent id creating the link; omitted when unknown. */
|
|
59
|
+
readonly linkedBy?: string;
|
|
60
|
+
}
|
|
61
|
+
/** Outcome of a {@link linkDecisionToAdr} call. */
|
|
62
|
+
export interface LinkDecisionToAdrResult {
|
|
63
|
+
/** The link row after the call. */
|
|
64
|
+
readonly row: DecisionAdrLinkRow;
|
|
65
|
+
/** True when this call inserted the row; false for an idempotent re-link. */
|
|
66
|
+
readonly inserted: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Link a `D####` decision to an ADR doc (append-only, natural identity).
|
|
70
|
+
*
|
|
71
|
+
* A repeat link of an already-linked pair is an idempotent no-op. A link of
|
|
72
|
+
* a REVOKED pair is refused: append-only means the tombstone latch never
|
|
73
|
+
* flips back, so re-linking would require resurrecting a dead row.
|
|
74
|
+
*
|
|
75
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
76
|
+
* @param input - The link facts.
|
|
77
|
+
* @returns The link row and whether it was inserted.
|
|
78
|
+
*/
|
|
79
|
+
export declare function linkDecisionToAdr(projectRoot: string, input: LinkDecisionToAdrInput): Promise<LinkDecisionToAdrResult>;
|
|
80
|
+
/** Input for {@link revokeDecisionAdrLink}. */
|
|
81
|
+
export interface RevokeDecisionAdrLinkInput {
|
|
82
|
+
/** The `D####` ledger decision id. */
|
|
83
|
+
readonly decisionId: string;
|
|
84
|
+
/** The ADR doc slug. */
|
|
85
|
+
readonly adrSlug: string;
|
|
86
|
+
/** Agent id revoking the link; omitted when unknown. */
|
|
87
|
+
readonly revokedBy?: string;
|
|
88
|
+
}
|
|
89
|
+
/** Outcome of a {@link revokeDecisionAdrLink} call. */
|
|
90
|
+
export interface RevokeDecisionAdrLinkResult {
|
|
91
|
+
/** The link row after the call. */
|
|
92
|
+
readonly row: DecisionAdrLinkRow;
|
|
93
|
+
/** True when this call flipped the latch; false for an idempotent re-revoke. */
|
|
94
|
+
readonly revoked: boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Revoke a link as a TOMBSTONE LATCH (never a DELETE, never a resurrection).
|
|
98
|
+
*
|
|
99
|
+
* `status` flips `linked` → `revoked` exactly once and `updated_at` records
|
|
100
|
+
* the tombstone instant; the row survives sync and restore as the permanent
|
|
101
|
+
* record that this pair was once linked and then unlinked. Re-revoking an
|
|
102
|
+
* already-revoked pair is an idempotent no-op. Revoking a pair that was
|
|
103
|
+
* never linked is refused — a tombstone must reference a real link.
|
|
104
|
+
*
|
|
105
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
106
|
+
* @param input - The revoke facts.
|
|
107
|
+
* @returns The link row and whether the latch flipped.
|
|
108
|
+
*/
|
|
109
|
+
export declare function revokeDecisionAdrLink(projectRoot: string, input: RevokeDecisionAdrLinkInput): Promise<RevokeDecisionAdrLinkResult>;
|
|
110
|
+
/** Read one link row by its natural key, any state. */
|
|
111
|
+
export declare function getDecisionAdrLink(projectRoot: string, decisionId: string, adrSlug: string): Promise<DecisionAdrLinkRow | null>;
|
|
112
|
+
/**
|
|
113
|
+
* The ADR docs linked to one decision — the decision→ADR direction of the
|
|
114
|
+
* two-way surfacing (spec §4: decision lookups surface their ADRs).
|
|
115
|
+
*
|
|
116
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
117
|
+
* @param decisionId - The `D####` ledger decision id.
|
|
118
|
+
* @param opts.includeRevoked - Also return tombstoned links (default false).
|
|
119
|
+
* @returns The link rows, ADR slug ascending.
|
|
120
|
+
*/
|
|
121
|
+
export declare function listAdrLinksForDecision(projectRoot: string, decisionId: string, opts?: {
|
|
122
|
+
includeRevoked?: boolean;
|
|
123
|
+
}): Promise<DecisionAdrLinkRow[]>;
|
|
124
|
+
/**
|
|
125
|
+
* The `D####` decisions linked to one ADR doc — the ADR→decision direction
|
|
126
|
+
* (`cleo docs fetch adr-NNN` surfaces its source decisions, spec §4 AC2).
|
|
127
|
+
*
|
|
128
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
129
|
+
* @param adrSlug - The ADR doc slug.
|
|
130
|
+
* @param opts.includeRevoked - Also return tombstoned links (default false).
|
|
131
|
+
* @returns The link rows, decision id ascending.
|
|
132
|
+
*/
|
|
133
|
+
export declare function listDecisionLinksForAdr(projectRoot: string, adrSlug: string, opts?: {
|
|
134
|
+
includeRevoked?: boolean;
|
|
135
|
+
}): Promise<DecisionAdrLinkRow[]>;
|
|
136
|
+
/** Optional `linkedAdrs` field attached by {@link attachLinkedAdrs}. */
|
|
137
|
+
export interface DecisionWithLinkedAdrs {
|
|
138
|
+
/** ADR doc slugs with a live link to this decision; absent when none. */
|
|
139
|
+
readonly linkedAdrs?: string[];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Attach each `D####` decision's linked ADR slugs — the decision→ADR
|
|
143
|
+
* direction of the two-way trace (spec §4: decision lookups surface their
|
|
144
|
+
* curated ADR records). Rows without a bare `D####` id pass through
|
|
145
|
+
* untouched.
|
|
146
|
+
*
|
|
147
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
148
|
+
* @param decisions - Decision rows as returned by the brain store.
|
|
149
|
+
* @returns The same rows, with `linkedAdrs` added where links exist.
|
|
150
|
+
*/
|
|
151
|
+
export declare function attachLinkedAdrs<T extends object>(projectRoot: string, decisions: readonly T[]): Promise<Array<T & DecisionWithLinkedAdrs>>;
|
|
152
|
+
//# sourceMappingURL=decision-adr-links.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-adr-links.d.ts","sourceRoot":"","sources":["../../src/store/decision-adr-links.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAMH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAIxE,0EAA0E;AAC1E,eAAO,MAAM,0BAA0B,gCAAiC,CAAC;AAEzE,yBAAyB;AACzB,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,CAAC,CAAC;AAkBhF;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,2CAA2C;AAC3C,MAAM,WAAW,sBAAsB;IACrC,sCAAsC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,sCAAsC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,mDAAmD;AACnD,MAAM,WAAW,uBAAuB;IACtC,mCAAmC;IACnC,QAAQ,CAAC,GAAG,EAAE,kBAAkB,CAAC;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;GAUG;AAEH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,uBAAuB,CAAC,CAgElC;AAED,+CAA+C;AAC/C,MAAM,WAAW,0BAA0B;IACzC,sCAAsC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wBAAwB;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wDAAwD;IACxD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,uDAAuD;AACvD,MAAM,WAAW,2BAA2B;IAC1C,mCAAmC;IACnC,QAAQ,CAAC,GAAG,EAAE,kBAAkB,CAAC;IACjC,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;;GAYG;AAEH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,2BAA2B,CAAC,CAwDtC;AAED,uDAAuD;AACvD,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAUpC;AAED;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,GAClC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAU/B;AAED;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,GAClC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAU/B;AAED,wEAAwE;AACxE,MAAM,WAAW,sBAAsB;IACrC,yEAAyE;IACzE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,SAAS,MAAM,EACrD,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,SAAS,CAAC,EAAE,GACtB,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAgB5C"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical accessors for `decision_adr_links` — the two-way link table
|
|
3
|
+
* between `D####` brain ledger decisions and curated ADR docs
|
|
4
|
+
* (T13359 · spec `cleodocs-rearch-design` §4/§10 · D11165).
|
|
5
|
+
*
|
|
6
|
+
* Every write goes through the functions here, on the `openDualScopeDb`
|
|
7
|
+
* chokepoint's drizzle handle ({@link getDb}) — gate 3 (no raw opens) and
|
|
8
|
+
* gate 28 (no raw writes to syncing tables).
|
|
9
|
+
*
|
|
10
|
+
* Semantics implemented here (the settled tiered methodology, spec §4):
|
|
11
|
+
*
|
|
12
|
+
* - The `D####` ledger stays the operational authority; ADR docs are the
|
|
13
|
+
* curated architecture record, authored when a decision graduates.
|
|
14
|
+
* Neither tier is generated from the other — this table is the only
|
|
15
|
+
* cross-reference, and it starts EMPTY (AC4: no ledger rewrite, no
|
|
16
|
+
* backfill of the ~360 existing decisions).
|
|
17
|
+
* - Identity is NATURAL over (decision_id, adr_slug): both devices derive
|
|
18
|
+
* the same uid for one pair, so concurrent identical links coalesce via
|
|
19
|
+
* `ON CONFLICT DO NOTHING` instead of breaking a unique index on apply
|
|
20
|
+
* (spec §10).
|
|
21
|
+
* - APPEND-ONLY: no DELETE exists. An unlink is a one-way monotonic latch
|
|
22
|
+
* flipping `status` to 'revoked' — the row itself is the tombstone and
|
|
23
|
+
* survives sync/restore. A revoked pair is never re-linked (rejection,
|
|
24
|
+
* not resurrection), so the latch can never be observed half-flipped.
|
|
25
|
+
* The latch is enforced at MERGE too (T13597): `SYNC_MERGE_RULES` marks
|
|
26
|
+
* 'revoked' absorbing with no leave op, so a concurrent same-uid link op
|
|
27
|
+
* from another device is refused by the merge engine and cannot
|
|
28
|
+
* resurrect the tombstone by LWW.
|
|
29
|
+
* - Neither column is a DB-level FK: `decision_id` is a cross-DB soft
|
|
30
|
+
* reference (the ledger lives in brain.db) and `adr_slug` is a mutable
|
|
31
|
+
* doc alias (old slugs resolve through `conduit_doc_slug_aliases`).
|
|
32
|
+
*
|
|
33
|
+
* @module
|
|
34
|
+
* @task T13359
|
|
35
|
+
* @epic T13340
|
|
36
|
+
* @saga T13339
|
|
37
|
+
*/
|
|
38
|
+
import { ExitCode } from '@cleocode/contracts/exit-codes.js';
|
|
39
|
+
import { and, eq } from 'drizzle-orm';
|
|
40
|
+
import { CleoError } from '../errors.js';
|
|
41
|
+
import { naturalRowUid } from './row-identity.js';
|
|
42
|
+
import { decisionAdrLinks } from './schema/cleo-project/docs.js';
|
|
43
|
+
import { getDb } from './sqlite.js';
|
|
44
|
+
/** Link states. The revoke latch only ever moves `linked` → `revoked`. */
|
|
45
|
+
export const DECISION_ADR_LINK_STATUSES = ['linked', 'revoked'];
|
|
46
|
+
/** Bare `D####` ledger decision id shape (real brain ids, per T13358). */
|
|
47
|
+
const DECISION_ID_PATTERN = /^D\d+$/;
|
|
48
|
+
/**
|
|
49
|
+
* Refuse an invalid input at the write boundary.
|
|
50
|
+
*
|
|
51
|
+
* @param name - Field name for the message.
|
|
52
|
+
* @param value - The value to check.
|
|
53
|
+
*/
|
|
54
|
+
function requireNonEmpty(name, value) {
|
|
55
|
+
if (value.trim().length === 0) {
|
|
56
|
+
// @sync-invariant none:input-shape a blank identifier is refused before any write; nothing was written
|
|
57
|
+
throw new CleoError(ExitCode.VALIDATION_ERROR, `${name} must be a non-empty string`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The natural row uid of one (decision, ADR) pair. Both devices derive this
|
|
62
|
+
* same value, which is what makes concurrent identical links coalesce.
|
|
63
|
+
*
|
|
64
|
+
* @param decisionId - The `D####` ledger decision id.
|
|
65
|
+
* @param adrSlug - The ADR doc slug.
|
|
66
|
+
* @returns The canonical natural uid.
|
|
67
|
+
*/
|
|
68
|
+
export function decisionAdrLinkUid(decisionId, adrSlug) {
|
|
69
|
+
return naturalRowUid('project', 'decision_adr_links', [decisionId, adrSlug]);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Link a `D####` decision to an ADR doc (append-only, natural identity).
|
|
73
|
+
*
|
|
74
|
+
* A repeat link of an already-linked pair is an idempotent no-op. A link of
|
|
75
|
+
* a REVOKED pair is refused: append-only means the tombstone latch never
|
|
76
|
+
* flips back, so re-linking would require resurrecting a dead row.
|
|
77
|
+
*
|
|
78
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
79
|
+
* @param input - The link facts.
|
|
80
|
+
* @returns The link row and whether it was inserted.
|
|
81
|
+
*/
|
|
82
|
+
// @sync-invariant docs.decision-adr-links.revoke-monotonic the read of the existing row and the conditional insert are one causal step: the read refuses a tombstoned pair (no resurrection) and the natural uid + ON CONFLICT DO NOTHING makes a concurrent identical link coalesce into the same row
|
|
83
|
+
export async function linkDecisionToAdr(projectRoot, input) {
|
|
84
|
+
requireNonEmpty('decisionId', input.decisionId);
|
|
85
|
+
requireNonEmpty('adrSlug', input.adrSlug);
|
|
86
|
+
if (!DECISION_ID_PATTERN.test(input.decisionId)) {
|
|
87
|
+
// @sync-invariant none:input-shape only bare D#### ledger ids may anchor a link; nothing was written
|
|
88
|
+
throw new CleoError(ExitCode.VALIDATION_ERROR, `decisionId must be a bare D#### ledger id (got '${input.decisionId}')`);
|
|
89
|
+
}
|
|
90
|
+
const db = await getDb(projectRoot);
|
|
91
|
+
const existing = await db
|
|
92
|
+
.select()
|
|
93
|
+
.from(decisionAdrLinks)
|
|
94
|
+
.where(and(eq(decisionAdrLinks.decisionId, input.decisionId), eq(decisionAdrLinks.adrSlug, input.adrSlug)))
|
|
95
|
+
.get();
|
|
96
|
+
if (existing) {
|
|
97
|
+
if (existing.status === 'revoked') {
|
|
98
|
+
// @sync-invariant docs.decision-adr-links.revoke-monotonic the revoke latch is one-way; re-linking a tombstoned pair would resurrect a dead row
|
|
99
|
+
throw new CleoError(ExitCode.VALIDATION_ERROR, `link ${input.decisionId} → ${input.adrSlug} was revoked; a revoked pair is never re-linked`);
|
|
100
|
+
}
|
|
101
|
+
return { row: existing, inserted: false };
|
|
102
|
+
}
|
|
103
|
+
const uid = decisionAdrLinkUid(input.decisionId, input.adrSlug);
|
|
104
|
+
const now = new Date().toISOString();
|
|
105
|
+
await db
|
|
106
|
+
.insert(decisionAdrLinks)
|
|
107
|
+
.values({
|
|
108
|
+
decisionId: input.decisionId,
|
|
109
|
+
adrSlug: input.adrSlug,
|
|
110
|
+
status: 'linked',
|
|
111
|
+
...(input.linkedBy !== undefined ? { linkedBy: input.linkedBy } : {}),
|
|
112
|
+
createdAt: now,
|
|
113
|
+
updatedAt: now,
|
|
114
|
+
uid,
|
|
115
|
+
})
|
|
116
|
+
.onConflictDoNothing()
|
|
117
|
+
.run();
|
|
118
|
+
const row = await db
|
|
119
|
+
.select()
|
|
120
|
+
.from(decisionAdrLinks)
|
|
121
|
+
.where(and(eq(decisionAdrLinks.decisionId, input.decisionId), eq(decisionAdrLinks.adrSlug, input.adrSlug)))
|
|
122
|
+
.get();
|
|
123
|
+
if (!row) {
|
|
124
|
+
// @sync-invariant docs.decision-adr-links.revoke-monotonic a link insert that cannot be read back means the store lost the write; fail loudly instead of returning a phantom
|
|
125
|
+
throw new CleoError(ExitCode.GENERAL_ERROR, `link insert for ${input.decisionId} → ${input.adrSlug} could not be read back`);
|
|
126
|
+
}
|
|
127
|
+
return { row, inserted: true };
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Revoke a link as a TOMBSTONE LATCH (never a DELETE, never a resurrection).
|
|
131
|
+
*
|
|
132
|
+
* `status` flips `linked` → `revoked` exactly once and `updated_at` records
|
|
133
|
+
* the tombstone instant; the row survives sync and restore as the permanent
|
|
134
|
+
* record that this pair was once linked and then unlinked. Re-revoking an
|
|
135
|
+
* already-revoked pair is an idempotent no-op. Revoking a pair that was
|
|
136
|
+
* never linked is refused — a tombstone must reference a real link.
|
|
137
|
+
*
|
|
138
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
139
|
+
* @param input - The revoke facts.
|
|
140
|
+
* @returns The link row and whether the latch flipped.
|
|
141
|
+
*/
|
|
142
|
+
// @sync-invariant docs.decision-adr-links.revoke-monotonic the read-modify-write IS the one-way latch: flip linked→revoked exactly once, refuse if never linked; no DELETE exists on this table
|
|
143
|
+
export async function revokeDecisionAdrLink(projectRoot, input) {
|
|
144
|
+
requireNonEmpty('decisionId', input.decisionId);
|
|
145
|
+
requireNonEmpty('adrSlug', input.adrSlug);
|
|
146
|
+
const db = await getDb(projectRoot);
|
|
147
|
+
const existing = await db
|
|
148
|
+
.select()
|
|
149
|
+
.from(decisionAdrLinks)
|
|
150
|
+
.where(and(eq(decisionAdrLinks.decisionId, input.decisionId), eq(decisionAdrLinks.adrSlug, input.adrSlug)))
|
|
151
|
+
.get();
|
|
152
|
+
if (!existing) {
|
|
153
|
+
// @sync-invariant docs.decision-adr-links.revoke-monotonic a tombstone must reference a real link; revoking a never-linked pair would fabricate history
|
|
154
|
+
throw new CleoError(ExitCode.NOT_FOUND, `link ${input.decisionId} → ${input.adrSlug} does not exist; nothing to revoke`);
|
|
155
|
+
}
|
|
156
|
+
if (existing.status === 'revoked') {
|
|
157
|
+
return { row: existing, revoked: false };
|
|
158
|
+
}
|
|
159
|
+
await db
|
|
160
|
+
.update(decisionAdrLinks)
|
|
161
|
+
.set({
|
|
162
|
+
status: 'revoked',
|
|
163
|
+
...(input.revokedBy !== undefined ? { revokedBy: input.revokedBy } : {}),
|
|
164
|
+
updatedAt: new Date().toISOString(),
|
|
165
|
+
})
|
|
166
|
+
.where(and(eq(decisionAdrLinks.decisionId, input.decisionId), eq(decisionAdrLinks.adrSlug, input.adrSlug)))
|
|
167
|
+
.run();
|
|
168
|
+
const row = await db
|
|
169
|
+
.select()
|
|
170
|
+
.from(decisionAdrLinks)
|
|
171
|
+
.where(and(eq(decisionAdrLinks.decisionId, input.decisionId), eq(decisionAdrLinks.adrSlug, input.adrSlug)))
|
|
172
|
+
.get();
|
|
173
|
+
if (!row) {
|
|
174
|
+
// @sync-invariant docs.decision-adr-links.revoke-monotonic a revoked row must still exist — the tombstone is a surviving row, never a disappearance
|
|
175
|
+
throw new CleoError(ExitCode.GENERAL_ERROR, `link ${input.decisionId} → ${input.adrSlug} vanished during its revoke tombstone`);
|
|
176
|
+
}
|
|
177
|
+
return { row, revoked: true };
|
|
178
|
+
}
|
|
179
|
+
/** Read one link row by its natural key, any state. */
|
|
180
|
+
export async function getDecisionAdrLink(projectRoot, decisionId, adrSlug) {
|
|
181
|
+
requireNonEmpty('decisionId', decisionId);
|
|
182
|
+
requireNonEmpty('adrSlug', adrSlug);
|
|
183
|
+
const db = await getDb(projectRoot);
|
|
184
|
+
const row = await db
|
|
185
|
+
.select()
|
|
186
|
+
.from(decisionAdrLinks)
|
|
187
|
+
.where(and(eq(decisionAdrLinks.decisionId, decisionId), eq(decisionAdrLinks.adrSlug, adrSlug)))
|
|
188
|
+
.get();
|
|
189
|
+
return row ?? null;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* The ADR docs linked to one decision — the decision→ADR direction of the
|
|
193
|
+
* two-way surfacing (spec §4: decision lookups surface their ADRs).
|
|
194
|
+
*
|
|
195
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
196
|
+
* @param decisionId - The `D####` ledger decision id.
|
|
197
|
+
* @param opts.includeRevoked - Also return tombstoned links (default false).
|
|
198
|
+
* @returns The link rows, ADR slug ascending.
|
|
199
|
+
*/
|
|
200
|
+
export async function listAdrLinksForDecision(projectRoot, decisionId, opts) {
|
|
201
|
+
requireNonEmpty('decisionId', decisionId);
|
|
202
|
+
const db = await getDb(projectRoot);
|
|
203
|
+
const rows = await db
|
|
204
|
+
.select()
|
|
205
|
+
.from(decisionAdrLinks)
|
|
206
|
+
.where(eq(decisionAdrLinks.decisionId, decisionId))
|
|
207
|
+
.all();
|
|
208
|
+
const filtered = opts?.includeRevoked === true ? rows : rows.filter((r) => r.status === 'linked');
|
|
209
|
+
return filtered.sort((a, b) => a.adrSlug.localeCompare(b.adrSlug));
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* The `D####` decisions linked to one ADR doc — the ADR→decision direction
|
|
213
|
+
* (`cleo docs fetch adr-NNN` surfaces its source decisions, spec §4 AC2).
|
|
214
|
+
*
|
|
215
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
216
|
+
* @param adrSlug - The ADR doc slug.
|
|
217
|
+
* @param opts.includeRevoked - Also return tombstoned links (default false).
|
|
218
|
+
* @returns The link rows, decision id ascending.
|
|
219
|
+
*/
|
|
220
|
+
export async function listDecisionLinksForAdr(projectRoot, adrSlug, opts) {
|
|
221
|
+
requireNonEmpty('adrSlug', adrSlug);
|
|
222
|
+
const db = await getDb(projectRoot);
|
|
223
|
+
const rows = await db
|
|
224
|
+
.select()
|
|
225
|
+
.from(decisionAdrLinks)
|
|
226
|
+
.where(eq(decisionAdrLinks.adrSlug, adrSlug))
|
|
227
|
+
.all();
|
|
228
|
+
const filtered = opts?.includeRevoked === true ? rows : rows.filter((r) => r.status === 'linked');
|
|
229
|
+
return filtered.sort((a, b) => a.decisionId.localeCompare(b.decisionId));
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Attach each `D####` decision's linked ADR slugs — the decision→ADR
|
|
233
|
+
* direction of the two-way trace (spec §4: decision lookups surface their
|
|
234
|
+
* curated ADR records). Rows without a bare `D####` id pass through
|
|
235
|
+
* untouched.
|
|
236
|
+
*
|
|
237
|
+
* @param projectRoot - Absolute path to the CLEO project root.
|
|
238
|
+
* @param decisions - Decision rows as returned by the brain store.
|
|
239
|
+
* @returns The same rows, with `linkedAdrs` added where links exist.
|
|
240
|
+
*/
|
|
241
|
+
export async function attachLinkedAdrs(projectRoot, decisions) {
|
|
242
|
+
const out = [];
|
|
243
|
+
for (const row of decisions) {
|
|
244
|
+
const id = 'id' in row ? row.id : undefined;
|
|
245
|
+
if (typeof id !== 'string' || !DECISION_ID_PATTERN.test(id)) {
|
|
246
|
+
out.push(row);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const links = await listAdrLinksForDecision(projectRoot, id);
|
|
250
|
+
if (links.length === 0) {
|
|
251
|
+
out.push(row);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
out.push({ ...row, linkedAdrs: links.map((link) => link.adrSlug) });
|
|
255
|
+
}
|
|
256
|
+
return out;
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=decision-adr-links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-adr-links.js","sourceRoot":"","sources":["../../src/store/decision-adr-links.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAC7D,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAU,CAAC;AAKzE,0EAA0E;AAC1E,MAAM,mBAAmB,GAAG,QAAQ,CAAC;AAErC;;;;;GAKG;AACH,SAAS,eAAe,CAAC,IAAY,EAAE,KAAa;IAClD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,uGAAuG;QACvG,MAAM,IAAI,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,GAAG,IAAI,6BAA6B,CAAC,CAAC;IACvF,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAkB,EAAE,OAAe;IACpE,OAAO,aAAa,CAAC,SAAS,EAAE,oBAAoB,EAAE,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;AAC/E,CAAC;AAoBD;;;;;;;;;;GAUG;AACH,uSAAuS;AACvS,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,WAAmB,EACnB,KAA6B;IAE7B,eAAe,CAAC,YAAY,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAChD,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QAChD,qGAAqG;QACrG,MAAM,IAAI,SAAS,CACjB,QAAQ,CAAC,gBAAgB,EACzB,mDAAmD,KAAK,CAAC,UAAU,IAAI,CACxE,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,EAAE;SACtB,MAAM,EAAE;SACR,IAAI,CAAC,gBAAgB,CAAC;SACtB,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,gBAAgB,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,EACjD,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAC5C,CACF;SACA,GAAG,EAAE,CAAC;IACT,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAClC,gJAAgJ;YAChJ,MAAM,IAAI,SAAS,CACjB,QAAQ,CAAC,gBAAgB,EACzB,QAAQ,KAAK,CAAC,UAAU,MAAM,KAAK,CAAC,OAAO,iDAAiD,CAC7F,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;IACD,MAAM,GAAG,GAAG,kBAAkB,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAChE,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,EAAE;SACL,MAAM,CAAC,gBAAgB,CAAC;SACxB,MAAM,CAAC;QACN,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,QAAQ;QAChB,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,SAAS,EAAE,GAAG;QACd,SAAS,EAAE,GAAG;QACd,GAAG;KACJ,CAAC;SACD,mBAAmB,EAAE;SACrB,GAAG,EAAE,CAAC;IACT,MAAM,GAAG,GAAG,MAAM,EAAE;SACjB,MAAM,EAAE;SACR,IAAI,CAAC,gBAAgB,CAAC;SACtB,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,gBAAgB,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,EACjD,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAC5C,CACF;SACA,GAAG,EAAE,CAAC;IACT,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,6KAA6K;QAC7K,MAAM,IAAI,SAAS,CACjB,QAAQ,CAAC,aAAa,EACtB,mBAAmB,KAAK,CAAC,UAAU,MAAM,KAAK,CAAC,OAAO,yBAAyB,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AACjC,CAAC;AAoBD;;;;;;;;;;;;GAYG;AACH,gMAAgM;AAChM,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,WAAmB,EACnB,KAAiC;IAEjC,eAAe,CAAC,YAAY,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAChD,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,EAAE;SACtB,MAAM,EAAE;SACR,IAAI,CAAC,gBAAgB,CAAC;SACtB,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,gBAAgB,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,EACjD,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAC5C,CACF;SACA,GAAG,EAAE,CAAC;IACT,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,wJAAwJ;QACxJ,MAAM,IAAI,SAAS,CACjB,QAAQ,CAAC,SAAS,EAClB,QAAQ,KAAK,CAAC,UAAU,MAAM,KAAK,CAAC,OAAO,oCAAoC,CAChF,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC3C,CAAC;IACD,MAAM,EAAE;SACL,MAAM,CAAC,gBAAgB,CAAC;SACxB,GAAG,CAAC;QACH,MAAM,EAAE,SAAS;QACjB,GAAG,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;SACD,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,gBAAgB,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,EACjD,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAC5C,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,GAAG,GAAG,MAAM,EAAE;SACjB,MAAM,EAAE;SACR,IAAI,CAAC,gBAAgB,CAAC;SACtB,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,gBAAgB,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,EACjD,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAC5C,CACF;SACA,GAAG,EAAE,CAAC;IACT,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,oJAAoJ;QACpJ,MAAM,IAAI,SAAS,CACjB,QAAQ,CAAC,aAAa,EACtB,QAAQ,KAAK,CAAC,UAAU,MAAM,KAAK,CAAC,OAAO,uCAAuC,CACnF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAChC,CAAC;AAED,uDAAuD;AACvD,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,UAAkB,EAClB,OAAe;IAEf,eAAe,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAC1C,eAAe,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACpC,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,MAAM,EAAE;SACjB,MAAM,EAAE;SACR,IAAI,CAAC,gBAAgB,CAAC;SACtB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;SAC9F,GAAG,EAAE,CAAC;IACT,OAAO,GAAG,IAAI,IAAI,CAAC;AACrB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,WAAmB,EACnB,UAAkB,EAClB,IAAmC;IAEnC,eAAe,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAC1C,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,EAAE;SAClB,MAAM,EAAE;SACR,IAAI,CAAC,gBAAgB,CAAC;SACtB,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;SAClD,GAAG,EAAE,CAAC;IACT,MAAM,QAAQ,GAAG,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC;IAClG,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AACrE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,WAAmB,EACnB,OAAe,EACf,IAAmC;IAEnC,eAAe,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACpC,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,EAAE;SAClB,MAAM,EAAE;SACR,IAAI,CAAC,gBAAgB,CAAC;SACtB,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;SAC5C,GAAG,EAAE,CAAC;IACT,MAAM,QAAQ,GAAG,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC;IAClG,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;AAC3E,CAAC;AAQD;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,WAAmB,EACnB,SAAuB;IAEvB,MAAM,GAAG,GAAsC,EAAE,CAAC;IAClD,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5C,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YAC5D,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACd,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,uBAAuB,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC7D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACd,SAAS;QACX,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,EAAE,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -81,9 +81,10 @@ export declare const ROW_IDENTITY_EXEMPT: Readonly<Record<TableScope, Readonly<R
|
|
|
81
81
|
* Since T12894 declared the brain text-keyed tables (11 project, 12 global),
|
|
82
82
|
* T12895 the brain natural-key tables (3 project, 3 global) and T12896 the
|
|
83
83
|
* brain integer-keyed tables (6 project, 6 global), T12915 declared the
|
|
84
|
-
* agent registry (1 global; 6 more reclassified derived or local-only),
|
|
84
|
+
* agent registry (1 global; 6 more reclassified derived or local-only),
|
|
85
85
|
* T13354 declared the re-keyed conduit doc family (5 project, dropping 4
|
|
86
|
-
* exemptions — contributors became derived and not syncing)
|
|
86
|
+
* exemptions — contributors became derived and not syncing), and T13467 the
|
|
87
|
+
* credential tables (4 global)
|
|
87
88
|
* ({@link rowIdentityExemptionSummary} prints the live numbers):
|
|
88
89
|
*
|
|
89
90
|
* | category | task | project | global |
|
|
@@ -93,14 +94,13 @@ export declare const ROW_IDENTITY_EXEMPT: Readonly<Record<TableScope, Readonly<R
|
|
|
93
94
|
* | planned | T12896 | 2 | 1 |
|
|
94
95
|
* | planned (conduit) | T12913 | 8 | 0 |
|
|
95
96
|
* | planned (lifecycle…) | T12914 | 16 | 0 |
|
|
96
|
-
* | planned (agents…) | T12915 | 0 | 4 |
|
|
97
97
|
* | planned (nexus) | T12916 | 0 | 8 |
|
|
98
98
|
* | planned (skills) | T12917 | 0 | 4 |
|
|
99
99
|
* | planned (docs) | T12919 | 3 | 0 |
|
|
100
100
|
* | planned (misc) | T12920 | 12 | 0 |
|
|
101
101
|
* | twin-collapse | T12535 | 30 | 0 |
|
|
102
102
|
* | not-row-replicated | T12918 | 1 | 1 |
|
|
103
|
-
* | total | | 70 |
|
|
103
|
+
* | total | | 70 | 13 |
|
|
104
104
|
*
|
|
105
105
|
* @task T12897
|
|
106
106
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"row-identity-registry.d.ts","sourceRoot":"","sources":["../../src/store/row-identity-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAkB,eAAe,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEvF,yCAAyC;AACzC,eAAO,MAAM,UAAU,QAAQ,CAAC;AAEhC,2EAA2E;AAC3E,eAAO,MAAM,eAAe,aAAa,CAAC;AA6P1C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"row-identity-registry.d.ts","sourceRoot":"","sources":["../../src/store/row-identity-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -450,6 +450,15 @@ export const ROW_IDENTITY = {
|
|
|
450
450
|
key: ['attachment_uid'],
|
|
451
451
|
task: 'T13354',
|
|
452
452
|
},
|
|
453
|
+
{
|
|
454
|
+
// Natural over the pair (spec §10): both devices derive one uid for one
|
|
455
|
+
// link, so concurrent identical links coalesce on apply. The revoke is
|
|
456
|
+
// a one-way tombstone latch on the same row, never a second row.
|
|
457
|
+
table: 'decision_adr_links',
|
|
458
|
+
kind: 'natural',
|
|
459
|
+
key: ['decision_id', 'adr_slug'],
|
|
460
|
+
task: 'T13359',
|
|
461
|
+
},
|
|
453
462
|
],
|
|
454
463
|
global: [
|
|
455
464
|
...brainTextKeyed(),
|
|
@@ -482,6 +491,41 @@ export const ROW_IDENTITY = {
|
|
|
482
491
|
localKey: 'id',
|
|
483
492
|
task: 'T12915',
|
|
484
493
|
},
|
|
494
|
+
// T13467 (uid spec D4 amended 2026-10-11): the credential tables are natural
|
|
495
|
+
// on their non-secret identifiers, so the same credential held on two
|
|
496
|
+
// devices is one row. Their INTEGER `id` is a local key; every other column
|
|
497
|
+
// is sealed on the home stream (§2.7), never sent in the clear.
|
|
498
|
+
{
|
|
499
|
+
table: 'accounts',
|
|
500
|
+
kind: 'natural',
|
|
501
|
+
key: ['provider', 'label'],
|
|
502
|
+
localKey: 'id',
|
|
503
|
+
task: 'T13467',
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
table: 'service_connections',
|
|
507
|
+
kind: 'natural',
|
|
508
|
+
key: ['provider', 'label'],
|
|
509
|
+
localKey: 'id',
|
|
510
|
+
task: 'T13467',
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
table: 'service_configs',
|
|
514
|
+
kind: 'natural',
|
|
515
|
+
key: ['provider'],
|
|
516
|
+
localKey: 'id',
|
|
517
|
+
task: 'T13467',
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
table: 'agent_service_grants',
|
|
521
|
+
kind: 'natural',
|
|
522
|
+
key: ['agent_id', 'service_connection_id'],
|
|
523
|
+
keyRefs: [
|
|
524
|
+
{ column: 'agent_id', table: 'agent_registry_agents' },
|
|
525
|
+
{ column: 'service_connection_id', table: 'service_connections' },
|
|
526
|
+
],
|
|
527
|
+
task: 'T13467',
|
|
528
|
+
},
|
|
485
529
|
],
|
|
486
530
|
};
|
|
487
531
|
/**
|
|
@@ -523,7 +567,6 @@ function cluster(task) {
|
|
|
523
567
|
}
|
|
524
568
|
const CONDUIT = cluster('T12913');
|
|
525
569
|
const LIFECYCLE_RELEASE_GIT = cluster('T12914');
|
|
526
|
-
const AGENT_ACCOUNTS_SERVICE = cluster('T12915');
|
|
527
570
|
const NEXUS_REGISTRY = cluster('T12916');
|
|
528
571
|
const SKILLS = cluster('T12917');
|
|
529
572
|
const DOCS = cluster('T12919');
|
|
@@ -638,7 +681,6 @@ export const ROW_IDENTITY_EXEMPT = {
|
|
|
638
681
|
},
|
|
639
682
|
global: {
|
|
640
683
|
...BRAIN_EXEMPT,
|
|
641
|
-
...exempt(['accounts', 'agent_service_grants', 'service_configs', 'service_connections'], AGENT_ACCOUNTS_SERVICE),
|
|
642
684
|
...exempt([
|
|
643
685
|
'nexus_audit_log',
|
|
644
686
|
'nexus_devices',
|
|
@@ -661,9 +703,10 @@ export const ROW_IDENTITY_EXEMPT = {
|
|
|
661
703
|
* Since T12894 declared the brain text-keyed tables (11 project, 12 global),
|
|
662
704
|
* T12895 the brain natural-key tables (3 project, 3 global) and T12896 the
|
|
663
705
|
* brain integer-keyed tables (6 project, 6 global), T12915 declared the
|
|
664
|
-
* agent registry (1 global; 6 more reclassified derived or local-only),
|
|
706
|
+
* agent registry (1 global; 6 more reclassified derived or local-only),
|
|
665
707
|
* T13354 declared the re-keyed conduit doc family (5 project, dropping 4
|
|
666
|
-
* exemptions — contributors became derived and not syncing)
|
|
708
|
+
* exemptions — contributors became derived and not syncing), and T13467 the
|
|
709
|
+
* credential tables (4 global)
|
|
667
710
|
* ({@link rowIdentityExemptionSummary} prints the live numbers):
|
|
668
711
|
*
|
|
669
712
|
* | category | task | project | global |
|
|
@@ -673,20 +716,19 @@ export const ROW_IDENTITY_EXEMPT = {
|
|
|
673
716
|
* | planned | T12896 | 2 | 1 |
|
|
674
717
|
* | planned (conduit) | T12913 | 8 | 0 |
|
|
675
718
|
* | planned (lifecycle…) | T12914 | 16 | 0 |
|
|
676
|
-
* | planned (agents…) | T12915 | 0 | 4 |
|
|
677
719
|
* | planned (nexus) | T12916 | 0 | 8 |
|
|
678
720
|
* | planned (skills) | T12917 | 0 | 4 |
|
|
679
721
|
* | planned (docs) | T12919 | 3 | 0 |
|
|
680
722
|
* | planned (misc) | T12920 | 12 | 0 |
|
|
681
723
|
* | twin-collapse | T12535 | 30 | 0 |
|
|
682
724
|
* | not-row-replicated | T12918 | 1 | 1 |
|
|
683
|
-
* | total | | 70 |
|
|
725
|
+
* | total | | 70 | 13 |
|
|
684
726
|
*
|
|
685
727
|
* @task T12897
|
|
686
728
|
*/
|
|
687
729
|
export const ROW_IDENTITY_EXEMPT_PINNED = {
|
|
688
730
|
project: 70,
|
|
689
|
-
global:
|
|
731
|
+
global: 13,
|
|
690
732
|
};
|
|
691
733
|
/**
|
|
692
734
|
* sha256 of each scope's sorted exempt table names
|
|
@@ -698,7 +740,7 @@ export const ROW_IDENTITY_EXEMPT_PINNED = {
|
|
|
698
740
|
*/
|
|
699
741
|
export const ROW_IDENTITY_EXEMPT_NAMES_SHA256 = {
|
|
700
742
|
project: '03413c00c1a3a2be35c1b1c8729ceb8c89f28624e7919e6e12e2f06b5fba5ef6',
|
|
701
|
-
global: '
|
|
743
|
+
global: '615415a2502708e84007335451603d186c3051cb7e8a69c0b48f561e485edd84',
|
|
702
744
|
};
|
|
703
745
|
/**
|
|
704
746
|
* sha256 (hex) of a set of table names, sorted and newline-joined.
|