0xray 4.0.21 → 4.0.22

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.
Files changed (56) hide show
  1. package/.grok-plugin/plugin.json +1 -1
  2. package/.opencode/skills/hermes-agent/SKILL.md +1 -1
  3. package/.opencode/skills/orchestrator/SKILL.md +22 -0
  4. package/.xray/features.json +1 -1
  5. package/AGENTS-consumer.md +11 -11
  6. package/AGENTS.md +8 -8
  7. package/README.md +27 -27
  8. package/SKILLS.md +1 -1
  9. package/dist/AGENTS.md +8 -8
  10. package/dist/CHANGELOG.md +75 -0
  11. package/dist/README.md +27 -27
  12. package/dist/SKILLS.md +1 -1
  13. package/dist/governance/governance-core.d.ts +2 -1
  14. package/dist/governance/governance-core.js +20 -11
  15. package/dist/governance/governance-service.js +9 -7
  16. package/dist/inference/inference-cycle.js +22 -5
  17. package/dist/integrations/cursor/README.md +1 -1
  18. package/dist/integrations/cursor/hooks/cursor-hook-utils.js +19 -0
  19. package/dist/integrations/cursor/hooks/pre-tool-use.js +2 -1
  20. package/dist/integrations/hooks/station-hook-runtime.cjs +90 -30
  21. package/dist/integrations/hooks/station-hook-runtime.mjs +1 -0
  22. package/dist/integrations/hooks/station-memory-ingest.mjs +4 -2
  23. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  24. package/dist/memory-routing/provider-loader.d.ts +3 -1
  25. package/dist/memory-routing/provider-loader.js +3 -1
  26. package/dist/nucleus/thin-dispatch.js +11 -2
  27. package/dist/processors/implementations/agents-md-validation-processor.js +1 -1
  28. package/dist/skills/hermes-agent/SKILL.md +1 -1
  29. package/dist/skills/orchestrator/SKILL.md +22 -0
  30. package/package.json +2 -2
  31. package/scripts/foundry/CHANGELOG.md +2 -0
  32. package/scripts/foundry/README.md +2 -2
  33. package/scripts/foundry/cli.mjs +1 -1
  34. package/scripts/foundry/inspect.mjs +160 -5
  35. package/scripts/foundry/plant/skills/inspect/SKILL.md +2 -0
  36. package/scripts/foundry/validate-release-docs.mjs +24 -20
  37. package/scripts/foundry/version-manager.mjs +127 -7
  38. package/scripts/mjs/test-consumer-readiness.mjs +10 -3
  39. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  40. package/src/skills/hermes-agent/SKILL.md +1 -1
  41. package/src/skills/orchestrator/SKILL.md +22 -0
  42. package/vendor/@0xray/repertoire/data/stack-overlay.json +154 -1
  43. package/vendor/@0xray/repertoire/dist/RepertoireService.js +7 -6
  44. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js +10 -11
  45. package/vendor/@0xray/repertoire/dist/paths.js +66 -4
  46. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js +54 -14
  47. package/xray/agents_template.md +10 -5
  48. package/xray/features.json +1 -1
  49. package/dist/mcps/shared/prompt-security-validator.d.ts +0 -44
  50. package/dist/mcps/shared/prompt-security-validator.js +0 -148
  51. package/dist/mcps/shared/security-scanner.d.ts +0 -119
  52. package/dist/mcps/shared/security-scanner.js +0 -429
  53. package/dist/scripts/activate-kernel-pipeline.d.ts +0 -7
  54. package/dist/scripts/activate-kernel-pipeline.js +0 -101
  55. package/src/mcps/shared/prompt-security-validator.ts +0 -199
  56. package/src/mcps/shared/security-scanner.ts +0 -599
@@ -17,6 +17,7 @@ Mill **does** these checks (`npx @0xray/foundry inspect`). Mint fails on a costu
17
17
  4. **CI.** GitHub mill CI report (`.opencode/logs/ci-cd-monitor-report.json`), not a green check in chat. No token is a skip, not a pass invented.
18
18
  5. **Live PUT.** HTTP 200 on the npm `.tgz`. Metadata without a tarball is not shipped.
19
19
  6. **Isolated HOME.** Last-mile dest is project `.grok/plugins/0xray`. Isolated HOME may wear `$HOME/.grok/plugins/0xray` and must not write machine `~/.grok/plugins/0xray`. Shared HOME (passwd `$HOME`) does not last-wins clobber the machine plugin. Mill compares `$HOME` to the passwd home (`os.userInfo().homedir`), not `os.homedir()` (which follows `$HOME`).
20
+ 7. **Harness (observational).** Bound Cursor/Grok hooks, fastened Repertoire organ, Station card. Profiles: `bare` / `suited` / `partial`. Does **not** fasten. `--go` prints a forge GO receipt so cloud return is mill, not chat. `--require-harness=bare|suited|partial` fails when the live plant disagrees.
20
21
 
21
22
  ## How to work
22
23
 
@@ -28,6 +29,7 @@ Mill **does** these checks (`npx @0xray/foundry inspect`). Mint fails on a costu
28
29
  ```bash
29
30
  npx @0xray/foundry mint
30
31
  npx @0xray/foundry inspect
32
+ npx @0xray/foundry inspect --skip-live --go --require-harness=bare
31
33
  npx @0xray/foundry ci --report
32
34
  ```
33
35
 
@@ -18,6 +18,7 @@ import {
18
18
  DOCS_SITE_HEADER_FILES,
19
19
  getFrameworkCounts,
20
20
  getReleaseArtifactPaths,
21
+ patchRefStripRelPaths,
21
22
  } from './version-manager.mjs';
22
23
  import { isXrayExoRepo, resolveMillRoot } from './mill-root.mjs';
23
24
 
@@ -31,30 +32,13 @@ function kernelLineHasPatchStamp(content) {
31
32
 
32
33
  /**
33
34
  * Present-tense cut pins. Keep: package.json, CHANGELOG, stamped JSON,
34
- * features-since headings. Rip from guides, Station, OP-PROC.
35
+ * features-since headings. The check set is the stamper set: shipped guides
36
+ * and shipped OP-PROC, including the mill-gate skill. Seat memory is not in it.
35
37
  */
36
38
  const PRESENT_TENSE_PATCH_RE =
37
39
  /This cut is|Do not republish \d+\.\d+\.\d+|0xray@\d+\.\d+\.\d+|npm is \*\*\d+\.\d+\.\d+\*\*|Product \*\*\d+\.\d+\.\d+\*\* is on npm/;
38
40
 
39
- const NO_PRESENT_TENSE_PATCH = [
40
- 'README.md',
41
- 'AGENTS.md',
42
- 'AGENTS-consumer.md',
43
- 'SKILLS.md',
44
- 'llms.txt',
45
- 'docs-site/docs/architecture/v4-now.md',
46
- 'docs-site/docs/architecture/v4-vision.md',
47
- 'docs-site/docs/guides/memory-wake.md',
48
- 'docs-site/docs/guides/memory-routing.md',
49
- 'docs-site/docs/guides/repertoire.md',
50
- 'docs-site/docs/guides/getting-started.md',
51
- 'docs-site/docs/guides/integrations.md',
52
- 'docs-site/docs/guides/consumer-migration.md',
53
- 'docs-site/docs/mcp/README.md',
54
- 'grok-bot/ops/LEAD-CADENCE.md',
55
- 'grok-bot/AGENTS.md',
56
- 'src/skills/orchestrator/SKILL.md',
57
- ];
41
+ const NO_PRESENT_TENSE_PATCH = patchRefStripRelPaths();
58
42
 
59
43
  function presentTensePatchPinErrors(content) {
60
44
  if (PRESENT_TENSE_PATCH_RE.test(content)) {
@@ -70,6 +54,7 @@ const REQUIRED_GUIDES = [
70
54
  'docs-site/docs/guides/memory-routing.md',
71
55
  'docs-site/docs/guides/aside-context.md',
72
56
  'docs-site/docs/guides/repertoire.md',
57
+ 'docs-site/docs/guides/station-vs-repertoire.md',
73
58
  'docs-site/docs/guides/integrations.md',
74
59
  'docs-site/docs/guides/consumer-migration.md',
75
60
  'docs-site/docs/mcp/README.md',
@@ -87,6 +72,18 @@ function millLawLagErrors(content) {
87
72
  return errors;
88
73
  }
89
74
 
75
+ /** Opt-out is features.json, not package-absence. */
76
+ function repertoireOptOutErrors(content) {
77
+ const errors = [];
78
+ if (/\(no @0xray\/repertoire\)/.test(content)) {
79
+ errors.push('opt-out must not teach package-absence (no @0xray/repertoire)');
80
+ }
81
+ if (/memory_routing stays off/.test(content)) {
82
+ errors.push('opt-out must not teach memory_routing stays off on npm install');
83
+ }
84
+ return errors;
85
+ }
86
+
90
87
  function millPlantMentionErrors(content) {
91
88
  if (!content.includes('mill') || !content.includes('inspect')) {
92
89
  return ['missing mill+inspect plant'];
@@ -357,6 +354,13 @@ export function validateReleaseDocs(rootDir = resolveMillRoot()) {
357
354
  }
358
355
  }
359
356
 
357
+ const stationVs = readFile(rootDir, 'docs-site/docs/guides/station-vs-repertoire.md');
358
+ if (stationVs) {
359
+ for (const e of repertoireOptOutErrors(stationVs)) {
360
+ errors.push(`docs-site/docs/guides/station-vs-repertoire.md: ${e}`);
361
+ }
362
+ }
363
+
360
364
  const docusaurus = readFile(rootDir, 'docs-site/docusaurus.config.ts');
361
365
  if (!docusaurus) {
362
366
  errors.push('docs-site/docusaurus.config.ts: missing');
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Foundry stamper — JSON version fields + CHANGELOG only.
4
+ * Foundry stamper — JSON version fields, CHANGELOG, and the present-tense patch-pin strip.
5
5
  *
6
- * Does NOT bump package.json (reconcile-version.mjs is the one bumper).
7
- * Does NOT rewrite markdown prose (validate-release-docs.mjs verifies).
6
+ * Does NOT bump package.json (reconcile-version.mjs advances the next cut).
7
+ * Does NOT publish.
8
+ * Strips every present-tense patch pin from shipped guides and shipped OP-PROC.
9
+ * Does NOT edit Station, NOTES, dest, or node_modules.
8
10
  *
9
11
  * Usage:
10
12
  * npx @0xray/foundry stamp
@@ -285,7 +287,7 @@ function updateChangelog(newVersion, changeDescription) {
285
287
  console.log(`✅ Updated CHANGELOG.md`);
286
288
  }
287
289
 
288
- /** Markdown prose is verified, not rewritten. */
290
+ /** Kernel headers stay era text. Patch refs are stripped from the shipped set below. */
289
291
 
290
292
  export const DOCS_SITE_HEADER_FILES = [
291
293
  'docs-site/docs/index.md',
@@ -328,6 +330,7 @@ function updateJsonVersionField(relPath, newVersion) {
328
330
 
329
331
  function updateFeaturesJsonVersion(newVersion) {
330
332
  updateJsonVersionField('xray/features.json', newVersion);
333
+ updateJsonVersionField('.xray/features.json', newVersion);
331
334
  }
332
335
 
333
336
  function updateOpenclawPluginVersion(newVersion) {
@@ -339,6 +342,117 @@ function getCurrentVersion() {
339
342
  return pkg.version;
340
343
  }
341
344
 
345
+ /**
346
+ * Shipped guides whose present-tense patch ref the stamper removes.
347
+ * Historical headings (features-since) stay. Seat memory is not in this list.
348
+ */
349
+ export const SHIPPED_GUIDE_STRIP_FILES = [
350
+ 'README.md',
351
+ 'AGENTS.md',
352
+ 'AGENTS-consumer.md',
353
+ 'SKILLS.md',
354
+ 'llms.txt',
355
+ 'docs-site/docs/index.md',
356
+ 'docs-site/docs/introduction.md',
357
+ 'docs-site/docs/full-reference.md',
358
+ 'docs-site/docs/architecture/v4-now.md',
359
+ 'docs-site/docs/architecture/v4-vision.md',
360
+ 'docs-site/docs/guides/memory-wake.md',
361
+ 'docs-site/docs/guides/memory-routing.md',
362
+ 'docs-site/docs/guides/repertoire.md',
363
+ 'docs-site/docs/guides/station-vs-repertoire.md',
364
+ 'docs-site/docs/guides/getting-started.md',
365
+ 'docs-site/docs/guides/integrations.md',
366
+ 'docs-site/docs/guides/consumer-migration.md',
367
+ 'docs-site/docs/guides/aside-context.md',
368
+ 'docs-site/docs/mcp/README.md',
369
+ 'docs-site/docs/agents/README.md',
370
+ 'grok-bot/AGENTS.md',
371
+ ];
372
+
373
+ /** Shipped OP-PROC. Station, NOTES, and dest are not procedure files the stamper may edit. */
374
+ export const SHIPPED_OP_PROC_STRIP_FILES = [
375
+ 'grok-bot/ops/LEAD-CADENCE.md',
376
+ 'src/skills/orchestrator/SKILL.md',
377
+ 'grok-bot/skills/ship-ready-mill-gate/SKILL.md',
378
+ ];
379
+
380
+ export function patchRefStripRelPaths() {
381
+ return [...SHIPPED_GUIDE_STRIP_FILES, ...SHIPPED_OP_PROC_STRIP_FILES];
382
+ }
383
+
384
+ /** Seat-local memory and installed trees. The stamper never writes these. */
385
+ export function isPatchRefStripRefused(relPath) {
386
+ const norm = path.posix.normalize(String(relPath).replace(/\\/g, '/')).replace(/^\.\//, '');
387
+ const parts = norm.split('/').filter((part) => part !== '' && part !== '.');
388
+ if (parts[parts.length - 1] === 'NOTES.md') return true;
389
+ if (parts.includes('node_modules')) return true;
390
+ const xrayAt = parts.indexOf('.xray');
391
+ if (xrayAt !== -1 && parts[xrayAt + 1] === 'state') return true;
392
+ return false;
393
+ }
394
+
395
+ /**
396
+ * Remove every present-tense patch pin from prose. Stamps in CHANGELOG
397
+ * headings stay, because this does not match `## [x.y.z]`.
398
+ * `version` must be semver or the call is a no-op. The pins removed are
399
+ * not limited to that version.
400
+ * @param {string} content
401
+ * @param {string} version
402
+ */
403
+ export function stripPatchRefText(content, version) {
404
+ if (typeof version !== 'string' || !/^\d+\.\d+\.\d+$/.test(version)) return content;
405
+ const semver = '\\d+\\.\\d+\\.\\d+';
406
+ const patterns = [
407
+ new RegExp(`0xray@${semver}\\b`, 'g'),
408
+ new RegExp(`npm is \\*\\*${semver}\\*\\*`, 'g'),
409
+ new RegExp(`Product \\*\\*${semver}\\*\\* is on npm`, 'g'),
410
+ new RegExp(`Do not republish ${semver}\\b`, 'g'),
411
+ new RegExp(`This cut is \\*\\*${semver}\\*\\*`, 'g'),
412
+ new RegExp(`This cut is ${semver}\\b`, 'g'),
413
+ ];
414
+ let next = content;
415
+ for (const re of patterns) next = next.replace(re, '');
416
+ return next.replace(/[ \t]{2,}/g, ' ');
417
+ }
418
+
419
+ /**
420
+ * @param {string} baseDir
421
+ * @param {string} rel
422
+ * @param {string} version
423
+ * @returns {boolean} true when the file changed
424
+ */
425
+ export function stripPatchRefFile(baseDir, rel, version) {
426
+ if (isPatchRefStripRefused(rel)) return false;
427
+ const full = path.resolve(baseDir, rel);
428
+ const relToBase = path.relative(baseDir, full);
429
+ if (relToBase.startsWith('..') || path.isAbsolute(relToBase)) return false;
430
+ if (isPatchRefStripRefused(relToBase.split(path.sep).join('/'))) return false;
431
+ if (!fs.existsSync(full) || !fs.statSync(full).isFile()) return false;
432
+ const before = fs.readFileSync(full, 'utf8');
433
+ const after = stripPatchRefText(before, version);
434
+ if (after === before) return false;
435
+ fs.writeFileSync(full, after);
436
+ return true;
437
+ }
438
+
439
+ /**
440
+ * Strip every present-tense patch pin from shipped guides and shipped OP-PROC under baseDir.
441
+ * `version` must be semver or the call is a no-op.
442
+ * Refuses `.xray/state/**`, NOTES, dest curated_signals.json, and node_modules.
443
+ * Does not bump. Does not publish.
444
+ * @param {string} baseDir
445
+ * @param {string} version
446
+ * @returns {string[]} relative paths that changed
447
+ */
448
+ export function stripLivePatchRefs(baseDir, version) {
449
+ const changed = [];
450
+ for (const rel of patchRefStripRelPaths()) {
451
+ if (stripPatchRefFile(baseDir, rel, version)) changed.push(rel);
452
+ }
453
+ return changed;
454
+ }
455
+
342
456
  /** Paths written by release artifact updates (existing files only). */
343
457
  export function getReleaseArtifactPaths(baseDir = resolveMillRoot()) {
344
458
  const candidates = [
@@ -374,16 +488,20 @@ export function getReleaseArtifactPaths(baseDir = resolveMillRoot()) {
374
488
  return candidates.filter((rel) => fs.existsSync(path.join(baseDir, rel)));
375
489
  }
376
490
 
377
- /** Update CHANGELOG + README + AGENTS (+ consumer/docs) for current package.json version (no bump). */
491
+ /** Stamp JSON + CHANGELOG, then strip every present-tense patch pin from shipped guides and OP-PROC. No bump. No publish. */
378
492
  function updateReleaseArtifactsOnly(changeDescription = '') {
379
493
  const current = getCurrentVersion();
380
494
  const counts = getFrameworkCounts();
381
- process.stdout.write(`Release artifacts for v${current} (JSON + CHANGELOG only)\n`);
495
+ process.stdout.write(`Release artifacts for v${current} (JSON + CHANGELOG; strip shipped guides and OP-PROC)\n`);
382
496
  process.stdout.write(`counts: ${counts.agents} agents, ${counts.mcps} MCPs, ${counts.skills} skills\n`);
383
497
  updateChangelog(current, changeDescription);
384
498
  updatePluginJsonVersion(current);
385
499
  updateFeaturesJsonVersion(current);
386
500
  updateOpenclawPluginVersion(current);
501
+ const stripped = stripLivePatchRefs(rootDir, current);
502
+ if (stripped.length > 0) {
503
+ process.stdout.write(`Stripped present-tense patch pins from ${stripped.join(', ')}\n`);
504
+ }
387
505
  runReleaseDocsValidation();
388
506
  process.stdout.write(`Release artifacts updated for v${current}\n`);
389
507
  }
@@ -415,7 +533,9 @@ function main() {
415
533
  if (args.includes('--help') || args.includes('-h')) {
416
534
  process.stdout.write(`Current version: ${getCurrentVersion()}\n`);
417
535
  process.stdout.write('Usage: npx @0xray/foundry stamp\n');
418
- process.stdout.write('Bump is refused. Use reconcile-version.mjs --apply, then this stamper.\n');
536
+ process.stdout.write('Bump is refused. Reconcile advances the next cut, then this stamper runs.\n');
537
+ process.stdout.write('The stamper strips every present-tense patch pin from shipped guides and shipped OP-PROC.\n');
538
+ process.stdout.write('It does not edit Station, NOTES, dest, or node_modules. It does not publish.\n');
419
539
  process.exit(0);
420
540
  }
421
541
 
@@ -17,9 +17,16 @@ const __dirname = path.dirname(__filename);
17
17
  class ConsumerReadinessCheck {
18
18
  constructor() {
19
19
  this.checks = [];
20
- // Check if we're running from a consumer environment (not the source directory)
20
+ // Mill = this repo (package name 0xray + src/). Consumer = installed tarball.
21
21
  const cwd = process.cwd();
22
- this.isConsumerEnvironment = !cwd.includes("dev/stringray") && cwd.includes("dev/jelly");
22
+ let isDevMill = false;
23
+ try {
24
+ const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf8"));
25
+ isDevMill = pkg.name === "0xray" && fs.existsSync(path.join(cwd, "src"));
26
+ } catch {
27
+ isDevMill = false;
28
+ }
29
+ this.isConsumerEnvironment = !isDevMill;
23
30
  }
24
31
 
25
32
  async runChecks() {
@@ -78,7 +85,7 @@ class ConsumerReadinessCheck {
78
85
  if (fs.existsSync(".mcp.json")) {
79
86
  const mcpConfig = JSON.parse(fs.readFileSync(".mcp.json", "utf8"));
80
87
  const serverCount = Object.keys(mcpConfig.mcpServers || {}).length;
81
- const hasServers = serverCount >= 16; // At least 16 servers
88
+ const hasServers = serverCount >= 7; // consumer mill: 7 0xRay MCP servers
82
89
 
83
90
  this.checks.push({
84
91
  name: "MCP server configuration",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xray-pre-tool",
3
- "version": "4.0.21",
3
+ "version": "4.0.22",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "0xRay constitution + temperament PreToolUse for OpenClaw",
@@ -64,7 +64,7 @@ Returns: pass/fail with per-file results and violations.
64
64
 
65
65
  ### xray_codex_check
66
66
 
67
- Validate code against the 60-term Universal Development Codex. Checks error-handling, type-safety, performance, security, architecture.
67
+ Validate code against the 69-term Universal Development Codex. Checks error-handling, type-safety, performance, security, architecture.
68
68
 
69
69
  ```
70
70
  xray_codex_check(code="const x: any = foo()", operation="create")
@@ -64,6 +64,10 @@ Config: `features.json` → `multi_agent_orchestration.lead_dev_mode`
64
64
 
65
65
  The seven rules are the engine. The cadence is the time signature. Disk SSOT: `grok-bot/ops/LEAD-CADENCE.md`.
66
66
 
67
+ Jargon is not the job. Review and cadence ask whether the change works. Do not spend the loop reciting dest names, mill liturgy, or host mantras.
68
+
69
+ - **Feat rebase:** when the mandated work turns into mantra, or the job has split across planes that no longer share one present, stop. Go up a level and answer the original message again from this seat. Do not continue the side thread and call that the job.
70
+
67
71
  - **Peer test:** another instance with only worn 0xRay continues the ticket. No command stream.
68
72
  - **Wear first:** Read `.xray/state/STATION.md`. Same cloud. Same critic (resume only). Dummy Reads Station.
69
73
  - **Dummy tests `SKILLS.md`:** a suited dummy proves the worn catalog. Same-sess bodies keep context and hold a Station card.
@@ -73,6 +77,24 @@ The seven rules are the engine. The cadence is the time signature. Disk SSOT: `g
73
77
  - **Live tick, not pacer:** `/loop` runs work now. Keep workstreams moving. Do not idle-wait 12 minutes. **Clean ticks every cycle** — unsubscribe then resubscribe so the prompt matches metal. Name-dedupe does not rewrite. A stale prompt (waiting URL after live, Hold after strip) is dirty: rewrite, do not act. **If the card and WAVEBOARD are idle, unsubscribe and do not resubscribe.**
74
78
  - **Board clock:** PR events + resume the same critic. Not two idle pacers. Merge after PASS+CI (gate A). Publish only after mill-gate D + `foundry gate`.
75
79
  - **Dispatch:** ticket + PR URLs + “Wear, then review.” The critic Reads `SEATS.md` Reviewer. Do not paste a command novel. When a law is spoken twice, encode it this wake.
80
+ - **Cascade:** Chat is not the brain. A cascade that must survive compaction is a named repertoire signal on the stack overlay, hydrated onto the project law list. NOTES holds the same cascade for the second Read. Station stays the ticket. A reflection is not this capture. Capture priority: a law is an invariant you would otherwise re-read the source to relearn. One law per subsystem bone. Heat touches last_seen and does not append a confidence sample, so more hits do not raise conviction. Heat touches last_seen only when the diary contains that signal's name; two definition words are not a hit; a weak session score is not lifted to 0.55 and a sample below 0.55 is not recorded. Constructing the organ does not append workspace samples. Hero names: `heat-is-not-conviction`, `inference-cycle-invariant`, `governance-vote-invariant`, `thin-dispatch-invariant`. Repertoire mergeStackOverlay refreshes a changed stack definition and keeps observation stats.
81
+ - **Cold brief:** a new critic or any new subagent that does not already have this thread gets the latest commit SHA on the branch under review (fetch it; do not reuse an earlier SHA from a previous pass) and the Station card (`.xray/state/STATION.md`: intent, plan, git). If the card is missing, give branch + HEAD + what just changed. If the branch moves after you brief them, send the new SHA before they verdict.
82
+ - **Control:** The lead keeps control. Do not assume the subagent has the context. Give the duty, the latest SHA, the Station card, and the laws that apply. The subagent wears the applicable 0xRay suit before it works: the tree under review, built and installed. An older published package is not that suit. The lead checks the result.
83
+
84
+ ### Engine cadence
85
+
86
+ This suite is the engine that builds the engine. When a seat sees OP-PROC, write it onto these existing surfaces in the same wake. Do not open a new skill.
87
+
88
+ 1. Change the engine with precision.
89
+ 2. Ship it local: build and install the tree. A live npm version stays live. Once a version is live, the stamper strips every present-tense patch pin from shipped guides and shipped OP-PROC, or reconcile advances the next cut. The stamper does not edit Station, NOTES, dest, or node_modules. It does not publish.
90
+ 3. Test the change.
91
+ 4. Run another deep review of the diff you just made.
92
+ 5. Monitor long-term memory (dest name count and whether observations are the only growth) and short-term memory (Station: intent, plan, git).
93
+ 6. Ship the PR.
94
+
95
+ ### Planes
96
+
97
+ Operating planes: code, OP-PROC, model, suit, mill, host, and test/ship. Test/ship is the critical OP-PROC plane. Test then ship is the hero. Ship without that proof is the catastrophe. Long-term memory is the project law list. Each wake refreshes a changed stack definition and keeps observation stats. Heat writes the matched names onto Station, the short-term card that survives compaction. Reactive decisions move out of TypeScript into OP-PROC as the model improves. That move is why the mill and the suit exist. It is not fully proved. Once a version is live, the stamper strips every present-tense patch pin from shipped guides and shipped OP-PROC, or reconcile advances the next cut. The stamper does not edit Station, NOTES, dest, or node_modules. It does not publish. Do not freeze the live cut.
76
98
  - **Close:** loop until Station is done. Compact: Station survives the cut. Overlay dest names are OP-PROC — reload `repertoire-working.json` `opProcNames`, not Station.
77
99
  - **Board:** critic Strict comments; forge implements; lead stays main thread.
78
100
  - **Codex 69:** rewire this skill and existing ops. Do not add a new `SKILL.md` to hold the cadence.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "description": "Stack-language overlay for 0xRay 4.0 memory routing. Additive to the 8-name factory seed. Not a plant diary and not the 0.1.8 brain.",
3
- "last_updated": "2026-09-20T00:00:00.000Z",
3
+ "last_updated": "2026-09-23T00:00:00.000Z",
4
4
  "source": "stack-overlay-4.0",
5
5
  "schema_version": "1.1",
6
6
  "implementation_guidance": {
@@ -745,6 +745,159 @@
745
745
  "last_seen": "2026-09-21T00:00:00.000Z",
746
746
  "governance_forced_count": 0
747
747
  }
748
+ },
749
+ {
750
+ "name": "feat-rebase",
751
+ "definition": "When the mandated work turns into mantra, or the job has split across planes that no longer share one present, stop. Go up a level and answer the original message again from this seat. Do not continue the side thread and call that the job.",
752
+ "tags": [
753
+ "cadence",
754
+ "rebase",
755
+ "lead"
756
+ ],
757
+ "priority": "high",
758
+ "status": "proposed",
759
+ "first_seen": "2026-09-23",
760
+ "evaluation_criteria": "Reply answers the original message from this seat instead of continuing a side thread.",
761
+ "validation_experiment": "A seat keeps answering notifications after the human asked for the original job \u2014 treat as fail.",
762
+ "master_index_integration": "Project-local curated_signals after hydrateWritableSignals merges data/stack-overlay.json. Never write the factory tarball.",
763
+ "implementation_notes": "LEAD-CADENCE.md Feat rebase. Not a new skill.",
764
+ "example_inference_snippet": "Go up a level and answer the original message again from this seat.",
765
+ "observation_stats": {
766
+ "observation_count": 1,
767
+ "avg_confidence": 0.55,
768
+ "max_confidence": 0.55,
769
+ "last_seen": "2026-09-23T00:00:00.000Z",
770
+ "governance_forced_count": 0
771
+ }
772
+ },
773
+ {
774
+ "name": "operating-planes",
775
+ "definition": "Operating planes are code, OP-PROC, model, suit, mill, host, and test/ship. Test/ship is the critical OP-PROC plane: test then ship is the hero; ship without that proof is the catastrophe. Long-term memory is the project law list. Each wake copies stack laws onto that list, keeps observation stats, and refreshes a changed definition. Heat writes the matched names onto Station, the short-term card that survives compaction. As the model improves, reactive decisions move out of code into OP-PROC. That move is not fully proved. Once a version is live, the stamper strips every present-tense patch pin from shipped guides and shipped OP-PROC, or reconcile advances the next cut. The stamper does not edit Station, NOTES, dest, or node_modules. It does not publish. Do not freeze the live cut.",
776
+ "tags": [
777
+ "planes",
778
+ "op-proc",
779
+ "test-ship",
780
+ "version-manager"
781
+ ],
782
+ "priority": "high",
783
+ "status": "proposed",
784
+ "first_seen": "2026-09-23",
785
+ "evaluation_criteria": "Reply names test/ship as the hero or the catastrophe, and treats a live cut as the stamper stripping shipped guides and shipped OP-PROC.",
786
+ "validation_experiment": "A seat ships without the test, or wakes after compact without the test/ship plane — treat as fail.",
787
+ "master_index_integration": "Project-local curated_signals after hydrateWritableSignals merges data/stack-overlay.json. Never write the factory tarball.",
788
+ "implementation_notes": "LEAD-CADENCE.md Planes. Not a new skill. Patch stamps stay in package.json, CHANGELOG, and stamped JSON. The stamper strips shipped guides and shipped OP-PROC. It does not edit Station, NOTES, dest, or node_modules. It does not publish.",
789
+ "example_inference_snippet": "Test then ship is the hero. Ship without that proof is the catastrophe.",
790
+ "observation_stats": {
791
+ "observation_count": 1,
792
+ "avg_confidence": 0.55,
793
+ "max_confidence": 0.55,
794
+ "last_seen": "2026-09-23T00:00:00.000Z",
795
+ "governance_forced_count": 0
796
+ }
797
+ },
798
+ {
799
+ "name": "wake-cascade",
800
+ "definition": "Chat is not the brain. Compaction drops the seat. Short-term memory is Station. Long-term memory is the project repertoire law list. NOTES is the deep cut: the pickup line cadences onto Station, and the body holds the cascade the card cannot. Capture a cascade by writing a named signal on the stack overlay and hydrating it onto dest. Observation counts are not capture. A reflection is not this capture. This wake, sorted: planes are code, OP-PROC, model, suit, mill, host, and test/ship; test then ship is the hero and ship without that proof is the catastrophe; each wake refreshes a changed stack definition and keeps observation stats, then heat writes matched names onto Station; the lead keeps control and a subagent gets the duty, the latest SHA, the Station card, the laws, and the suit built from the tree under review; when the work becomes mantra or the planes no longer share one present, answer the original message from this seat; once a version is live, the stamper strips every present-tense patch pin from shipped guides and shipped OP-PROC, or reconcile advances the next cut; the stamper does not edit Station, NOTES, dest, or node_modules; it does not publish; engine cadence is change, ship local, test, review the diff, monitor dest against Station, ship the PR; missing moral fusion is not an isotopic reject, vote confidence is a clamped weighted mean, thinDispatch strategy follows the adjusted level, and an empty inference cycle saves state.",
801
+ "tags": [
802
+ "cascade",
803
+ "memory",
804
+ "repertoire"
805
+ ],
806
+ "priority": "high",
807
+ "status": "proposed",
808
+ "first_seen": "2026-09-23",
809
+ "evaluation_criteria": "After compact the seat can state the cascade from the repertoire name and from NOTES, without the chat.",
810
+ "validation_experiment": "A seat wakes blank and rebuilds the planes from the thread summary — treat as fail.",
811
+ "master_index_integration": "Project-local curated_signals after hydrateWritableSignals merges data/stack-overlay.json. Never write the factory tarball.",
812
+ "implementation_notes": "NOTES body is the same cascade for the second Read. Not a reflection. Not a new skill.",
813
+ "example_inference_snippet": "Chat is not the brain. The cascade is the repertoire name wake-cascade.",
814
+ "observation_stats": {
815
+ "observation_count": 1,
816
+ "avg_confidence": 0.55,
817
+ "max_confidence": 0.55,
818
+ "last_seen": "2026-09-23T00:00:00.000Z",
819
+ "governance_forced_count": 0
820
+ }
821
+ },
822
+ {
823
+ "name": "heat-is-not-conviction",
824
+ "definition": "Heat touches last_seen on names the kernel diary already matches. Heat touches last_seen only when the diary contains that signal's name; two definition words are not a hit; a weak session score is not lifted to 0.55 and a sample below 0.55 is not recorded. Constructing the organ does not append workspace samples. It does not append a confidence sample, so a second heat does not raise observation_count and does not move avg_confidence. More hits do not raise conviction. Promotion still flips a proposed law to validated at two observations and confidence 0.55. Conviction moves on feedback, not on heat. Kernel diary text is logs/framework/activity.log, routing outcomes, pattern metrics, and .xray/inference/latest-workflow.json plus workflow-status.json. Heat matches that text onto existing names and does not mint a law. A missing session file or a stale inference-state timestamp is telemetry, not a law. Repertoire mergeStackOverlay refreshes a changed stack definition and keeps observation stats. It does not pass fleshGeneric. Subject overlay still fleshes only a generic field-observed stub.",
825
+ "tags": ["memory", "heat", "kernel"],
826
+ "priority": "high",
827
+ "status": "proposed",
828
+ "first_seen": "2026-09-23",
829
+ "evaluation_criteria": "A rising observation count at 0.55 is not treated as learning.",
830
+ "validation_experiment": "A seat reads observation mass as conviction — treat as fail.",
831
+ "master_index_integration": "Project-local curated_signals after hydrateWritableSignals merges data/stack-overlay.json. Never write the factory tarball.",
832
+ "implementation_notes": "heatKernelDiary calls touchLastSeen. recordPrimitiveObservations still appends samples for session and field ingest. promoteQualifiedSignals uses DEFAULT_PROMOTION_MIN_CONFIDENCE 0.55 and min observations 2. mergeStackOverlay in repertoire src/paths.ts refreshes stack law fields and keeps observation_stats. It does not pass fleshGeneric.",
833
+ "example_inference_snippet": "Heat touches last_seen and does not append a confidence sample. A changed stack definition refreshes and the observation count stays.",
834
+ "observation_stats": {
835
+ "observation_count": 1,
836
+ "avg_confidence": 0.55,
837
+ "max_confidence": 0.55,
838
+ "last_seen": "2026-09-23T00:00:00.000Z",
839
+ "governance_forced_count": 0
840
+ }
841
+ },
842
+ {
843
+ "name": "inference-cycle-invariant",
844
+ "definition": "An inference cycle that generates no proposals still writes inference-cycle-state.json. Without that write the one-day trigger never starts and the next call repeats. InferenceCycle.getInstance applies later options, including force and skipApply. Governance failure rejects the proposals. Proposal text is a keyword map over session problem strings. generateProposals also emits recurring-pattern titles.",
845
+ "tags": ["inference", "invariant"],
846
+ "priority": "high",
847
+ "status": "proposed",
848
+ "first_seen": "2026-09-23",
849
+ "evaluation_criteria": "Empty cycles persist state, and later getInstance options apply.",
850
+ "validation_experiment": "An empty cycle returns without a state file — treat as fail.",
851
+ "master_index_integration": "Project-local curated_signals after hydrateWritableSignals merges data/stack-overlay.json. Never write the factory tarball.",
852
+ "implementation_notes": "src/inference/inference-cycle.ts and inference-proposal-generator.ts. generateProposals maps session problem strings through a keyword map and also emits recurring-pattern titles. Not a new skill.",
853
+ "example_inference_snippet": "An empty inference cycle still saves state. Proposal text is a keyword map over session problem strings. generateProposals also emits recurring-pattern titles.",
854
+ "observation_stats": {
855
+ "observation_count": 1,
856
+ "avg_confidence": 0.55,
857
+ "max_confidence": 0.55,
858
+ "last_seen": "2026-09-23T00:00:00.000Z",
859
+ "governance_forced_count": 0
860
+ }
861
+ },
862
+ {
863
+ "name": "governance-vote-invariant",
864
+ "definition": "PHI 1.666 and TAU 0.865 are unused constants. The matrix cutoffs are the literals 0.92, 0.95, 0.82, 0.88, 0.75, and 0.80. Reason text does not claim TAU. mergeVotes averageConfidence is the weighted mean of confidence, sum of weight times confidence over sum of weight, clamped to 0..1. That number is resonance. isotopicRatio is compared only when it is a finite number. moralFusion is not defaulted to 0.5. A missing fusion number does not force reject. Critical moral tension does reject. Without moral fields the three-skill vote stands.",
865
+ "tags": ["governance", "invariant"],
866
+ "priority": "high",
867
+ "status": "proposed",
868
+ "first_seen": "2026-09-23",
869
+ "evaluation_criteria": "A vote with no moral fusion is not an isotopic reject, and average confidence stays in 0..1.",
870
+ "validation_experiment": "Reason text names TAU, or average confidence exceeds 1 — treat as fail.",
871
+ "master_index_integration": "Project-local curated_signals after hydrateWritableSignals merges data/stack-overlay.json. Never write the factory tarball.",
872
+ "implementation_notes": "src/governance/governance-core.ts and governance-service.ts. Not a new skill.",
873
+ "example_inference_snippet": "No moral fusion is not an isotopic reject. Average confidence is a clamped weighted mean.",
874
+ "observation_stats": {
875
+ "observation_count": 1,
876
+ "avg_confidence": 0.55,
877
+ "max_confidence": 0.55,
878
+ "last_seen": "2026-09-23T00:00:00.000Z",
879
+ "governance_forced_count": 0
880
+ }
881
+ },
882
+ {
883
+ "name": "thin-dispatch-invariant",
884
+ "definition": "scoreComplexity applies the thresholds it is given. After Repertoire adjusts the score, level, recommendedStrategy, and estimatedAgents follow the adjusted level. A high-confidence trap with an adjusted score of at least 26 forces architect. A provenance-failure tag with an adjusted score under 51 forces bug-triage-specialist. On a frontier host the spawn gate warns and allows a spawn with no plan. Guided and strict hosts deny. The lead-dev plan builder is on.",
885
+ "tags": ["engine", "invariant", "thin-dispatch"],
886
+ "priority": "high",
887
+ "status": "proposed",
888
+ "first_seen": "2026-09-23",
889
+ "evaluation_criteria": "Strategy follows the adjusted level, and the trap routes are architect at 26 and bug-triage under 51.",
890
+ "validation_experiment": "Strategy stays on the pre-adjust level after Repertoire moves the score — treat as fail.",
891
+ "master_index_integration": "Project-local curated_signals after hydrateWritableSignals merges data/stack-overlay.json. Never write the factory tarball.",
892
+ "implementation_notes": "src/nucleus/thin-dispatch.ts and RepertoireOrchestratorBridge.resolveThinDispatchAgent. Not a new skill.",
893
+ "example_inference_snippet": "After Repertoire adjusts the score, strategy follows the adjusted level. A trap at 26 forces architect.",
894
+ "observation_stats": {
895
+ "observation_count": 1,
896
+ "avg_confidence": 0.55,
897
+ "max_confidence": 0.55,
898
+ "last_seen": "2026-09-23T00:00:00.000Z",
899
+ "governance_forced_count": 0
900
+ }
748
901
  }
749
902
  ]
750
903
  }
@@ -39,7 +39,6 @@ export class RepertoireService {
39
39
  }
40
40
  if (shouldAutoSyncXray(options.syncXray)) {
41
41
  this.syncXrayMemory();
42
- this.syncWorkspaceRepos();
43
42
  this.heatKernelDiary();
44
43
  }
45
44
  }
@@ -126,6 +125,10 @@ export class RepertoireService {
126
125
  }
127
126
  /**
128
127
  * Heat existing dest names from kernel diary text. No new names.
128
+ * Touches last_seen only when the diary contains that signal's id,
129
+ * or the id with hyphens read as spaces. Does not append a confidence sample.
130
+ * Two definition words are not a hit. Heat must not mint and must not
131
+ * change observation_count or avg_confidence.
129
132
  * Colon pattern ids like `architect:architect_skill` never become dest keys.
130
133
  */
131
134
  heatKernelDiary(collected) {
@@ -134,11 +137,9 @@ export class RepertoireService {
134
137
  return { heated: [], sources: diary.sources };
135
138
  }
136
139
  const hits = this.signalsManager.matchByText(diary.text, 2);
137
- const matches = hits.map((hit) => ({
138
- name: hit.signal.name,
139
- confidence: 0.55,
140
- }));
141
- const heated = matches.length > 0 ? this.signalsManager.recordPrimitiveObservations(matches) : [];
140
+ const heated = hits.length > 0
141
+ ? this.signalsManager.touchLastSeen(hits.map((hit) => hit.signal.name))
142
+ : [];
142
143
  return { heated, sources: diary.sources };
143
144
  }
144
145
  reloadOpProc() {
@@ -36,7 +36,7 @@ export class XraySessionIngester {
36
36
  const entry = this.sessionToInferenceEntry(session);
37
37
  const targetFile = join(this.targetDir, `${session.timestamp.split('T')[0]}.jsonl`);
38
38
  appendFileSync(targetFile, JSON.stringify(entry) + '\n');
39
- this.recordObservations(session, entry);
39
+ this.recordObservations(session);
40
40
  if (this.stateManager && session.sessionId) {
41
41
  this.stateManager.markProcessed([session.sessionId], 'session');
42
42
  }
@@ -50,13 +50,13 @@ export class XraySessionIngester {
50
50
  const promoted = imported > 0 ? this.signalsManager.promoteQualifiedSignals() : [];
51
51
  return { imported, skipped, promoted };
52
52
  }
53
- recordObservations(session, entry) {
54
- const matches = this.extractMatches(session, entry);
53
+ recordObservations(session) {
54
+ const matches = this.extractMatches(session);
55
55
  if (matches.length === 0)
56
56
  return;
57
57
  this.signalsManager.recordPrimitiveObservations(matches);
58
58
  }
59
- extractMatches(session, entry) {
59
+ extractMatches(session) {
60
60
  const matches = [];
61
61
  const seen = new Set();
62
62
  const add = (name, confidence) => {
@@ -70,20 +70,19 @@ export class XraySessionIngester {
70
70
  for (const pattern of session.patterns ?? []) {
71
71
  const raw = pattern.name || pattern.type || '';
72
72
  const slug = slugFieldPrimitiveName(raw);
73
- const confidence = typeof pattern.confidence === 'number' ? pattern.confidence : DEFAULT_PROMOTION_MIN_CONFIDENCE;
74
- if (slug)
75
- add(slug, Math.max(confidence, DEFAULT_PROMOTION_MIN_CONFIDENCE));
73
+ if (!slug || typeof pattern.confidence !== 'number')
74
+ continue;
75
+ add(slug, pattern.confidence);
76
76
  }
77
77
  for (const name of session.matched_primitives ?? []) {
78
- const confidence = session.match_confidence?.[name] ?? DEFAULT_PROMOTION_MIN_CONFIDENCE;
78
+ const confidence = session.match_confidence?.[name];
79
+ if (typeof confidence !== 'number')
80
+ continue;
79
81
  add(name, confidence);
80
82
  }
81
83
  if (this.repoPrimitive) {
82
84
  add(this.repoPrimitive, DEFAULT_PROMOTION_MIN_CONFIDENCE);
83
85
  }
84
- for (const hit of this.signalsManager.matchByText(entry.inference, 2)) {
85
- add(hit.signal.name, DEFAULT_PROMOTION_MIN_CONFIDENCE);
86
- }
87
86
  return matches;
88
87
  }
89
88
  sessionToInferenceEntry(session) {