@codeledger/engine 0.7.1 → 0.7.3
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/claims/claims.d.ts +14 -0
- package/dist/claims/claims.d.ts.map +1 -0
- package/dist/claims/claims.js +125 -0
- package/dist/claims/claims.js.map +1 -0
- package/dist/claims/index.d.ts +2 -0
- package/dist/claims/index.d.ts.map +1 -0
- package/dist/claims/index.js +2 -0
- package/dist/claims/index.js.map +1 -0
- package/dist/dashboard/index.d.ts +3 -0
- package/dist/dashboard/index.d.ts.map +1 -0
- package/dist/dashboard/index.js +3 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/dashboard/metrics.d.ts +24 -0
- package/dist/dashboard/metrics.d.ts.map +1 -0
- package/dist/dashboard/metrics.js +358 -0
- package/dist/dashboard/metrics.js.map +1 -0
- package/dist/dashboard/store.d.ts +26 -0
- package/dist/dashboard/store.d.ts.map +1 -0
- package/dist/dashboard/store.js +97 -0
- package/dist/dashboard/store.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/integration/ambient-coordinator.d.ts +75 -0
- package/dist/integration/ambient-coordinator.d.ts.map +1 -0
- package/dist/integration/ambient-coordinator.js +183 -0
- package/dist/integration/ambient-coordinator.js.map +1 -0
- package/dist/integration/index.d.ts +4 -0
- package/dist/integration/index.d.ts.map +1 -0
- package/dist/integration/index.js +3 -0
- package/dist/integration/index.js.map +1 -0
- package/dist/integration/warning-throttle.d.ts +63 -0
- package/dist/integration/warning-throttle.d.ts.map +1 -0
- package/dist/integration/warning-throttle.js +347 -0
- package/dist/integration/warning-throttle.js.map +1 -0
- package/dist/intervention/escalation.d.ts +23 -0
- package/dist/intervention/escalation.d.ts.map +1 -0
- package/dist/intervention/escalation.js +149 -0
- package/dist/intervention/escalation.js.map +1 -0
- package/dist/intervention/generator.d.ts +12 -0
- package/dist/intervention/generator.d.ts.map +1 -0
- package/dist/intervention/generator.js +191 -0
- package/dist/intervention/generator.js.map +1 -0
- package/dist/intervention/index.d.ts +4 -0
- package/dist/intervention/index.d.ts.map +1 -0
- package/dist/intervention/index.js +4 -0
- package/dist/intervention/index.js.map +1 -0
- package/dist/intervention/signal-aggregator.d.ts +26 -0
- package/dist/intervention/signal-aggregator.d.ts.map +1 -0
- package/dist/intervention/signal-aggregator.js +274 -0
- package/dist/intervention/signal-aggregator.js.map +1 -0
- package/dist/layer-d/conflict-detector.d.ts +12 -0
- package/dist/layer-d/conflict-detector.d.ts.map +1 -0
- package/dist/layer-d/conflict-detector.js +150 -0
- package/dist/layer-d/conflict-detector.js.map +1 -0
- package/dist/layer-d/index.d.ts +3 -0
- package/dist/layer-d/index.d.ts.map +1 -0
- package/dist/layer-d/index.js +3 -0
- package/dist/layer-d/index.js.map +1 -0
- package/dist/layer-d/surface-extractor.d.ts +10 -0
- package/dist/layer-d/surface-extractor.d.ts.map +1 -0
- package/dist/layer-d/surface-extractor.js +96 -0
- package/dist/layer-d/surface-extractor.js.map +1 -0
- package/dist/policy-domains/domains.d.ts +31 -0
- package/dist/policy-domains/domains.d.ts.map +1 -0
- package/dist/policy-domains/domains.js +445 -0
- package/dist/policy-domains/domains.js.map +1 -0
- package/dist/policy-domains/index.d.ts +8 -0
- package/dist/policy-domains/index.d.ts.map +1 -0
- package/dist/policy-domains/index.js +8 -0
- package/dist/policy-domains/index.js.map +1 -0
- package/dist/pulse/index.d.ts +2 -0
- package/dist/pulse/index.d.ts.map +1 -0
- package/dist/pulse/index.js +2 -0
- package/dist/pulse/index.js.map +1 -0
- package/dist/pulse/pulse.d.ts +20 -0
- package/dist/pulse/pulse.d.ts.map +1 -0
- package/dist/pulse/pulse.js +253 -0
- package/dist/pulse/pulse.js.map +1 -0
- package/dist/severity/index.d.ts +2 -0
- package/dist/severity/index.d.ts.map +1 -0
- package/dist/severity/index.js +2 -0
- package/dist/severity/index.js.map +1 -0
- package/dist/severity/severity.d.ts +48 -0
- package/dist/severity/severity.d.ts.map +1 -0
- package/dist/severity/severity.js +134 -0
- package/dist/severity/severity.js.map +1 -0
- package/dist/staleness/index.d.ts +2 -0
- package/dist/staleness/index.d.ts.map +1 -0
- package/dist/staleness/index.js +2 -0
- package/dist/staleness/index.js.map +1 -0
- package/dist/staleness/jit-staleness.d.ts +29 -0
- package/dist/staleness/jit-staleness.d.ts.map +1 -0
- package/dist/staleness/jit-staleness.js +240 -0
- package/dist/staleness/jit-staleness.js.map +1 -0
- package/dist/team-policy/index.d.ts +1 -1
- package/dist/team-policy/index.d.ts.map +1 -1
- package/dist/team-policy/index.js +1 -1
- package/dist/team-policy/index.js.map +1 -1
- package/dist/team-policy/policy.d.ts +7 -0
- package/dist/team-policy/policy.d.ts.map +1 -1
- package/dist/team-policy/policy.js +38 -0
- package/dist/team-policy/policy.js.map +1 -1
- package/dist/trust/approval.d.ts +16 -0
- package/dist/trust/approval.d.ts.map +1 -0
- package/dist/trust/approval.js +183 -0
- package/dist/trust/approval.js.map +1 -0
- package/dist/trust/capsule.d.ts +36 -0
- package/dist/trust/capsule.d.ts.map +1 -0
- package/dist/trust/capsule.js +206 -0
- package/dist/trust/capsule.js.map +1 -0
- package/dist/trust/index.d.ts +6 -0
- package/dist/trust/index.d.ts.map +1 -0
- package/dist/trust/index.js +6 -0
- package/dist/trust/index.js.map +1 -0
- package/dist/trust/learning-feedback.d.ts +18 -0
- package/dist/trust/learning-feedback.d.ts.map +1 -0
- package/dist/trust/learning-feedback.js +146 -0
- package/dist/trust/learning-feedback.js.map +1 -0
- package/dist/trust/risk-scoring.d.ts +16 -0
- package/dist/trust/risk-scoring.d.ts.map +1 -0
- package/dist/trust/risk-scoring.js +142 -0
- package/dist/trust/risk-scoring.js.map +1 -0
- package/dist/trust/timeline.d.ts +42 -0
- package/dist/trust/timeline.d.ts.map +1 -0
- package/dist/trust/timeline.js +154 -0
- package/dist/trust/timeline.js.map +1 -0
- package/package.json +6 -2
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JIT Staleness — Context Shadowing (Hardened v2.5.1)
|
|
3
|
+
*
|
|
4
|
+
* Detects relevant changes to a session's context and prepares
|
|
5
|
+
* actionable shadow updates. Advisory only — never auto-merges.
|
|
6
|
+
*
|
|
7
|
+
* Hardening guarantees:
|
|
8
|
+
* - Three-tier change relevance:
|
|
9
|
+
* 1. Direct file staleness (bundle files changed) — strongest signal
|
|
10
|
+
* 2. Dependency-surface staleness (direct imports changed) — moderate signal
|
|
11
|
+
* 3. Test-surface staleness (shared tests changed) — weaker signal
|
|
12
|
+
* - Incidental branch churn (unrelated commits) does NOT cause staleness
|
|
13
|
+
* - Staleness severity is weighted, not just ratio-based:
|
|
14
|
+
* - Direct changes count 1.0× toward staleness
|
|
15
|
+
* - Dependency changes count 0.5× toward staleness
|
|
16
|
+
* - Test-only changes count 0.25× toward staleness
|
|
17
|
+
* - Deterministic thresholds:
|
|
18
|
+
* HIGH: weighted ratio ≥ 0.40 (40% of context weight is stale)
|
|
19
|
+
* MEDIUM: weighted ratio ≥ 0.15
|
|
20
|
+
* LOW: weighted ratio ≥ 0.05
|
|
21
|
+
* FRESH: below 0.05
|
|
22
|
+
*/
|
|
23
|
+
import { execSync } from 'node:child_process';
|
|
24
|
+
/**
|
|
25
|
+
* Staleness thresholds — based on *weighted* change ratio, not raw file count.
|
|
26
|
+
* These reflect actual risk to context quality:
|
|
27
|
+
* - HIGH means substantial portions of the working context are outdated
|
|
28
|
+
* - MEDIUM means some relevant files changed and context may be incomplete
|
|
29
|
+
* - LOW means minor peripheral changes occurred
|
|
30
|
+
*/
|
|
31
|
+
const STALENESS_THRESHOLDS = {
|
|
32
|
+
high: 0.40,
|
|
33
|
+
medium: 0.15,
|
|
34
|
+
low: 0.05,
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Change relevance weights — how much each category of change contributes
|
|
38
|
+
* to the staleness score. Direct bundle changes are the strongest signal.
|
|
39
|
+
*/
|
|
40
|
+
const CHANGE_RELEVANCE_WEIGHTS = {
|
|
41
|
+
direct: 1.0, // Bundle file changed — full weight
|
|
42
|
+
dependency: 0.5, // Direct import/dependent changed — half weight
|
|
43
|
+
test: 0.25, // Shared test changed — quarter weight
|
|
44
|
+
};
|
|
45
|
+
/** Classify staleness level from a weighted ratio */
|
|
46
|
+
export function classifyStaleness(weightedRatio) {
|
|
47
|
+
if (weightedRatio >= STALENESS_THRESHOLDS.high)
|
|
48
|
+
return 'high';
|
|
49
|
+
if (weightedRatio >= STALENESS_THRESHOLDS.medium)
|
|
50
|
+
return 'medium';
|
|
51
|
+
if (weightedRatio >= STALENESS_THRESHOLDS.low)
|
|
52
|
+
return 'low';
|
|
53
|
+
return 'fresh';
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Classify staleness with direct file precedence.
|
|
57
|
+
* Bundle file changes dominate; dependency-only changes are capped at MEDIUM.
|
|
58
|
+
*/
|
|
59
|
+
export function classifyStalenessWeighted(bundleChangedCount, bundleTotal, depChangedCount) {
|
|
60
|
+
// Any tracked bundle file change → at least MEDIUM
|
|
61
|
+
if (bundleChangedCount > 0) {
|
|
62
|
+
const ratio = bundleTotal > 0 ? bundleChangedCount / bundleTotal : 0;
|
|
63
|
+
return classifyStaleness(ratio);
|
|
64
|
+
}
|
|
65
|
+
// Dependency-only changes → capped at MEDIUM, requires meaningful count
|
|
66
|
+
if (depChangedCount >= 3)
|
|
67
|
+
return 'medium';
|
|
68
|
+
if (depChangedCount >= 1)
|
|
69
|
+
return 'low';
|
|
70
|
+
return 'fresh';
|
|
71
|
+
}
|
|
72
|
+
/** Get files changed since a given git ref */
|
|
73
|
+
export function getChangesSince(repoRoot, sinceRef) {
|
|
74
|
+
const results = [];
|
|
75
|
+
try {
|
|
76
|
+
// Get file changes with status
|
|
77
|
+
const diffOutput = execSync(`git log --name-status --format='%aE' ${sinceRef}..HEAD 2>/dev/null`, { cwd: repoRoot, encoding: 'utf-8', timeout: 10000 }).trim();
|
|
78
|
+
if (!diffOutput)
|
|
79
|
+
return results;
|
|
80
|
+
let currentAuthor = 'unknown';
|
|
81
|
+
for (const line of diffOutput.split('\n')) {
|
|
82
|
+
const trimmed = line.trim();
|
|
83
|
+
if (!trimmed)
|
|
84
|
+
continue;
|
|
85
|
+
// Lines with @ are author emails
|
|
86
|
+
if (trimmed.includes('@') && !trimmed.includes('\t')) {
|
|
87
|
+
currentAuthor = trimmed.replace(/'/g, '');
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
// Status + path lines
|
|
91
|
+
const match = trimmed.match(/^([AMDRT])\t(.+)/);
|
|
92
|
+
if (match) {
|
|
93
|
+
results.push({
|
|
94
|
+
path: match[2],
|
|
95
|
+
status: match[1],
|
|
96
|
+
author: currentAuthor,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Non-fatal
|
|
103
|
+
}
|
|
104
|
+
return results;
|
|
105
|
+
}
|
|
106
|
+
/** Map git status letter to change kind */
|
|
107
|
+
function mapChangeKind(status) {
|
|
108
|
+
switch (status) {
|
|
109
|
+
case 'A': return 'added';
|
|
110
|
+
case 'D': return 'deleted';
|
|
111
|
+
case 'R': return 'renamed';
|
|
112
|
+
default: return 'modified';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Test file detection pattern */
|
|
116
|
+
const TEST_FILE_PATTERN = /\.(test|spec)\.[jt]sx?$/;
|
|
117
|
+
/**
|
|
118
|
+
* Compute a shadow update for a session.
|
|
119
|
+
*
|
|
120
|
+
* Hardened: uses weighted change relevance instead of raw file count ratio.
|
|
121
|
+
* Direct bundle file changes carry full weight, dependency changes carry half,
|
|
122
|
+
* and test-only changes carry a quarter.
|
|
123
|
+
*/
|
|
124
|
+
export function computeShadowUpdate(repoRoot, sessionId, bundleFiles, dependencySurface, sinceRef) {
|
|
125
|
+
const changes = getChangesSince(repoRoot, sinceRef);
|
|
126
|
+
const bundleSet = new Set(bundleFiles);
|
|
127
|
+
const depSet = new Set(dependencySurface);
|
|
128
|
+
const changedFiles = [];
|
|
129
|
+
const seen = new Set();
|
|
130
|
+
for (const change of changes) {
|
|
131
|
+
if (seen.has(change.path))
|
|
132
|
+
continue;
|
|
133
|
+
seen.add(change.path);
|
|
134
|
+
const inBundle = bundleSet.has(change.path);
|
|
135
|
+
const inDep = depSet.has(change.path);
|
|
136
|
+
// Hardened: only include files relevant to the session
|
|
137
|
+
if (!inBundle && !inDep)
|
|
138
|
+
continue;
|
|
139
|
+
changedFiles.push({
|
|
140
|
+
path: change.path,
|
|
141
|
+
changedBy: change.author,
|
|
142
|
+
changeKind: mapChangeKind(change.status),
|
|
143
|
+
inBundle,
|
|
144
|
+
inDependencySurface: inDep,
|
|
145
|
+
description: buildChangeDescription(change.path, change.status, change.author),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
// Hardened: weighted staleness computation
|
|
149
|
+
const totalContextSize = Math.max(bundleFiles.length, 1);
|
|
150
|
+
let weightedChangeScore = 0;
|
|
151
|
+
for (const file of changedFiles) {
|
|
152
|
+
if (file.inBundle) {
|
|
153
|
+
// Direct bundle change — full weight
|
|
154
|
+
// Test files in the bundle are still direct changes (they were selected for the task)
|
|
155
|
+
weightedChangeScore += CHANGE_RELEVANCE_WEIGHTS.direct;
|
|
156
|
+
}
|
|
157
|
+
else if (file.inDependencySurface) {
|
|
158
|
+
// Dependency-surface change — classify as test or dependency
|
|
159
|
+
const isTest = TEST_FILE_PATTERN.test(file.path);
|
|
160
|
+
weightedChangeScore += isTest
|
|
161
|
+
? CHANGE_RELEVANCE_WEIGHTS.test
|
|
162
|
+
: CHANGE_RELEVANCE_WEIGHTS.dependency;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const weightedRatio = weightedChangeScore / totalContextSize;
|
|
166
|
+
const stalenessLevel = classifyStaleness(weightedRatio);
|
|
167
|
+
return {
|
|
168
|
+
sessionId,
|
|
169
|
+
changedFiles,
|
|
170
|
+
summary: buildSummary(changedFiles, stalenessLevel),
|
|
171
|
+
computedAt: new Date().toISOString(),
|
|
172
|
+
stalenessLevel,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/** Build a human-readable change description */
|
|
176
|
+
function buildChangeDescription(path, status, author) {
|
|
177
|
+
const action = status === 'A' ? 'added' : status === 'D' ? 'deleted' : status === 'R' ? 'renamed' : 'modified';
|
|
178
|
+
return `${path} ${action} by ${author}`;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Build a concise summary of all changes.
|
|
182
|
+
*
|
|
183
|
+
* Hardened tone: measured, specific, actionable.
|
|
184
|
+
*/
|
|
185
|
+
function buildSummary(changes, level) {
|
|
186
|
+
if (changes.length === 0)
|
|
187
|
+
return 'No relevant changes detected.';
|
|
188
|
+
const bundleChanges = changes.filter(c => c.inBundle);
|
|
189
|
+
const depChanges = changes.filter(c => !c.inBundle && c.inDependencySurface);
|
|
190
|
+
const parts = [];
|
|
191
|
+
if (bundleChanges.length > 0) {
|
|
192
|
+
const files = bundleChanges.slice(0, 3).map(c => c.path).join(', ');
|
|
193
|
+
parts.push(`${bundleChanges.length} tracked file(s) changed: ${files}${bundleChanges.length > 3 ? '...' : ''}`);
|
|
194
|
+
}
|
|
195
|
+
if (depChanges.length > 0) {
|
|
196
|
+
parts.push(`${depChanges.length} dependency file(s) changed`);
|
|
197
|
+
}
|
|
198
|
+
const authors = [...new Set(changes.map(c => c.changedBy).filter(a => a && a !== 'unknown'))];
|
|
199
|
+
if (authors.length > 0) {
|
|
200
|
+
parts.push(`by ${authors.join(', ')}`);
|
|
201
|
+
}
|
|
202
|
+
// Hardened: use measured labels, not shouting
|
|
203
|
+
const levelLabel = level === 'high' ? 'Staleness: high' :
|
|
204
|
+
level === 'medium' ? 'Staleness: medium' :
|
|
205
|
+
level === 'low' ? 'Staleness: low' :
|
|
206
|
+
'Fresh';
|
|
207
|
+
return `[${levelLabel}] ${parts.join('. ')}.`;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Format a shadow update as an advisory message for the user.
|
|
211
|
+
*
|
|
212
|
+
* Hardened: concise, specific, actionable.
|
|
213
|
+
*/
|
|
214
|
+
export function formatShadowAdvisory(update) {
|
|
215
|
+
if (update.stalenessLevel === 'fresh') {
|
|
216
|
+
return 'Context is fresh — no relevant changes detected.';
|
|
217
|
+
}
|
|
218
|
+
const lines = [];
|
|
219
|
+
lines.push(update.summary);
|
|
220
|
+
lines.push('');
|
|
221
|
+
// Show bundle changes first (more important), then deps
|
|
222
|
+
const bundleChanges = update.changedFiles.filter(f => f.inBundle);
|
|
223
|
+
const depChanges = update.changedFiles.filter(f => !f.inBundle);
|
|
224
|
+
for (const file of bundleChanges.slice(0, 5)) {
|
|
225
|
+
lines.push(` [TRACKED] ${file.description}`);
|
|
226
|
+
}
|
|
227
|
+
for (const file of depChanges.slice(0, 5)) {
|
|
228
|
+
lines.push(` [DEP] ${file.description}`);
|
|
229
|
+
}
|
|
230
|
+
const totalShown = Math.min(bundleChanges.length, 5) + Math.min(depChanges.length, 5);
|
|
231
|
+
if (update.changedFiles.length > totalShown) {
|
|
232
|
+
lines.push(` ... and ${update.changedFiles.length - totalShown} more`);
|
|
233
|
+
}
|
|
234
|
+
if (update.stalenessLevel === 'medium' || update.stalenessLevel === 'high') {
|
|
235
|
+
lines.push('');
|
|
236
|
+
lines.push('Refresh context bundle to incorporate these changes before continuing.');
|
|
237
|
+
}
|
|
238
|
+
return lines.join('\n');
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=jit-staleness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jit-staleness.js","sourceRoot":"","sources":["../../src/staleness/jit-staleness.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* Moves from "what NOT to do" → "what MUST be included".
|
|
5
5
|
* Evaluates required file inclusion rules against context bundles.
|
|
6
6
|
*/
|
|
7
|
-
export { loadTeamPolicy, evaluateTeamPolicy, matchTriggeredRules, DEFAULT_TEAM_POLICY, } from './policy.js';
|
|
7
|
+
export { loadTeamPolicy, evaluateTeamPolicy, matchTriggeredRules, DEFAULT_TEAM_POLICY, recordPolicyEvent, readPolicyEvents, } from './policy.js';
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/team-policy/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/team-policy/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,aAAa,CAAC"}
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* Moves from "what NOT to do" → "what MUST be included".
|
|
5
5
|
* Evaluates required file inclusion rules against context bundles.
|
|
6
6
|
*/
|
|
7
|
-
export { loadTeamPolicy, evaluateTeamPolicy, matchTriggeredRules, DEFAULT_TEAM_POLICY, } from './policy.js';
|
|
7
|
+
export { loadTeamPolicy, evaluateTeamPolicy, matchTriggeredRules, DEFAULT_TEAM_POLICY, recordPolicyEvent, readPolicyEvents, } from './policy.js';
|
|
8
8
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/team-policy/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/team-policy/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,aAAa,CAAC"}
|
|
@@ -32,4 +32,11 @@ export declare function matchTriggeredRules(policy: TeamPolicyConfig, task: stri
|
|
|
32
32
|
* in the bundle's file list. Returns pass/fail with details.
|
|
33
33
|
*/
|
|
34
34
|
export declare function evaluateTeamPolicy(policy: TeamPolicyConfig, task: string, bundleFiles: string[]): TeamPolicyEvaluation;
|
|
35
|
+
/**
|
|
36
|
+
* Record a policy evaluation as an append-only event.
|
|
37
|
+
* Closes the audit gap: policy decisions are now persisted.
|
|
38
|
+
*/
|
|
39
|
+
export declare function recordPolicyEvent(repoRoot: string, task: string, evaluation: TeamPolicyEvaluation): void;
|
|
40
|
+
/** Read all policy evaluation events from the log */
|
|
41
|
+
export declare function readPolicyEvents(repoRoot: string): Array<Record<string, unknown>>;
|
|
35
42
|
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/team-policy/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAI3B,eAAO,MAAM,mBAAmB,EAAE,gBAIjC,CAAC;AASF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAcjE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,MAAM,GACX,cAAc,EAAE,CAOlB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EAAE,GACpB,oBAAoB,CAsCtB"}
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/team-policy/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAI3B,eAAO,MAAM,mBAAmB,EAAE,gBAIjC,CAAC;AASF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAcjE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,MAAM,GACX,cAAc,EAAE,CAOlB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EAAE,GACpB,oBAAoB,CAsCtB;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,oBAAoB,GAC/B,IAAI,CAmBN;AAED,qDAAqD;AACrD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAajF"}
|
|
@@ -97,4 +97,42 @@ export function evaluateTeamPolicy(policy, task, bundleFiles) {
|
|
|
97
97
|
violatedRules: violated,
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
|
+
// ─── Policy Event Recording ────────────────────────────────────────────────
|
|
101
|
+
/**
|
|
102
|
+
* Record a policy evaluation as an append-only event.
|
|
103
|
+
* Closes the audit gap: policy decisions are now persisted.
|
|
104
|
+
*/
|
|
105
|
+
export function recordPolicyEvent(repoRoot, task, evaluation) {
|
|
106
|
+
const { appendFileSync, existsSync: exists, mkdirSync: mkDir } = require('fs');
|
|
107
|
+
const { join: pathJoin, dirname: pathDirname } = require('path');
|
|
108
|
+
const logPath = pathJoin(repoRoot, '.codeledger', 'policy-events.jsonl');
|
|
109
|
+
const dir = pathDirname(logPath);
|
|
110
|
+
if (!exists(dir))
|
|
111
|
+
mkDir(dir, { recursive: true });
|
|
112
|
+
const event = {
|
|
113
|
+
type: 'policy_evaluation',
|
|
114
|
+
task,
|
|
115
|
+
passed: evaluation.passed,
|
|
116
|
+
triggeredRules: evaluation.triggeredRules,
|
|
117
|
+
satisfiedRules: evaluation.satisfiedRules,
|
|
118
|
+
violatedRules: evaluation.violatedRules.map(r => r.rule),
|
|
119
|
+
timestamp: new Date().toISOString(),
|
|
120
|
+
};
|
|
121
|
+
appendFileSync(logPath, JSON.stringify(event) + '\n', 'utf-8');
|
|
122
|
+
}
|
|
123
|
+
/** Read all policy evaluation events from the log */
|
|
124
|
+
export function readPolicyEvents(repoRoot) {
|
|
125
|
+
const { readFileSync, existsSync: exists } = require('fs');
|
|
126
|
+
const { join: pathJoin } = require('path');
|
|
127
|
+
const logPath = pathJoin(repoRoot, '.codeledger', 'policy-events.jsonl');
|
|
128
|
+
if (!exists(logPath))
|
|
129
|
+
return [];
|
|
130
|
+
try {
|
|
131
|
+
const lines = readFileSync(logPath, 'utf-8').trim().split('\n');
|
|
132
|
+
return lines.filter(l => l.trim()).map(l => JSON.parse(l));
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
100
138
|
//# sourceMappingURL=policy.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/team-policy/policy.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/team-policy/policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ApprovalAuthority, ApprovalPolicyConfig, ApprovalRequirement, ApprovalDecision, CommandCapsule } from '@codeledger/types';
|
|
2
|
+
export declare const DEFAULT_APPROVAL_POLICY: ApprovalPolicyConfig;
|
|
3
|
+
/** Load approval policy from disk */
|
|
4
|
+
export declare function loadApprovalPolicy(repoRoot: string): ApprovalPolicyConfig;
|
|
5
|
+
/** Find authorities that cover the given file targets */
|
|
6
|
+
export declare function findMatchingAuthorities(authorities: ApprovalAuthority[], targets: string[]): ApprovalAuthority[];
|
|
7
|
+
/** Determine approval requirements for a capsule */
|
|
8
|
+
export declare function deriveApprovalRequirements(capsule: CommandCapsule, policy: ApprovalPolicyConfig): ApprovalRequirement[];
|
|
9
|
+
/** Validate an approval decision against policy */
|
|
10
|
+
export declare function validateApproval(capsule: CommandCapsule, decision: ApprovalDecision, policy: ApprovalPolicyConfig): {
|
|
11
|
+
valid: boolean;
|
|
12
|
+
reason: string;
|
|
13
|
+
};
|
|
14
|
+
/** Explain why a specific approver was selected */
|
|
15
|
+
export declare function explainApproverSelection(capsule: CommandCapsule, policy: ApprovalPolicyConfig): string[];
|
|
16
|
+
//# sourceMappingURL=approval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../../src/trust/approval.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EAEf,MAAM,mBAAmB,CAAC;AAoD3B,eAAO,MAAM,uBAAuB,EAAE,oBAOrC,CAAC;AAIF,qCAAqC;AACrC,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,oBAAoB,CASzE;AAID,yDAAyD;AACzD,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,iBAAiB,EAAE,EAChC,OAAO,EAAE,MAAM,EAAE,GAChB,iBAAiB,EAAE,CAMrB;AAED,oDAAoD;AACpD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,oBAAoB,GAC3B,mBAAmB,EAAE,CA0CvB;AAID,mDAAmD;AACnD,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,oBAAoB,GAC3B;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAuCpC;AAED,mDAAmD;AACnD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,oBAAoB,GAC3B,MAAM,EAAE,CAuBV"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approval Workflow — Authority Model & Approver Selection
|
|
3
|
+
*
|
|
4
|
+
* Determines WHO must approve a capsule based on:
|
|
5
|
+
* - Action targets (file patterns)
|
|
6
|
+
* - Risk level
|
|
7
|
+
* - Policy requirements
|
|
8
|
+
*
|
|
9
|
+
* Handles edge cases:
|
|
10
|
+
* - No approver found → blocks with explicit error
|
|
11
|
+
* - Self-approval prevention
|
|
12
|
+
* - Delegation with depth limit
|
|
13
|
+
* - Multiple approvers required
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
const APPROVAL_POLICY_FILE = '.codeledger/approval-policy.json';
|
|
18
|
+
// ─── Simple Glob Matching ──────────────────────────────────────────────────
|
|
19
|
+
/**
|
|
20
|
+
* Simple glob matcher supporting *, **, and ? wildcards.
|
|
21
|
+
* Avoids external minimatch dependency.
|
|
22
|
+
*/
|
|
23
|
+
function simpleGlobMatch(pattern, target) {
|
|
24
|
+
// Escape regex special chars except our glob wildcards
|
|
25
|
+
let regexStr = '';
|
|
26
|
+
let i = 0;
|
|
27
|
+
while (i < pattern.length) {
|
|
28
|
+
const ch = pattern[i];
|
|
29
|
+
if (ch === '*') {
|
|
30
|
+
if (pattern[i + 1] === '*') {
|
|
31
|
+
// ** matches any path segment(s)
|
|
32
|
+
if (pattern[i + 2] === '/') {
|
|
33
|
+
regexStr += '(?:.+/)?';
|
|
34
|
+
i += 3;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
regexStr += '.*';
|
|
38
|
+
i += 2;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// * matches anything except /
|
|
43
|
+
regexStr += '[^/]*';
|
|
44
|
+
i += 1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else if (ch === '?') {
|
|
48
|
+
regexStr += '[^/]';
|
|
49
|
+
i += 1;
|
|
50
|
+
}
|
|
51
|
+
else if ('.+^${}()|[]\\'.includes(ch)) {
|
|
52
|
+
regexStr += '\\' + ch;
|
|
53
|
+
i += 1;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
regexStr += ch;
|
|
57
|
+
i += 1;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
return new RegExp(`^${regexStr}$`).test(target);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// ─── Default Policy ────────────────────────────────────────────────────────
|
|
68
|
+
export const DEFAULT_APPROVAL_POLICY = {
|
|
69
|
+
version: 1,
|
|
70
|
+
enabled: false,
|
|
71
|
+
authorities: [],
|
|
72
|
+
preventSelfApproval: true,
|
|
73
|
+
maxDelegationDepth: 2,
|
|
74
|
+
approvalThreshold: 'medium',
|
|
75
|
+
};
|
|
76
|
+
// ─── Policy Loading ────────────────────────────────────────────────────────
|
|
77
|
+
/** Load approval policy from disk */
|
|
78
|
+
export function loadApprovalPolicy(repoRoot) {
|
|
79
|
+
const path = join(repoRoot, APPROVAL_POLICY_FILE);
|
|
80
|
+
if (!existsSync(path))
|
|
81
|
+
return DEFAULT_APPROVAL_POLICY;
|
|
82
|
+
try {
|
|
83
|
+
const raw = JSON.parse(readFileSync(path, 'utf-8'));
|
|
84
|
+
return { ...DEFAULT_APPROVAL_POLICY, ...raw };
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return DEFAULT_APPROVAL_POLICY;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// ─── Approver Selection ────────────────────────────────────────────────────
|
|
91
|
+
/** Find authorities that cover the given file targets */
|
|
92
|
+
export function findMatchingAuthorities(authorities, targets) {
|
|
93
|
+
return authorities.filter((auth) => targets.some((target) => auth.patterns.some((pattern) => simpleGlobMatch(pattern, target))));
|
|
94
|
+
}
|
|
95
|
+
/** Determine approval requirements for a capsule */
|
|
96
|
+
export function deriveApprovalRequirements(capsule, policy) {
|
|
97
|
+
if (!policy.enabled)
|
|
98
|
+
return [];
|
|
99
|
+
// Check if risk level meets approval threshold
|
|
100
|
+
const riskOrder = ['low', 'medium', 'high', 'critical'];
|
|
101
|
+
const capsuleRiskIdx = riskOrder.indexOf(capsule.risk.level);
|
|
102
|
+
const thresholdIdx = riskOrder.indexOf(policy.approvalThreshold);
|
|
103
|
+
if (capsuleRiskIdx < thresholdIdx) {
|
|
104
|
+
// Risk below threshold — no approval needed
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
// Find matching authorities for this capsule's targets
|
|
108
|
+
const matching = findMatchingAuthorities(policy.authorities, capsule.action.targets);
|
|
109
|
+
if (matching.length === 0) {
|
|
110
|
+
// No authority covers these targets — require owner approval
|
|
111
|
+
return [{
|
|
112
|
+
policyId: 'default_owner_approval',
|
|
113
|
+
ruleName: 'No specific authority — owner required',
|
|
114
|
+
requiredScope: 'owner',
|
|
115
|
+
reason: `No authority covers targets: ${capsule.action.targets.slice(0, 3).join(', ')}`,
|
|
116
|
+
delegatable: false,
|
|
117
|
+
}];
|
|
118
|
+
}
|
|
119
|
+
// Group by scope — require the highest scope authority
|
|
120
|
+
const scopeOrder = ['any_peer', 'team_lead', 'security', 'owner'];
|
|
121
|
+
const highestScope = matching.reduce((best, auth) => {
|
|
122
|
+
const bestIdx = scopeOrder.indexOf(best.scope);
|
|
123
|
+
const authIdx = scopeOrder.indexOf(auth.scope);
|
|
124
|
+
return authIdx > bestIdx ? auth : best;
|
|
125
|
+
}, matching[0]);
|
|
126
|
+
return [{
|
|
127
|
+
policyId: `authority_${highestScope.actorId}`,
|
|
128
|
+
ruleName: `Authority: ${highestScope.actorId} (${highestScope.scope})`,
|
|
129
|
+
requiredScope: highestScope.scope,
|
|
130
|
+
reason: `Covers target patterns: ${highestScope.patterns.join(', ')}`,
|
|
131
|
+
delegatable: highestScope.canDelegate,
|
|
132
|
+
}];
|
|
133
|
+
}
|
|
134
|
+
// ─── Validation ────────────────────────────────────────────────────────────
|
|
135
|
+
/** Validate an approval decision against policy */
|
|
136
|
+
export function validateApproval(capsule, decision, policy) {
|
|
137
|
+
// Self-approval check
|
|
138
|
+
if (policy.preventSelfApproval && decision.approver === capsule.createdBy) {
|
|
139
|
+
return { valid: false, reason: 'Self-approval is not permitted' };
|
|
140
|
+
}
|
|
141
|
+
// Check delegation depth
|
|
142
|
+
if (decision.decision === 'delegated') {
|
|
143
|
+
const delegationCount = capsule.approvalDecisions.filter((d) => d.decision === 'delegated').length;
|
|
144
|
+
if (delegationCount >= policy.maxDelegationDepth) {
|
|
145
|
+
return {
|
|
146
|
+
valid: false,
|
|
147
|
+
reason: `Maximum delegation depth (${policy.maxDelegationDepth}) exceeded`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// Check if this requirement is delegatable
|
|
151
|
+
const requirement = capsule.approvalRequirements.find((r) => r.policyId === decision.policyId);
|
|
152
|
+
if (requirement && !requirement.delegatable) {
|
|
153
|
+
return { valid: false, reason: 'This approval requirement cannot be delegated' };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Check authority scope
|
|
157
|
+
const requirement = capsule.approvalRequirements.find((r) => r.policyId === decision.policyId);
|
|
158
|
+
if (requirement) {
|
|
159
|
+
const scopeOrder = ['any_peer', 'team_lead', 'security', 'owner'];
|
|
160
|
+
const requiredIdx = scopeOrder.indexOf(requirement.requiredScope ?? '');
|
|
161
|
+
const actualIdx = scopeOrder.indexOf(decision.authorityScope ?? '');
|
|
162
|
+
if (actualIdx < requiredIdx) {
|
|
163
|
+
return {
|
|
164
|
+
valid: false,
|
|
165
|
+
reason: `Insufficient authority scope: requires '${requirement.requiredScope}', got '${decision.authorityScope}'`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { valid: true, reason: 'Approval is valid' };
|
|
170
|
+
}
|
|
171
|
+
/** Explain why a specific approver was selected */
|
|
172
|
+
export function explainApproverSelection(capsule, policy) {
|
|
173
|
+
const explanations = [];
|
|
174
|
+
for (const req of capsule.approvalRequirements) {
|
|
175
|
+
const matchingAuthorities = findMatchingAuthorities(policy.authorities, capsule.action.targets);
|
|
176
|
+
explanations.push(`Requirement: ${req.ruleName}`, ` Scope: ${req.requiredScope}`, ` Reason: ${req.reason}`, ` Delegatable: ${req.delegatable}`, ` Matching authorities: ${matchingAuthorities.map(a => a.actorId).join(', ') || 'none (defaults to owner)'}`);
|
|
177
|
+
}
|
|
178
|
+
if (explanations.length === 0) {
|
|
179
|
+
explanations.push('No approval required for this capsule.');
|
|
180
|
+
}
|
|
181
|
+
return explanations;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=approval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval.js","sourceRoot":"","sources":["../../src/trust/approval.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CommandCapsule, CapsuleState, ActionSpec, CapsuleOutcome, ExecutionCondition, ApprovalRequirement, ApprovalDecision } from '@codeledger/types';
|
|
2
|
+
/** Create a new Command Capsule in draft state */
|
|
3
|
+
export declare function createCapsule(action: ActionSpec, createdBy: string, intentSignature?: string): CommandCapsule;
|
|
4
|
+
/** Transition a capsule to a new state (validates transition) */
|
|
5
|
+
export declare function transitionCapsule(capsule: CommandCapsule, to: CapsuleState, trigger: string, actor: string, metadata?: Record<string, unknown>): CommandCapsule;
|
|
6
|
+
/** Submit capsule for policy evaluation */
|
|
7
|
+
export declare function submitForPolicy(capsule: CommandCapsule, policyIds: string[], requirements: ApprovalRequirement[], actor: string): CommandCapsule;
|
|
8
|
+
/** Record an approval decision */
|
|
9
|
+
export declare function recordApproval(capsule: CommandCapsule, decision: ApprovalDecision): CommandCapsule;
|
|
10
|
+
/** Revoke a previously approved capsule */
|
|
11
|
+
export declare function revokeCapsule(capsule: CommandCapsule, actor: string, reason: string): CommandCapsule;
|
|
12
|
+
/** Begin execution of an approved capsule */
|
|
13
|
+
export declare function beginExecution(capsule: CommandCapsule, actor: string): CommandCapsule;
|
|
14
|
+
/** Record execution outcome */
|
|
15
|
+
export declare function completeExecution(capsule: CommandCapsule, outcome: CapsuleOutcome, actor: string): CommandCapsule;
|
|
16
|
+
/** Evaluate a precondition or postcondition */
|
|
17
|
+
export declare function evaluateCondition(condition: ExecutionCondition, passed: boolean, message: string): ExecutionCondition;
|
|
18
|
+
/** Check if all required preconditions pass */
|
|
19
|
+
export declare function checkPreconditions(capsule: CommandCapsule): {
|
|
20
|
+
passed: boolean;
|
|
21
|
+
failures: ExecutionCondition[];
|
|
22
|
+
};
|
|
23
|
+
/** Check if all required postconditions pass */
|
|
24
|
+
export declare function checkPostconditions(capsule: CommandCapsule): {
|
|
25
|
+
passed: boolean;
|
|
26
|
+
failures: ExecutionCondition[];
|
|
27
|
+
};
|
|
28
|
+
/** Save a capsule to disk */
|
|
29
|
+
export declare function saveCapsule(repoRoot: string, capsule: CommandCapsule): void;
|
|
30
|
+
/** Load a capsule from disk */
|
|
31
|
+
export declare function loadCapsule(repoRoot: string, capsuleId: string): CommandCapsule | null;
|
|
32
|
+
/** List all capsule IDs */
|
|
33
|
+
export declare function listCapsules(repoRoot: string): string[];
|
|
34
|
+
/** Record a capsule event to the append-only log */
|
|
35
|
+
export declare function recordCapsuleEvent(repoRoot: string, capsuleId: string, event: string, actor: string, metadata?: Record<string, unknown>): void;
|
|
36
|
+
//# sourceMappingURL=capsule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capsule.d.ts","sourceRoot":"","sources":["../../src/trust/capsule.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EAEZ,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAEjB,MAAM,mBAAmB,CAAC;AAqB3B,kDAAkD;AAClD,wBAAgB,aAAa,CAC3B,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,MAAM,EACjB,eAAe,CAAC,EAAE,MAAM,GACvB,cAAc,CAoBhB;AAID,iEAAiE;AACjE,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,cAAc,EACvB,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,cAAc,CAwBhB;AAID,2CAA2C;AAC3C,wBAAgB,eAAe,CAC7B,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EAAE,EACnB,YAAY,EAAE,mBAAmB,EAAE,EACnC,KAAK,EAAE,MAAM,GACZ,cAAc,CAYhB;AAID,kCAAkC;AAClC,wBAAgB,cAAc,CAC5B,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,gBAAgB,GACzB,cAAc,CA6BhB;AAED,2CAA2C;AAC3C,wBAAgB,aAAa,CAC3B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,cAAc,CAKhB;AAID,6CAA6C;AAC7C,wBAAgB,cAAc,CAC5B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,MAAM,GACZ,cAAc,CAEhB;AAED,+BAA+B;AAC/B,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,MAAM,GACZ,cAAc,CAKhB;AAID,+CAA+C;AAC/C,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,kBAAkB,EAC7B,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,GACd,kBAAkB,CASpB;AAED,+CAA+C;AAC/C,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG;IAC3D,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAChC,CAOA;AAED,gDAAgD;AAChD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,GAAG;IAC5D,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAChC,CAOA;AAID,6BAA6B;AAC7B,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAK3E;AAED,+BAA+B;AAC/B,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAQtF;AAED,2BAA2B;AAC3B,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAMvD;AAID,oDAAoD;AACpD,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,IAAI,CAcN"}
|