instar 0.28.118 → 0.28.120
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/monitoring/FrameworkParitySentinel.d.ts +91 -0
- package/dist/monitoring/FrameworkParitySentinel.d.ts.map +1 -0
- package/dist/monitoring/FrameworkParitySentinel.js +286 -0
- package/dist/monitoring/FrameworkParitySentinel.js.map +1 -0
- package/dist/providers/parity/conversationalActionCatalog.d.ts +57 -0
- package/dist/providers/parity/conversationalActionCatalog.d.ts.map +1 -0
- package/dist/providers/parity/conversationalActionCatalog.js +148 -0
- package/dist/providers/parity/conversationalActionCatalog.js.map +1 -0
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/0.28.119.md +37 -0
- package/upgrades/0.28.120.md +44 -0
- package/upgrades/side-effects/feat-conversational-action.md +101 -0
- package/upgrades/side-effects/feat-framework-parity-sentinel.md +94 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FrameworkParitySentinel — consumer of the Layer-3 parity rules registry.
|
|
3
|
+
*
|
|
4
|
+
* Spec: specs/instar-foundations/framework-parity-sentinel.md
|
|
5
|
+
*
|
|
6
|
+
* Walks the registry on its scan cadence (and on triggers), calls each rule's
|
|
7
|
+
* verify() per instance, surfaces drift via events + degradation reports, and
|
|
8
|
+
* (per rule's remediationPolicy) optionally calls remediate() to re-render
|
|
9
|
+
* canonical into the framework-native shape.
|
|
10
|
+
*
|
|
11
|
+
* v0.1 scope:
|
|
12
|
+
* - Registry walker (consume listParityRules → iterate → collect verify).
|
|
13
|
+
* - State file persistence (.instar/state/framework-parity-sentinel.json).
|
|
14
|
+
* - Interval trigger (single setInterval; no chokidar watcher yet).
|
|
15
|
+
* - EventEmitter wiring (5 events: gap-found, remediated, remediation-refused,
|
|
16
|
+
* orphan-found, scan-complete).
|
|
17
|
+
* - Degradation reporter on persistent unresolved gaps.
|
|
18
|
+
*
|
|
19
|
+
* v0.2 deferred: chokidar source-change watcher, per-instance remediate HTTP
|
|
20
|
+
* route, trust-level integration for mirror-trust gating, backfill migration.
|
|
21
|
+
*/
|
|
22
|
+
import { EventEmitter } from 'node:events';
|
|
23
|
+
import { getParityRule } from '../providers/parity/registry.js';
|
|
24
|
+
import type { IntelligenceFramework } from '../core/intelligenceProviderFactory.js';
|
|
25
|
+
export interface FrameworkParitySentinelConfig {
|
|
26
|
+
/** Project root (passed to each rule's verify/remediate). */
|
|
27
|
+
projectRoot: string;
|
|
28
|
+
/** State directory (typically `.instar`). */
|
|
29
|
+
stateDir: string;
|
|
30
|
+
/** Which frameworks are currently enabled for this agent. */
|
|
31
|
+
enabledFrameworks: ReadonlyArray<IntelligenceFramework>;
|
|
32
|
+
/** Scan interval in ms. Default: 1_800_000 (30 min). */
|
|
33
|
+
scanIntervalMs?: number;
|
|
34
|
+
/** Initial scan delay in ms. Default: 60_000 (1 min after start). */
|
|
35
|
+
initialScanDelayMs?: number;
|
|
36
|
+
/** If true, mirror-trust rules call remediate(); if false, all rules are flag-only. */
|
|
37
|
+
remediationEnabled?: boolean;
|
|
38
|
+
}
|
|
39
|
+
interface PerInstanceCursor {
|
|
40
|
+
lastScanAt: string;
|
|
41
|
+
lastResult: 'ok' | 'drift' | 'conflict' | 'error';
|
|
42
|
+
lastDetail?: string;
|
|
43
|
+
/** Number of consecutive scans with unresolved drift — used for degradation reporting. */
|
|
44
|
+
unresolvedCount: number;
|
|
45
|
+
}
|
|
46
|
+
export interface ParityScanReport {
|
|
47
|
+
scannedAt: string;
|
|
48
|
+
rulesWalked: number;
|
|
49
|
+
instancesChecked: number;
|
|
50
|
+
gapsFound: number;
|
|
51
|
+
remediated: number;
|
|
52
|
+
remediationRefused: number;
|
|
53
|
+
orphansFound: number;
|
|
54
|
+
}
|
|
55
|
+
export declare class FrameworkParitySentinel extends EventEmitter {
|
|
56
|
+
private config;
|
|
57
|
+
private state;
|
|
58
|
+
private statePath;
|
|
59
|
+
private interval;
|
|
60
|
+
private initialTimeout;
|
|
61
|
+
private isScanning;
|
|
62
|
+
constructor(config: FrameworkParitySentinelConfig);
|
|
63
|
+
start(): void;
|
|
64
|
+
stop(): void;
|
|
65
|
+
/**
|
|
66
|
+
* Run a full scan immediately. Returns a structured report. Safe to call
|
|
67
|
+
* concurrently — overlapping calls are short-circuited.
|
|
68
|
+
*/
|
|
69
|
+
scan(): Promise<ParityScanReport>;
|
|
70
|
+
/**
|
|
71
|
+
* Returns the current scan state — used by the HTTP /api/framework-parity/status route.
|
|
72
|
+
*/
|
|
73
|
+
getStatus(): {
|
|
74
|
+
lastScanAt: string | null;
|
|
75
|
+
cursors: Record<string, PerInstanceCursor>;
|
|
76
|
+
};
|
|
77
|
+
private scanRule;
|
|
78
|
+
private attemptRemediate;
|
|
79
|
+
private shouldRemediate;
|
|
80
|
+
private recordRuleError;
|
|
81
|
+
private loadState;
|
|
82
|
+
private saveState;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Lookup a single rule by primitive. Re-exported here so the sentinel's
|
|
86
|
+
* tests and the HTTP routes have a single import surface.
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
export declare const _getParityRuleForTest: typeof getParityRule;
|
|
90
|
+
export {};
|
|
91
|
+
//# sourceMappingURL=FrameworkParitySentinel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FrameworkParitySentinel.d.ts","sourceRoot":"","sources":["../../src/monitoring/FrameworkParitySentinel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAEL,aAAa,EACd,MAAM,iCAAiC,CAAC;AAMzC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAKpF,MAAM,WAAW,6BAA6B;IAC5C,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,iBAAiB,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;IACxD,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,uFAAuF;IACvF,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,UAAU,iBAAiB;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,IAAI,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,eAAe,EAAE,MAAM,CAAC;CACzB;AASD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;CACtB;AAUD,qBAAa,uBAAwB,SAAQ,YAAY;IACvD,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAA+C;IAC/D,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,UAAU,CAAS;gBAEf,MAAM,EAAE,6BAA6B;IASjD,KAAK,IAAI,IAAI;IAsBb,IAAI,IAAI,IAAI;IAaZ;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAkDvC;;OAEG;IACH,SAAS,IAAI;QAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;KAAE;YASxE,QAAQ;YAiFR,gBAAgB;IA4B9B,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,SAAS;CAWlB;AAID;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,sBAAgB,CAAC"}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FrameworkParitySentinel — consumer of the Layer-3 parity rules registry.
|
|
3
|
+
*
|
|
4
|
+
* Spec: specs/instar-foundations/framework-parity-sentinel.md
|
|
5
|
+
*
|
|
6
|
+
* Walks the registry on its scan cadence (and on triggers), calls each rule's
|
|
7
|
+
* verify() per instance, surfaces drift via events + degradation reports, and
|
|
8
|
+
* (per rule's remediationPolicy) optionally calls remediate() to re-render
|
|
9
|
+
* canonical into the framework-native shape.
|
|
10
|
+
*
|
|
11
|
+
* v0.1 scope:
|
|
12
|
+
* - Registry walker (consume listParityRules → iterate → collect verify).
|
|
13
|
+
* - State file persistence (.instar/state/framework-parity-sentinel.json).
|
|
14
|
+
* - Interval trigger (single setInterval; no chokidar watcher yet).
|
|
15
|
+
* - EventEmitter wiring (5 events: gap-found, remediated, remediation-refused,
|
|
16
|
+
* orphan-found, scan-complete).
|
|
17
|
+
* - Degradation reporter on persistent unresolved gaps.
|
|
18
|
+
*
|
|
19
|
+
* v0.2 deferred: chokidar source-change watcher, per-instance remediate HTTP
|
|
20
|
+
* route, trust-level integration for mirror-trust gating, backfill migration.
|
|
21
|
+
*/
|
|
22
|
+
import { EventEmitter } from 'node:events';
|
|
23
|
+
import fs from 'node:fs';
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
import { listParityRules, getParityRule, } from '../providers/parity/registry.js';
|
|
26
|
+
import { DegradationReporter } from './DegradationReporter.js';
|
|
27
|
+
// ── Helpers ───────────────────────────────────────────────────────
|
|
28
|
+
function cursorKey(primitive, instance) {
|
|
29
|
+
return `${primitive}::${instance}`;
|
|
30
|
+
}
|
|
31
|
+
// ── Implementation ────────────────────────────────────────────────
|
|
32
|
+
export class FrameworkParitySentinel extends EventEmitter {
|
|
33
|
+
config;
|
|
34
|
+
state;
|
|
35
|
+
statePath;
|
|
36
|
+
interval = null;
|
|
37
|
+
initialTimeout = null;
|
|
38
|
+
isScanning = false;
|
|
39
|
+
constructor(config) {
|
|
40
|
+
super();
|
|
41
|
+
this.config = config;
|
|
42
|
+
this.statePath = path.join(config.stateDir, 'state', 'framework-parity-sentinel.json');
|
|
43
|
+
this.state = this.loadState();
|
|
44
|
+
}
|
|
45
|
+
// ── Lifecycle ──────────────────────────────────────────────────
|
|
46
|
+
start() {
|
|
47
|
+
if (this.interval)
|
|
48
|
+
return;
|
|
49
|
+
const intervalMs = this.config.scanIntervalMs ?? 1_800_000;
|
|
50
|
+
const initialDelay = this.config.initialScanDelayMs ?? 60_000;
|
|
51
|
+
this.initialTimeout = setTimeout(() => {
|
|
52
|
+
void this.scan();
|
|
53
|
+
}, initialDelay);
|
|
54
|
+
this.initialTimeout.unref?.();
|
|
55
|
+
this.interval = setInterval(() => {
|
|
56
|
+
void this.scan();
|
|
57
|
+
}, intervalMs);
|
|
58
|
+
this.interval.unref?.();
|
|
59
|
+
console.log(`[FrameworkParitySentinel] started — interval ${Math.round(intervalMs / 60_000)}m, ` +
|
|
60
|
+
`first scan in ${Math.round(initialDelay / 1000)}s`);
|
|
61
|
+
}
|
|
62
|
+
stop() {
|
|
63
|
+
if (this.initialTimeout) {
|
|
64
|
+
clearTimeout(this.initialTimeout);
|
|
65
|
+
this.initialTimeout = null;
|
|
66
|
+
}
|
|
67
|
+
if (this.interval) {
|
|
68
|
+
clearInterval(this.interval);
|
|
69
|
+
this.interval = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// ── Public API ─────────────────────────────────────────────────
|
|
73
|
+
/**
|
|
74
|
+
* Run a full scan immediately. Returns a structured report. Safe to call
|
|
75
|
+
* concurrently — overlapping calls are short-circuited.
|
|
76
|
+
*/
|
|
77
|
+
async scan() {
|
|
78
|
+
if (this.isScanning) {
|
|
79
|
+
return {
|
|
80
|
+
scannedAt: new Date().toISOString(),
|
|
81
|
+
rulesWalked: 0,
|
|
82
|
+
instancesChecked: 0,
|
|
83
|
+
gapsFound: 0,
|
|
84
|
+
remediated: 0,
|
|
85
|
+
remediationRefused: 0,
|
|
86
|
+
orphansFound: 0,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
this.isScanning = true;
|
|
90
|
+
const scannedAt = new Date().toISOString();
|
|
91
|
+
const report = {
|
|
92
|
+
scannedAt,
|
|
93
|
+
rulesWalked: 0,
|
|
94
|
+
instancesChecked: 0,
|
|
95
|
+
gapsFound: 0,
|
|
96
|
+
remediated: 0,
|
|
97
|
+
remediationRefused: 0,
|
|
98
|
+
orphansFound: 0,
|
|
99
|
+
};
|
|
100
|
+
try {
|
|
101
|
+
const rules = listParityRules();
|
|
102
|
+
for (const rule of rules) {
|
|
103
|
+
report.rulesWalked += 1;
|
|
104
|
+
await this.scanRule(rule, report);
|
|
105
|
+
}
|
|
106
|
+
this.state.lastScanAt = scannedAt;
|
|
107
|
+
this.saveState();
|
|
108
|
+
this.emit('parity:scan-complete', report);
|
|
109
|
+
return report;
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
DegradationReporter.getInstance().report({
|
|
113
|
+
feature: 'FrameworkParitySentinel.scan',
|
|
114
|
+
primary: 'Walk parity rules registry + verify',
|
|
115
|
+
fallback: 'Scan aborted; cursors not advanced',
|
|
116
|
+
reason: `Scan error: ${err instanceof Error ? err.message : String(err)}`,
|
|
117
|
+
impact: 'Cross-framework drift may go undetected until next successful scan',
|
|
118
|
+
});
|
|
119
|
+
console.error('[FrameworkParitySentinel] scan error:', err);
|
|
120
|
+
return report;
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
this.isScanning = false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Returns the current scan state — used by the HTTP /api/framework-parity/status route.
|
|
128
|
+
*/
|
|
129
|
+
getStatus() {
|
|
130
|
+
return {
|
|
131
|
+
lastScanAt: this.state.lastScanAt,
|
|
132
|
+
cursors: { ...this.state.cursors },
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// ── Scan implementation ────────────────────────────────────────
|
|
136
|
+
async scanRule(rule, report) {
|
|
137
|
+
let instances;
|
|
138
|
+
try {
|
|
139
|
+
instances = await rule.listInstances(this.config.projectRoot);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
this.recordRuleError(rule, err);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
for (const instance of instances) {
|
|
146
|
+
report.instancesChecked += 1;
|
|
147
|
+
const key = cursorKey(rule.primitive, instance);
|
|
148
|
+
let result = 'ok';
|
|
149
|
+
let detail;
|
|
150
|
+
try {
|
|
151
|
+
const verifyResult = await rule.verify(this.config.projectRoot, instance);
|
|
152
|
+
if (!verifyResult.ok) {
|
|
153
|
+
report.gapsFound += verifyResult.mismatches.length;
|
|
154
|
+
const hasConflict = verifyResult.mismatches.some((m) => m.reasonCode === 'user-edit-conflict');
|
|
155
|
+
result = hasConflict ? 'conflict' : 'drift';
|
|
156
|
+
detail = verifyResult.mismatches.map((m) => `${m.framework}: ${m.detail}`).join('; ');
|
|
157
|
+
for (const m of verifyResult.mismatches) {
|
|
158
|
+
this.emit('parity:gap-found', m);
|
|
159
|
+
}
|
|
160
|
+
if (result === 'drift' && this.shouldRemediate(rule)) {
|
|
161
|
+
for (const m of verifyResult.mismatches) {
|
|
162
|
+
if (m.framework === 'canonical')
|
|
163
|
+
continue; // can't remediate canonical drift
|
|
164
|
+
await this.attemptRemediate(rule, instance, m.framework, report);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else if (result === 'conflict') {
|
|
168
|
+
for (const m of verifyResult.mismatches) {
|
|
169
|
+
this.emit('parity:remediation-refused', m);
|
|
170
|
+
report.remediationRefused += 1;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
result = 'error';
|
|
177
|
+
detail = err instanceof Error ? err.message : String(err);
|
|
178
|
+
}
|
|
179
|
+
const prior = this.state.cursors[key];
|
|
180
|
+
const unresolvedCount = result === 'ok'
|
|
181
|
+
? 0
|
|
182
|
+
: (prior?.unresolvedCount ?? 0) + 1;
|
|
183
|
+
this.state.cursors[key] = {
|
|
184
|
+
lastScanAt: report.scannedAt,
|
|
185
|
+
lastResult: result,
|
|
186
|
+
lastDetail: detail,
|
|
187
|
+
unresolvedCount,
|
|
188
|
+
};
|
|
189
|
+
if (unresolvedCount >= 3 && result !== 'ok') {
|
|
190
|
+
DegradationReporter.getInstance().report({
|
|
191
|
+
feature: `parity:${rule.primitive}:${instance}`,
|
|
192
|
+
primary: 'Cross-framework parity for this instance',
|
|
193
|
+
fallback: 'Drift persists across multiple scans',
|
|
194
|
+
reason: detail ?? 'unknown',
|
|
195
|
+
impact: `Drift unresolved for ${unresolvedCount} consecutive scans`,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Orphan detection (per rule)
|
|
200
|
+
try {
|
|
201
|
+
const orphans = await rule.listOrphans(this.config.projectRoot);
|
|
202
|
+
for (const o of orphans) {
|
|
203
|
+
report.orphansFound += 1;
|
|
204
|
+
this.emit('parity:orphan-found', o);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
this.recordRuleError(rule, err);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async attemptRemediate(rule, instance, framework, report) {
|
|
212
|
+
if (framework === 'canonical')
|
|
213
|
+
return;
|
|
214
|
+
if (!this.config.enabledFrameworks.includes(framework))
|
|
215
|
+
return;
|
|
216
|
+
try {
|
|
217
|
+
await rule.remediate(this.config.projectRoot, instance, framework);
|
|
218
|
+
report.remediated += 1;
|
|
219
|
+
this.emit('parity:remediated', {
|
|
220
|
+
primitive: rule.primitive,
|
|
221
|
+
instance,
|
|
222
|
+
framework,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
report.remediationRefused += 1;
|
|
227
|
+
this.emit('parity:remediation-refused', {
|
|
228
|
+
primitive: rule.primitive,
|
|
229
|
+
instanceName: instance,
|
|
230
|
+
framework,
|
|
231
|
+
reasonCode: 'user-edit-conflict',
|
|
232
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
shouldRemediate(rule) {
|
|
237
|
+
if (rule.remediationPolicy === 'flag-only')
|
|
238
|
+
return false;
|
|
239
|
+
if (this.config.remediationEnabled === false)
|
|
240
|
+
return false;
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
recordRuleError(rule, err) {
|
|
244
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
245
|
+
console.error(`[FrameworkParitySentinel] rule ${rule.primitive} error: ${detail}`);
|
|
246
|
+
DegradationReporter.getInstance().report({
|
|
247
|
+
feature: `parity:${rule.primitive}`,
|
|
248
|
+
primary: 'Walk this primitive\'s parity rule',
|
|
249
|
+
fallback: 'Skip this rule for this scan; other rules unaffected',
|
|
250
|
+
reason: detail,
|
|
251
|
+
impact: `Drift detection for ${rule.primitive} primitive paused until next successful scan`,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
// ── Persistence ────────────────────────────────────────────────
|
|
255
|
+
loadState() {
|
|
256
|
+
try {
|
|
257
|
+
if (fs.existsSync(this.statePath)) {
|
|
258
|
+
return JSON.parse(fs.readFileSync(this.statePath, 'utf-8'));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
/* start fresh */
|
|
263
|
+
}
|
|
264
|
+
return { cursors: {}, lastScanAt: null };
|
|
265
|
+
}
|
|
266
|
+
saveState() {
|
|
267
|
+
try {
|
|
268
|
+
const dir = path.dirname(this.statePath);
|
|
269
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
270
|
+
const tmpPath = `${this.statePath}.${process.pid}.tmp`;
|
|
271
|
+
fs.writeFileSync(tmpPath, JSON.stringify(this.state, null, 2));
|
|
272
|
+
fs.renameSync(tmpPath, this.statePath);
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// @silent-fallback-ok — state loss just re-scans on next pass
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// ── Test seam ─────────────────────────────────────────────────────
|
|
280
|
+
/**
|
|
281
|
+
* Lookup a single rule by primitive. Re-exported here so the sentinel's
|
|
282
|
+
* tests and the HTTP routes have a single import surface.
|
|
283
|
+
* @internal
|
|
284
|
+
*/
|
|
285
|
+
export const _getParityRuleForTest = getParityRule;
|
|
286
|
+
//# sourceMappingURL=FrameworkParitySentinel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FrameworkParitySentinel.js","sourceRoot":"","sources":["../../src/monitoring/FrameworkParitySentinel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* conversationalActionCatalog — discovery + rendering for the conversational
|
|
3
|
+
* action catalog (Layer-3 primitive #10, v0.1).
|
|
4
|
+
*
|
|
5
|
+
* Spec: specs/instar-concepts/conversational-action.md
|
|
6
|
+
*
|
|
7
|
+
* Walks canonical skills under `.instar/skills/<name>/SKILL.md`, generates a
|
|
8
|
+
* stable markdown block listing each one as a conversational action, and
|
|
9
|
+
* idempotently inserts/replaces that block in canonical AGENT.md.
|
|
10
|
+
*
|
|
11
|
+
* v0.1 scope:
|
|
12
|
+
* - Catalog discovery + filtering.
|
|
13
|
+
* - Markdown block generation with delimiter comments.
|
|
14
|
+
* - Idempotent applyCatalogBlock() — insert or replace cleanly.
|
|
15
|
+
*
|
|
16
|
+
* v0.2 deferred:
|
|
17
|
+
* - `user-invocable: true` frontmatter filter (pending Skill v0.2 field surface).
|
|
18
|
+
* - Authed POST execution endpoints.
|
|
19
|
+
* - Per-action JSON Schema / action-shape declarations.
|
|
20
|
+
* - Wiring into FrameworkParitySentinel as a parity rule (catalog-drift detection).
|
|
21
|
+
*/
|
|
22
|
+
export interface ConversationalAction {
|
|
23
|
+
/** Canonical skill name (== slug == slash-command suffix) */
|
|
24
|
+
name: string;
|
|
25
|
+
/** Short description used in catalog entry + agent-side intent classification */
|
|
26
|
+
description: string;
|
|
27
|
+
/** Framework-agnostic invocation path. v0.1: always `/${name}` */
|
|
28
|
+
invocation: string;
|
|
29
|
+
}
|
|
30
|
+
declare function parseFrontmatter(raw: string): {
|
|
31
|
+
fm: Record<string, unknown>;
|
|
32
|
+
rest: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Walk `.instar/skills/<name>/SKILL.md` under `projectRoot`, return the
|
|
36
|
+
* discovered conversational actions sorted by name.
|
|
37
|
+
*
|
|
38
|
+
* v0.1: enumerates ALL canonical skills (no `user-invocable` filter).
|
|
39
|
+
*/
|
|
40
|
+
export declare function discoverActions(projectRoot: string): Promise<ConversationalAction[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Render the catalog markdown block (with start/end delimiter comments).
|
|
43
|
+
*
|
|
44
|
+
* Stable output — given the same actions list, returns the same string.
|
|
45
|
+
* Pure function: no I/O. Caller decides where the block lands (typically a
|
|
46
|
+
* Tier 2 ContextHierarchy segment, a SelfKnowledgeTree probe response, or a
|
|
47
|
+
* Playbook context item — NOT inlined into AGENT.md, per the bloat-aware
|
|
48
|
+
* design constraint documented in the concept spec).
|
|
49
|
+
*/
|
|
50
|
+
export declare function renderCatalogBlock(actions: ReadonlyArray<ConversationalAction>): string;
|
|
51
|
+
export declare const _internals: {
|
|
52
|
+
parseFrontmatter: typeof parseFrontmatter;
|
|
53
|
+
BLOCK_START: string;
|
|
54
|
+
BLOCK_END: string;
|
|
55
|
+
};
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=conversationalActionCatalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversationalActionCatalog.d.ts","sourceRoot":"","sources":["../../../src/providers/parity/conversationalActionCatalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAWH,MAAM,WAAW,oBAAoB;IACnC,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,UAAU,EAAE,MAAM,CAAC;CACpB;AAkBD,iBAAS,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAiBpF;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAoC1F;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,CAAC,oBAAoB,CAAC,GAAG,MAAM,CAiBvF;AAgBD,eAAO,MAAM,UAAU;;;;CAA+C,CAAC"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* conversationalActionCatalog — discovery + rendering for the conversational
|
|
3
|
+
* action catalog (Layer-3 primitive #10, v0.1).
|
|
4
|
+
*
|
|
5
|
+
* Spec: specs/instar-concepts/conversational-action.md
|
|
6
|
+
*
|
|
7
|
+
* Walks canonical skills under `.instar/skills/<name>/SKILL.md`, generates a
|
|
8
|
+
* stable markdown block listing each one as a conversational action, and
|
|
9
|
+
* idempotently inserts/replaces that block in canonical AGENT.md.
|
|
10
|
+
*
|
|
11
|
+
* v0.1 scope:
|
|
12
|
+
* - Catalog discovery + filtering.
|
|
13
|
+
* - Markdown block generation with delimiter comments.
|
|
14
|
+
* - Idempotent applyCatalogBlock() — insert or replace cleanly.
|
|
15
|
+
*
|
|
16
|
+
* v0.2 deferred:
|
|
17
|
+
* - `user-invocable: true` frontmatter filter (pending Skill v0.2 field surface).
|
|
18
|
+
* - Authed POST execution endpoints.
|
|
19
|
+
* - Per-action JSON Schema / action-shape declarations.
|
|
20
|
+
* - Wiring into FrameworkParitySentinel as a parity rule (catalog-drift detection).
|
|
21
|
+
*/
|
|
22
|
+
import { promises as fs } from 'node:fs';
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
import yaml from 'js-yaml';
|
|
25
|
+
const CANONICAL_SKILLS_ROOT = '.instar/skills';
|
|
26
|
+
const BLOCK_START = '<!-- instar:conversational-actions:start -->';
|
|
27
|
+
const BLOCK_END = '<!-- instar:conversational-actions:end -->';
|
|
28
|
+
const SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
29
|
+
class CatalogError extends Error {
|
|
30
|
+
constructor(message) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = 'CatalogError';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function pathExists(p) {
|
|
36
|
+
try {
|
|
37
|
+
await fs.access(p);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function parseFrontmatter(raw) {
|
|
45
|
+
if (!raw.startsWith('---\n')) {
|
|
46
|
+
return { fm: {}, rest: raw };
|
|
47
|
+
}
|
|
48
|
+
const close = raw.indexOf('\n---', 4);
|
|
49
|
+
if (close < 0) {
|
|
50
|
+
throw new CatalogError('skill SKILL.md has unterminated YAML frontmatter');
|
|
51
|
+
}
|
|
52
|
+
const fmRaw = raw.slice(4, close);
|
|
53
|
+
let fm;
|
|
54
|
+
try {
|
|
55
|
+
fm = yaml.load(fmRaw, { schema: yaml.FAILSAFE_SCHEMA, json: false });
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
throw new CatalogError(`skill SKILL.md YAML parse error: ${err.message}`);
|
|
59
|
+
}
|
|
60
|
+
const rest = raw.slice(close + 4).replace(/^\n/, '');
|
|
61
|
+
return { fm: (fm && typeof fm === 'object' ? fm : {}), rest };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Walk `.instar/skills/<name>/SKILL.md` under `projectRoot`, return the
|
|
65
|
+
* discovered conversational actions sorted by name.
|
|
66
|
+
*
|
|
67
|
+
* v0.1: enumerates ALL canonical skills (no `user-invocable` filter).
|
|
68
|
+
*/
|
|
69
|
+
export async function discoverActions(projectRoot) {
|
|
70
|
+
const skillsRoot = path.join(projectRoot, CANONICAL_SKILLS_ROOT);
|
|
71
|
+
if (!(await pathExists(skillsRoot)))
|
|
72
|
+
return [];
|
|
73
|
+
const entries = await fs.readdir(skillsRoot, { withFileTypes: true });
|
|
74
|
+
const actions = [];
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
if (!entry.isDirectory())
|
|
77
|
+
continue;
|
|
78
|
+
if (!SLUG_RE.test(entry.name))
|
|
79
|
+
continue;
|
|
80
|
+
const skillMdPath = path.join(skillsRoot, entry.name, 'SKILL.md');
|
|
81
|
+
if (!(await pathExists(skillMdPath)))
|
|
82
|
+
continue;
|
|
83
|
+
let raw;
|
|
84
|
+
try {
|
|
85
|
+
raw = await fs.readFile(skillMdPath, 'utf-8');
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
let parsed;
|
|
91
|
+
try {
|
|
92
|
+
parsed = parseFrontmatter(raw);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Skip broken skills; the Skill parity rule surfaces them separately.
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const name = String(parsed.fm.name ?? entry.name);
|
|
99
|
+
const description = typeof parsed.fm.description === 'string'
|
|
100
|
+
? parsed.fm.description
|
|
101
|
+
: '(no description)';
|
|
102
|
+
actions.push({
|
|
103
|
+
name,
|
|
104
|
+
description,
|
|
105
|
+
invocation: `/${name}`,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
actions.sort((a, b) => a.name.localeCompare(b.name));
|
|
109
|
+
return actions;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Render the catalog markdown block (with start/end delimiter comments).
|
|
113
|
+
*
|
|
114
|
+
* Stable output — given the same actions list, returns the same string.
|
|
115
|
+
* Pure function: no I/O. Caller decides where the block lands (typically a
|
|
116
|
+
* Tier 2 ContextHierarchy segment, a SelfKnowledgeTree probe response, or a
|
|
117
|
+
* Playbook context item — NOT inlined into AGENT.md, per the bloat-aware
|
|
118
|
+
* design constraint documented in the concept spec).
|
|
119
|
+
*/
|
|
120
|
+
export function renderCatalogBlock(actions) {
|
|
121
|
+
const lines = [BLOCK_START, '## Conversational Actions', ''];
|
|
122
|
+
if (actions.length === 0) {
|
|
123
|
+
lines.push('_No conversational actions installed yet. Install a skill under `.instar/skills/<name>/` to make it available here._');
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
lines.push('When the user expresses intent that maps to one of these, invoke the slash-command (or guide them conversationally to the equivalent action). You do not need to surface the slash-command name to the user — translate intent into invocation.', '');
|
|
127
|
+
for (const a of actions) {
|
|
128
|
+
lines.push(`- \`${a.invocation}\` — ${a.description}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
lines.push('', BLOCK_END);
|
|
132
|
+
return lines.join('\n') + '\n';
|
|
133
|
+
}
|
|
134
|
+
/*
|
|
135
|
+
* NOTE: applyCatalogBlock(...) deliberately NOT exported in v0.1.
|
|
136
|
+
*
|
|
137
|
+
* Writing the catalog block directly into AGENT.md (the always-loaded Tier 0
|
|
138
|
+
* identity file) is the AGENT.md-bloat antipattern Instar built three
|
|
139
|
+
* defenses against (ContextHierarchy, Playbook, SelfKnowledgeTree). The
|
|
140
|
+
* catalog block is returned as a string from renderCatalogBlock(); the
|
|
141
|
+
* caller (a v0.2 ContextHierarchy Tier 2 segment writer, a SelfKnowledgeTree
|
|
142
|
+
* probe handler, or a Playbook context item) decides where it lands.
|
|
143
|
+
*
|
|
144
|
+
* See specs/instar-concepts/conversational-action.md ("Bloat-awareness as a
|
|
145
|
+
* v0.1 design constraint") for the full rationale.
|
|
146
|
+
*/
|
|
147
|
+
export const _internals = { parseFrontmatter, BLOCK_START, BLOCK_END };
|
|
148
|
+
//# sourceMappingURL=conversationalActionCatalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversationalActionCatalog.js","sourceRoot":"","sources":["../../../src/providers/parity/conversationalActionCatalog.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-05-
|
|
5
|
-
"instarVersion": "0.28.
|
|
4
|
+
"generatedAt": "2026-05-19T04:26:32.444Z",
|
|
5
|
+
"instarVersion": "0.28.120",
|
|
6
6
|
"entryCount": 189,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Upgrade Guide — v1.0.5
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
Lands the FrameworkParitySentinel — the class that walks the Layer-3 parity rules registry (Skill / Hook / Memory), runs each rule's verify() per instance, and routes drift to events (or to remediation, per the rule's declared policy).
|
|
8
|
+
|
|
9
|
+
v0.1 ships the sentinel as a building block — class + 12 unit tests. HTTP routes (GET /api/framework-parity/status, POST /api/framework-parity/scan) and server.ts boot integration are deferred to a focused follow-up PR. This matches the precedent set by Skill / Hook / Memory parity rules: each shipped as a registry building block first, with operational wiring as a separate step.
|
|
10
|
+
|
|
11
|
+
Key safety locks: per-rule remediationPolicy is the authority (flag-only never auto-remediates; mirror-trust does); the sentinel-level remediationEnabled config can DOWNGRADE mirror-trust to flag-only but never UPGRADE flag-only. Memory's sacrosanct status is preserved unconditionally. Concurrent scan calls short-circuit to prevent pileup. User-edit-conflict refuses remediation and emits a structured event.
|
|
12
|
+
|
|
13
|
+
Five new events: parity:gap-found, parity:remediated, parity:remediation-refused, parity:orphan-found, parity:scan-complete. Persistent state at .instar/state/framework-parity-sentinel.json with per (primitive × instance) cursors.
|
|
14
|
+
|
|
15
|
+
Spec at specs/instar-foundations/framework-parity-sentinel.md (converged + approved). ELI16 companion + convergence report alongside. Original 2026-05-18 proposal at specs/provider-portability/13-framework-parity-sentinel.md is now superseded — left in place for historical reference, frontmatter unchanged.
|
|
16
|
+
|
|
17
|
+
## What to Tell Your User
|
|
18
|
+
|
|
19
|
+
- "The piece that runs the cross-framework drift checks is now built. It walks the parity rules every 30 minutes (when wired into the server, coming next), spots when your canonical files have drifted from their rendered forms on Claude or Codex, and either re-renders or flags the drift for you — based on the rule's safety policy."
|
|
20
|
+
- "Memory stays sacrosanct: the sentinel will never auto-regenerate your AGENT.md, USER.md, or MEMORY.md. If they're corrupted, you get a structured alert pointing at the repair procedure."
|
|
21
|
+
|
|
22
|
+
## Summary of New Capabilities
|
|
23
|
+
|
|
24
|
+
| Capability | How to Use |
|
|
25
|
+
|-----------|-----------|
|
|
26
|
+
| FrameworkParitySentinel class | Import from src/monitoring/FrameworkParitySentinel.js. Construct with projectRoot + stateDir + enabledFrameworks. Call scan() for one pass or start() for interval mode. |
|
|
27
|
+
| Per-instance scan cursors | sentinel.getStatus() returns the per (primitive × instance) cursor state — used by the upcoming HTTP route. |
|
|
28
|
+
| Five EventEmitter events | parity:gap-found, parity:remediated, parity:remediation-refused, parity:orphan-found, parity:scan-complete. |
|
|
29
|
+
|
|
30
|
+
## Deferred (Tracked Follow-ups)
|
|
31
|
+
|
|
32
|
+
- HTTP routes (GET /api/framework-parity/status, POST /api/framework-parity/scan).
|
|
33
|
+
- server.ts boot integration (construction + start + RouteContext wiring).
|
|
34
|
+
- Integration + E2E tests (land with the HTTP wiring).
|
|
35
|
+
- chokidar source-change watcher (v0.2).
|
|
36
|
+
- Per-instance POST /api/framework-parity/remediate route (v0.2 — pending trust integration).
|
|
37
|
+
- Conversational-action layer wiring (Step 6 of the rollout).
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Upgrade Guide — v1.0.6
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
Lands the Conversational-action primitive (Layer-3 required primitive #10) — but **deliberately narrow**: pure-data catalog primitives only, no direct AGENT.md write.
|
|
8
|
+
|
|
9
|
+
Foundational stance, locked 2026-05-18 per Justin: users should never have to know any Instar internals. Conversational is the default; slash-commands are a backstop. When the user says "let's switch to local" the agent recognizes that intent and either invokes /local-model or guides the user there — without surfacing the slash-command name.
|
|
10
|
+
|
|
11
|
+
For the agent to translate intent to action, it needs a catalog of what is invocable. The earlier draft of this PR would have written that catalog directly into AGENT.md (the always-loaded identity file). After a scope-coherence pause + Justin's research request, we caught that this contradicts three Instar systems built specifically to prevent AGENT.md bloat: ContextHierarchy (Tier 0/1/2 segments), Playbook (scored decaying items), Self-Knowledge Tree (on-demand probes). So we removed the AGENT.md-writing API from v0.1.
|
|
12
|
+
|
|
13
|
+
v0.1 ships only the pure-data primitives:
|
|
14
|
+
- discoverActions(projectRoot) walks .instar/skills/ and returns the discovered actions sorted by name
|
|
15
|
+
- renderCatalogBlock(actions) generates a stable markdown block as a string (caller decides where it lands)
|
|
16
|
+
|
|
17
|
+
v0.1 explicitly does NOT ship:
|
|
18
|
+
- A function that writes the catalog into AGENT.md. v0.2 will route the catalog through ContextHierarchy Tier 2 segments (loaded on demand when the agent is interpreting user intent), a Self-Knowledge Tree probe (queried on demand), and a Playbook item (scored + decaying). All three load conditionally, not always.
|
|
19
|
+
- Authed POST execution endpoints. v0.2 with per-action shape declarations + trust integration.
|
|
20
|
+
- The user-invocable: true frontmatter filter. v0.2 once Skill primitive surfaces that field.
|
|
21
|
+
|
|
22
|
+
14 unit tests cover discovery and rendering, including a structural assertion that the AGENT.md-writing API is absent from the public surface — the bloat-aware design is enforced at the test layer.
|
|
23
|
+
|
|
24
|
+
Spec at specs/instar-concepts/conversational-action.md (converged + approved per hybrid-C pre-auth). ELI16 + convergence report alongside. Side-effects review documents the amendment in full.
|
|
25
|
+
|
|
26
|
+
## What to Tell Your User
|
|
27
|
+
|
|
28
|
+
- "The first piece of the conversational-action primitive is built — it discovers what is invocable and generates the catalog. The wiring step is held until v0.2 so we can load the catalog only when the agent needs it, instead of inlining it into the always-on identity prompt. That avoids the context-bloat trap we hit three times before."
|
|
29
|
+
|
|
30
|
+
## Summary of New Capabilities
|
|
31
|
+
|
|
32
|
+
| Capability | How to Use |
|
|
33
|
+
|-----------|-----------|
|
|
34
|
+
| Conversational action catalog (pure-data) | Import discoverActions and renderCatalogBlock from src/providers/parity/conversationalActionCatalog.js. Walks installed skills, generates the catalog block as a string. |
|
|
35
|
+
| Bloat-aware design enforced at test layer | The applyCatalogBlock API is deliberately absent from the public module exports; a structural test asserts this. |
|
|
36
|
+
|
|
37
|
+
## Deferred (Tracked Follow-ups)
|
|
38
|
+
|
|
39
|
+
- v0.2 wiring through ContextHierarchy Tier 2 segment + SelfKnowledgeTree catalog probe + Playbook context item (all three on-demand loaders).
|
|
40
|
+
- v0.2 FrameworkParitySentinel wiring as a catalog-drift parity rule.
|
|
41
|
+
- v0.2 authed POST /api/conversational/execute endpoints with per-action shape declarations + trust integration.
|
|
42
|
+
- v0.2 user-invocable: true frontmatter filter (pending Skill v0.2 field surface).
|
|
43
|
+
- InstructionFile primitive (separate spec, next) — decides what minimal pointer goes into framework-native instruction files.
|
|
44
|
+
- Intent-classification examples in skill frontmatter.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Side-Effects Review — Conversational-action primitive v0.1 (bloat-aware catalog primitives)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `feat-conversational-action`
|
|
4
|
+
**Date:** 2026-05-19
|
|
5
|
+
**Author:** Echo (autonomous mode, hybrid C, amended after scope-coherence grounding pause)
|
|
6
|
+
|
|
7
|
+
## Summary of the change
|
|
8
|
+
|
|
9
|
+
Lands the Conversational-action primitive (Layer-3 required primitive #10) as **bloat-aware pure-data primitives**: `discoverActions` (walks installed canonical skills) + `renderCatalogBlock` (generates the catalog block as a string). 14 unit tests including a structural assertion that `applyCatalogBlock` is deliberately NOT exported.
|
|
10
|
+
|
|
11
|
+
**Amended scope (post-grounding):** the original draft included `applyCatalogBlock(projectRoot, actions)` that wrote directly into `.instar/AGENT.md`. The scope-coherence hook + Justin's explicit research request surfaced that this contradicts three existing Instar systems built specifically to prevent AGENT.md bloat:
|
|
12
|
+
|
|
13
|
+
- `ContextHierarchy` (`src/core/ContextHierarchy.ts`) — Tier 0/1/2 segments, "right context at right moment > all context all the time" (born from the Luna incident 2026-02-25)
|
|
14
|
+
- `Playbook` (`docs/PLAYBOOK-GETTING-STARTED.md`) — scored decaying context items
|
|
15
|
+
- `SelfKnowledgeTree` (`src/knowledge/SelfKnowledgeTree.ts`) — on-demand probe queries instead of memorized lists
|
|
16
|
+
- Plus the `Structure > Willpower` principle codified in CLAUDE.md template
|
|
17
|
+
|
|
18
|
+
`applyCatalogBlock` was removed from v0.1 to prevent baking the wrong placement decision into a primitive's surface. v0.2 wiring goes through the three infrastructures above (Tier 2 segment writer, `catalog` probe, Playbook item) — not direct AGENT.md insert.
|
|
19
|
+
|
|
20
|
+
**Files changed (specs):**
|
|
21
|
+
- specs/instar-concepts/conversational-action.md (new, converged + approved per pre-auth; includes "Bloat-awareness as a v0.1 design constraint" section)
|
|
22
|
+
- specs/instar-concepts/conversational-action.eli16.md (new)
|
|
23
|
+
- docs/specs/reports/conversational-action-concept-convergence.md (new — records the amended scope as iteration 2 finding)
|
|
24
|
+
|
|
25
|
+
**Files changed (source):**
|
|
26
|
+
- src/providers/parity/conversationalActionCatalog.ts (new — discoverActions + renderCatalogBlock only; applyCatalogBlock deliberately NOT exported)
|
|
27
|
+
|
|
28
|
+
**Files changed (tests):**
|
|
29
|
+
- tests/unit/providers/parity/conversationalActionCatalog.test.ts (new — 14 tests; includes structural assertion that applyCatalogBlock is absent from public API)
|
|
30
|
+
|
|
31
|
+
**Files changed (release notes):**
|
|
32
|
+
- upgrades/NEXT.md (new)
|
|
33
|
+
- package.json (version bump)
|
|
34
|
+
|
|
35
|
+
## Decision-point inventory
|
|
36
|
+
|
|
37
|
+
- **Building-block scope, bloat-aware**: ships `discoverActions` + `renderCatalogBlock` only. No direct AGENT.md write. Caller (v0.2 ContextHierarchy / SelfKnowledgeTree / Playbook wiring) decides where the block lands.
|
|
38
|
+
- **`applyCatalogBlock` removal**: structural prevention of AGENT.md bloat. Spec body documents why; one test enforces the public API surface.
|
|
39
|
+
- **Skill discovery enumerates all canonical skills**: `user-invocable: true` filter deferred to v0.2 (Skill v0.1 doesn't surface that field).
|
|
40
|
+
- **Catalog block format**: HTML-comment delimited (`<!-- instar:conversational-actions:start -->` / `:end -->`), stable + machine-readable, idempotent re-renders.
|
|
41
|
+
- **Discovery skip-on-error**: skills with broken YAML frontmatter or invalid slugs are silently skipped (Skill parity rule surfaces them separately as drift). Avoids double-reporting.
|
|
42
|
+
- **No registry entry**: catalog is consumed via direct import (matches Tool primitive precedent). v0.2 sentinel-wired drift detection is the registry consumer.
|
|
43
|
+
- **No I/O writes**: `discoverActions` reads `.instar/skills/`; `renderCatalogBlock` is pure function. Module never writes anywhere.
|
|
44
|
+
|
|
45
|
+
## Over-block / under-block analysis
|
|
46
|
+
|
|
47
|
+
**Over-block risk (catalog rendering):** none — block is pure data, never imposed on AGENT.md.
|
|
48
|
+
|
|
49
|
+
**Under-block risk (broken skills skipped):** a skill with malformed frontmatter is silently absent from the catalog. The Skill parity rule already surfaces this as drift on its own pass; v0.2 sentinel wiring will combine signals.
|
|
50
|
+
|
|
51
|
+
**Under-block risk (v0.2 not yet wired):** until v0.2 wires the catalog into ContextHierarchy / SelfKnowledgeTree / Playbook, the catalog data exists but nothing reads it. That's intentional — better to ship correct pure-data primitives now than to ship a half-baked wiring that bakes in the bloat path.
|
|
52
|
+
|
|
53
|
+
## Level-of-abstraction fit
|
|
54
|
+
|
|
55
|
+
- Catalog primitives are pure data — discovery + rendering. No I/O writes, no LLM calls, no AGENT.md awareness.
|
|
56
|
+
- Boundary clean: catalog OWNS the block format; doesn't OWN where it lands.
|
|
57
|
+
- Pure-function shape: `discoverActions`, `renderCatalogBlock` — composable with any downstream loading vehicle.
|
|
58
|
+
|
|
59
|
+
## Signal-vs-authority compliance
|
|
60
|
+
|
|
61
|
+
- Catalog is signal (here's what's invocable + when).
|
|
62
|
+
- Authority over loading placement: the v0.2 ContextHierarchy / SelfKnowledgeTree / Playbook integrations + ultimately the agent's runtime intent classifier.
|
|
63
|
+
- v0.2 authed POST endpoints will gate execution via trust — separate authority layer.
|
|
64
|
+
|
|
65
|
+
## Interaction surface
|
|
66
|
+
|
|
67
|
+
- One new file under `src/providers/parity/`. No HTTP routes, no server.ts wiring, no config additions, no AGENT.md writes.
|
|
68
|
+
- Reads `.instar/skills/<name>/SKILL.md`.
|
|
69
|
+
- No changes to existing rules, registries, scaffold, or migration paths.
|
|
70
|
+
- No new dependencies.
|
|
71
|
+
|
|
72
|
+
## Rollback cost
|
|
73
|
+
|
|
74
|
+
- Pure-add. Revert removes the module + tests + spec. No data migrations, no AGENT.md state to undo (because no writes).
|
|
75
|
+
- Worst-case bug: `renderCatalogBlock` produces malformed markdown. Mitigated by stability test + delimiter shape test.
|
|
76
|
+
|
|
77
|
+
## Test coverage
|
|
78
|
+
|
|
79
|
+
- Unit: 14 tests covering discovery (empty/single/multiple/sort/fallback/slug-filter/broken-YAML-skip/files-vs-dirs), rendering (empty placeholder, bullet shape, stability), end-to-end discover → render, **structural assertion that applyCatalogBlock is NOT in the public API** (enforces the bloat-aware design constraint at the test layer).
|
|
80
|
+
- Integration + E2E: not applicable for pure-data primitives. v0.2 wiring PR will add integration + E2E when HTTP/sentinel/segment-writer surfaces land.
|
|
81
|
+
|
|
82
|
+
## Documentation
|
|
83
|
+
|
|
84
|
+
- Concept spec + ELI16 at `specs/instar-concepts/` (both updated to lead with the bloat-awareness design constraint).
|
|
85
|
+
- Convergence report at `docs/specs/reports/` (iteration 2 records the bloat-aware amendment).
|
|
86
|
+
- NEXT.md with "What to Tell Your User" entries explaining the deliberately-narrow v0.1 scope.
|
|
87
|
+
|
|
88
|
+
## Deferred (tracked, not silent)
|
|
89
|
+
|
|
90
|
+
- **ContextHierarchy Tier 2 segment writer** that consumes `renderCatalogBlock` output → `.instar/context/conversational-actions.md` with triggers `interpreting-user-intent` / `answering-what-can-you-do` — v0.2.
|
|
91
|
+
- **SelfKnowledgeTree `catalog` probe** that returns matching actions for a query at runtime — v0.2.
|
|
92
|
+
- **Playbook context item** seeded with the catalog discovery pattern + decay configuration — v0.2.
|
|
93
|
+
- **FrameworkParitySentinel wiring** as a catalog-drift parity rule (catalog-out-of-sync detection on scan) — v0.2.
|
|
94
|
+
- **Authed POST /api/conversational/execute endpoints** — v0.2 with per-action shape declarations + trust integration.
|
|
95
|
+
- **`user-invocable: true` filter** — v0.2, pending Skill v0.2 field surface.
|
|
96
|
+
- **InstructionFile primitive** — separate spec, decides what minimal pointer (3 sentences max) goes into framework-native CLAUDE.md / AGENTS.md.
|
|
97
|
+
- **Intent-classification examples in skill frontmatter** — v0.2.
|
|
98
|
+
|
|
99
|
+
## Why this amendment matters
|
|
100
|
+
|
|
101
|
+
The pre-amendment v0.1 would have shipped `applyCatalogBlock` as the headline API, with a NEXT.md line saying "v0.2 will fix the bloat concern." That's the "Phase 2" anti-pattern Justin explicitly listed in this autonomous session's stop-hook brief: "every task has a concrete done state. No deferrals disguised as planning." Removing `applyCatalogBlock` makes the v0.1 surface honest — the primitive ships what it can ship correctly, and the wiring decision waits for the right loading vehicle to land.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Side-Effects Review — FrameworkParitySentinel v0.1 (building block)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `feat-framework-parity-sentinel`
|
|
4
|
+
**Date:** 2026-05-19
|
|
5
|
+
**Author:** Echo (autonomous mode, hybrid C)
|
|
6
|
+
|
|
7
|
+
## Summary of the change
|
|
8
|
+
|
|
9
|
+
Lands the FrameworkParitySentinel as a **building block** — the class that walks the Layer-3 parity rules registry, calls each rule's `verify()` per instance, surfaces drift via events, and (per rule's `remediationPolicy`) optionally calls `remediate()`. Ships with unit tests and structured spec docs.
|
|
10
|
+
|
|
11
|
+
**Deliberately scoped narrow:** HTTP routes (`GET /api/framework-parity/status`, `POST /api/framework-parity/scan`) and server.ts boot integration are deferred to a follow-up PR. This matches the precedent set by Skill/Hook/Memory parity rules in PRs #252/#253/#254 — each shipped as a building block via the parity registry, with boot integration as a separate step. Shipping the sentinel class now unblocks the registry consumer side; the follow-up wires it into the server lifecycle.
|
|
12
|
+
|
|
13
|
+
**Files changed (specs):**
|
|
14
|
+
- specs/instar-foundations/framework-parity-sentinel.md (new, converged + approved per pre-auth)
|
|
15
|
+
- specs/instar-foundations/framework-parity-sentinel.eli16.md (new)
|
|
16
|
+
- docs/specs/reports/framework-parity-sentinel-convergence.md (new)
|
|
17
|
+
|
|
18
|
+
**Files changed (source):**
|
|
19
|
+
- src/monitoring/FrameworkParitySentinel.ts (new — class + lifecycle + scan + state I/O + EventEmitter)
|
|
20
|
+
|
|
21
|
+
**Files changed (tests):**
|
|
22
|
+
- tests/unit/monitoring/FrameworkParitySentinel.test.ts (new — 12 tests)
|
|
23
|
+
|
|
24
|
+
**Files changed (release notes):**
|
|
25
|
+
- upgrades/NEXT.md (new)
|
|
26
|
+
- package.json (version bump)
|
|
27
|
+
|
|
28
|
+
## Decision-point inventory
|
|
29
|
+
|
|
30
|
+
- **Building-block scope**: ships the class + unit tests; HTTP routes + boot wiring as a follow-up. Matches PR #252/#253/#254 precedent.
|
|
31
|
+
- **Scan cadence default**: 30 min (1_800_000 ms). Configurable.
|
|
32
|
+
- **Initial scan delay**: 60s (allows server to settle before first pass).
|
|
33
|
+
- **Concurrent-scan short-circuit**: second concurrent `scan()` call returns an empty report; prevents pileup on slow rules.
|
|
34
|
+
- **Remediation gating**: per-rule `remediationPolicy` is the authority. Sentinel-level `remediationEnabled` config can DOWNGRADE mirror-trust to flag-only but never UPGRADE flag-only to mirror-trust.
|
|
35
|
+
- **State schema**: `cursors[primitive::instance]` with `lastScanAt`, `lastResult`, `lastDetail`, `unresolvedCount`. Atomic writes via tempfile+rename.
|
|
36
|
+
- **Degradation threshold**: 3 consecutive scans with unresolved drift trips a `DegradationReporter.report()`.
|
|
37
|
+
- **Canonical-side mismatches are signal-only**: when `verify()` returns `framework: 'canonical'` (e.g. missing AGENT.md), the sentinel emits `parity:gap-found` but does NOT call `remediate()` — only the rule can mutate canonical, and Memory's rule throws by design.
|
|
38
|
+
- **Event vocabulary**: `parity:gap-found`, `parity:remediated`, `parity:remediation-refused`, `parity:orphan-found`, `parity:scan-complete`.
|
|
39
|
+
- **Orphan removal is opt-in**: `listOrphans()` always runs; `removeOrphans()` requires explicit operator action (no auto-fire in v0.1).
|
|
40
|
+
|
|
41
|
+
## Over-block / under-block analysis
|
|
42
|
+
|
|
43
|
+
**Over-block risk:** the sentinel never blocks operations — it scans + emits + (optionally) re-renders. Worst case is a noisy event stream, filterable at the EventEmitter consumer.
|
|
44
|
+
|
|
45
|
+
**Under-block risk:** if a rule's `verify()` throws, the sentinel marks that rule failed (via DegradationReporter) and continues with other rules. The failed rule's instances stay at their prior cursor state — drift may persist longer than 30 min until the rule's verify recovers. Mitigated by Degradation reporting (3-consecutive-failure trip) so operators are notified.
|
|
46
|
+
|
|
47
|
+
**Under-block risk (concurrent scan):** the short-circuit returns an empty report. If a user POSTs `/scan` while an interval scan is in flight (when routes ship), they get back empty + the interval scan continues. Acceptable for v0.1 — adding queue/wait complicates state.
|
|
48
|
+
|
|
49
|
+
## Level-of-abstraction fit
|
|
50
|
+
|
|
51
|
+
- Sentinel is the orchestrator. It owns scan timing, state, event emission. Routes/wiring are NOT in v0.1.
|
|
52
|
+
- Rules own canonical definitions, rendering, verification, remediation.
|
|
53
|
+
- Boundary is clean — sentinel never reads canonical formats directly, never knows what a "skill" or "hook" is. Just the registry interface.
|
|
54
|
+
|
|
55
|
+
## Signal-vs-authority compliance
|
|
56
|
+
|
|
57
|
+
- Sentinel emits signals (`parity:gap-found`). Rules' `remediationPolicy` and sentinel's `remediationEnabled` are the layered authority.
|
|
58
|
+
- Sentinel never overrides a rule's `flag-only` policy (cannot upgrade).
|
|
59
|
+
- Sentinel respects `user-edit-conflict` by short-circuiting remediation and emitting `parity:remediation-refused`.
|
|
60
|
+
|
|
61
|
+
## Interaction surface
|
|
62
|
+
|
|
63
|
+
- Adds one new class under `src/monitoring/`. No HTTP routes, no server.ts wiring, no config additions in v0.1.
|
|
64
|
+
- Optional state file at `.instar/state/framework-parity-sentinel.json` — only written when `scan()` is called. Cold-start safe.
|
|
65
|
+
- No changes to existing rules, registries, or per-primitive infrastructure.
|
|
66
|
+
- No migrations needed (cold-start is a no-op until the follow-up wires it in).
|
|
67
|
+
|
|
68
|
+
## Rollback cost
|
|
69
|
+
|
|
70
|
+
- Pure-add. Revert removes the sentinel + tests + spec + state file. No data migrations.
|
|
71
|
+
- Worst-case bug: spurious remediation on healthy artifacts when the follow-up wires it. Mitigated by user-edit-conflict short-circuit + per-rule policy + future `remediationEnabled` kill-switch.
|
|
72
|
+
- Operator emergency (post-follow-up): set `frameworkParity.remediationEnabled: false` in `.instar/config.json` and restart — sentinel becomes signal-only.
|
|
73
|
+
|
|
74
|
+
## Test coverage
|
|
75
|
+
|
|
76
|
+
- Unit: 12 tests covering rule walking, event emission for all 5 events, remediation policy routing (mirror-trust + flag-only + user-edit-conflict + config override), concurrent-scan short-circuit, state persistence round-trip, start/stop lifecycle idempotency.
|
|
77
|
+
- Integration + E2E: deferred to the follow-up PR that wires HTTP routes — matches PR #252/#253/#254 precedent (the parity rules in those PRs also shipped unit-tests-only as building blocks).
|
|
78
|
+
|
|
79
|
+
## Documentation
|
|
80
|
+
|
|
81
|
+
- Concept spec + ELI16 at `specs/instar-foundations/`.
|
|
82
|
+
- Convergence report at `docs/specs/reports/`.
|
|
83
|
+
- NEXT.md with "What to Tell Your User" entries.
|
|
84
|
+
|
|
85
|
+
## Deferred (tracked, not silent)
|
|
86
|
+
|
|
87
|
+
- **HTTP routes** (`GET /api/framework-parity/status`, `POST /api/framework-parity/scan`) — follow-up PR.
|
|
88
|
+
- **server.ts boot integration** (construction + start + RouteContext wiring) — follow-up PR.
|
|
89
|
+
- **Integration + E2E tests** — land with the HTTP wiring.
|
|
90
|
+
- **chokidar source-change watcher** — v0.2.
|
|
91
|
+
- **POST /api/framework-parity/remediate per-instance route** — v0.2 (pending trust integration).
|
|
92
|
+
- **Trust-level integration** for mirror-trust gating (v0.2 — hardcodes allow for v0.1).
|
|
93
|
+
- **Backfill migration** of existing-agent installed base (separate one-shot tool).
|
|
94
|
+
- **Conversational-action layer** (Step 6 of the rollout).
|