@holmes-lab/holmes-kit 0.19.5 → 0.20.0
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/CHANGELOG.md +135 -0
- package/README.md +17 -1
- package/dist/.build-id +1 -1
- package/dist/holmes/cli/approve-status.d.ts +24 -0
- package/dist/holmes/cli/approve-status.js +166 -0
- package/dist/holmes/cli/approve-watch.d.ts +6 -0
- package/dist/holmes/cli/approve-watch.js +6 -0
- package/dist/holmes/cli/approve.d.ts +27 -0
- package/dist/holmes/cli/approve.js +68 -7
- package/dist/holmes/cli/cli-execution.d.ts +2 -0
- package/dist/holmes/cli/cli-execution.js +24 -0
- package/dist/holmes/cli/gitignore-merge.d.ts +0 -5
- package/dist/holmes/cli/gitignore-merge.js +6 -2
- package/dist/holmes/cli/index.js +176 -139
- package/dist/holmes/cli/playbook-skills.d.ts +2 -22
- package/dist/holmes/cli/playbook-skills.js +10 -54
- package/dist/holmes/governance/approval-grants.d.ts +22 -0
- package/dist/holmes/governance/approval-grants.js +55 -1
- package/dist/holmes/governance/approval-queue.d.ts +43 -4
- package/dist/holmes/governance/approval-queue.js +86 -12
- package/dist/holmes/governance/display-id-aliases.d.ts +29 -0
- package/dist/holmes/governance/display-id-aliases.js +86 -0
- package/dist/holmes/governance/ledger-store.d.ts +42 -14
- package/dist/holmes/governance/ledger-store.js +49 -3
- package/dist/holmes/governance/ledger-timeline.d.ts +3 -1
- package/dist/holmes/governance/ledger-timeline.js +11 -1
- package/dist/holmes/governance/provenance-chain.d.ts +2 -0
- package/dist/holmes/governance/provenance-chain.js +13 -2
- package/dist/holmes/governance/provenance-ledger.d.ts +28 -0
- package/dist/holmes/governance/provenance-ledger.js +69 -16
- package/dist/holmes/governance/session-context.d.ts +3 -0
- package/dist/holmes/governance/session-context.js +26 -8
- package/dist/holmes/guardrail/risk-classifier.js +2 -2
- package/dist/holmes/guardrail/write-target.js +2 -1
- package/dist/holmes/hooks/pre-tool-use.js +12 -1
- package/dist/holmes/hooks/stop.js +14 -3
- package/dist/holmes/mcp/handlers/adoption.d.ts +118 -0
- package/dist/holmes/mcp/handlers/adoption.js +186 -0
- package/dist/holmes/mcp/handlers/entity-integration.d.ts +100 -0
- package/dist/holmes/mcp/handlers/entity-integration.js +324 -0
- package/dist/holmes/mcp/handlers/entity-renumber.d.ts +137 -0
- package/dist/holmes/mcp/handlers/entity-renumber.js +228 -0
- package/dist/holmes/mcp/handlers/entity-store.d.ts +78 -0
- package/dist/holmes/mcp/handlers/entity-store.js +212 -0
- package/dist/holmes/mcp/handlers/graph-operations.d.ts +90 -0
- package/dist/holmes/mcp/handlers/graph-operations.js +260 -0
- package/dist/holmes/mcp/handlers/maintenance-analysis.d.ts +34 -0
- package/dist/holmes/mcp/handlers/maintenance-analysis.js +288 -0
- package/dist/holmes/mcp/handlers/maintenance-evidence.d.ts +40 -0
- package/dist/holmes/mcp/handlers/maintenance-evidence.js +104 -0
- package/dist/holmes/mcp/handlers/operator-inspection.d.ts +167 -0
- package/dist/holmes/mcp/handlers/operator-inspection.js +284 -0
- package/dist/holmes/mcp/handlers/phase-queries.d.ts +34 -0
- package/dist/holmes/mcp/handlers/phase-queries.js +86 -0
- package/dist/holmes/mcp/handlers/review-evidence.d.ts +47 -0
- package/dist/holmes/mcp/handlers/review-evidence.js +370 -0
- package/dist/holmes/mcp/handlers/review-queries.d.ts +68 -0
- package/dist/holmes/mcp/handlers/review-queries.js +79 -0
- package/dist/holmes/mcp/handlers/risk-assessment.d.ts +25 -0
- package/dist/holmes/mcp/handlers/risk-assessment.js +181 -0
- package/dist/holmes/mcp/handlers/slice-orchestration.d.ts +55 -0
- package/dist/holmes/mcp/handlers/slice-orchestration.js +324 -0
- package/dist/holmes/mcp/handlers/spec-approval.d.ts +105 -0
- package/dist/holmes/mcp/handlers/spec-approval.js +504 -0
- package/dist/holmes/mcp/handlers/spec-authoring.d.ts +57 -0
- package/dist/holmes/mcp/handlers/spec-authoring.js +326 -0
- package/dist/holmes/mcp/handlers/spec-lifecycle.d.ts +122 -0
- package/dist/holmes/mcp/handlers/spec-lifecycle.js +339 -0
- package/dist/holmes/mcp/handlers/spec-queries.d.ts +30 -0
- package/dist/holmes/mcp/handlers/spec-queries.js +74 -0
- package/dist/holmes/mcp/handlers/taint-screening.d.ts +48 -0
- package/dist/holmes/mcp/handlers/taint-screening.js +183 -0
- package/dist/holmes/mcp/handlers/test-execution.d.ts +63 -0
- package/dist/holmes/mcp/handlers/test-execution.js +210 -0
- package/dist/holmes/mcp/handlers/workspace-identity.d.ts +30 -0
- package/dist/holmes/mcp/handlers/workspace-identity.js +68 -0
- package/dist/holmes/mcp/handlers/workspace-queries.d.ts +77 -0
- package/dist/holmes/mcp/handlers/workspace-queries.js +193 -0
- package/dist/holmes/mcp/handlers.d.ts +653 -424
- package/dist/holmes/mcp/handlers.js +267 -3250
- package/dist/holmes/mcp/server.js +2 -1
- package/dist/holmes/mcp/tool-schemas.js +43 -2
- package/dist/holmes/project/execution-context.d.ts +17 -0
- package/dist/holmes/project/execution-context.js +120 -0
- package/dist/holmes/project/installer-markers.d.ts +34 -0
- package/dist/holmes/project/installer-markers.js +65 -0
- package/dist/holmes/project/root.d.ts +3 -1
- package/dist/holmes/project/root.js +4 -3
- package/dist/holmes/project/workspace-identity.d.ts +29 -0
- package/dist/holmes/project/workspace-identity.js +181 -0
- package/dist/holmes/review/test-outcomes.d.ts +6 -1
- package/dist/holmes/review/test-outcomes.js +23 -5
- package/dist/holmes/review/test-runner.d.ts +18 -0
- package/dist/holmes/review/test-runner.js +132 -4
- package/dist/holmes/rtm/graph-store.d.ts +50 -1
- package/dist/holmes/rtm/graph-store.js +244 -3
- package/dist/holmes/rtm/incremental.d.ts +1 -0
- package/dist/holmes/rtm/incremental.js +12 -3
- package/dist/holmes/rtm/localize.js +7 -0
- package/dist/holmes/rtm/rtm-graph.d.ts +9 -0
- package/dist/holmes/rtm/rtm-graph.js +13 -0
- package/dist/holmes/semantic/vector-cache.d.ts +16 -1
- package/dist/holmes/semantic/vector-cache.js +155 -19
- package/dist/holmes/spec/approval-status.d.ts +10 -0
- package/dist/holmes/spec/approval-status.js +7 -3
- package/dist/holmes/spec/entity-git-snapshot.d.ts +50 -0
- package/dist/holmes/spec/entity-git-snapshot.js +276 -0
- package/dist/holmes/spec/entity-integration-discharge.d.ts +73 -0
- package/dist/holmes/spec/entity-integration-discharge.js +233 -0
- package/dist/holmes/spec/entity-integration-transaction.d.ts +179 -0
- package/dist/holmes/spec/entity-integration-transaction.js +1221 -0
- package/dist/holmes/spec/entity-integration.d.ts +207 -0
- package/dist/holmes/spec/entity-integration.js +747 -0
- package/dist/holmes/spec/entity-renumber-transaction.d.ts +82 -0
- package/dist/holmes/spec/entity-renumber-transaction.js +711 -0
- package/dist/holmes/spec/entity-renumber.d.ts +32 -0
- package/dist/holmes/spec/entity-renumber.js +156 -0
- package/dist/holmes/spec/entity-store.d.ts +135 -0
- package/dist/holmes/spec/entity-store.js +1051 -0
- package/dist/holmes/spec/entity-transaction.d.ts +89 -0
- package/dist/holmes/spec/entity-transaction.js +701 -0
- package/dist/holmes/spec/renumber.d.ts +58 -0
- package/dist/holmes/spec/renumber.js +200 -1
- package/dist/holmes/spec/spec-store.d.ts +3 -2
- package/dist/holmes/spec/spec-store.js +23 -2
- package/dist/holmes/spec/transition-policy.d.ts +36 -0
- package/dist/holmes/spec/transition-policy.js +135 -0
- package/package.json +1 -1
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createEntityRenumberHandlers = createEntityRenumberHandlers;
|
|
37
|
+
// @implements A-SPEC-628
|
|
38
|
+
// @implements A-SPEC-642
|
|
39
|
+
// @implements A-SPEC-639
|
|
40
|
+
const fs = __importStar(require("node:fs"));
|
|
41
|
+
const entity_store_1 = require("../../spec/entity-store");
|
|
42
|
+
const entity_renumber_1 = require("../../spec/entity-renumber");
|
|
43
|
+
const root_1 = require("../../project/root");
|
|
44
|
+
const renumber_1 = require("../../spec/renumber");
|
|
45
|
+
const entity_transaction_1 = require("../../spec/entity-transaction");
|
|
46
|
+
const path = __importStar(require("node:path"));
|
|
47
|
+
const entity_renumber_transaction_1 = require("../../spec/entity-renumber-transaction");
|
|
48
|
+
const approval_grants_1 = require("../../governance/approval-grants");
|
|
49
|
+
const approval_queue_1 = require("../../governance/approval-queue");
|
|
50
|
+
const ledger_store_1 = require("../../governance/ledger-store");
|
|
51
|
+
const provenance_chain_1 = require("../../governance/provenance-chain");
|
|
52
|
+
function createEntityRenumberHandlers(context) {
|
|
53
|
+
return {
|
|
54
|
+
async entity_renumber(a) {
|
|
55
|
+
try {
|
|
56
|
+
if (!context.storeRoot)
|
|
57
|
+
throw new entity_store_1.EntityStoreError('unsupported-store', 'Entity renumbering requires a configured filesystem spec store.');
|
|
58
|
+
const bound = (0, entity_store_1.entityStoreBinding)(context.storeRoot);
|
|
59
|
+
if (a.root !== undefined && fs.realpathSync((0, root_1.resolveProjectRoot)(a.root, { includeGit: false }).root) !== bound.root)
|
|
60
|
+
throw new entity_store_1.EntityStoreError('foreign-workspace', 'The requested workspace differs from the configured store.');
|
|
61
|
+
if (a.operation === 'recovery-plan')
|
|
62
|
+
return { ok: true, plan: (0, entity_renumber_transaction_1.planEntityRenumberRecovery)(context.storeRoot) };
|
|
63
|
+
// @implements A-SPEC-642 — an authority-spending operation is refused, naming the exit, while the
|
|
64
|
+
// merged ledger holds an unreconciled double-spend; the audit adapter's `audit-conflict` stays behind.
|
|
65
|
+
if (a.operation === 'apply' || a.operation === 'recover' || a.operation === 'abandon') {
|
|
66
|
+
const conflict = (0, ledger_store_1.ledgerConflictReason)(path.join(bound.root, '.ax', 'ledger'));
|
|
67
|
+
if (conflict)
|
|
68
|
+
throw new entity_store_1.EntityStoreError('ledger-conflict', conflict);
|
|
69
|
+
}
|
|
70
|
+
if (a.operation === 'abandon') {
|
|
71
|
+
// @implements A-SPEC-639 — an authorized exit for a pending renumber a third state blocked.
|
|
72
|
+
const recovery = (0, entity_renumber_transaction_1.validateEntityRenumberRecoveryPlan)(a.plan);
|
|
73
|
+
const target = 'entity-store:renumber-abandon:' + (0, entity_transaction_1.entityContentVersion)(JSON.stringify(a.plan));
|
|
74
|
+
let env;
|
|
75
|
+
try {
|
|
76
|
+
env = process.env.HOLMES_APPROVAL ? JSON.parse(process.env.HOLMES_APPROVAL) : undefined;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
env = undefined;
|
|
80
|
+
}
|
|
81
|
+
const authorities = new Set();
|
|
82
|
+
const requireAuthority = () => {
|
|
83
|
+
const action = { kind: 'config-write', target };
|
|
84
|
+
const resolved = (0, approval_grants_1.resolveApproval)(bound.root, env, action, new Date().toISOString());
|
|
85
|
+
if (!resolved)
|
|
86
|
+
throw new entity_store_1.EntityStoreError('approval-required', 'Current authority is required to abandon this renumber operation.' + (0, approval_queue_1.queueHint)(bound.root, { ...action, why: 'Abandon the blocked renumber: revert its own publications, preserve foreign bytes, release ownership.' }));
|
|
87
|
+
authorities.add((0, provenance_chain_1.authorizationRef)(resolved.approval.actor, resolved.approval.token));
|
|
88
|
+
return resolved;
|
|
89
|
+
};
|
|
90
|
+
requireAuthority();
|
|
91
|
+
const audit = (operation, outcome) => {
|
|
92
|
+
const authority = requireAuthority(), ledger = new ledger_store_1.FileLedgerStore(path.join(bound.root, '.ax/ledger'));
|
|
93
|
+
if (!ledger.verifyAll().ok)
|
|
94
|
+
throw new entity_store_1.EntityStoreError('audit-conflict', 'Abandonment audit cannot extend an invalid ledger.');
|
|
95
|
+
const ref = 'entity-operation:' + operation.operationId;
|
|
96
|
+
if (ledger.loadAll().some(event => event.kind === 'entity-renumber-abandoned' && event.inputs?.includes(ref)))
|
|
97
|
+
return;
|
|
98
|
+
ledger.append({ ts: new Date().toISOString(), actor: authority.approval.actor, kind: 'entity-renumber-abandoned', summary: 'Abandoned entity renumbering ' + operation.operationId,
|
|
99
|
+
inputs: [ref, (0, entity_renumber_transaction_1.entityRenumberTarget)(operation), 'workspace:' + operation.snapshot.workspaceId, 'store:' + operation.snapshot.storeId, recovery.journalVersion, target,
|
|
100
|
+
...outcome.restored.map(locator => 'restored:' + locator), ...outcome.preserved.map(locator => 'preserved:' + locator), ...authorities],
|
|
101
|
+
rationale: authority.approval.rationale, authorization: (0, provenance_chain_1.authorizationRef)(authority.approval.actor, authority.approval.token) });
|
|
102
|
+
};
|
|
103
|
+
return await (0, entity_renumber_transaction_1.abandonEntityRenumber)(context.storeRoot, recovery, requireAuthority, audit);
|
|
104
|
+
}
|
|
105
|
+
if (a.operation === 'apply' || a.operation === 'recover') {
|
|
106
|
+
const recovery = a.operation === 'recover' ? (0, entity_renumber_transaction_1.validateEntityRenumberRecoveryPlan)(a.plan) : undefined;
|
|
107
|
+
const plan = recovery ? undefined : (0, entity_renumber_transaction_1.validateEntityRenumberPlan)(a.plan), target = recovery ? (0, entity_renumber_transaction_1.entityRenumberRecoveryTarget)(recovery) : (0, entity_renumber_transaction_1.entityRenumberTarget)(plan);
|
|
108
|
+
let env;
|
|
109
|
+
try {
|
|
110
|
+
env = process.env.HOLMES_APPROVAL ? JSON.parse(process.env.HOLMES_APPROVAL) : undefined;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
env = undefined;
|
|
114
|
+
}
|
|
115
|
+
const grants = new Set(), authorities = new Set();
|
|
116
|
+
const usedGrantRefs = new Set();
|
|
117
|
+
const grantRef = (approval) => 'grant-use:' + (0, entity_transaction_1.entityContentVersion)(JSON.stringify({ actor: approval.actor, token: approval.token, nonce: approval.nonce, scope: approval.scope, expires: approval.expires }));
|
|
118
|
+
const requireAuthority = (specId) => {
|
|
119
|
+
const action = { kind: specId ? 'spec-approve' : 'config-write', target: specId ?? target };
|
|
120
|
+
const resolved = (0, approval_grants_1.resolveApproval)(bound.root, env, action, new Date().toISOString());
|
|
121
|
+
if (!resolved)
|
|
122
|
+
throw new entity_store_1.EntityStoreError(specId ? 'spec-approval-required' : 'approval-required', 'Current authority is required for this renumber operation.' + (0, approval_queue_1.queueHint)(bound.root, { ...action, why: 'Preserve UUIDs and the official approval closure while renumbering.' }));
|
|
123
|
+
if (resolved.source === 'grant' && resolved.approval.nonce)
|
|
124
|
+
grants.add(resolved.approval.nonce);
|
|
125
|
+
if (resolved.source === 'grant')
|
|
126
|
+
usedGrantRefs.add(grantRef(resolved.approval));
|
|
127
|
+
authorities.add((0, provenance_chain_1.authorizationRef)(resolved.approval.actor, resolved.approval.token));
|
|
128
|
+
return resolved;
|
|
129
|
+
};
|
|
130
|
+
requireAuthority();
|
|
131
|
+
const bindingFor = (operation) => ['entity-operation:' + operation.operationId, (0, entity_renumber_transaction_1.entityRenumberTarget)(operation), 'workspace:' + operation.snapshot.workspaceId, 'store:' + operation.snapshot.storeId];
|
|
132
|
+
const priorGrantUses = (ledger, operation) => {
|
|
133
|
+
if (!ledger.verifyAll().ok)
|
|
134
|
+
throw new entity_store_1.EntityStoreError('audit-conflict', 'Grant usage cannot rely on an invalid ledger.');
|
|
135
|
+
const binding = bindingFor(operation), refs = new Set();
|
|
136
|
+
for (const event of ledger.loadAll().filter(event => event.kind === 'entity-renumber-grant-used' && event.inputs?.includes(binding[0]))) {
|
|
137
|
+
const usages = (event.inputs ?? []).filter(ref => ref.startsWith('grant-use:'));
|
|
138
|
+
if (binding.some(ref => !event.inputs?.includes(ref)) || usages.length === 0 || usages.some(ref => !/^grant-use:sha256:[0-9a-f]{64}$/.test(ref) || refs.has(ref)))
|
|
139
|
+
throw new entity_store_1.EntityStoreError('audit-conflict', 'Grant usage has conflicting operation or plan bindings.');
|
|
140
|
+
for (const ref of usages)
|
|
141
|
+
refs.add(ref);
|
|
142
|
+
}
|
|
143
|
+
return refs;
|
|
144
|
+
};
|
|
145
|
+
const recordGrantUse = (operation) => {
|
|
146
|
+
const authority = requireAuthority(), ledger = new ledger_store_1.FileLedgerStore(path.join(bound.root, '.ax/ledger'));
|
|
147
|
+
const prior = priorGrantUses(ledger, operation);
|
|
148
|
+
const additional = [...usedGrantRefs].filter(ref => !prior.has(ref));
|
|
149
|
+
for (const ref of prior)
|
|
150
|
+
usedGrantRefs.add(ref);
|
|
151
|
+
if (additional.length === 0)
|
|
152
|
+
return;
|
|
153
|
+
ledger.append({ ts: new Date().toISOString(), actor: authority.approval.actor, kind: 'entity-renumber-grant-used',
|
|
154
|
+
summary: 'Reserved grant usage before renumber publication ' + operation.operationId,
|
|
155
|
+
inputs: [...bindingFor(operation), target, ...additional, ...authorities], rationale: authority.approval.rationale,
|
|
156
|
+
authorization: (0, provenance_chain_1.authorizationRef)(authority.approval.actor, authority.approval.token) });
|
|
157
|
+
};
|
|
158
|
+
const audit = (completed, verifyOnly = false) => {
|
|
159
|
+
const authority = requireAuthority(), ledger = new ledger_store_1.FileLedgerStore(path.join(bound.root, '.ax/ledger'));
|
|
160
|
+
if (!ledger.verifyAll().ok)
|
|
161
|
+
throw new entity_store_1.EntityStoreError('audit-conflict', 'Renumber completion cannot extend an invalid ledger.');
|
|
162
|
+
const operation = 'entity-operation:' + completed.operationId;
|
|
163
|
+
const binding = bindingFor(completed);
|
|
164
|
+
for (const ref of priorGrantUses(ledger, completed))
|
|
165
|
+
usedGrantRefs.add(ref);
|
|
166
|
+
const existing = ledger.loadAll().filter(event => event.kind === 'entity-renumbered' && event.inputs?.includes(operation));
|
|
167
|
+
if (existing.length) {
|
|
168
|
+
if (existing.length !== 1 || binding.some(ref => !existing[0].inputs?.includes(ref)))
|
|
169
|
+
throw new entity_store_1.EntityStoreError('audit-conflict', 'Renumber audit has conflicting completion bindings.');
|
|
170
|
+
for (const ref of existing[0].inputs ?? [])
|
|
171
|
+
if (ref.startsWith('grant-use:'))
|
|
172
|
+
usedGrantRefs.add(ref);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (verifyOnly)
|
|
176
|
+
throw new entity_store_1.EntityStoreError('audit-conflict', 'Completion receipt has no matching completion audit.');
|
|
177
|
+
const proposal = (0, entity_renumber_1.planEntityRenumber)(completed.snapshot.entries, completed.changes);
|
|
178
|
+
ledger.append({ ts: new Date().toISOString(), actor: authority.approval.actor, kind: 'entity-renumbered', summary: 'Completed entity renumbering ' + completed.operationId,
|
|
179
|
+
inputs: [...binding, target,
|
|
180
|
+
...proposal.entries.filter(e => e.oldId !== e.newId).flatMap(e => ['entity:' + e.entityId, 'old-id:' + e.oldId, 'new-id:' + e.newId]), ...authorities, ...usedGrantRefs],
|
|
181
|
+
rationale: authority.approval.rationale, authorization: (0, provenance_chain_1.authorizationRef)(authority.approval.actor, authority.approval.token) });
|
|
182
|
+
};
|
|
183
|
+
const result = recovery
|
|
184
|
+
? await (0, entity_renumber_transaction_1.recoverEntityRenumber)(context.storeRoot, recovery, requireAuthority, audit, recordGrantUse)
|
|
185
|
+
: await (0, entity_renumber_transaction_1.applyEntityRenumber)(context.storeRoot, plan, requireAuthority, verifyOnly => audit(plan, verifyOnly), recordGrantUse);
|
|
186
|
+
for (const grant of (0, approval_grants_1.readGrants)(bound.root).grants)
|
|
187
|
+
if (grant.nonce && usedGrantRefs.has(grantRef(grant)))
|
|
188
|
+
grants.add(grant.nonce);
|
|
189
|
+
for (const nonce of grants)
|
|
190
|
+
(0, approval_grants_1.consumeGrantFile)(bound.root, nonce);
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
if (a.operation !== undefined && a.operation !== 'plan')
|
|
194
|
+
return { ok: false, code: 'unsupported-operation', reason: 'Use plan, apply, recovery-plan, recover or abandon.' };
|
|
195
|
+
if (!Array.isArray(a.changes) || a.changes.some(change => !change || typeof change.entityId !== 'string' || typeof change.newDisplayId !== 'string'))
|
|
196
|
+
throw new entity_store_1.EntityStoreError('invalid-request', 'Provide entityId/newDisplayId changes.');
|
|
197
|
+
const changes = a.changes.map(change => ({ entityId: change.entityId, newDisplayId: change.newDisplayId }));
|
|
198
|
+
const snapshot = (0, entity_store_1.readEntityRenumberSnapshot)(context.storeRoot);
|
|
199
|
+
const proposal = (0, entity_renumber_1.planEntityRenumber)(snapshot.entries, changes);
|
|
200
|
+
if (!proposal.ok)
|
|
201
|
+
return { ok: false, code: 'plan-conflict', conflicts: proposal.conflicts };
|
|
202
|
+
const pairs = proposal.entries.filter(entry => entry.oldId !== entry.newId).map(entry => ({ oldId: entry.oldId, newId: entry.newId }));
|
|
203
|
+
const sources = await (0, renumber_1.planSourceRenumber)(bound.root, pairs, path.relative(bound.root, bound.store).split(path.sep).join('/'));
|
|
204
|
+
if (JSON.stringify((0, entity_store_1.readEntityRenumberSnapshot)(context.storeRoot)) !== JSON.stringify(snapshot))
|
|
205
|
+
throw new entity_store_1.EntityStoreError('plan-changed', 'Documents or identity mappings changed during source inventory.');
|
|
206
|
+
const ambiguous = new Set();
|
|
207
|
+
for (const file of sources.files)
|
|
208
|
+
for (const id of file.anchorIds) {
|
|
209
|
+
if (snapshot.referenceNamespaces.some(record => record.displayId === id && record.storeId !== snapshot.storeId))
|
|
210
|
+
ambiguous.add(id);
|
|
211
|
+
}
|
|
212
|
+
if (ambiguous.size)
|
|
213
|
+
return { ok: false, code: 'ambiguous-reference', ids: [...ambiguous].sort(), reason: 'Source anchors name display IDs owned by more than one activated store.' };
|
|
214
|
+
const preview = { schema: 'holmes-entity-renumber-preview/1', ...snapshot, proposal, sources };
|
|
215
|
+
if (sources.diagnostics.length)
|
|
216
|
+
return { ok: true, executable: false, blockers: sources.diagnostics.map(d => d.file + ': ' + d.reason), inputVersion: (0, entity_transaction_1.entityContentVersion)(JSON.stringify(preview)), preview };
|
|
217
|
+
const plan = (0, entity_renumber_transaction_1.prepareEntityRenumberPlan)(bound.root, snapshot, changes, sources);
|
|
218
|
+
return { ok: true, executable: true, plan, blockers: [], recoveryAvailable: true,
|
|
219
|
+
inputVersion: (0, entity_transaction_1.entityContentVersion)(JSON.stringify(preview)), preview };
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
if (error instanceof entity_store_1.EntityStoreError)
|
|
223
|
+
return { ok: false, code: error.code, reason: error.message };
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export declare function createEntityStoreHandlers(context: {
|
|
2
|
+
storeRoot?: string;
|
|
3
|
+
}): {
|
|
4
|
+
entity_store(a: {
|
|
5
|
+
root?: string;
|
|
6
|
+
operation?: string;
|
|
7
|
+
plan?: unknown;
|
|
8
|
+
}): Promise<{
|
|
9
|
+
ok: true;
|
|
10
|
+
state: "active";
|
|
11
|
+
storeLocator: string;
|
|
12
|
+
storeId: string;
|
|
13
|
+
} | {
|
|
14
|
+
ok: true;
|
|
15
|
+
state: "legacy";
|
|
16
|
+
storeLocator: string;
|
|
17
|
+
storeId?: undefined;
|
|
18
|
+
} | {
|
|
19
|
+
ok: boolean;
|
|
20
|
+
plan: import("../../spec/entity-store").EntityAdoptionPlan;
|
|
21
|
+
code?: undefined;
|
|
22
|
+
reason?: undefined;
|
|
23
|
+
} | {
|
|
24
|
+
ok: boolean;
|
|
25
|
+
plan: {
|
|
26
|
+
sourceOwner?: {
|
|
27
|
+
token: string;
|
|
28
|
+
version: string;
|
|
29
|
+
} | undefined;
|
|
30
|
+
lockOwner?: {
|
|
31
|
+
token: string;
|
|
32
|
+
version: string;
|
|
33
|
+
} | undefined;
|
|
34
|
+
schema: "holmes-entity-recovery/1";
|
|
35
|
+
workspaceId: string;
|
|
36
|
+
storeLocator: string;
|
|
37
|
+
operations: ({
|
|
38
|
+
operationId: string;
|
|
39
|
+
kind: "aborted";
|
|
40
|
+
storeId: string;
|
|
41
|
+
journalVersion: null;
|
|
42
|
+
state: "empty";
|
|
43
|
+
targets: Array<{
|
|
44
|
+
locator: string;
|
|
45
|
+
beforeVersion: string | null;
|
|
46
|
+
afterVersion: string | null;
|
|
47
|
+
currentVersion: string | null;
|
|
48
|
+
state: "unchanged" | "applied" | "unapplied" | "conflict";
|
|
49
|
+
}>;
|
|
50
|
+
inventoryConflicts: string[];
|
|
51
|
+
leftovers: string[];
|
|
52
|
+
} | {
|
|
53
|
+
retired?: true | undefined;
|
|
54
|
+
operationId: string;
|
|
55
|
+
kind: "adoption" | "write" | "legacy-renumber";
|
|
56
|
+
storeId: string | null;
|
|
57
|
+
journalVersion: string;
|
|
58
|
+
state: string;
|
|
59
|
+
targets: {
|
|
60
|
+
locator: string;
|
|
61
|
+
beforeVersion: string | null;
|
|
62
|
+
afterVersion: string | null;
|
|
63
|
+
currentVersion: string | null;
|
|
64
|
+
state: string;
|
|
65
|
+
}[];
|
|
66
|
+
inventoryConflicts: string[];
|
|
67
|
+
leftovers?: undefined;
|
|
68
|
+
})[];
|
|
69
|
+
};
|
|
70
|
+
code?: undefined;
|
|
71
|
+
reason?: undefined;
|
|
72
|
+
} | {
|
|
73
|
+
ok: boolean;
|
|
74
|
+
code: string;
|
|
75
|
+
reason: string;
|
|
76
|
+
plan?: undefined;
|
|
77
|
+
}>;
|
|
78
|
+
};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createEntityStoreHandlers = createEntityStoreHandlers;
|
|
37
|
+
// @implements A-SPEC-627, A-SPEC-627.1
|
|
38
|
+
// @implements A-SPEC-642
|
|
39
|
+
// @implements A-SPEC-641
|
|
40
|
+
// @implements A-SPEC-638
|
|
41
|
+
// @implements A-SPEC-634
|
|
42
|
+
const fs = __importStar(require("node:fs"));
|
|
43
|
+
const path = __importStar(require("node:path"));
|
|
44
|
+
const node_util_1 = require("node:util");
|
|
45
|
+
const transition_policy_1 = require("../../spec/transition-policy");
|
|
46
|
+
const spec_parser_1 = require("../../spec/spec-parser");
|
|
47
|
+
const ledger_store_1 = require("../../governance/ledger-store");
|
|
48
|
+
const provenance_chain_1 = require("../../governance/provenance-chain");
|
|
49
|
+
const entity_transaction_1 = require("../../spec/entity-transaction");
|
|
50
|
+
const entity_store_1 = require("../../spec/entity-store");
|
|
51
|
+
const root_1 = require("../../project/root");
|
|
52
|
+
const workspace_identity_1 = require("../../project/workspace-identity");
|
|
53
|
+
const approval_grants_1 = require("../../governance/approval-grants");
|
|
54
|
+
const approval_queue_1 = require("../../governance/approval-queue");
|
|
55
|
+
function createEntityStoreHandlers(context) {
|
|
56
|
+
return {
|
|
57
|
+
async entity_store(a) {
|
|
58
|
+
try {
|
|
59
|
+
if (!context.storeRoot)
|
|
60
|
+
throw new entity_store_1.EntityStoreError('unsupported-store', 'Entity persistence requires a configured filesystem spec store.');
|
|
61
|
+
const bound = (0, entity_store_1.entityStoreBinding)(context.storeRoot);
|
|
62
|
+
if (a.root !== undefined && (0, root_1.resolveProjectRoot)(a.root).root !== bound.root)
|
|
63
|
+
throw new entity_store_1.EntityStoreError('foreign-workspace', 'The requested workspace differs from the configured store.');
|
|
64
|
+
if (a.operation === undefined || a.operation === 'status')
|
|
65
|
+
return (0, entity_store_1.inspectEntityStore)(context.storeRoot);
|
|
66
|
+
if (a.operation === 'plan-adoption')
|
|
67
|
+
return { ok: true, plan: (0, entity_store_1.planEntityAdoption)(context.storeRoot) };
|
|
68
|
+
if (a.operation === 'recovery-plan')
|
|
69
|
+
return { ok: true, plan: (0, entity_store_1.planEntityRecovery)(context.storeRoot) };
|
|
70
|
+
// @implements A-SPEC-642 — an authority-spending operation is refused, naming the exit, while the
|
|
71
|
+
// merged ledger holds an unreconciled double-spend; the audit adapter's `audit-conflict` stays behind.
|
|
72
|
+
if (a.operation === 'recover' || a.operation === 'adopt') {
|
|
73
|
+
const conflict = (0, ledger_store_1.ledgerConflictReason)(path.join(bound.root, '.ax', 'ledger'));
|
|
74
|
+
if (conflict)
|
|
75
|
+
throw new entity_store_1.EntityStoreError('ledger-conflict', conflict);
|
|
76
|
+
}
|
|
77
|
+
if (a.operation === 'recover') {
|
|
78
|
+
const plan = (0, entity_store_1.validateEntityRecoveryPlan)(a.plan);
|
|
79
|
+
const action = { kind: 'config-write', target: (0, entity_store_1.entityRecoveryTarget)(plan) };
|
|
80
|
+
let env;
|
|
81
|
+
try {
|
|
82
|
+
env = process.env.HOLMES_APPROVAL ? JSON.parse(process.env.HOLMES_APPROVAL) : undefined;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
env = undefined;
|
|
86
|
+
}
|
|
87
|
+
const resolved = (0, approval_grants_1.resolveApproval)(bound.root, env, action, new Date().toISOString());
|
|
88
|
+
if (!resolved)
|
|
89
|
+
return { ok: false, code: 'approval-required', reason: 'Recovery requires approval for this exact inspected plan.' + (0, approval_queue_1.queueHint)(bound.root, { ...action, why: 'Recover the interrupted entity operation while preserving external changes.' }) };
|
|
90
|
+
const specGrants = new Set();
|
|
91
|
+
const specAuthorizations = new Set();
|
|
92
|
+
const usedGrantRefs = new Set();
|
|
93
|
+
const grantRef = (approval) => 'grant-use:' + (0, entity_transaction_1.entityContentVersion)(JSON.stringify({ actor: approval.actor, token: approval.token, nonce: approval.nonce, scope: approval.scope, expires: approval.expires }));
|
|
94
|
+
const auditRecovery = () => {
|
|
95
|
+
// @implements A-SPEC-634 — an aborted operation binds journal:none; a lock-only release binds no operation.
|
|
96
|
+
const op = plan.operations[0] ?? { operationId: 'lock-only', storeId: 'none', journalVersion: null, targets: [] }, operationRef = 'entity-operation:' + op.operationId;
|
|
97
|
+
const ledger = new ledger_store_1.FileLedgerStore(path.join(bound.root, '.ax/ledger'));
|
|
98
|
+
if (!ledger.verifyAll().ok)
|
|
99
|
+
throw new entity_store_1.EntityStoreError('audit-conflict', 'Recovery audit cannot extend an invalid ledger.');
|
|
100
|
+
const binding = [operationRef, 'workspace:' + plan.workspaceId, 'store:' + (op.storeId ?? 'legacy'), 'store-locator:' + plan.storeLocator, op.journalVersion ?? 'journal:none'];
|
|
101
|
+
const existing = ledger.loadAll().filter(event => event.kind === 'entity-recovered' && event.inputs?.includes(operationRef));
|
|
102
|
+
if (existing.length) {
|
|
103
|
+
if (existing.length !== 1 || binding.some(ref => !existing[0].inputs?.includes(ref)))
|
|
104
|
+
throw new entity_store_1.EntityStoreError('audit-conflict', 'Recovery audit has conflicting operation bindings.');
|
|
105
|
+
for (const ref of existing[0].inputs ?? [])
|
|
106
|
+
if (ref.startsWith('grant-use:'))
|
|
107
|
+
usedGrantRefs.add(ref);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const authority = (0, approval_grants_1.resolveApproval)(bound.root, env, action, new Date().toISOString());
|
|
111
|
+
if (!authority)
|
|
112
|
+
throw new entity_store_1.EntityStoreError('approval-required', 'Recovery approval expired before audit completion.');
|
|
113
|
+
if (authority.source === 'grant')
|
|
114
|
+
usedGrantRefs.add(grantRef(authority.approval));
|
|
115
|
+
const ids = op.targets.filter(target => target.locator.startsWith(bound.locator + '/') && target.locator.endsWith('.md') && target.afterVersion !== null).map(target => {
|
|
116
|
+
const bytes = (0, entity_transaction_1.readEntityBytes)(path.join(bound.root, target.locator));
|
|
117
|
+
if (!bytes || (0, entity_transaction_1.entityContentVersion)(bytes) !== target.afterVersion)
|
|
118
|
+
throw new entity_store_1.EntityStoreError('plan-changed', 'Recovered document changed before audit completion.');
|
|
119
|
+
return (0, spec_parser_1.parseSpec)(bytes.toString('utf8')).id;
|
|
120
|
+
});
|
|
121
|
+
ledger.append({ ts: new Date().toISOString(), actor: authority.approval.actor, kind: 'entity-recovered', summary: 'Completed entity recovery ' + op.operationId,
|
|
122
|
+
inputs: [...binding, ...ids, (0, entity_store_1.entityRecoveryTarget)(plan), ...specAuthorizations, ...usedGrantRefs], rationale: authority.approval.rationale,
|
|
123
|
+
authorization: (0, provenance_chain_1.authorizationRef)(authority.approval.actor, authority.approval.token) });
|
|
124
|
+
};
|
|
125
|
+
const result = (0, entity_store_1.recoverEntityStore)(context.storeRoot, plan, (before, after, inventory) => {
|
|
126
|
+
const specAction = { kind: 'spec-approve', target: after.id };
|
|
127
|
+
const requireAuthority = () => {
|
|
128
|
+
const authority = (0, approval_grants_1.resolveApproval)(bound.root, env, specAction, new Date().toISOString());
|
|
129
|
+
if (!authority)
|
|
130
|
+
throw new entity_store_1.EntityStoreError('spec-approval-required', 'This recovery also requires current spec transition authority.' + (0, approval_queue_1.queueHint)(bound.root, { ...specAction, why: 'Recover the interrupted specification transition.' }));
|
|
131
|
+
return authority;
|
|
132
|
+
};
|
|
133
|
+
requireAuthority();
|
|
134
|
+
if (!before || !['approved', 'draft', 'outdated'].includes(after.status))
|
|
135
|
+
throw new entity_store_1.EntityStoreError('unsupported-recovery', 'Unsupported specification transition.');
|
|
136
|
+
const files = new Map();
|
|
137
|
+
const readFile = (relative) => {
|
|
138
|
+
let text;
|
|
139
|
+
try {
|
|
140
|
+
text = fs.readFileSync(path.join(bound.root, relative), 'utf8');
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
text = null;
|
|
144
|
+
}
|
|
145
|
+
files.set(relative, text);
|
|
146
|
+
return text;
|
|
147
|
+
};
|
|
148
|
+
const prepared = after.status === 'approved'
|
|
149
|
+
? (0, transition_policy_1.prepareApprovedSpec)(before, inventory, specs => id => specs.find(s => s.id === id) ?? null, readFile)
|
|
150
|
+
: (0, transition_policy_1.prepareLifecycleSpec)(before, inventory, after.status === 'draft' ? 'unseal' : 'retire', typeof after.frontmatter.retired_reason === 'string' ? after.frontmatter.retired_reason : undefined);
|
|
151
|
+
if (!prepared.ok)
|
|
152
|
+
throw new entity_store_1.EntityStoreError('spec-policy-refused', after.id + ': ' + prepared.reason); // A-SPEC-641
|
|
153
|
+
if (!(0, node_util_1.isDeepStrictEqual)((0, spec_parser_1.parseSpec)((0, spec_parser_1.serializeSpec)(prepared.candidate)), after))
|
|
154
|
+
throw new entity_store_1.EntityStoreError('spec-policy-refused', 'The journal differs from the canonical approved transition.');
|
|
155
|
+
return () => {
|
|
156
|
+
for (const [relative, expected] of files) {
|
|
157
|
+
let actual;
|
|
158
|
+
try {
|
|
159
|
+
actual = fs.readFileSync(path.join(bound.root, relative), 'utf8');
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
actual = null;
|
|
163
|
+
}
|
|
164
|
+
if (actual !== expected)
|
|
165
|
+
throw new entity_store_1.EntityStoreError('plan-changed', 'Compatibility policy inputs changed before recovery publication.');
|
|
166
|
+
}
|
|
167
|
+
const authority = requireAuthority();
|
|
168
|
+
specAuthorizations.add('spec-authorization:' + (0, provenance_chain_1.authorizationRef)(authority.approval.actor, authority.approval.token));
|
|
169
|
+
if (authority.source === 'grant')
|
|
170
|
+
usedGrantRefs.add(grantRef(authority.approval));
|
|
171
|
+
if (authority.source === 'grant' && authority.approval.nonce)
|
|
172
|
+
specGrants.add(authority.approval.nonce);
|
|
173
|
+
};
|
|
174
|
+
}, auditRecovery);
|
|
175
|
+
const consumed = new Set(specGrants);
|
|
176
|
+
if (resolved.source === 'grant' && resolved.approval.nonce)
|
|
177
|
+
consumed.add(resolved.approval.nonce);
|
|
178
|
+
for (const grant of (0, approval_grants_1.readGrants)(bound.root).grants)
|
|
179
|
+
if (grant.nonce && usedGrantRefs.has(grantRef(grant)))
|
|
180
|
+
consumed.add(grant.nonce);
|
|
181
|
+
for (const nonce of consumed)
|
|
182
|
+
(0, approval_grants_1.consumeGrantFile)(bound.root, nonce);
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
if (a.operation === 'adopt') {
|
|
186
|
+
const plan = (0, entity_store_1.validateEntityAdoptionPlan)(a.plan);
|
|
187
|
+
const action = { kind: 'config-write', target: (0, entity_store_1.entityAdoptionTarget)(plan) };
|
|
188
|
+
let env;
|
|
189
|
+
try {
|
|
190
|
+
env = process.env.HOLMES_APPROVAL ? JSON.parse(process.env.HOLMES_APPROVAL) : undefined;
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
env = undefined;
|
|
194
|
+
}
|
|
195
|
+
const resolved = (0, approval_grants_1.resolveApproval)(bound.root, env, action, new Date().toISOString());
|
|
196
|
+
if (!resolved)
|
|
197
|
+
return { ok: false, code: 'approval-required', reason: 'Entity adoption requires approval for this exact plan.' + (0, approval_queue_1.queueHint)(bound.root, { ...action, why: 'Adopt the configured spec store without changing sealed documents.' }) };
|
|
198
|
+
const result = (0, entity_store_1.adoptEntityStore)(context.storeRoot, plan);
|
|
199
|
+
if (resolved.source === 'grant' && resolved.approval.nonce)
|
|
200
|
+
(0, approval_grants_1.consumeGrantFile)(bound.root, resolved.approval.nonce);
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
throw new entity_store_1.EntityStoreError('unsupported-operation', 'Use status, plan-adoption, recovery-plan or explicitly authorized adopt/recover.');
|
|
204
|
+
}
|
|
205
|
+
catch (e) {
|
|
206
|
+
if (e instanceof entity_store_1.EntityStoreError || e instanceof workspace_identity_1.WorkspaceIdentityError)
|
|
207
|
+
return { ok: false, code: e.code, reason: e.message };
|
|
208
|
+
throw e;
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Spec } from '../../spec/spec-parser';
|
|
2
|
+
import { ScannedFile } from '../../cpg/cpg-scanner';
|
|
3
|
+
import { ChangeSet, ChangeSourceInfo } from '../../project/change-source';
|
|
4
|
+
import { type GraphBasis } from '../../rtm/graph-store';
|
|
5
|
+
export interface GraphOperationsContext {
|
|
6
|
+
listSpecs(): Promise<Spec[]>;
|
|
7
|
+
assertStoreReachable(tool: string, root: unknown): void;
|
|
8
|
+
foreignRootReason(root?: string): string | null;
|
|
9
|
+
refusal(reason: string): Error;
|
|
10
|
+
projectRootOf(root: string): string;
|
|
11
|
+
cachedScan(root: string): ScannedFile[];
|
|
12
|
+
cacheDirFor(root: string): string;
|
|
13
|
+
deriveChangedContext(root: string, args: {
|
|
14
|
+
base?: string;
|
|
15
|
+
head?: string;
|
|
16
|
+
since?: string;
|
|
17
|
+
}, tool: string): Promise<{
|
|
18
|
+
root: string;
|
|
19
|
+
changes: ChangeSet;
|
|
20
|
+
specs: Spec[];
|
|
21
|
+
scanned: ScannedFile[];
|
|
22
|
+
bySourcePath: Map<string, ScannedFile>;
|
|
23
|
+
changeSource: ChangeSourceInfo;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
26
|
+
/** Publish the complete graph for `specs` over `scanned` at the live path (A-SPEC-631 primitive). */
|
|
27
|
+
export declare function publishCurrentGraph(root: string, specs: Spec[], scanned: ScannedFile[]): {
|
|
28
|
+
basis: GraphBasis;
|
|
29
|
+
nodes: number;
|
|
30
|
+
edges: number;
|
|
31
|
+
};
|
|
32
|
+
export type SemanticWarmOutcome = {
|
|
33
|
+
status: 'discharged';
|
|
34
|
+
tier: string;
|
|
35
|
+
computed: number;
|
|
36
|
+
cached: number;
|
|
37
|
+
} | {
|
|
38
|
+
status: 'not-applicable';
|
|
39
|
+
tier: 'none';
|
|
40
|
+
} | {
|
|
41
|
+
status: 'unavailable';
|
|
42
|
+
tier: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* @implements A-SPEC-478 — the semantic cache WARMING lives in explicit heavy operations, so the
|
|
46
|
+
* ranking hot path only ever LOOKS UP vectors. Idempotent through the cache. The outcome says
|
|
47
|
+
* whether there was nothing to do (tier none), whether it happened, or whether a configured tier
|
|
48
|
+
* could not embed — a discharge must not record the last one as done.
|
|
49
|
+
*/
|
|
50
|
+
export declare function warmSemanticCache(root: string, scanned: readonly ScannedFile[]): Promise<SemanticWarmOutcome>;
|
|
51
|
+
export declare function createGraphOperationsHandlers(context: GraphOperationsContext): {
|
|
52
|
+
rtm_impact(a: {
|
|
53
|
+
root: string;
|
|
54
|
+
changed: string[];
|
|
55
|
+
}): Promise<{
|
|
56
|
+
breadthWarning?: string | undefined;
|
|
57
|
+
rankedImpact: {
|
|
58
|
+
file: string;
|
|
59
|
+
score: number;
|
|
60
|
+
}[];
|
|
61
|
+
reachedByDepth: number[];
|
|
62
|
+
bounded: {
|
|
63
|
+
id: string;
|
|
64
|
+
reason: "hub" | "depth";
|
|
65
|
+
inDegree?: number;
|
|
66
|
+
}[] | undefined;
|
|
67
|
+
summariesOmitted?: number | undefined;
|
|
68
|
+
impacted: string[];
|
|
69
|
+
impactedSummaries: {
|
|
70
|
+
id: string;
|
|
71
|
+
summary: string | null;
|
|
72
|
+
}[];
|
|
73
|
+
}>;
|
|
74
|
+
rtm_reindex(a: {
|
|
75
|
+
root: string;
|
|
76
|
+
base?: string;
|
|
77
|
+
head?: string;
|
|
78
|
+
since?: string;
|
|
79
|
+
}): Promise<{
|
|
80
|
+
semanticWarm?: {
|
|
81
|
+
tier: string;
|
|
82
|
+
computed: number;
|
|
83
|
+
cached: number;
|
|
84
|
+
} | undefined;
|
|
85
|
+
changed: number;
|
|
86
|
+
nodes: number;
|
|
87
|
+
edges: number;
|
|
88
|
+
changeSource: ChangeSourceInfo;
|
|
89
|
+
}>;
|
|
90
|
+
};
|