@principles/core 1.187.0 → 1.189.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/dist/runtime-v2/__tests__/architecture-regression.test.js +11 -0
- package/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
- package/dist/runtime-v2/__tests__/evidence-chain-intent-tension.test.d.ts +2 -0
- package/dist/runtime-v2/__tests__/evidence-chain-intent-tension.test.d.ts.map +1 -0
- package/dist/runtime-v2/__tests__/evidence-chain-intent-tension.test.js +284 -0
- package/dist/runtime-v2/__tests__/evidence-chain-intent-tension.test.js.map +1 -0
- package/dist/runtime-v2/diagnostician/diag-rootcause-output.d.ts +8 -0
- package/dist/runtime-v2/diagnostician/diag-rootcause-output.d.ts.map +1 -1
- package/dist/runtime-v2/diagnostician/diag-rootcause-output.js +23 -0
- package/dist/runtime-v2/diagnostician/diag-rootcause-output.js.map +1 -1
- package/dist/runtime-v2/evidence-triage/source-descriptors.d.ts +1 -1
- package/dist/runtime-v2/evidence-triage/source-descriptors.js +1 -1
- package/dist/runtime-v2/index.d.ts +4 -3
- package/dist/runtime-v2/index.d.ts.map +1 -1
- package/dist/runtime-v2/index.js +6 -2
- package/dist/runtime-v2/index.js.map +1 -1
- package/dist/runtime-v2/intent/__tests__/intent-decision-record.test.d.ts +2 -0
- package/dist/runtime-v2/intent/__tests__/intent-decision-record.test.d.ts.map +1 -0
- package/dist/runtime-v2/intent/__tests__/intent-decision-record.test.js +183 -0
- package/dist/runtime-v2/intent/__tests__/intent-decision-record.test.js.map +1 -0
- package/dist/runtime-v2/intent/index.d.ts +1 -0
- package/dist/runtime-v2/intent/index.d.ts.map +1 -1
- package/dist/runtime-v2/intent/index.js +1 -0
- package/dist/runtime-v2/intent/index.js.map +1 -1
- package/dist/runtime-v2/intent/intent-decision-record.d.ts +105 -0
- package/dist/runtime-v2/intent/intent-decision-record.d.ts.map +1 -0
- package/dist/runtime-v2/intent/intent-decision-record.js +21 -0
- package/dist/runtime-v2/intent/intent-decision-record.js.map +1 -0
- package/dist/runtime-v2/store/intent/__tests__/sqlite-intent-decision-store.test.d.ts +2 -0
- package/dist/runtime-v2/store/intent/__tests__/sqlite-intent-decision-store.test.d.ts.map +1 -0
- package/dist/runtime-v2/store/intent/__tests__/sqlite-intent-decision-store.test.js +207 -0
- package/dist/runtime-v2/store/intent/__tests__/sqlite-intent-decision-store.test.js.map +1 -0
- package/dist/runtime-v2/store/intent/index.d.ts +6 -0
- package/dist/runtime-v2/store/intent/index.d.ts.map +1 -0
- package/dist/runtime-v2/store/intent/index.js +5 -0
- package/dist/runtime-v2/store/intent/index.js.map +1 -0
- package/dist/runtime-v2/store/intent/sqlite-intent-decision-store.d.ts +36 -0
- package/dist/runtime-v2/store/intent/sqlite-intent-decision-store.d.ts.map +1 -0
- package/dist/runtime-v2/store/intent/sqlite-intent-decision-store.js +259 -0
- package/dist/runtime-v2/store/intent/sqlite-intent-decision-store.js.map +1 -0
- package/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
- package/dist/runtime-v2/store/sqlite-connection.js +33 -0
- package/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
- package/dist/runtime-v2/types/evidence-chain-contract.d.ts +63 -0
- package/dist/runtime-v2/types/evidence-chain-contract.d.ts.map +1 -1
- package/dist/runtime-v2/types/evidence-chain-contract.js +234 -9
- package/dist/runtime-v2/types/evidence-chain-contract.js.map +1 -1
- package/dist/runtime-v2/types/index.d.ts +2 -2
- package/dist/runtime-v2/types/index.d.ts.map +1 -1
- package/dist/runtime-v2/types/index.js +3 -1
- package/dist/runtime-v2/types/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SqliteIntentDecisionStore tests (PRI-470).
|
|
3
|
+
*
|
|
4
|
+
* Verifies idempotency, snapshot storage, evidence truncation, ordering, and
|
|
5
|
+
* summary aggregation against a real SQLite database.
|
|
6
|
+
*
|
|
7
|
+
* ERR checklist:
|
|
8
|
+
* - EP-01 / ERR-001, ERR-005: store treats DB rows as unknown and validates via guards
|
|
9
|
+
* - EP-03 / ERR-002: write failures throw (fail loud), never silent
|
|
10
|
+
* - EP-07 / ERR-015, ERR-018: idempotency distinguishes current vs recorded state
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import * as os from 'node:os';
|
|
15
|
+
import * as fs from 'node:fs';
|
|
16
|
+
import { SqliteConnection } from '../../sqlite-connection.js';
|
|
17
|
+
import { SqliteIntentDecisionStore } from '../sqlite-intent-decision-store.js';
|
|
18
|
+
function createTestConnection() {
|
|
19
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-test-intent-decision-'));
|
|
20
|
+
return new SqliteConnection(tmpDir);
|
|
21
|
+
}
|
|
22
|
+
function makeInput(overrides = {}) {
|
|
23
|
+
return {
|
|
24
|
+
id: 'rec-' + Math.random().toString(36).slice(2, 8),
|
|
25
|
+
painId: 'pain-1',
|
|
26
|
+
taskId: 'task-1',
|
|
27
|
+
runId: 'run-1',
|
|
28
|
+
intentDocHash: 'sha256:abc',
|
|
29
|
+
source: 'action_drift',
|
|
30
|
+
evidenceStrength: 'moderate',
|
|
31
|
+
relatedIntentFields: ['why'],
|
|
32
|
+
ownerAction: 'confirm_drift',
|
|
33
|
+
evidenceRefs: ['ref-1', 'ref-2'],
|
|
34
|
+
...overrides,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
describe('SqliteIntentDecisionStore', () => {
|
|
38
|
+
let connection = null;
|
|
39
|
+
let store = null;
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
connection = createTestConnection();
|
|
42
|
+
// Touch getDb() so the schema (including intent_decisions) is initialized.
|
|
43
|
+
connection.getDb();
|
|
44
|
+
store = new SqliteIntentDecisionStore(connection);
|
|
45
|
+
});
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
connection?.close();
|
|
48
|
+
});
|
|
49
|
+
describe('record()', () => {
|
|
50
|
+
it('creates a new record with correct fields', async () => {
|
|
51
|
+
const input = makeInput({ id: 'rec-new' });
|
|
52
|
+
const result = await store.record(input);
|
|
53
|
+
expect(result.created).toBe(true);
|
|
54
|
+
expect(result.record.id).toBe('rec-new');
|
|
55
|
+
expect(result.record.painId).toBe('pain-1');
|
|
56
|
+
expect(result.record.taskId).toBe('task-1');
|
|
57
|
+
expect(result.record.source).toBe('action_drift');
|
|
58
|
+
expect(result.record.evidenceStrength).toBe('moderate');
|
|
59
|
+
expect(result.record.relatedIntentFields).toEqual(['why']);
|
|
60
|
+
expect(result.record.ownerAction).toBe('confirm_drift');
|
|
61
|
+
expect(result.record.evidenceRefs).toEqual(['ref-1', 'ref-2']);
|
|
62
|
+
expect(result.record.createdAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
63
|
+
});
|
|
64
|
+
it('is idempotent when same painId + intentDocHash + ownerAction is resubmitted', async () => {
|
|
65
|
+
const input = makeInput({ id: 'rec-first', painId: 'pain-1', intentDocHash: 'sha256:abc', ownerAction: 'confirm_drift' });
|
|
66
|
+
const first = await store.record(input);
|
|
67
|
+
expect(first.created).toBe(true);
|
|
68
|
+
// Same idempotency key, different id — must return the existing record.
|
|
69
|
+
const replay = await store.record(makeInput({ id: 'rec-second', painId: 'pain-1', intentDocHash: 'sha256:abc', ownerAction: 'confirm_drift' }));
|
|
70
|
+
expect(replay.created).toBe(false);
|
|
71
|
+
expect(replay.record.id).toBe('rec-first');
|
|
72
|
+
});
|
|
73
|
+
it('does NOT treat different ownerAction as idempotent', async () => {
|
|
74
|
+
await store.record(makeInput({ id: 'rec-a', painId: 'pain-1', intentDocHash: 'sha256:abc', ownerAction: 'confirm_drift' }));
|
|
75
|
+
const second = await store.record(makeInput({ id: 'rec-b', painId: 'pain-1', intentDocHash: 'sha256:abc', ownerAction: 'observe' }));
|
|
76
|
+
expect(second.created).toBe(true);
|
|
77
|
+
expect(second.record.id).toBe('rec-b');
|
|
78
|
+
});
|
|
79
|
+
it('with null painId uses taskId-based idempotency', async () => {
|
|
80
|
+
const input = makeInput({
|
|
81
|
+
id: 'rec-task-1',
|
|
82
|
+
painId: undefined,
|
|
83
|
+
taskId: 'task-X',
|
|
84
|
+
intentDocHash: 'sha256:xyz',
|
|
85
|
+
ownerAction: 'dismiss',
|
|
86
|
+
});
|
|
87
|
+
const first = await store.record(input);
|
|
88
|
+
expect(first.created).toBe(true);
|
|
89
|
+
const replay = await store.record(makeInput({
|
|
90
|
+
id: 'rec-task-2',
|
|
91
|
+
painId: undefined,
|
|
92
|
+
taskId: 'task-X',
|
|
93
|
+
intentDocHash: 'sha256:xyz',
|
|
94
|
+
ownerAction: 'dismiss',
|
|
95
|
+
}));
|
|
96
|
+
expect(replay.created).toBe(false);
|
|
97
|
+
expect(replay.record.id).toBe('rec-task-1');
|
|
98
|
+
});
|
|
99
|
+
it('with null painId and null intentDocHash still idempotency-checks via IS operator', async () => {
|
|
100
|
+
await store.record(makeInput({
|
|
101
|
+
id: 'rec-null-1', painId: undefined, taskId: 'task-Y', intentDocHash: undefined, ownerAction: 'observe',
|
|
102
|
+
}));
|
|
103
|
+
const replay = await store.record(makeInput({
|
|
104
|
+
id: 'rec-null-2', painId: undefined, taskId: 'task-Y', intentDocHash: undefined, ownerAction: 'observe',
|
|
105
|
+
}));
|
|
106
|
+
expect(replay.created).toBe(false);
|
|
107
|
+
expect(replay.record.id).toBe('rec-null-1');
|
|
108
|
+
});
|
|
109
|
+
it('truncates evidence to max 3 items', async () => {
|
|
110
|
+
const input = makeInput({ id: 'rec-trunc', evidenceRefs: ['r1', 'r2', 'r3', 'r4', 'r5'] });
|
|
111
|
+
const result = await store.record(input);
|
|
112
|
+
expect(result.record.evidenceRefs).toEqual(['r1', 'r2', 'r3']);
|
|
113
|
+
});
|
|
114
|
+
it('stores immutable snapshots matching the decision-time values', async () => {
|
|
115
|
+
const input = makeInput({
|
|
116
|
+
id: 'rec-snap',
|
|
117
|
+
source: 'intent_suspect',
|
|
118
|
+
evidenceStrength: 'strong',
|
|
119
|
+
relatedIntentFields: ['why', 'desired_outcome'],
|
|
120
|
+
evidenceRefs: ['e1', 'e2'],
|
|
121
|
+
});
|
|
122
|
+
const result = await store.record(input);
|
|
123
|
+
// Read the raw row to verify snapshot columns exist and match.
|
|
124
|
+
const row = connection.getDb().prepare('SELECT source_snapshot, evidence_strength_snapshot, related_intent_fields_snapshot, evidence_refs_snapshot FROM intent_decisions WHERE id = ?').get(result.record.id);
|
|
125
|
+
expect(row.source_snapshot).toBe('intent_suspect');
|
|
126
|
+
expect(row.evidence_strength_snapshot).toBe('strong');
|
|
127
|
+
expect(JSON.parse(row.related_intent_fields_snapshot)).toEqual(['why', 'desired_outcome']);
|
|
128
|
+
expect(JSON.parse(row.evidence_refs_snapshot)).toEqual(['e1', 'e2']);
|
|
129
|
+
});
|
|
130
|
+
it('persists the optional note when provided', async () => {
|
|
131
|
+
const result = await store.record(makeInput({ id: 'rec-note', note: 'owner note text' }));
|
|
132
|
+
const row = connection.getDb().prepare('SELECT note FROM intent_decisions WHERE id = ?').get(result.record.id);
|
|
133
|
+
expect(row.note).toBe('owner note text');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe('getById()', () => {
|
|
137
|
+
it('returns the record when found', async () => {
|
|
138
|
+
const created = await store.record(makeInput({ id: 'rec-get' }));
|
|
139
|
+
const found = await store.getById('rec-get');
|
|
140
|
+
expect(found).not.toBeNull();
|
|
141
|
+
expect(found?.id).toBe('rec-get');
|
|
142
|
+
expect(found?.source).toBe(created.record.source);
|
|
143
|
+
});
|
|
144
|
+
it('returns null when not found', async () => {
|
|
145
|
+
const found = await store.getById('does-not-exist');
|
|
146
|
+
expect(found).toBeNull();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('listByPainId()', () => {
|
|
150
|
+
it('returns records ordered by createdAt DESC', async () => {
|
|
151
|
+
await store.record(makeInput({ id: 'r1', painId: 'pain-list', ownerAction: 'confirm_drift' }));
|
|
152
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
153
|
+
await store.record(makeInput({ id: 'r2', painId: 'pain-list', ownerAction: 'observe', intentDocHash: 'sha256:other' }));
|
|
154
|
+
const list = await store.listByPainId('pain-list');
|
|
155
|
+
expect(list).toHaveLength(2);
|
|
156
|
+
expect(list[0]?.id).toBe('r2');
|
|
157
|
+
expect(list[1]?.id).toBe('r1');
|
|
158
|
+
});
|
|
159
|
+
it('returns empty array for unknown painId', async () => {
|
|
160
|
+
const list = await store.listByPainId('unknown');
|
|
161
|
+
expect(list).toEqual([]);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe('listByTaskId()', () => {
|
|
165
|
+
it('returns records ordered by createdAt DESC', async () => {
|
|
166
|
+
await store.record(makeInput({ id: 't1', taskId: 'task-list', painId: 'p1', intentDocHash: 'h1', ownerAction: 'confirm_drift' }));
|
|
167
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
168
|
+
await store.record(makeInput({ id: 't2', taskId: 'task-list', painId: 'p2', intentDocHash: 'h2', ownerAction: 'observe' }));
|
|
169
|
+
const list = await store.listByTaskId('task-list');
|
|
170
|
+
expect(list).toHaveLength(2);
|
|
171
|
+
expect(list[0]?.id).toBe('t2');
|
|
172
|
+
expect(list[1]?.id).toBe('t1');
|
|
173
|
+
});
|
|
174
|
+
it('returns empty array for unknown taskId', async () => {
|
|
175
|
+
const list = await store.listByTaskId('unknown');
|
|
176
|
+
expect(list).toEqual([]);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
describe('getSummary()', () => {
|
|
180
|
+
it('returns all-zero counts and null lastDecisionAt when no records exist', async () => {
|
|
181
|
+
const summary = await store.getSummary();
|
|
182
|
+
expect(summary.lastDecisionAt).toBeNull();
|
|
183
|
+
expect(summary.counts).toEqual({
|
|
184
|
+
confirm_drift: 0,
|
|
185
|
+
revise_intent: 0,
|
|
186
|
+
observe: 0,
|
|
187
|
+
dismiss: 0,
|
|
188
|
+
promote_to_principle: 0,
|
|
189
|
+
promote_to_rulehost: 0,
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
it('returns counts per ownerAction and the most recent lastDecisionAt', async () => {
|
|
193
|
+
await store.record(makeInput({ id: 's1', painId: 'p1', intentDocHash: 'h1', ownerAction: 'confirm_drift' }));
|
|
194
|
+
await store.record(makeInput({ id: 's2', painId: 'p2', intentDocHash: 'h2', ownerAction: 'confirm_drift' }));
|
|
195
|
+
await store.record(makeInput({ id: 's3', painId: 'p3', intentDocHash: 'h3', ownerAction: 'observe' }));
|
|
196
|
+
const summary = await store.getSummary();
|
|
197
|
+
expect(summary.counts.confirm_drift).toBe(2);
|
|
198
|
+
expect(summary.counts.observe).toBe(1);
|
|
199
|
+
expect(summary.counts.dismiss).toBe(0);
|
|
200
|
+
expect(summary.counts.revise_intent).toBe(0);
|
|
201
|
+
expect(summary.counts.promote_to_principle).toBe(0);
|
|
202
|
+
expect(summary.counts.promote_to_rulehost).toBe(0);
|
|
203
|
+
expect(summary.lastDecisionAt).not.toBeNull();
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
//# sourceMappingURL=sqlite-intent-decision-store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-intent-decision-store.test.js","sourceRoot":"","sources":["../../../../../src/runtime-v2/store/intent/__tests__/sqlite-intent-decision-store.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Barrel for the IntentDecisionRecord SQLite store (PRI-470).
|
|
3
|
+
*/
|
|
4
|
+
export { SqliteIntentDecisionStore } from './sqlite-intent-decision-store.js';
|
|
5
|
+
export type { IntentDecisionStore } from '../../intent/intent-decision-record.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/runtime-v2/store/intent/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,YAAY,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/runtime-v2/store/intent/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SqliteIntentDecisionStore — SQLite implementation of IntentDecisionStore.
|
|
3
|
+
*
|
|
4
|
+
* PRI-470 / SPEC §21.7 durable persistence for Owner decisions on
|
|
5
|
+
* intentTension. Records are written to the `intent_decisions` table
|
|
6
|
+
* (schema created by SqliteConnection.initSchema).
|
|
7
|
+
*
|
|
8
|
+
* Idempotency (SPEC §21.7 req 4):
|
|
9
|
+
* - When `painId` is non-null: dedupe on (pain_id, intent_doc_hash, owner_action).
|
|
10
|
+
* - When `painId` is null: dedupe on (task_id, intent_doc_hash, owner_action)
|
|
11
|
+
* using the NULL-safe `IS` operator so a null intentDocHash compares equal.
|
|
12
|
+
*
|
|
13
|
+
* Snapshots (SPEC §21.7 audit boundary):
|
|
14
|
+
* - source / evidenceStrength / relatedIntentFields / evidenceRefs are stored
|
|
15
|
+
* twice: once in the live columns and once in the `_snapshot` columns. The
|
|
16
|
+
* snapshot is the immutable audit copy; the live columns mirror it at write
|
|
17
|
+
* time. This keeps the audit trail accurate even if a future migration or
|
|
18
|
+
* artifact change alters the live columns.
|
|
19
|
+
*
|
|
20
|
+
* ERR checklist:
|
|
21
|
+
* - EP-01 / ERR-001, ERR-005: DB rows treated as unknown, mapped via guards
|
|
22
|
+
* - EP-03 / ERR-002: write failures throw (fail loud)
|
|
23
|
+
* - EP-07 / ERR-015, ERR-018: idempotency SELECT reads fresh state each call
|
|
24
|
+
*/
|
|
25
|
+
import type { SqliteConnection } from '../sqlite-connection.js';
|
|
26
|
+
import type { IntentDecisionInput, IntentDecisionRecord, IntentDecisionRecordResult, IntentDecisionSummary, IntentDecisionStore } from '../../intent/intent-decision-record.js';
|
|
27
|
+
export declare class SqliteIntentDecisionStore implements IntentDecisionStore {
|
|
28
|
+
private readonly connection;
|
|
29
|
+
constructor(connection: SqliteConnection);
|
|
30
|
+
record(input: IntentDecisionInput): Promise<IntentDecisionRecordResult>;
|
|
31
|
+
getById(id: string): Promise<IntentDecisionRecord | null>;
|
|
32
|
+
listByPainId(painId: string): Promise<IntentDecisionRecord[]>;
|
|
33
|
+
listByTaskId(taskId: string): Promise<IntentDecisionRecord[]>;
|
|
34
|
+
getSummary(): Promise<IntentDecisionSummary>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=sqlite-intent-decision-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-intent-decision-store.d.ts","sourceRoot":"","sources":["../../../../src/runtime-v2/store/intent/sqlite-intent-decision-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,wCAAwC,CAAC;AAsJhD,qBAAa,yBAA0B,YAAW,mBAAmB;IACvD,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,gBAAgB;IAEnD,MAAM,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAiFvE,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAOzD,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAS7D,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAS7D,UAAU,IAAI,OAAO,CAAC,qBAAqB,CAAC;CAsBnD"}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SqliteIntentDecisionStore — SQLite implementation of IntentDecisionStore.
|
|
3
|
+
*
|
|
4
|
+
* PRI-470 / SPEC §21.7 durable persistence for Owner decisions on
|
|
5
|
+
* intentTension. Records are written to the `intent_decisions` table
|
|
6
|
+
* (schema created by SqliteConnection.initSchema).
|
|
7
|
+
*
|
|
8
|
+
* Idempotency (SPEC §21.7 req 4):
|
|
9
|
+
* - When `painId` is non-null: dedupe on (pain_id, intent_doc_hash, owner_action).
|
|
10
|
+
* - When `painId` is null: dedupe on (task_id, intent_doc_hash, owner_action)
|
|
11
|
+
* using the NULL-safe `IS` operator so a null intentDocHash compares equal.
|
|
12
|
+
*
|
|
13
|
+
* Snapshots (SPEC §21.7 audit boundary):
|
|
14
|
+
* - source / evidenceStrength / relatedIntentFields / evidenceRefs are stored
|
|
15
|
+
* twice: once in the live columns and once in the `_snapshot` columns. The
|
|
16
|
+
* snapshot is the immutable audit copy; the live columns mirror it at write
|
|
17
|
+
* time. This keeps the audit trail accurate even if a future migration or
|
|
18
|
+
* artifact change alters the live columns.
|
|
19
|
+
*
|
|
20
|
+
* ERR checklist:
|
|
21
|
+
* - EP-01 / ERR-001, ERR-005: DB rows treated as unknown, mapped via guards
|
|
22
|
+
* - EP-03 / ERR-002: write failures throw (fail loud)
|
|
23
|
+
* - EP-07 / ERR-015, ERR-018: idempotency SELECT reads fresh state each call
|
|
24
|
+
*/
|
|
25
|
+
import { isIntentTensionSource, isEvidenceStrength, isIntentRelatedField, isSuggestedOwnerAction, } from '../../diagnostician/diag-rootcause-output.js';
|
|
26
|
+
const MAX_EVIDENCE_REFS = 3;
|
|
27
|
+
function parseStringArray(raw) {
|
|
28
|
+
let parsed;
|
|
29
|
+
try {
|
|
30
|
+
parsed = JSON.parse(raw);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
if (!Array.isArray(parsed))
|
|
36
|
+
return [];
|
|
37
|
+
return parsed.filter((v) => typeof v === 'string');
|
|
38
|
+
}
|
|
39
|
+
// ── Row type guards (Runtime Contract Rule 2: no `as` bypass) ────────────────
|
|
40
|
+
function isRecord(v) {
|
|
41
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
42
|
+
}
|
|
43
|
+
function isIntentDecisionRow(v) {
|
|
44
|
+
if (!isRecord(v))
|
|
45
|
+
return false;
|
|
46
|
+
if (typeof v.id !== 'string')
|
|
47
|
+
return false;
|
|
48
|
+
if (v.pain_id !== null && typeof v.pain_id !== 'string')
|
|
49
|
+
return false;
|
|
50
|
+
if (typeof v.task_id !== 'string')
|
|
51
|
+
return false;
|
|
52
|
+
if (v.run_id !== null && typeof v.run_id !== 'string')
|
|
53
|
+
return false;
|
|
54
|
+
if (v.intent_doc_hash !== null && typeof v.intent_doc_hash !== 'string')
|
|
55
|
+
return false;
|
|
56
|
+
if (typeof v.source !== 'string')
|
|
57
|
+
return false;
|
|
58
|
+
if (typeof v.evidence_strength !== 'string')
|
|
59
|
+
return false;
|
|
60
|
+
if (typeof v.related_intent_fields !== 'string')
|
|
61
|
+
return false;
|
|
62
|
+
if (typeof v.owner_action !== 'string')
|
|
63
|
+
return false;
|
|
64
|
+
if (typeof v.evidence_refs !== 'string')
|
|
65
|
+
return false;
|
|
66
|
+
if (typeof v.source_snapshot !== 'string')
|
|
67
|
+
return false;
|
|
68
|
+
if (typeof v.evidence_strength_snapshot !== 'string')
|
|
69
|
+
return false;
|
|
70
|
+
if (typeof v.related_intent_fields_snapshot !== 'string')
|
|
71
|
+
return false;
|
|
72
|
+
if (typeof v.evidence_refs_snapshot !== 'string')
|
|
73
|
+
return false;
|
|
74
|
+
if (v.resulting_candidate_id !== null && typeof v.resulting_candidate_id !== 'string')
|
|
75
|
+
return false;
|
|
76
|
+
if (v.resulting_rule_candidate_id !== null && typeof v.resulting_rule_candidate_id !== 'string')
|
|
77
|
+
return false;
|
|
78
|
+
if (v.patch_proposal_id !== null && typeof v.patch_proposal_id !== 'string')
|
|
79
|
+
return false;
|
|
80
|
+
if (typeof v.created_at !== 'string')
|
|
81
|
+
return false;
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
function isSummaryRow(v) {
|
|
85
|
+
if (!isRecord(v))
|
|
86
|
+
return false;
|
|
87
|
+
if (typeof v.owner_action !== 'string')
|
|
88
|
+
return false;
|
|
89
|
+
if (typeof v.cnt !== 'number' || !Number.isFinite(v.cnt))
|
|
90
|
+
return false;
|
|
91
|
+
if (v.last_at !== null && typeof v.last_at !== 'string')
|
|
92
|
+
return false;
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
function zeroCounts() {
|
|
96
|
+
return {
|
|
97
|
+
confirm_drift: 0,
|
|
98
|
+
revise_intent: 0,
|
|
99
|
+
observe: 0,
|
|
100
|
+
dismiss: 0,
|
|
101
|
+
promote_to_principle: 0,
|
|
102
|
+
promote_to_rulehost: 0,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function mapRow(r) {
|
|
106
|
+
// Validate enum fields read from the DB (ERR-001). Snapshot columns are the
|
|
107
|
+
// auditable source of truth, so we read from them; the live column is a
|
|
108
|
+
// secondary fallback. Both are written by this store, so they should always
|
|
109
|
+
// be valid — the defaults below are defensive against DB corruption.
|
|
110
|
+
const source = isIntentTensionSource(r.source_snapshot)
|
|
111
|
+
? r.source_snapshot
|
|
112
|
+
: isIntentTensionSource(r.source)
|
|
113
|
+
? r.source
|
|
114
|
+
: 'none';
|
|
115
|
+
const evidenceStrength = isEvidenceStrength(r.evidence_strength_snapshot)
|
|
116
|
+
? r.evidence_strength_snapshot
|
|
117
|
+
: isEvidenceStrength(r.evidence_strength)
|
|
118
|
+
? r.evidence_strength
|
|
119
|
+
: 'weak';
|
|
120
|
+
const ownerAction = isSuggestedOwnerAction(r.owner_action)
|
|
121
|
+
? r.owner_action
|
|
122
|
+
: 'observe';
|
|
123
|
+
const fieldsRaw = parseStringArray(r.related_intent_fields_snapshot);
|
|
124
|
+
const relatedIntentFields = fieldsRaw.length > 0
|
|
125
|
+
? fieldsRaw.filter(isIntentRelatedField)
|
|
126
|
+
: parseStringArray(r.related_intent_fields).filter(isIntentRelatedField);
|
|
127
|
+
const evidenceRefs = parseStringArray(r.evidence_refs_snapshot);
|
|
128
|
+
const record = {
|
|
129
|
+
id: r.id,
|
|
130
|
+
source,
|
|
131
|
+
evidenceStrength,
|
|
132
|
+
relatedIntentFields,
|
|
133
|
+
ownerAction,
|
|
134
|
+
evidenceRefs,
|
|
135
|
+
createdAt: r.created_at,
|
|
136
|
+
};
|
|
137
|
+
if (r.pain_id !== null)
|
|
138
|
+
record.painId = r.pain_id;
|
|
139
|
+
if (r.run_id !== null)
|
|
140
|
+
record.runId = r.run_id;
|
|
141
|
+
if (r.intent_doc_hash !== null)
|
|
142
|
+
record.intentDocHash = r.intent_doc_hash;
|
|
143
|
+
// task_id is NOT NULL in the schema, but the type marks it optional — only
|
|
144
|
+
// surface it when it is non-empty so an empty string does not masquerade as
|
|
145
|
+
// a real lineage id.
|
|
146
|
+
if (r.task_id !== '')
|
|
147
|
+
record.taskId = r.task_id;
|
|
148
|
+
if (r.resulting_candidate_id !== null)
|
|
149
|
+
record.resultingCandidateId = r.resulting_candidate_id;
|
|
150
|
+
if (r.resulting_rule_candidate_id !== null)
|
|
151
|
+
record.resultingRuleCandidateId = r.resulting_rule_candidate_id;
|
|
152
|
+
if (r.patch_proposal_id !== null)
|
|
153
|
+
record.patchProposalId = r.patch_proposal_id;
|
|
154
|
+
return record;
|
|
155
|
+
}
|
|
156
|
+
export class SqliteIntentDecisionStore {
|
|
157
|
+
connection;
|
|
158
|
+
constructor(connection) {
|
|
159
|
+
this.connection = connection;
|
|
160
|
+
}
|
|
161
|
+
async record(input) {
|
|
162
|
+
const db = this.connection.getDb();
|
|
163
|
+
// Truncate evidence to the SPEC §22.1.2 maximum of 3 items.
|
|
164
|
+
const truncatedEvidence = input.evidenceRefs.slice(0, MAX_EVIDENCE_REFS);
|
|
165
|
+
const relatedFieldsJson = JSON.stringify(input.relatedIntentFields);
|
|
166
|
+
const evidenceRefsJson = JSON.stringify(truncatedEvidence);
|
|
167
|
+
const createdAt = new Date().toISOString();
|
|
168
|
+
// ── Idempotency check ──────────────────────────────────────────────────
|
|
169
|
+
// SPEC §21.7 req 4: same painId + intentDocHash + ownerAction must be
|
|
170
|
+
// idempotent. When painId is null, fall back to taskId-based dedupe.
|
|
171
|
+
let existing;
|
|
172
|
+
if (input.painId !== undefined) {
|
|
173
|
+
const row = db.prepare('SELECT * FROM intent_decisions WHERE pain_id = ? AND intent_doc_hash IS ? AND owner_action = ? LIMIT 1').get(input.painId, input.intentDocHash ?? null, input.ownerAction);
|
|
174
|
+
if (isIntentDecisionRow(row))
|
|
175
|
+
existing = row;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
const taskId = input.taskId ?? '';
|
|
179
|
+
const row = db.prepare('SELECT * FROM intent_decisions WHERE pain_id IS NULL AND task_id = ? AND intent_doc_hash IS ? AND owner_action = ? LIMIT 1').get(taskId, input.intentDocHash ?? null, input.ownerAction);
|
|
180
|
+
if (isIntentDecisionRow(row))
|
|
181
|
+
existing = row;
|
|
182
|
+
}
|
|
183
|
+
if (existing) {
|
|
184
|
+
return { record: mapRow(existing), created: false };
|
|
185
|
+
}
|
|
186
|
+
// ── Insert ─────────────────────────────────────────────────────────────
|
|
187
|
+
db.prepare(`
|
|
188
|
+
INSERT INTO intent_decisions (
|
|
189
|
+
id, pain_id, task_id, run_id, intent_doc_hash,
|
|
190
|
+
source, evidence_strength, related_intent_fields, owner_action, evidence_refs,
|
|
191
|
+
note,
|
|
192
|
+
source_snapshot, evidence_strength_snapshot,
|
|
193
|
+
related_intent_fields_snapshot, evidence_refs_snapshot,
|
|
194
|
+
resulting_candidate_id, resulting_rule_candidate_id, patch_proposal_id,
|
|
195
|
+
created_at
|
|
196
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
197
|
+
`).run(input.id, input.painId ?? null, input.taskId ?? '', input.runId ?? null, input.intentDocHash ?? null, input.source, input.evidenceStrength, relatedFieldsJson, input.ownerAction, evidenceRefsJson, input.note ?? null,
|
|
198
|
+
// Snapshots — immutable audit copies (identical to live values at write time).
|
|
199
|
+
input.source, input.evidenceStrength, relatedFieldsJson, evidenceRefsJson, null, null, null, createdAt);
|
|
200
|
+
const record = {
|
|
201
|
+
id: input.id,
|
|
202
|
+
source: input.source,
|
|
203
|
+
evidenceStrength: input.evidenceStrength,
|
|
204
|
+
relatedIntentFields: [...input.relatedIntentFields],
|
|
205
|
+
ownerAction: input.ownerAction,
|
|
206
|
+
evidenceRefs: [...truncatedEvidence],
|
|
207
|
+
createdAt,
|
|
208
|
+
};
|
|
209
|
+
if (input.painId !== undefined)
|
|
210
|
+
record.painId = input.painId;
|
|
211
|
+
if (input.taskId !== undefined)
|
|
212
|
+
record.taskId = input.taskId;
|
|
213
|
+
if (input.runId !== undefined)
|
|
214
|
+
record.runId = input.runId;
|
|
215
|
+
if (input.intentDocHash !== undefined)
|
|
216
|
+
record.intentDocHash = input.intentDocHash;
|
|
217
|
+
return { record, created: true };
|
|
218
|
+
}
|
|
219
|
+
async getById(id) {
|
|
220
|
+
const db = this.connection.getDb();
|
|
221
|
+
const row = db.prepare('SELECT * FROM intent_decisions WHERE id = ?').get(id);
|
|
222
|
+
if (!isIntentDecisionRow(row))
|
|
223
|
+
return null;
|
|
224
|
+
return mapRow(row);
|
|
225
|
+
}
|
|
226
|
+
async listByPainId(painId) {
|
|
227
|
+
const db = this.connection.getDb();
|
|
228
|
+
const rawRows = db.prepare('SELECT * FROM intent_decisions WHERE pain_id = ? ORDER BY created_at DESC').all(painId);
|
|
229
|
+
if (!Array.isArray(rawRows))
|
|
230
|
+
return [];
|
|
231
|
+
return rawRows.filter(isIntentDecisionRow).map(mapRow);
|
|
232
|
+
}
|
|
233
|
+
async listByTaskId(taskId) {
|
|
234
|
+
const db = this.connection.getDb();
|
|
235
|
+
const rawRows = db.prepare('SELECT * FROM intent_decisions WHERE task_id = ? ORDER BY created_at DESC').all(taskId);
|
|
236
|
+
if (!Array.isArray(rawRows))
|
|
237
|
+
return [];
|
|
238
|
+
return rawRows.filter(isIntentDecisionRow).map(mapRow);
|
|
239
|
+
}
|
|
240
|
+
async getSummary() {
|
|
241
|
+
const db = this.connection.getDb();
|
|
242
|
+
const rawRows = db.prepare('SELECT owner_action, COUNT(*) AS cnt, MAX(created_at) AS last_at FROM intent_decisions GROUP BY owner_action').all();
|
|
243
|
+
if (!Array.isArray(rawRows))
|
|
244
|
+
return { counts: zeroCounts(), lastDecisionAt: null };
|
|
245
|
+
const rows = rawRows.filter(isSummaryRow);
|
|
246
|
+
const counts = zeroCounts();
|
|
247
|
+
let lastDecisionAt = null;
|
|
248
|
+
for (const row of rows) {
|
|
249
|
+
if (isSuggestedOwnerAction(row.owner_action)) {
|
|
250
|
+
counts[row.owner_action] = row.cnt;
|
|
251
|
+
}
|
|
252
|
+
if (row.last_at && (lastDecisionAt === null || row.last_at > lastDecisionAt)) {
|
|
253
|
+
lastDecisionAt = row.last_at;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return { counts, lastDecisionAt };
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=sqlite-intent-decision-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-intent-decision-store.js","sourceRoot":"","sources":["../../../../src/runtime-v2/store/intent/sqlite-intent-decision-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-connection.d.ts","sourceRoot":"","sources":["../../../src/runtime-v2/store/sqlite-connection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAKtC,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,EAAE,CAAkC;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAU;gBAE3B,kBAAkB,EAAE,MAAM,GAAG,uBAAuB;IAYhE,KAAK,IAAI,QAAQ,CAAC,QAAQ;IAsD1B,eAAe,IAAI,kBAAkB;IA4BrC,OAAO,CAAC,UAAU;
|
|
1
|
+
{"version":3,"file":"sqlite-connection.d.ts","sourceRoot":"","sources":["../../../src/runtime-v2/store/sqlite-connection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAKtC,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,EAAE,CAAkC;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAU;gBAE3B,kBAAkB,EAAE,MAAM,GAAG,uBAAuB;IAYhE,KAAK,IAAI,QAAQ,CAAC,QAAQ;IAsD1B,eAAe,IAAI,kBAAkB;IA4BrC,OAAO,CAAC,UAAU;IAyRlB,OAAO,CAAC,aAAa;IA+BrB,iDAAiD;IACjD,KAAK,IAAI,IAAI;CAWd"}
|
|
@@ -338,6 +338,39 @@ export class SqliteConnection {
|
|
|
338
338
|
);
|
|
339
339
|
CREATE INDEX IF NOT EXISTS idx_confirm_first_state_last_seen
|
|
340
340
|
ON confirm_first_state(last_seen_at);
|
|
341
|
+
`);
|
|
342
|
+
// PRI-470: IntentDecisionRecord durable store (SPEC §21.7).
|
|
343
|
+
// Stores immutable snapshots of source / evidenceStrength /
|
|
344
|
+
// relatedIntentFields / evidenceRefs so the audit trail stays accurate
|
|
345
|
+
// even if the underlying artifact is later modified.
|
|
346
|
+
db.exec(`
|
|
347
|
+
CREATE TABLE IF NOT EXISTS intent_decisions (
|
|
348
|
+
id TEXT PRIMARY KEY,
|
|
349
|
+
pain_id TEXT,
|
|
350
|
+
task_id TEXT NOT NULL,
|
|
351
|
+
run_id TEXT,
|
|
352
|
+
intent_doc_hash TEXT,
|
|
353
|
+
source TEXT NOT NULL,
|
|
354
|
+
evidence_strength TEXT NOT NULL,
|
|
355
|
+
related_intent_fields TEXT NOT NULL,
|
|
356
|
+
owner_action TEXT NOT NULL,
|
|
357
|
+
evidence_refs TEXT NOT NULL,
|
|
358
|
+
note TEXT,
|
|
359
|
+
source_snapshot TEXT NOT NULL,
|
|
360
|
+
evidence_strength_snapshot TEXT NOT NULL,
|
|
361
|
+
related_intent_fields_snapshot TEXT NOT NULL,
|
|
362
|
+
evidence_refs_snapshot TEXT NOT NULL,
|
|
363
|
+
resulting_candidate_id TEXT,
|
|
364
|
+
resulting_rule_candidate_id TEXT,
|
|
365
|
+
patch_proposal_id TEXT,
|
|
366
|
+
created_at TEXT NOT NULL
|
|
367
|
+
);
|
|
368
|
+
CREATE INDEX IF NOT EXISTS idx_intent_decisions_pain_id ON intent_decisions(pain_id);
|
|
369
|
+
CREATE INDEX IF NOT EXISTS idx_intent_decisions_task_id ON intent_decisions(task_id);
|
|
370
|
+
CREATE INDEX IF NOT EXISTS idx_intent_decisions_owner_action ON intent_decisions(owner_action);
|
|
371
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_intent_decisions_pain_hash_action
|
|
372
|
+
ON intent_decisions(pain_id, intent_doc_hash, owner_action)
|
|
373
|
+
WHERE pain_id IS NOT NULL;
|
|
341
374
|
`);
|
|
342
375
|
}
|
|
343
376
|
migrateSchema() {
|