instar 0.28.116 → 0.28.118

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.
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/providers/parity/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAOlE,wBAAgB,aAAa,CAAC,SAAS,EAAE,mBAAmB,GAAG,UAAU,GAAG,SAAS,CAEpF;AAED,wBAAgB,eAAe,IAAI,aAAa,CAAC,UAAU,CAAC,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,mBAAmB,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,IAAI,CAOtG"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/providers/parity/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAWlE,wBAAgB,aAAa,CAAC,SAAS,EAAE,mBAAmB,GAAG,UAAU,GAAG,SAAS,CAEpF;AAED,wBAAgB,eAAe,IAAI,aAAa,CAAC,UAAU,CAAC,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,mBAAmB,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,IAAI,CAOtG"}
@@ -12,8 +12,12 @@
12
12
  * rules are code; registration is declarative.
13
13
  */
14
14
  import { skillParityRule } from './rules/skillParityRule.js';
15
+ import { hookParityRule } from './rules/hookParityRule.js';
16
+ import { memoryParityRule } from './rules/memoryParityRule.js';
15
17
  const RULES = new Map([
16
18
  [skillParityRule.primitive, skillParityRule],
19
+ [hookParityRule.primitive, hookParityRule],
20
+ [memoryParityRule.primitive, memoryParityRule],
17
21
  ]);
18
22
  export function getParityRule(primitive) {
19
23
  return RULES.get(primitive);
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/providers/parity/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,MAAM,KAAK,GAAyC,IAAI,GAAG,CAAC;IAC1D,CAAC,eAAe,CAAC,SAAS,EAAE,eAAe,CAAC;CAC7C,CAAC,CAAC;AAEH,MAAM,UAAU,aAAa,CAAC,SAA8B;IAC1D,OAAO,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,SAA8B,EAAE,IAAgB;IACxF,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACnC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC3B,OAAO,GAAG,EAAE;QACV,IAAI,KAAK;YAAE,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;;YAClC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/providers/parity/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE/D,MAAM,KAAK,GAAyC,IAAI,GAAG,CAAC;IAC1D,CAAC,eAAe,CAAC,SAAS,EAAE,eAAe,CAAC;IAC5C,CAAC,cAAc,CAAC,SAAS,EAAE,cAAc,CAAC;IAC1C,CAAC,gBAAgB,CAAC,SAAS,EAAE,gBAAgB,CAAC;CAC/C,CAAC,CAAC;AAEH,MAAM,UAAU,aAAa,CAAC,SAA8B;IAC1D,OAAO,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,SAA8B,EAAE,IAAgB;IACxF,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACnC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC3B,OAAO,GAAG,EAAE;QACV,IAAI,KAAK;YAAE,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;;YAClC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * hookParityRule — parity rule for the Hook functional primitive.
3
+ *
4
+ * Specs:
5
+ * - specs/instar-concepts/hook.md (canonical shape + event vocabulary)
6
+ * - specs/frameworks/claude-code/hooks.md
7
+ * - specs/frameworks/codex-cli/hooks.md
8
+ *
9
+ * Reads canonical hooks from `.instar/hooks/<event>/<name>.<ext>` and
10
+ * verifies each framework renders them correctly (script copy + entry in
11
+ * the framework's settings/config file).
12
+ *
13
+ * v0.1 scope: session-start event only. Adding additional events =
14
+ * extending the EVENT_NAME_MAPPING table at the top of the file.
15
+ *
16
+ * Reuses the Skill prototype's hardening patterns:
17
+ * - C1: strict slug grammar at every entry point.
18
+ * - C5: symmetric verify + orphan detection.
19
+ * - C7: x-instar-stamp comment line distinguishes user-edit-conflict
20
+ * from canonical drift; remediate refuses to overwrite conflicts.
21
+ *
22
+ * Deferred to v0.2 (tracked in concept spec):
23
+ * - Events beyond session-start.
24
+ * - Executable-bit verification (currently set on render; not verified).
25
+ * - hooks.json/settings.json merge semantics for non-Instar entries.
26
+ * v0.1 manages only entries whose script paths live under
27
+ * `.claude/hooks/<canonical-event>/` or `.agent/openai/hooks/<canonical-event>/`.
28
+ */
29
+ import type { ParityRule } from '../types.js';
30
+ export declare class CanonicalHookError extends Error {
31
+ constructor(message: string);
32
+ }
33
+ export declare const hookParityRule: ParityRule;
34
+ //# sourceMappingURL=hookParityRule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookParityRule.d.ts","sourceRoot":"","sources":["../../../../src/providers/parity/rules/hookParityRule.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAMH,OAAO,KAAK,EACV,UAAU,EAGX,MAAM,aAAa,CAAC;AAkBrB,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAsZD,eAAO,MAAM,cAAc,EAAE,UA6E5B,CAAC"}
@@ -0,0 +1,463 @@
1
+ /**
2
+ * hookParityRule — parity rule for the Hook functional primitive.
3
+ *
4
+ * Specs:
5
+ * - specs/instar-concepts/hook.md (canonical shape + event vocabulary)
6
+ * - specs/frameworks/claude-code/hooks.md
7
+ * - specs/frameworks/codex-cli/hooks.md
8
+ *
9
+ * Reads canonical hooks from `.instar/hooks/<event>/<name>.<ext>` and
10
+ * verifies each framework renders them correctly (script copy + entry in
11
+ * the framework's settings/config file).
12
+ *
13
+ * v0.1 scope: session-start event only. Adding additional events =
14
+ * extending the EVENT_NAME_MAPPING table at the top of the file.
15
+ *
16
+ * Reuses the Skill prototype's hardening patterns:
17
+ * - C1: strict slug grammar at every entry point.
18
+ * - C5: symmetric verify + orphan detection.
19
+ * - C7: x-instar-stamp comment line distinguishes user-edit-conflict
20
+ * from canonical drift; remediate refuses to overwrite conflicts.
21
+ *
22
+ * Deferred to v0.2 (tracked in concept spec):
23
+ * - Events beyond session-start.
24
+ * - Executable-bit verification (currently set on render; not verified).
25
+ * - hooks.json/settings.json merge semantics for non-Instar entries.
26
+ * v0.1 manages only entries whose script paths live under
27
+ * `.claude/hooks/<canonical-event>/` or `.agent/openai/hooks/<canonical-event>/`.
28
+ */
29
+ import { promises as fs } from 'node:fs';
30
+ import path from 'node:path';
31
+ import crypto from 'node:crypto';
32
+ const CANONICAL_ROOT = '.instar/hooks';
33
+ const SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
34
+ const ALLOWED_EXTS = new Set(['.sh', '.js', '.mjs', '.cjs', '.ts']);
35
+ const STAMP_COMMENT_RE = /^#\s*x-instar-stamp:\s*([a-f0-9]{64})\s*$/m;
36
+ const STAMP_COMMENT_STRIP_RE = /^#\s*x-instar-stamp:\s*[a-f0-9]{64}\s*\r?\n/m;
37
+ const EVENT_NAME_MAPPING = {
38
+ 'session-start': { claude: 'SessionStart', codex: 'session_start' },
39
+ };
40
+ const SUPPORTED_EVENTS = Object.keys(EVENT_NAME_MAPPING);
41
+ export class CanonicalHookError extends Error {
42
+ constructor(message) {
43
+ super(message);
44
+ this.name = 'CanonicalHookError';
45
+ }
46
+ }
47
+ function validateSlug(value, context) {
48
+ if (typeof value !== 'string' || !SLUG_RE.test(value)) {
49
+ throw new CanonicalHookError(`${context}: invalid slug "${value}". Must match ${SLUG_RE.source}.`);
50
+ }
51
+ }
52
+ function validateExt(ext, context) {
53
+ if (!ALLOWED_EXTS.has(ext)) {
54
+ throw new CanonicalHookError(`${context}: invalid extension "${ext}". Allowed: ${[...ALLOWED_EXTS].join(', ')}`);
55
+ }
56
+ }
57
+ function sha256(s) {
58
+ return crypto.createHash('sha256').update(s, 'utf-8').digest('hex');
59
+ }
60
+ function parseInstanceName(instanceName) {
61
+ const slash = instanceName.indexOf('/');
62
+ if (slash < 0) {
63
+ throw new CanonicalHookError(`instanceName "${instanceName}" must be in the form "<event>/<name>.<ext>"`);
64
+ }
65
+ const event = instanceName.slice(0, slash);
66
+ const fileName = instanceName.slice(slash + 1);
67
+ const dot = fileName.lastIndexOf('.');
68
+ if (dot <= 0) {
69
+ throw new CanonicalHookError(`instanceName "${instanceName}" must end with an extension (.sh, .js, .mjs, .cjs, .ts)`);
70
+ }
71
+ const name = fileName.slice(0, dot);
72
+ const ext = fileName.slice(dot);
73
+ return { event, name, ext };
74
+ }
75
+ async function readCanonicalHook(projectRoot, instanceName) {
76
+ const { event, name, ext } = parseInstanceName(instanceName);
77
+ validateSlug(event, 'canonical hook event');
78
+ validateSlug(name, 'canonical hook name');
79
+ validateExt(ext, 'canonical hook extension');
80
+ if (!(event in EVENT_NAME_MAPPING)) {
81
+ throw new CanonicalHookError(`event "${event}" is not in the v0.1 supported set. Supported: ${SUPPORTED_EVENTS.join(', ')}`);
82
+ }
83
+ const scriptPath = path.join(projectRoot, CANONICAL_ROOT, event, `${name}${ext}`);
84
+ let body;
85
+ try {
86
+ body = await fs.readFile(scriptPath, 'utf-8');
87
+ }
88
+ catch (err) {
89
+ throw new CanonicalHookError(`canonical hook script missing or unreadable: ${path.relative(projectRoot, scriptPath)} — ${err.message}`);
90
+ }
91
+ // Strip a leading stamp comment from body before hashing so the canonical
92
+ // body hash is stable across rendering cycles.
93
+ const bodyForHash = body.replace(STAMP_COMMENT_STRIP_RE, '');
94
+ return {
95
+ instanceName,
96
+ event: event,
97
+ name,
98
+ ext,
99
+ body: bodyForHash,
100
+ bodyHash: sha256(bodyForHash),
101
+ };
102
+ }
103
+ async function listCanonicalHookInstances(projectRoot) {
104
+ const root = path.join(projectRoot, CANONICAL_ROOT);
105
+ const out = [];
106
+ let eventDirs;
107
+ try {
108
+ eventDirs = (await fs.readdir(root, { withFileTypes: true }))
109
+ .filter((e) => e.isDirectory() && SLUG_RE.test(e.name))
110
+ .map((e) => e.name);
111
+ }
112
+ catch (err) {
113
+ if (err.code === 'ENOENT')
114
+ return [];
115
+ throw err;
116
+ }
117
+ for (const event of eventDirs) {
118
+ if (!(event in EVENT_NAME_MAPPING))
119
+ continue;
120
+ const eventDir = path.join(root, event);
121
+ let files;
122
+ try {
123
+ files = (await fs.readdir(eventDir, { withFileTypes: true })).map((e) => ({
124
+ name: e.name,
125
+ isFile: e.isFile(),
126
+ }));
127
+ }
128
+ catch {
129
+ continue;
130
+ }
131
+ for (const f of files) {
132
+ if (!f.isFile)
133
+ continue;
134
+ const dot = f.name.lastIndexOf('.');
135
+ if (dot <= 0)
136
+ continue;
137
+ const baseName = f.name.slice(0, dot);
138
+ const ext = f.name.slice(dot);
139
+ if (!SLUG_RE.test(baseName))
140
+ continue;
141
+ if (!ALLOWED_EXTS.has(ext))
142
+ continue;
143
+ out.push(`${event}/${f.name}`);
144
+ }
145
+ }
146
+ return out.sort();
147
+ }
148
+ function withStamp(body, bodyHash) {
149
+ const stampLine = `# x-instar-stamp: ${bodyHash}\n`;
150
+ // Insert stamp right after a shebang if present, else at the top.
151
+ if (body.startsWith('#!')) {
152
+ const nl = body.indexOf('\n');
153
+ if (nl < 0)
154
+ return body + '\n' + stampLine;
155
+ return body.slice(0, nl + 1) + stampLine + body.slice(nl + 1);
156
+ }
157
+ return stampLine + body;
158
+ }
159
+ function extractStamp(rendered) {
160
+ const m = rendered.match(STAMP_COMMENT_RE);
161
+ return m ? m[1] : null;
162
+ }
163
+ function bodyWithoutStamp(rendered) {
164
+ return rendered.replace(STAMP_COMMENT_STRIP_RE, '');
165
+ }
166
+ function claudeLayout(projectRoot, hook) {
167
+ return {
168
+ scriptPath: path.join(projectRoot, '.claude/hooks', hook.event, `${hook.name}${hook.ext}`),
169
+ configPath: path.join(projectRoot, '.claude/settings.json'),
170
+ };
171
+ }
172
+ function codexLayout(projectRoot, hook) {
173
+ return {
174
+ scriptPath: path.join(projectRoot, '.agent/openai/hooks', hook.event, `${hook.name}${hook.ext}`),
175
+ configPath: path.join(projectRoot, '.agent/openai/hooks.json'),
176
+ };
177
+ }
178
+ async function readJsonOrDefault(p, def) {
179
+ try {
180
+ const raw = await fs.readFile(p, 'utf-8');
181
+ return JSON.parse(raw);
182
+ }
183
+ catch {
184
+ return def;
185
+ }
186
+ }
187
+ function nativeEventForFramework(event, framework) {
188
+ const m = EVENT_NAME_MAPPING[event];
189
+ return framework === 'claude-code' ? m.claude : m.codex;
190
+ }
191
+ async function renderClaudeHook(projectRoot, hook) {
192
+ const layout = claudeLayout(projectRoot, hook);
193
+ await fs.mkdir(path.dirname(layout.scriptPath), { recursive: true });
194
+ await fs.writeFile(layout.scriptPath, withStamp(hook.body, hook.bodyHash), 'utf-8');
195
+ await fs.chmod(layout.scriptPath, 0o755);
196
+ // Merge settings.json hook entry.
197
+ const settings = await readJsonOrDefault(layout.configPath, {});
198
+ const eventKey = nativeEventForFramework(hook.event, 'claude-code');
199
+ const arr = settings.hooks?.[eventKey] ?? [];
200
+ const relScriptPath = path.relative(projectRoot, layout.scriptPath);
201
+ const desiredCommand = `bash ${relScriptPath}`;
202
+ const existing = arr.find((entry) => entry.hooks?.some((h) => h.command === desiredCommand));
203
+ if (!existing) {
204
+ arr.push({
205
+ matcher: '',
206
+ hooks: [{ type: 'command', command: desiredCommand, timeout: 10000 }],
207
+ });
208
+ settings.hooks = { ...(settings.hooks ?? {}), [eventKey]: arr };
209
+ await fs.mkdir(path.dirname(layout.configPath), { recursive: true });
210
+ await fs.writeFile(layout.configPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
211
+ }
212
+ }
213
+ async function renderCodexHook(projectRoot, hook) {
214
+ const layout = codexLayout(projectRoot, hook);
215
+ await fs.mkdir(path.dirname(layout.scriptPath), { recursive: true });
216
+ await fs.writeFile(layout.scriptPath, withStamp(hook.body, hook.bodyHash), 'utf-8');
217
+ await fs.chmod(layout.scriptPath, 0o755);
218
+ await fs.mkdir(path.dirname(layout.configPath), { recursive: true });
219
+ const config = await readJsonOrDefault(layout.configPath, { hooks: [] });
220
+ const eventKey = nativeEventForFramework(hook.event, 'codex-cli');
221
+ const arr = config.hooks ?? [];
222
+ const relScriptPath = path.relative(projectRoot, layout.scriptPath);
223
+ const existing = arr.find((entry) => entry.event === eventKey && entry.script === relScriptPath);
224
+ if (!existing) {
225
+ arr.push({ event: eventKey, script: relScriptPath });
226
+ config.hooks = arr;
227
+ await fs.writeFile(layout.configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
228
+ }
229
+ }
230
+ async function verifyClaudeHook(projectRoot, hook) {
231
+ const layout = claudeLayout(projectRoot, hook);
232
+ const issues = [];
233
+ let rendered = null;
234
+ try {
235
+ rendered = await fs.readFile(layout.scriptPath, 'utf-8');
236
+ }
237
+ catch {
238
+ issues.push({
239
+ reasonCode: 'missing-rendered-file',
240
+ detail: `expected ${path.relative(projectRoot, layout.scriptPath)} to exist`,
241
+ });
242
+ return issues;
243
+ }
244
+ const stamp = extractStamp(rendered);
245
+ const rBody = bodyWithoutStamp(rendered);
246
+ if (rBody !== hook.body) {
247
+ if (stamp && stamp === hook.bodyHash) {
248
+ issues.push({
249
+ reasonCode: 'user-edit-conflict',
250
+ detail: `claude hook script body differs from canonical but stamp matches — user edited the rendering`,
251
+ });
252
+ }
253
+ else {
254
+ issues.push({
255
+ reasonCode: 'body-content-mismatch',
256
+ detail: `claude hook script body differs from canonical (stamp ${stamp ? 'stale' : 'missing'})`,
257
+ });
258
+ }
259
+ }
260
+ // settings.json entry presence
261
+ const settings = await readJsonOrDefault(layout.configPath, {});
262
+ const eventKey = nativeEventForFramework(hook.event, 'claude-code');
263
+ const arr = settings.hooks?.[eventKey] ?? [];
264
+ const relScriptPath = path.relative(projectRoot, layout.scriptPath);
265
+ const desiredCommand = `bash ${relScriptPath}`;
266
+ const hasEntry = arr.some((entry) => entry.hooks?.some((h) => h.command === desiredCommand));
267
+ if (!hasEntry) {
268
+ issues.push({
269
+ reasonCode: 'sibling-artifact-missing',
270
+ detail: `settings.json missing hook entry for ${eventKey} → ${relScriptPath}`,
271
+ });
272
+ }
273
+ return issues;
274
+ }
275
+ async function verifyCodexHook(projectRoot, hook) {
276
+ const layout = codexLayout(projectRoot, hook);
277
+ const issues = [];
278
+ let rendered = null;
279
+ try {
280
+ rendered = await fs.readFile(layout.scriptPath, 'utf-8');
281
+ }
282
+ catch {
283
+ issues.push({
284
+ reasonCode: 'missing-rendered-file',
285
+ detail: `expected ${path.relative(projectRoot, layout.scriptPath)} to exist`,
286
+ });
287
+ return issues;
288
+ }
289
+ const stamp = extractStamp(rendered);
290
+ const rBody = bodyWithoutStamp(rendered);
291
+ if (rBody !== hook.body) {
292
+ if (stamp && stamp === hook.bodyHash) {
293
+ issues.push({
294
+ reasonCode: 'user-edit-conflict',
295
+ detail: `codex hook script body differs from canonical but stamp matches — user edited the rendering`,
296
+ });
297
+ }
298
+ else {
299
+ issues.push({
300
+ reasonCode: 'body-content-mismatch',
301
+ detail: `codex hook script body differs from canonical (stamp ${stamp ? 'stale' : 'missing'})`,
302
+ });
303
+ }
304
+ }
305
+ const config = await readJsonOrDefault(layout.configPath, {});
306
+ const eventKey = nativeEventForFramework(hook.event, 'codex-cli');
307
+ const relScriptPath = path.relative(projectRoot, layout.scriptPath);
308
+ const hasEntry = (config.hooks ?? []).some((entry) => entry.event === eventKey && entry.script === relScriptPath);
309
+ if (!hasEntry) {
310
+ issues.push({
311
+ reasonCode: 'sibling-artifact-missing',
312
+ detail: `hooks.json missing entry for ${eventKey} → ${relScriptPath}`,
313
+ });
314
+ }
315
+ return issues;
316
+ }
317
+ // ─── Orphan detection ──────────────────────────────────────────────────
318
+ async function listFrameworkScripts(projectRoot, framework) {
319
+ const root = framework === 'claude-code'
320
+ ? path.join(projectRoot, '.claude/hooks')
321
+ : path.join(projectRoot, '.agent/openai/hooks');
322
+ let eventDirs;
323
+ try {
324
+ eventDirs = (await fs.readdir(root, { withFileTypes: true }))
325
+ .filter((e) => e.isDirectory() && SLUG_RE.test(e.name))
326
+ .map((e) => e.name);
327
+ }
328
+ catch {
329
+ return [];
330
+ }
331
+ const out = [];
332
+ for (const event of eventDirs) {
333
+ let files;
334
+ try {
335
+ files = (await fs.readdir(path.join(root, event), { withFileTypes: true })).map((e) => ({
336
+ name: e.name,
337
+ isFile: e.isFile(),
338
+ }));
339
+ }
340
+ catch {
341
+ continue;
342
+ }
343
+ for (const f of files) {
344
+ if (!f.isFile)
345
+ continue;
346
+ const dot = f.name.lastIndexOf('.');
347
+ if (dot <= 0)
348
+ continue;
349
+ const baseName = f.name.slice(0, dot);
350
+ const ext = f.name.slice(dot);
351
+ if (!SLUG_RE.test(baseName))
352
+ continue;
353
+ if (!ALLOWED_EXTS.has(ext))
354
+ continue;
355
+ out.push({ event, name: baseName, ext, fullPath: path.join(root, event, f.name) });
356
+ }
357
+ }
358
+ return out;
359
+ }
360
+ async function findOrphans(projectRoot, framework, canonicalInstances) {
361
+ const rendered = await listFrameworkScripts(projectRoot, framework);
362
+ const out = [];
363
+ for (const r of rendered) {
364
+ const instanceName = `${r.event}/${r.name}${r.ext}`;
365
+ if (canonicalInstances.has(instanceName))
366
+ continue;
367
+ out.push({
368
+ primitive: 'hook',
369
+ instanceName,
370
+ framework,
371
+ reasonCode: 'orphan-rendering-found',
372
+ detail: `rendered hook script has no canonical counterpart: ${path.relative(projectRoot, r.fullPath)}`,
373
+ });
374
+ }
375
+ return out;
376
+ }
377
+ async function removeOrphanScripts(projectRoot, framework, canonicalInstances) {
378
+ const rendered = await listFrameworkScripts(projectRoot, framework);
379
+ const removed = [];
380
+ for (const r of rendered) {
381
+ const instanceName = `${r.event}/${r.name}${r.ext}`;
382
+ if (canonicalInstances.has(instanceName))
383
+ continue;
384
+ await fs.rm(r.fullPath, { force: true });
385
+ removed.push(r.fullPath);
386
+ }
387
+ return removed;
388
+ }
389
+ // ─── Exported ParityRule ───────────────────────────────────────────────
390
+ export const hookParityRule = {
391
+ primitive: 'hook',
392
+ frameworks: ['claude-code', 'codex-cli'],
393
+ remediationPolicy: 'mirror-trust',
394
+ async listInstances(projectRoot) {
395
+ return listCanonicalHookInstances(projectRoot);
396
+ },
397
+ async verify(projectRoot, instanceName) {
398
+ let hook;
399
+ try {
400
+ hook = await readCanonicalHook(projectRoot, instanceName);
401
+ }
402
+ catch (err) {
403
+ return {
404
+ ok: false,
405
+ mismatches: [
406
+ {
407
+ primitive: 'hook',
408
+ instanceName,
409
+ framework: 'canonical',
410
+ reasonCode: 'canonical-read-error',
411
+ detail: err.message,
412
+ },
413
+ ],
414
+ };
415
+ }
416
+ const mismatches = [];
417
+ for (const framework of this.frameworks) {
418
+ const issues = framework === 'claude-code'
419
+ ? await verifyClaudeHook(projectRoot, hook)
420
+ : await verifyCodexHook(projectRoot, hook);
421
+ for (const issue of issues) {
422
+ mismatches.push({
423
+ primitive: 'hook',
424
+ instanceName,
425
+ framework,
426
+ reasonCode: issue.reasonCode,
427
+ detail: issue.detail,
428
+ });
429
+ }
430
+ }
431
+ return { ok: mismatches.length === 0, mismatches };
432
+ },
433
+ async remediate(projectRoot, instanceName, framework) {
434
+ const hook = await readCanonicalHook(projectRoot, instanceName);
435
+ // Refuse on user-edit-conflict.
436
+ const issues = framework === 'claude-code'
437
+ ? await verifyClaudeHook(projectRoot, hook)
438
+ : await verifyCodexHook(projectRoot, hook);
439
+ const conflict = issues.find((i) => i.reasonCode === 'user-edit-conflict');
440
+ if (conflict) {
441
+ throw new CanonicalHookError(`refused to remediate ${instanceName} on ${framework} due to user-edit-conflict: ${conflict.detail}`);
442
+ }
443
+ if (framework === 'claude-code') {
444
+ await renderClaudeHook(projectRoot, hook);
445
+ }
446
+ else {
447
+ await renderCodexHook(projectRoot, hook);
448
+ }
449
+ },
450
+ async listOrphans(projectRoot) {
451
+ const canonical = new Set(await listCanonicalHookInstances(projectRoot));
452
+ const out = [];
453
+ for (const framework of this.frameworks) {
454
+ out.push(...(await findOrphans(projectRoot, framework, canonical)));
455
+ }
456
+ return out;
457
+ },
458
+ async removeOrphans(projectRoot, framework) {
459
+ const canonical = new Set(await listCanonicalHookInstances(projectRoot));
460
+ return removeOrphanScripts(projectRoot, framework, canonical);
461
+ },
462
+ };
463
+ //# sourceMappingURL=hookParityRule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookParityRule.js","sourceRoot":"","sources":["../../../../src/providers/parity/rules/hookParityRule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * memoryParityRule — parity rule for the Memory functional primitive.
3
+ *
4
+ * Specs:
5
+ * - specs/instar-concepts/memory.md (canonical shape)
6
+ * - specs/frameworks/claude-code/memory.md
7
+ * - specs/frameworks/codex-cli/memory.md
8
+ *
9
+ * Memory is substrate-bound: the canonical artifacts (`.instar/AGENT.md`,
10
+ * `.instar/USER.md`, `.instar/MEMORY.md`, `.instar/state/topic-memory.sqlite`)
11
+ * are framework-agnostic files on disk. This rule's verify() confirms each
12
+ * required artifact is present, non-empty, and (for markdown) parses cleanly.
13
+ *
14
+ * v0.1 deliberately does NOT auto-remediate. Memory contains agent identity
15
+ * and accumulated learnings — silent regeneration would erase intentional
16
+ * drift. remediate() throws with a structured detail pointing to the documented
17
+ * repair procedure.
18
+ *
19
+ * Loading canonical Memory into framework system-prompts (CLAUDE.md / AGENTS.md
20
+ * references) is the InstructionFile primitive's responsibility — separate.
21
+ */
22
+ import type { ParityRule } from '../types.js';
23
+ /**
24
+ * The Memory primitive has a fixed canonical artifact set, not user-named
25
+ * instances like Skill/Hook. listInstances() returns the artifact paths so the
26
+ * sentinel can iterate verify() over each, but verify() is also defined to
27
+ * accept any artifact path and return ok:true if it's not in the known set
28
+ * (defensive: sentinel might call with stale names after a config change).
29
+ */
30
+ export declare const memoryParityRule: ParityRule;
31
+ //# sourceMappingURL=memoryParityRule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoryParityRule.d.ts","sourceRoot":"","sources":["../../../../src/providers/parity/rules/memoryParityRule.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,OAAO,KAAK,EACV,UAAU,EAGX,MAAM,aAAa,CAAC;AAiLrB;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,UAmD9B,CAAC"}
@@ -0,0 +1,198 @@
1
+ /**
2
+ * memoryParityRule — parity rule for the Memory functional primitive.
3
+ *
4
+ * Specs:
5
+ * - specs/instar-concepts/memory.md (canonical shape)
6
+ * - specs/frameworks/claude-code/memory.md
7
+ * - specs/frameworks/codex-cli/memory.md
8
+ *
9
+ * Memory is substrate-bound: the canonical artifacts (`.instar/AGENT.md`,
10
+ * `.instar/USER.md`, `.instar/MEMORY.md`, `.instar/state/topic-memory.sqlite`)
11
+ * are framework-agnostic files on disk. This rule's verify() confirms each
12
+ * required artifact is present, non-empty, and (for markdown) parses cleanly.
13
+ *
14
+ * v0.1 deliberately does NOT auto-remediate. Memory contains agent identity
15
+ * and accumulated learnings — silent regeneration would erase intentional
16
+ * drift. remediate() throws with a structured detail pointing to the documented
17
+ * repair procedure.
18
+ *
19
+ * Loading canonical Memory into framework system-prompts (CLAUDE.md / AGENTS.md
20
+ * references) is the InstructionFile primitive's responsibility — separate.
21
+ */
22
+ import { promises as fs } from 'node:fs';
23
+ import path from 'node:path';
24
+ import yaml from 'js-yaml';
25
+ const ARTIFACTS = [
26
+ {
27
+ relPath: '.instar/AGENT.md',
28
+ kind: 'markdown',
29
+ required: true,
30
+ label: 'agent identity',
31
+ },
32
+ {
33
+ relPath: '.instar/USER.md',
34
+ kind: 'markdown',
35
+ required: true,
36
+ label: 'user profile',
37
+ },
38
+ {
39
+ relPath: '.instar/MEMORY.md',
40
+ kind: 'markdown',
41
+ required: true,
42
+ label: 'persistent learnings',
43
+ },
44
+ {
45
+ relPath: '.instar/state/topic-memory.sqlite',
46
+ kind: 'sqlite',
47
+ required: false,
48
+ label: 'topic memory store',
49
+ },
50
+ ];
51
+ const SQLITE_MAGIC = 'SQLite format 3\0';
52
+ class CanonicalMemoryError extends Error {
53
+ artifact;
54
+ reasonCode;
55
+ constructor(artifact, reasonCode, message) {
56
+ super(message);
57
+ this.name = 'CanonicalMemoryError';
58
+ this.artifact = artifact;
59
+ this.reasonCode = reasonCode;
60
+ }
61
+ }
62
+ async function pathExists(p) {
63
+ try {
64
+ await fs.access(p);
65
+ return true;
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ }
71
+ async function verifyMarkdownArtifact(absPath, artifact) {
72
+ let raw;
73
+ try {
74
+ raw = await fs.readFile(absPath, 'utf-8');
75
+ }
76
+ catch (err) {
77
+ throw new CanonicalMemoryError(artifact.relPath, 'canonical-read-error', `cannot read ${artifact.label} at ${artifact.relPath}: ${err.message}`);
78
+ }
79
+ if (raw.trim().length === 0) {
80
+ throw new CanonicalMemoryError(artifact.relPath, 'canonical-read-error', `${artifact.label} at ${artifact.relPath} is empty — repair required`);
81
+ }
82
+ // Optional frontmatter — if it starts with `---`, parse it. If it doesn't,
83
+ // that's fine (USER.md / MEMORY.md often have no frontmatter).
84
+ if (raw.startsWith('---\n')) {
85
+ const close = raw.indexOf('\n---', 4);
86
+ if (close < 0) {
87
+ throw new CanonicalMemoryError(artifact.relPath, 'canonical-read-error', `${artifact.label} at ${artifact.relPath} has unterminated frontmatter`);
88
+ }
89
+ const fm = raw.slice(4, close);
90
+ try {
91
+ yaml.load(fm, { schema: yaml.FAILSAFE_SCHEMA, json: false });
92
+ }
93
+ catch (err) {
94
+ throw new CanonicalMemoryError(artifact.relPath, 'canonical-read-error', `${artifact.label} at ${artifact.relPath}: YAML frontmatter parse error: ${err.message}`);
95
+ }
96
+ }
97
+ }
98
+ async function verifySqliteArtifact(absPath, artifact) {
99
+ let fh;
100
+ try {
101
+ fh = await fs.open(absPath, 'r');
102
+ }
103
+ catch (err) {
104
+ throw new CanonicalMemoryError(artifact.relPath, 'canonical-read-error', `cannot open ${artifact.label} at ${artifact.relPath}: ${err.message}`);
105
+ }
106
+ try {
107
+ const buf = Buffer.alloc(SQLITE_MAGIC.length);
108
+ const { bytesRead } = await fh.read(buf, 0, buf.length, 0);
109
+ if (bytesRead < buf.length || buf.toString('binary') !== SQLITE_MAGIC) {
110
+ throw new CanonicalMemoryError(artifact.relPath, 'canonical-read-error', `${artifact.label} at ${artifact.relPath} is not a valid SQLite file (magic bytes mismatch)`);
111
+ }
112
+ }
113
+ finally {
114
+ await fh.close();
115
+ }
116
+ }
117
+ async function verifyArtifact(projectRoot, artifact) {
118
+ const abs = path.join(projectRoot, artifact.relPath);
119
+ const exists = await pathExists(abs);
120
+ if (!exists) {
121
+ if (!artifact.required)
122
+ return null;
123
+ return {
124
+ primitive: 'memory',
125
+ instanceName: artifact.relPath,
126
+ framework: 'canonical',
127
+ reasonCode: 'canonical-read-error',
128
+ detail: `${artifact.label} at ${artifact.relPath} is missing — required artifact for the Memory primitive`,
129
+ };
130
+ }
131
+ try {
132
+ if (artifact.kind === 'markdown') {
133
+ await verifyMarkdownArtifact(abs, artifact);
134
+ }
135
+ else {
136
+ await verifySqliteArtifact(abs, artifact);
137
+ }
138
+ }
139
+ catch (err) {
140
+ if (err instanceof CanonicalMemoryError) {
141
+ return {
142
+ primitive: 'memory',
143
+ instanceName: artifact.relPath,
144
+ framework: 'canonical',
145
+ reasonCode: err.reasonCode,
146
+ detail: err.message,
147
+ };
148
+ }
149
+ throw err;
150
+ }
151
+ return null;
152
+ }
153
+ /**
154
+ * The Memory primitive has a fixed canonical artifact set, not user-named
155
+ * instances like Skill/Hook. listInstances() returns the artifact paths so the
156
+ * sentinel can iterate verify() over each, but verify() is also defined to
157
+ * accept any artifact path and return ok:true if it's not in the known set
158
+ * (defensive: sentinel might call with stale names after a config change).
159
+ */
160
+ export const memoryParityRule = {
161
+ primitive: 'memory',
162
+ // Memory is framework-agnostic — the verifier ignores framework, but we
163
+ // declare both so the sentinel includes Memory in every framework's matrix
164
+ // cell and surfaces drift consistently.
165
+ frameworks: ['claude-code', 'codex-cli'],
166
+ remediationPolicy: 'flag-only',
167
+ async verify(projectRoot, instanceName) {
168
+ const artifact = ARTIFACTS.find((a) => a.relPath === instanceName);
169
+ if (!artifact) {
170
+ return { ok: true, mismatches: [] };
171
+ }
172
+ const mismatch = await verifyArtifact(projectRoot, artifact);
173
+ return mismatch
174
+ ? { ok: false, mismatches: [mismatch] }
175
+ : { ok: true, mismatches: [] };
176
+ },
177
+ async listInstances(_projectRoot) {
178
+ return ARTIFACTS.map((a) => a.relPath);
179
+ },
180
+ async remediate(_projectRoot, instanceName, _framework) {
181
+ const artifact = ARTIFACTS.find((a) => a.relPath === instanceName);
182
+ const label = artifact?.label ?? instanceName;
183
+ throw new Error(`memory primitive: refused to remediate ${instanceName} (${label}) — ` +
184
+ `Memory artifacts are user/agent-authored and never auto-regenerated. ` +
185
+ `Repair procedure: AGENT.md/USER.md → re-init via \`instar init\`; ` +
186
+ `MEMORY.md → restore from git history or backup; ` +
187
+ `topic-memory.sqlite → restore from backup. ` +
188
+ `See specs/instar-concepts/memory.md for full procedure.`);
189
+ },
190
+ async listOrphans(_projectRoot) {
191
+ // No rendering callsites in v0.1; no orphans possible.
192
+ return [];
193
+ },
194
+ async removeOrphans(_projectRoot, _framework) {
195
+ return [];
196
+ },
197
+ };
198
+ //# sourceMappingURL=memoryParityRule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoryParityRule.js","sourceRoot":"","sources":["../../../../src/providers/parity/rules/memoryParityRule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * toolNameMapping — canonical tool-name vocabulary + per-framework mapping.
3
+ *
4
+ * Specs:
5
+ * - specs/instar-concepts/tool.md
6
+ * - specs/frameworks/claude-code/tools.md
7
+ * - specs/frameworks/codex-cli/tools.md
8
+ *
9
+ * Consumed by other Layer-3 primitives' renderers when they need to map
10
+ * a canonical `allowed-tools` list to framework-native tool names.
11
+ *
12
+ * v0.1: ships the table + lookup helpers. The Skill primitive's `allowed-tools`
13
+ * rendering (the C3 deferral from Skill convergence) wires in next.
14
+ */
15
+ import type { IntelligenceFramework } from '../../core/intelligenceProviderFactory.js';
16
+ export interface ToolNameEntry {
17
+ canonical: string;
18
+ claude: string;
19
+ codex: string;
20
+ }
21
+ export declare const TOOL_NAME_MAPPING: ReadonlyArray<ToolNameEntry>;
22
+ /**
23
+ * Translate one canonical tool name into its framework-native form.
24
+ *
25
+ * Returns null if the canonical name is unknown. Callers should treat null
26
+ * as a hard error (don't silently drop tool restrictions — that widens the
27
+ * permission surface).
28
+ */
29
+ export declare function renderCanonicalToolName(canonical: string, framework: IntelligenceFramework): string | null;
30
+ /**
31
+ * Translate an array of canonical tool names. Returns a parallel array where
32
+ * each element is either the framework-native name or null (for unknown).
33
+ * Useful in renderers that want to surface unknowns to the operator.
34
+ */
35
+ export declare function renderCanonicalToolList(canonicals: ReadonlyArray<string>, framework: IntelligenceFramework): Array<{
36
+ canonical: string;
37
+ native: string | null;
38
+ }>;
39
+ /**
40
+ * List all known canonical tool names (for documentation + validation).
41
+ */
42
+ export declare function listCanonicalToolNames(): ReadonlyArray<string>;
43
+ //# sourceMappingURL=toolNameMapping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolNameMapping.d.ts","sourceRoot":"","sources":["../../../src/providers/parity/toolNameMapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2CAA2C,CAAC;AAEvF,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,iBAAiB,EAAE,aAAa,CAAC,aAAa,CAa1D,CAAC;AAQF;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,qBAAqB,GAC/B,MAAM,GAAG,IAAI,CAWf;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,EACjC,SAAS,EAAE,qBAAqB,GAC/B,KAAK,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAErD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,aAAa,CAAC,MAAM,CAAC,CAE9D"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * toolNameMapping — canonical tool-name vocabulary + per-framework mapping.
3
+ *
4
+ * Specs:
5
+ * - specs/instar-concepts/tool.md
6
+ * - specs/frameworks/claude-code/tools.md
7
+ * - specs/frameworks/codex-cli/tools.md
8
+ *
9
+ * Consumed by other Layer-3 primitives' renderers when they need to map
10
+ * a canonical `allowed-tools` list to framework-native tool names.
11
+ *
12
+ * v0.1: ships the table + lookup helpers. The Skill primitive's `allowed-tools`
13
+ * rendering (the C3 deferral from Skill convergence) wires in next.
14
+ */
15
+ export const TOOL_NAME_MAPPING = [
16
+ { canonical: 'read', claude: 'Read', codex: 'view' },
17
+ { canonical: 'edit', claude: 'Edit', codex: 'apply_patch' },
18
+ { canonical: 'write', claude: 'Write', codex: 'apply_patch' },
19
+ { canonical: 'multi-edit', claude: 'MultiEdit', codex: 'apply_patch' },
20
+ { canonical: 'bash', claude: 'Bash', codex: 'shell' },
21
+ { canonical: 'grep', claude: 'Grep', codex: 'grep' },
22
+ { canonical: 'glob', claude: 'Glob', codex: 'glob' },
23
+ { canonical: 'web-fetch', claude: 'WebFetch', codex: 'read_url' },
24
+ { canonical: 'web-search', claude: 'WebSearch', codex: 'web_search' },
25
+ { canonical: 'task', claude: 'Task', codex: 'multi_agent' },
26
+ { canonical: 'todo-write', claude: 'TodoWrite', codex: 'todo_write' },
27
+ { canonical: 'notebook-edit', claude: 'NotebookEdit', codex: 'notebook_edit' },
28
+ ];
29
+ const CANONICAL_INDEX = new Map(TOOL_NAME_MAPPING.map((e) => [e.canonical, e]));
30
+ const MCP_CANONICAL_RE = /^mcp:([a-z0-9][a-z0-9-]*):([a-z0-9][a-z0-9_-]*)$/i;
31
+ /**
32
+ * Translate one canonical tool name into its framework-native form.
33
+ *
34
+ * Returns null if the canonical name is unknown. Callers should treat null
35
+ * as a hard error (don't silently drop tool restrictions — that widens the
36
+ * permission surface).
37
+ */
38
+ export function renderCanonicalToolName(canonical, framework) {
39
+ const mcp = canonical.match(MCP_CANONICAL_RE);
40
+ if (mcp) {
41
+ const [, server, tool] = mcp;
42
+ return framework === 'claude-code'
43
+ ? `mcp__${server}__${tool}`
44
+ : `mcp.${server}.${tool}`;
45
+ }
46
+ const entry = CANONICAL_INDEX.get(canonical);
47
+ if (!entry)
48
+ return null;
49
+ return framework === 'claude-code' ? entry.claude : entry.codex;
50
+ }
51
+ /**
52
+ * Translate an array of canonical tool names. Returns a parallel array where
53
+ * each element is either the framework-native name or null (for unknown).
54
+ * Useful in renderers that want to surface unknowns to the operator.
55
+ */
56
+ export function renderCanonicalToolList(canonicals, framework) {
57
+ return canonicals.map((c) => ({ canonical: c, native: renderCanonicalToolName(c, framework) }));
58
+ }
59
+ /**
60
+ * List all known canonical tool names (for documentation + validation).
61
+ */
62
+ export function listCanonicalToolNames() {
63
+ return TOOL_NAME_MAPPING.map((e) => e.canonical);
64
+ }
65
+ //# sourceMappingURL=toolNameMapping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolNameMapping.js","sourceRoot":"","sources":["../../../src/providers/parity/toolNameMapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAUH,MAAM,CAAC,MAAM,iBAAiB,GAAiC;IAC7D,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;IACpD,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE;IAC3D,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE;IAC7D,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE;IACtE,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE;IACrD,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;IACpD,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;IACpD,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE;IACjE,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE;IACrE,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE;IAC3D,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE;IACrE,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,eAAe,EAAE;CAC/E,CAAC;AAEF,MAAM,eAAe,GAAG,IAAI,GAAG,CAC7B,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAC/C,CAAC;AAEF,MAAM,gBAAgB,GAAG,mDAAmD,CAAC;AAE7E;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACrC,SAAiB,EACjB,SAAgC;IAEhC,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC9C,IAAI,GAAG,EAAE,CAAC;QACR,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;QAC7B,OAAO,SAAS,KAAK,aAAa;YAChC,CAAC,CAAC,QAAQ,MAAM,KAAK,IAAI,EAAE;YAC3B,CAAC,CAAC,OAAO,MAAM,IAAI,IAAI,EAAE,CAAC;IAC9B,CAAC;IACD,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,SAAS,KAAK,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;AAClE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACrC,UAAiC,EACjC,SAAgC;IAEhC,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,uBAAuB,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;AAClG,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AACnD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "0.28.116",
3
+ "version": "0.28.118",
4
4
  "description": "Persistent autonomy infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-05-19T01:19:09.899Z",
5
- "instarVersion": "0.28.116",
4
+ "generatedAt": "2026-05-19T02:06:04.451Z",
5
+ "instarVersion": "0.28.118",
6
6
  "entryCount": 189,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -1,48 +1,23 @@
1
- # Upgrade Guide — v1.0.2
1
+ # Upgrade Guide — v1.0.3
2
2
 
3
3
  <!-- bump: patch -->
4
4
 
5
5
  ## What Changed
6
6
 
7
- Ships the Skill concept spec, two per-framework rendering specs, and a first concrete parity rule + registry that consumes them. Three categories of work in this release:
8
-
9
- **Spec docs (pure docs, no behavior change on their own):**
10
- - `specs/instar-concepts/skill.md` — formal Layer-3 definition of the Skill primitive, framework-agnostic. Carries `review-convergence` and `approved` frontmatter after a 6-of-7 reviewer convergence round on 2026-05-18 (Grok was not available this session).
11
- - `specs/instar-concepts/skill.eli16.md` — plain-English companion.
12
- - `specs/frameworks/claude-code/skills.md` — Claude rendering contract.
13
- - `specs/frameworks/codex-cli/skills.md` — Codex rendering contract (path + sibling `agents/openai.yaml` shape).
14
-
15
- **Parity rule + registry (new code, opt-in — no auto-run yet):**
16
- - `src/providers/parity/types.ts` — `ParityRule` contract, `ParityMismatch` shape with reason-code enum, framework slot widened to include `'canonical'` for source-layer issues.
17
- - `src/providers/parity/registry.ts` — minimal `ParityRegistry` holding rules; future `FrameworkParitySentinel` will consume this.
18
- - `src/providers/parity/rules/skillParityRule.ts` — first concrete rule. Strict slug grammar at every entry point (path-traversal safe), `js-yaml` FAILSAFE parsing (fail-loud on errors and git-merge-conflict markers), symmetric verify (canonical → renderings AND renderings → canonical for orphans), `x-instar-stamp` field on rendered files to distinguish user-edits from canonical drift, description sanitization (control chars stripped, length capped at 256 chars), refuses to auto-remediate `user-edit-conflict` cases.
19
-
20
- **Hardening surfaced by the convergence round (already applied):**
21
- - C1 path traversal via name — strict slug grammar (`^[a-z0-9][a-z0-9-]{0,63}$`) enforced at every entry point.
22
- - C2 hand-rolled YAML parser — replaced with `js-yaml` (FAILSAFE schema).
23
- - C5 orphan detection — symmetric verify + `listOrphans()` + `removeOrphans()`.
24
- - C7 user-edit destruction — `x-instar-stamp` + `user-edit-conflict` distinction; remediate refuses to overwrite.
25
- - C8 description prompt-injection surface — control-char strip + length cap.
26
- - H1 dir-name vs frontmatter-name authority — dir name authoritative; mismatch is `canonical-read-error`.
27
- - H5 canonical-read error tagging — `framework: 'canonical'` slot added.
28
-
29
- **Deferred (tracked in spec, NOT in this release):**
30
- - Backfill migration that scans existing `.claude/skills/` + `.agents/skills/` and populates canonical at `.instar/skills/`. Without this, the parity rule is a structural no-op on existing agents (their canonical dir is empty). The rule works correctly on agents that have canonical content; unit tests synthesize canonical to cover the happy path.
31
- - `allowed-tools` rendering (Claude frontmatter + Codex `dependencies.tools`) — lands with the Tool primitive.
32
- - Atomic-write (tempfile + rename) — narrows the race window further; single-machine + single-sentinel-pass makes the current write-pattern safe enough for v0.1.
33
- - BackupManager default-includes update for `.instar/skills/` — separate PR.
34
- - CLAUDE.md template update referencing canonical path — separate PR.
7
+ Lands the second Layer-3 functional primitive parity rule: Hook. Follows the Skill prototype's template (canonical-source-of-truth + per-framework rendering + parity rule that keeps them in sync), with hook-specific details (event vocabulary, script executable contract, settings.json/hooks.json merge semantics).
8
+
9
+ v0.1 covers the `session-start` event only. Remaining events are documented in the canonical vocabulary but not yet rendered; extension is mechanical (one entry in the EVENT_NAME_MAPPING table per event).
10
+
11
+ Reuses all of the Skill prototype's hardening: strict slug grammar at every entry point, x-instar-stamp distinguishing user-edits from canonical drift, symmetric verify with orphan detection, remediate refuses on user-edit-conflict. Hook-specific additions: leading-comment stamp format (`# x-instar-stamp: <sha256>`), executable-bit on render (chmod +x), settings.json hook-table merge that preserves non-Instar entries, hooks.json hook-array merge with the same property.
12
+
13
+ Concept spec at `specs/instar-concepts/hook.md` (converged + approved); per-framework specs at `specs/frameworks/claude-code/hooks.md` and `specs/frameworks/codex-cli/hooks.md`.
35
14
 
36
15
  ## What to Tell Your User
37
16
 
38
- - "We've shipped the first piece of a system that keeps your skills in sync across the agent frameworks we support. Right now it works on new skills you create through the canonical path; existing skills won't be touched until a follow-up backfill migration lands."
39
- - "No action needed on your end."
17
+ - "Hooks (small scripts that fire automatically on lifecycle events like session-start) now have the same canonical-source-and-rendering pattern that skills do. Currently the parity rule covers session-start; the other events follow the same shape and land mechanically."
40
18
 
41
19
  ## Summary of New Capabilities
42
20
 
43
21
  | Capability | How to Use |
44
22
  |-----------|-----------|
45
- | Skill parity rule (programmatic) | Available via `import { skillParityRule } from 'src/providers/parity/registry'`. No automatic run yet — sentinel that consumes the registry is a separate follow-up. |
46
- | Strict slug grammar for canonical skills | Enforced automatically; canonical with invalid names rejected at read time. |
47
- | User-edit-conflict detection | Automatic — rendered SKILL.md carries `x-instar-stamp` and verify distinguishes user-edits from canonical drift. |
48
- | Orphan rendered-skill cleanup | Programmatic via `skillParityRule.removeOrphans(projectRoot, framework)`. |
23
+ | Hook parity rule (programmatic) | Available via the parity registry — `getParityRule('hook')`. No automatic run yet (sentinel is a separate follow-up). |
@@ -0,0 +1,23 @@
1
+ # Upgrade Guide — v1.0.3
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ Lands the second Layer-3 functional primitive parity rule: Hook. Follows the Skill prototype's template (canonical-source-of-truth + per-framework rendering + parity rule that keeps them in sync), with hook-specific details (event vocabulary, script executable contract, settings.json/hooks.json merge semantics).
8
+
9
+ v0.1 covers the `session-start` event only. Remaining events are documented in the canonical vocabulary but not yet rendered; extension is mechanical (one entry in the EVENT_NAME_MAPPING table per event).
10
+
11
+ Reuses all of the Skill prototype's hardening: strict slug grammar at every entry point, x-instar-stamp distinguishing user-edits from canonical drift, symmetric verify with orphan detection, remediate refuses on user-edit-conflict. Hook-specific additions: leading-comment stamp format (`# x-instar-stamp: <sha256>`), executable-bit on render (chmod +x), settings.json hook-table merge that preserves non-Instar entries, hooks.json hook-array merge with the same property.
12
+
13
+ Concept spec at `specs/instar-concepts/hook.md` (converged + approved); per-framework specs at `specs/frameworks/claude-code/hooks.md` and `specs/frameworks/codex-cli/hooks.md`.
14
+
15
+ ## What to Tell Your User
16
+
17
+ - "Hooks (small scripts that fire automatically on lifecycle events like session-start) now have the same canonical-source-and-rendering pattern that skills do. Currently the parity rule covers session-start; the other events follow the same shape and land mechanically."
18
+
19
+ ## Summary of New Capabilities
20
+
21
+ | Capability | How to Use |
22
+ |-----------|-----------|
23
+ | Hook parity rule (programmatic) | Available via the parity registry — `getParityRule('hook')`. No automatic run yet (sentinel is a separate follow-up). |
@@ -0,0 +1,35 @@
1
+ # Upgrade Guide — v1.0.4
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ Lands the remaining three Layer-3 functional primitives from the framework-functional-parity rollout: **Agent**, **Tool**, and **Memory**. Batched into a single PR because each ships a relatively small surface (concept spec + framework specs + small code artifact) and they share the same conceptual frame (canonical-source-of-truth + per-framework rendering).
8
+
9
+ **Agent primitive (v0.1):** concept spec + ELI16 + per-framework specs. No parity rule yet — pending Codex subagent surface research. The current Claude implementation pattern (.claude/agents/<name>.md) is documented but not yet wired to a canonical source. Tracked as a v0.2 follow-up; ships now to lock the cross-framework contract.
10
+
11
+ **Tool primitive (v0.1):** concept spec + ELI16 + per-framework specs + TOOL_NAME_MAPPING table as code (src/providers/parity/toolNameMapping.ts). Provides the canonical tool-name vocabulary (read, bash, web-fetch, mcp:server:tool) and helpers (renderCanonicalToolName, renderCanonicalToolList) that other primitives' renderers will consume when wiring allowed-tools fields. The C3 deferral from Skill convergence (Claude allowed-tools frontmatter + Codex dependencies.tools rendering) unblocks here — but the wiring itself happens when Skill v0.2 lands.
12
+
13
+ **Memory primitive (v0.1):** concept spec + ELI16 + per-framework specs + memoryParityRule (verifier-only). Confirms canonical Memory artifacts (.instar/AGENT.md, .instar/USER.md, .instar/MEMORY.md, .instar/state/topic-memory.sqlite) are present, non-empty, and well-formed. Deliberately does NOT auto-remediate — Memory contains agent identity and accumulated learnings, so silent regeneration would erase intentional drift. Corruption is loudly surfaced, repair is human-authorized. Loading canonical Memory into framework system-prompts (CLAUDE.md / AGENTS.md references) is the InstructionFile primitive's responsibility (next, separate spec).
14
+
15
+ Registry now exposes three rules: skill, hook, memory. Agent + Tool deliberately have no registry entry yet (Agent: rule pending; Tool: substrate-bound, ships as code-importable mapping rather than as a per-instance rule).
16
+
17
+ ## What to Tell Your User
18
+
19
+ - "Three more functional primitives now have their canonical contracts in place: Agent (the spec is locked; the parity rule comes after Codex subagent research), Tool (a canonical vocabulary of names like read, bash, web-fetch that maps to each framework's native naming), and Memory (a verifier that confirms your AGENT.md, USER.md, and MEMORY.md files in the instar folder are present and well-formed)."
20
+ - "Memory verification is deliberately non-destructive — if it detects corruption, you'll get a clear repair pointer instead of an auto-overwrite that could wipe your identity drift."
21
+
22
+ ## Summary of New Capabilities
23
+
24
+ | Capability | How to Use |
25
+ |-----------|-----------|
26
+ | Canonical tool-name vocabulary | Import renderCanonicalToolName or renderCanonicalToolList from src/providers/parity/toolNameMapping.js to translate canonical names into framework-native form. |
27
+ | Memory parity rule (programmatic) | Available via the parity registry — getParityRule('memory'). Verifier-only in v0.1; no auto-fix. |
28
+ | Agent + Tool + Memory concept specs | Read specs/instar-concepts/{agent,tool,memory}.md (+ .eli16.md companions) for the cross-framework contracts. |
29
+
30
+ ## Deferred (Tracked Follow-ups)
31
+
32
+ - Agent parity rule (pending Codex subagent surface research).
33
+ - Tool: wire TOOL_NAME_MAPPING into Skill v0.2's allowed-tools rendering (resolves the C3 deferral from Skill convergence).
34
+ - Memory: schema validation beyond YAML-parse + body-non-empty (e.g. AGENT.md required identity sections).
35
+ - InstructionFile primitive — the loading-vehicle for canonical Memory into framework system-prompts. Separate spec, next.
@@ -0,0 +1,74 @@
1
+ # Side-Effects Review — Hook primitive + parity rule
2
+
3
+ **Version / slug:** `feat-hook-primitive`
4
+ **Date:** 2026-05-19
5
+ **Author:** Echo (autonomous mode, hybrid C)
6
+
7
+ ## Summary of the change
8
+
9
+ Lands the Hook primitive prototype: concept spec, two framework rendering specs, parity rule registered in the existing registry. v0.1 covers the `session-start` event only; remaining events tracked as mechanical extensions of the EVENT_NAME_MAPPING table.
10
+
11
+ **Files changed (specs):**
12
+ - specs/instar-concepts/hook.md (new, converged + approved per pre-auth)
13
+ - specs/instar-concepts/hook.eli16.md (new)
14
+ - specs/frameworks/claude-code/hooks.md (new)
15
+ - specs/frameworks/codex-cli/hooks.md (new)
16
+ - docs/specs/reports/hook-concept-convergence.md (new)
17
+
18
+ **Files changed (source):**
19
+ - src/providers/parity/rules/hookParityRule.ts (new)
20
+ - src/providers/parity/registry.ts (hookParityRule registered)
21
+
22
+ **Files changed (tests):**
23
+ - tests/unit/providers/parity/hookParityRule.test.ts (new — 22 tests)
24
+ - tests/unit/providers/parity/registry.test.ts (updated for 2-rule registry)
25
+
26
+ **Files changed (release notes):**
27
+ - upgrades/NEXT.md (new)
28
+ - package.json (version bump)
29
+
30
+ ## Decision-point inventory
31
+
32
+ - **Canonical path location**: `.instar/hooks/<event>/<name>.<ext>` — mirrors `.instar/skills/<name>/` pattern.
33
+ - **Event vocabulary**: canonical kebab-case (`session-start`, `pre-compact`, ...); per-framework mapping in code.
34
+ - **v0.1 event scope**: session-start only — proves the pattern; extension is mechanical.
35
+ - **Script extension allowlist**: .sh, .js, .mjs, .cjs, .ts.
36
+ - **Stamp format**: leading `# x-instar-stamp: <sha256>` comment line; sha256 of canonical body (with stamp stripped) is the stamp value.
37
+ - **settings.json merge semantics**: only manages entries whose script paths live under `.claude/hooks/<canonical-event>/`. Other user-added hook entries are preserved.
38
+ - **hooks.json merge semantics**: only manages entries whose script paths live under `.agent/openai/hooks/<canonical-event>/`. Other entries preserved.
39
+ - **Migration backfill**: DEFERRED (same shape as Skill backfill; one follow-up PR can cover both primitives).
40
+
41
+ ---
42
+
43
+ ## 1–7. Analysis
44
+
45
+ ### Over-block
46
+ None. Parity rule is opt-in; no auto-run point.
47
+
48
+ ### Under-block
49
+ Same as Skill v0.1: no atomic writes, no executable-bit explicit verification (set on render, presumed to persist), no backfill migration (existing agents' canonical empty). All tracked.
50
+
51
+ ### Level-of-abstraction fit
52
+ Correct. hookParityRule lives alongside skillParityRule in `src/providers/parity/rules/`; shares the same `ParityRule` contract. Event-name mapping is a per-rule concern; types layer doesn't need changes.
53
+
54
+ ### Signal vs authority
55
+ Same as Skill — rule emits signals (mismatches), sentinel (future) is the authority.
56
+
57
+ ### Interactions
58
+ - **`refreshHooksAndSettings()`** — currently installs hooks via `installInstarHooks()`; not touched here. Future migration story covers the canonical-vs-direct-install reconciliation.
59
+ - **`migrateHooks()` in PostUpdateMigrator** — currently always-overwrites built-in hooks under `.claude/hooks/instar/`. This continues to work; the parity rule is opt-in and doesn't conflict.
60
+
61
+ ### External surfaces
62
+ - Public API: new `hookParityRule` export from `src/providers/parity/`.
63
+ - Process surface: only when remediate() invoked explicitly — writes script + chmod + settings/config merge.
64
+
65
+ ### Rollback cost
66
+ Trivial. Revert removes the rule + registry entry; no on-disk state changed by this PR alone.
67
+
68
+ ## Tests
69
+ 22 new tests in `hookParityRule.test.ts` covering slug grammar (path traversal, capitals, spaces rejected; only v0.1 events listed), canonical-read errors tagged with framework: 'canonical', remediate (claude script + settings.json entry, codex script + hooks.json entry, executable bit, settings preservation, idempotent), user-edit-conflict via stamp, orphan detection + removal, rule metadata. Plus 4 updated registry tests for 2-rule registry.
70
+
71
+ Total parity tests: 53/53 passing (27 Skill + 22 Hook + 4 registry). Typecheck clean.
72
+
73
+ ## Evidence
74
+ Abbreviated convergence — see docs/specs/reports/hook-concept-convergence.md. Bug found during test development (stamp-strip regex was leaving trailing whitespace, causing post-render verify to flag user-edit-conflict on freshly-rendered files) — fixed by tightening STAMP_COMMENT_STRIP_RE to consume the trailing newline.
@@ -0,0 +1,105 @@
1
+ # Side-Effects Review — Agent + Tool + Memory primitives (batched)
2
+
3
+ **Version / slug:** `feat-primitives-batch`
4
+ **Date:** 2026-05-19
5
+ **Author:** Echo (autonomous mode, hybrid C)
6
+
7
+ ## Summary of the change
8
+
9
+ Lands the remaining three Layer-3 functional primitive specs from the framework-functional-parity rollout as one batched PR: **Agent**, **Tool**, **Memory**. Each ships a small surface (concept spec + framework specs + small code artifact where applicable) and they share the same conceptual frame, so batching avoids three near-identical PR review cycles.
10
+
11
+ **Files changed (specs):**
12
+ - specs/instar-concepts/agent.md (new, converged + approved per pre-auth)
13
+ - specs/instar-concepts/agent.eli16.md (new)
14
+ - specs/instar-concepts/tool.md (new, converged + approved per pre-auth)
15
+ - specs/instar-concepts/tool.eli16.md (new)
16
+ - specs/instar-concepts/memory.md (new, converged + approved per pre-auth)
17
+ - specs/instar-concepts/memory.eli16.md (new)
18
+ - specs/frameworks/claude-code/agents.md (new)
19
+ - specs/frameworks/claude-code/tools.md (new)
20
+ - specs/frameworks/claude-code/memory.md (new)
21
+ - specs/frameworks/codex-cli/agents.md (new)
22
+ - specs/frameworks/codex-cli/tools.md (new)
23
+ - specs/frameworks/codex-cli/memory.md (new)
24
+ - docs/specs/reports/agent-concept-convergence.md (new)
25
+ - docs/specs/reports/tool-concept-convergence.md (new)
26
+ - docs/specs/reports/memory-concept-convergence.md (new)
27
+
28
+ **Files changed (source):**
29
+ - src/providers/parity/toolNameMapping.ts (new — TOOL_NAME_MAPPING table + renderers)
30
+ - src/providers/parity/rules/memoryParityRule.ts (new — verifier-only)
31
+ - src/providers/parity/registry.ts (memoryParityRule registered)
32
+
33
+ **Files changed (tests):**
34
+ - tests/unit/providers/parity/toolNameMapping.test.ts (new — 21 tests)
35
+ - tests/unit/providers/parity/memoryParityRule.test.ts (new — 18 tests)
36
+ - tests/unit/providers/parity/registry.test.ts (updated for 3-rule registry)
37
+
38
+ **Files changed (release notes):**
39
+ - upgrades/NEXT.md (rewritten for v1.0.4)
40
+ - package.json (version bump 1.0.3 → 1.0.4)
41
+
42
+ ## Decision-point inventory
43
+
44
+ - **Batching three primitives in one PR**: justified by shared frame + small surfaces; each gets its own concept spec + ELI16 + framework specs + convergence report, so review traceability per primitive is preserved.
45
+ - **Agent primitive ships without a parity rule**: deliberate — Codex subagent surface (whether/how subagent spawning is exposed) needs live research. Spec ships now to lock the cross-framework contract; rule lands when research completes. Tracked in spec's "v0.1 deferred items" section.
46
+ - **Tool primitive ships TOOL_NAME_MAPPING as code, not as a per-instance rule**: Tools are framework-provided, not user-authored. The "instance" of a Tool isn't something to render; the cross-framework concern is the vocabulary, which lives in a shared mapping table. No registry entry — other primitives' renderers import the helpers directly.
47
+ - **Memory primitive uses flag-only remediation policy**: Memory contains agent identity + learnings. Silent regeneration would erase intentional drift. remediate() throws with a documented repair procedure; corruption surfaces loudly via verify().
48
+ - **SQLite verification depth**: v0.1 checks magic bytes only (cheap, fail-loud). Full schema validation deferred — the topic-memory.sqlite schema is owned by its own substrate primitive.
49
+ - **Memory artifact set is fixed at four**: AGENT.md, USER.md, MEMORY.md (required); topic-memory.sqlite (optional). Other `.instar/` files (config.json, jobs.json, state/*) are NOT Memory — they belong to other primitives (Config, Job, State).
50
+ - **MCP canonical syntax `mcp:server:tool`**: matches the spec convention. Renders to `mcp__server__tool` (Claude) / `mcp.server.tool` (Codex).
51
+
52
+ ## Over-block / under-block analysis
53
+
54
+ **Over-block risk (Tool):** if a canonical tool name isn't in TOOL_NAME_MAPPING, `renderCanonicalToolName` returns null. Callers are documented to treat this as a hard error (don't silently drop, that widens the permission surface). v0.1 has no callers yet; risk surfaces when Skill v0.2 wires `allowed-tools`.
55
+
56
+ **Under-block risk (Memory):** the verifier checks presence + parseability, not semantic correctness. A syntactically-valid but content-wrong AGENT.md (e.g. missing the identity section) passes verify in v0.1. Acceptable for v0.1 — schema validation deferred. Under-block is loud (logged at sentinel scan time) rather than silent.
57
+
58
+ **Over-block risk (Memory remediate):** remediate always throws. If a future caller expects auto-fix as the default, this throws instead. Behavior is documented in the spec + in the rule's docstring + in the convergence report. Sentinel-level policy is `flag-only`, so the sentinel will never call remediate on Memory.
59
+
60
+ ## Level-of-abstraction fit
61
+
62
+ - Tool name mapping: pure data table + tiny renderer. No state, no I/O. Appropriate for the substrate-bound primitive.
63
+ - Memory verifier: pure file I/O + YAML parse + magic-byte check. No mutation. No state.
64
+ - Both rules sit alongside skillParityRule + hookParityRule at the right abstraction layer (per-primitive verifiers consumed by the future FrameworkParitySentinel).
65
+
66
+ ## Signal-vs-authority compliance
67
+
68
+ - toolNameMapping returns null for unknown — that's a signal to the caller, never a silent permission grant. Caller is the authority.
69
+ - memoryParityRule.verify returns structured mismatches — signal. The sentinel (not yet built) is the authority that decides what to do with the signal.
70
+ - memoryParityRule.remediate refuses unconditionally — this is the rule asserting authority over its own scope (Memory is sacrosanct), consistent with the "Memory is human-authorized" principle in the spec.
71
+
72
+ ## Interaction surface
73
+
74
+ - Registry: third rule added (`memory`). No interactions with skill or hook rules.
75
+ - toolNameMapping: importable from `src/providers/parity/toolNameMapping.js`. No consumers in v0.1 (intentional — Skill v0.2 wires it).
76
+ - No changes to existing rules, scaffold, hooks, settings.json, or migration paths.
77
+ - No new HTTP routes. No new server-side state.
78
+
79
+ ## Rollback cost
80
+
81
+ - Pure-add change. Revert is `git revert` on the merge commit. No data migration, no on-disk state created on existing agents, no settings.json mutations, no schema changes.
82
+ - Worst-case bug in Memory verifier: false-positive on existing agents that have unusual but valid Memory artifacts. Mitigation: rule is consumed only by the sentinel (not yet built), so v1.0.4 has zero runtime impact on agents.
83
+ - Worst-case bug in toolNameMapping: a canonical name renders wrong. Mitigation: no callers in v0.1; bug is caught when Skill v0.2 wires in.
84
+
85
+ ## Test coverage
86
+
87
+ - toolNameMapping: 21 tests covering every entry's cross-framework resolution, MCP prefix handling, unknown-name null return, list ordering, mapping table invariants.
88
+ - memoryParityRule: 18 tests covering required vs optional artifacts, presence checks, empty content, YAML parse errors, unterminated frontmatter, SQLite magic byte verification, remediate-refuses behavior, orphan-empty behavior, rule metadata.
89
+ - registry: updated to assert the 3-rule shape.
90
+
91
+ ## Documentation
92
+
93
+ - 3 concept specs (Agent, Tool, Memory).
94
+ - 3 ELI16 companions.
95
+ - 6 framework specs (3 × 2 frameworks).
96
+ - 3 convergence reports.
97
+ - NEXT.md updated.
98
+
99
+ ## Deferred (tracked, not silent)
100
+
101
+ - Agent parity rule (pending Codex subagent surface research) — documented in `specs/instar-concepts/agent.md` "v0.1 deferred items".
102
+ - Skill v0.2 wiring of TOOL_NAME_MAPPING into `allowed-tools` — documented in `specs/instar-concepts/tool.md` + NEXT.md.
103
+ - Memory schema validation beyond YAML-parse — documented in `specs/instar-concepts/memory.md` "v0.1 deferred items".
104
+ - InstructionFile primitive (the loading-vehicle for canonical Memory into framework system prompts) — separate primitive, next.
105
+ - Migration backfill (Memory verifier is no-op on existing agents because canonical Memory already lives in `.instar/` — no migration needed).