@gcunharodrigues/wrxn 0.1.0 → 0.2.1

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.
@@ -2,9 +2,10 @@
2
2
  'use strict';
3
3
 
4
4
  // WRXN managed hook — Constitution Article I (Agent Authority).
5
- // PreToolUse:Bash gate: a remote git op (push / PR / tag push) is allowed only when
6
- // the session declares the devops role via WRXN_ACTIVE_AGENT=devops. A bare push runs
7
- // as @unknown and is denied. Fails OPEN on any internal error (never over-blocks).
5
+ // PreToolUse:Bash gate: a remote git op (push / PR / tag push) is a deliberate act, held
6
+ // behind a confirmation flag to prevent an accidental push. The op proceeds only once the
7
+ // session confirms intent by setting WRXN_ACTIVE_AGENT=devops in .claude/settings.local.json;
8
+ // an unconfirmed op is denied. Fails OPEN on any internal error (never over-blocks).
8
9
  //
9
10
  // Contract: reads a PreToolUse hook event as JSON on stdin, writes a decision to stdout.
10
11
  // allow → {} (exit 0)
@@ -33,13 +34,13 @@ function main() {
33
34
  }
34
35
 
35
36
  if (process.env.WRXN_ACTIVE_AGENT === 'devops') {
36
- return emit({}); // authorized
37
+ return emit({}); // confirmed
37
38
  }
38
39
 
39
40
  return emit({
40
41
  decision: 'block',
41
42
  reason:
42
- 'Remote git op is devops-exclusive (Constitution Art. I). Re-run with WRXN_ACTIVE_AGENT=devops, or delegate to the devops role.',
43
+ 'Remote git ops (push / PR / tag) are held behind a deliberate-push confirmation flag, to prevent an accidental push. To confirm intent, set WRXN_ACTIVE_AGENT=devops in .claude/settings.local.json (machine-local), then retry.',
43
44
  });
44
45
  }
45
46
 
@@ -1,21 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- // WRXN session-end hook — the episodic writer (wrxn-kernel-10).
4
+ // WRXN session-end hook — the episodic writer + session janitor (wrxn-kernel-10, foundation-honesty-02).
5
5
  // SessionEnd. Writes a dated session page into the install's own wiki sessions tier from the
6
- // captured turn trail, then clears the trail. CONTINUITY DOCTRINE: this writer touches ONLY
7
- // dated session pages it NEVER writes the continuity baton (.wrxn/continuity/latest.md).
8
- // That slot has a single writer (the handoff skill); keeping the paths disjoint is the
9
- // structural fix for the clobber observed live 2026-06-12.
6
+ // captured turn trail, then reaps the session's scratch state. Hygiene (foundation-honesty-02):
7
+ // - skip-empty: a session that captured no turns writes NO page;
8
+ // - reap: the consumed trail AND the first-touch marker (.wrxn/history/<sid>.touched, written by
9
+ // code-intel-push) are removed so .wrxn/history/ can't grow without bound;
10
+ // - bound: the sessions tier is capped (WRXN_SESSIONS_MAX, default 50) — oldest pages rotate out.
11
+ // CONTINUITY DOCTRINE: this writer touches ONLY the sessions tier + the session's own history
12
+ // scratch — it NEVER writes OR deletes the continuity baton (.wrxn/continuity/latest.md). That slot
13
+ // has a single writer (the handoff skill); keeping the paths disjoint is the structural fix for the
14
+ // clobber observed live 2026-06-12.
10
15
  //
11
16
  // Self-contained: ships into installs, MUST NOT import the kernel lib (node stdlib only).
12
17
  // Fail-open + side-effect-only: emits nothing useful, never blocks; any fault exits 0 silently.
13
18
  //
14
- // Contract: SessionEnd event JSON on stdin → exit 0. Side effect: a sessions/<date>-<sid>.md page.
19
+ // Contract: SessionEnd event JSON on stdin → exit 0. Side effect: a sessions/<date>-<sid>.md page
20
+ // for a non-empty session, plus reaping of that session's trail + touched marker.
15
21
 
16
22
  const fs = require('fs');
17
23
  const path = require('path');
18
24
 
25
+ // Bound the sessions tier to the most-recent N pages (override: WRXN_SESSIONS_MAX).
26
+ const DEFAULT_SESSIONS_MAX = 50;
27
+
19
28
  function done() {
20
29
  process.exit(0);
21
30
  }
@@ -64,6 +73,32 @@ function readTrail(root, sid) {
64
73
  return { turns, trail };
65
74
  }
66
75
 
76
+ // Best-effort removal — `force` ignores a missing file; any other fault is swallowed (fail-open).
77
+ function rmQuiet(p) {
78
+ try {
79
+ fs.rmSync(p, { force: true });
80
+ } catch {
81
+ /* best-effort cleanup, never block session close */
82
+ }
83
+ }
84
+
85
+ // Bound the sessions tier: keep at most `max` most-recent dated pages, reaping the oldest. Dated
86
+ // `YYYY-MM-DD-…` slugs sort chronologically, so the oldest are the lexicographically-first ones.
87
+ // Cap = WRXN_SESSIONS_MAX (env) or DEFAULT_SESSIONS_MAX. Self-contained: never throws.
88
+ function capSessions(dir) {
89
+ const max = Number(process.env.WRXN_SESSIONS_MAX) || DEFAULT_SESSIONS_MAX;
90
+ if (!Number.isFinite(max) || max <= 0) return;
91
+ let pages;
92
+ try {
93
+ pages = fs.readdirSync(dir).filter((f) => /^\d{4}-\d{2}-\d{2}-.+\.md$/.test(f)).sort();
94
+ } catch {
95
+ return;
96
+ }
97
+ for (let i = 0; i < pages.length - max; i++) {
98
+ rmQuiet(path.join(dir, pages[i]));
99
+ }
100
+ }
101
+
67
102
  function main() {
68
103
  let event = {};
69
104
  try {
@@ -76,52 +111,57 @@ function main() {
76
111
  const root = findInstallRoot();
77
112
  if (!root) done();
78
113
 
79
- const sid = oneLine(event.session_id || 'session');
80
- const reason = oneLine(event.reason || 'unknown');
81
- const date = nowISO().slice(0, 10); // YYYY-MM-DD
82
- const slug = `${date}-${safeId(event.session_id)}`;
83
-
84
114
  const { turns, trail } = readTrail(root, event.session_id);
85
- const trailLines = turns.length
86
- ? turns.map((t, i) => {
87
- const tab = t.indexOf('\t');
88
- const line = tab > -1 ? t.slice(tab + 1) : t;
89
- return `${i + 1}. ${line}`;
90
- })
91
- : ['_(no turns captured)_'];
92
-
93
- const page = [
94
- '---',
95
- `name: ${slug}`,
96
- `description: Session ${sid} ${turns.length} turn(s), ended ${reason}`,
97
- 'tier: sessions',
98
- 'source: session-end-hook',
99
- '---',
100
- '',
101
- `# Session ${date} (${sid})`,
102
- '',
103
- `- Ended: ${reason}`,
104
- `- Turns: ${turns.length}`,
105
- '',
106
- '## Turn trail',
107
- ...trailLines,
108
- '',
109
- ].join('\n');
110
-
111
- const dir = path.join(root, '.wrxn', 'wiki', 'sessions');
112
- try {
113
- fs.mkdirSync(dir, { recursive: true });
114
- fs.writeFileSync(path.join(dir, `${slug}.md`), page);
115
- // Consume the trail so the next session starts clean.
115
+
116
+ // Skip-empty: a session that captured no turns leaves NO page — the first half of bounding the
117
+ // sessions tier. Only write the page (and only then consume its trail) when there is activity.
118
+ if (turns.length) {
119
+ const sid = oneLine(event.session_id || 'session');
120
+ const reason = oneLine(event.reason || 'unknown');
121
+ const date = nowISO().slice(0, 10); // YYYY-MM-DD
122
+ const slug = `${date}-${safeId(event.session_id)}`;
123
+
124
+ const trailLines = turns.map((t, i) => {
125
+ const tab = t.indexOf('\t');
126
+ const line = tab > -1 ? t.slice(tab + 1) : t;
127
+ return `${i + 1}. ${line}`;
128
+ });
129
+
130
+ const page = [
131
+ '---',
132
+ `name: ${slug}`,
133
+ `description: Session ${sid} — ${turns.length} turn(s), ended ${reason}`,
134
+ 'tier: sessions',
135
+ 'source: session-end-hook',
136
+ '---',
137
+ '',
138
+ `# Session ${date} (${sid})`,
139
+ '',
140
+ `- Ended: ${reason}`,
141
+ `- Turns: ${turns.length}`,
142
+ '',
143
+ '## Turn trail',
144
+ ...trailLines,
145
+ '',
146
+ ].join('\n');
147
+
148
+ const dir = path.join(root, '.wrxn', 'wiki', 'sessions');
116
149
  try {
117
- fs.rmSync(trail, { force: true });
150
+ fs.mkdirSync(dir, { recursive: true });
151
+ fs.writeFileSync(path.join(dir, `${slug}.md`), page);
152
+ rmQuiet(trail); // consume the trail only after its page has landed
153
+ capSessions(dir); // rotation: bound the sessions tier (reap the oldest beyond the cap)
118
154
  } catch {
119
- /* trail cleanup is best-effort */
155
+ /* page write failed → fail-open; leave the trail intact for no-loss */
120
156
  }
121
- } catch {
122
- /* page write failed fail-open, never block session close */
157
+ } else {
158
+ rmQuiet(trail); // empty session: nothing to write; drop any stray empty trail file
123
159
  }
124
160
 
161
+ // The first-touch gate marker (written by code-intel-push) is pure per-session scratch — always
162
+ // reap it so .wrxn/history/ can't grow without bound. NEVER touches the continuity baton.
163
+ rmQuiet(path.join(root, '.wrxn', 'history', `${safeId(event.session_id)}.touched`));
164
+
125
165
  done();
126
166
  }
127
167
 
@@ -179,26 +179,49 @@ function readResidentTokens(transcriptPath) {
179
179
  }
180
180
  }
181
181
 
182
- // Model context window, resolved by an explicit precedence (issue 29). On [1m] sessions
183
- // lastModelUsage is often EMPTY and the transcript model id lacks the [1m] tag there is no
184
- // reliable auto-signal — so the window must be settable explicitly rather than guessed:
185
- // 1. env WRXN_CONTEXT_WINDOW a positive finite number wins unconditionally.
186
- // 2. manifest CONTEXT_WINDOW a positive finite value (when manifestText is supplied).
187
- // 3. ~/.claude.json lastModelUsage KEYS — a [1m] tag ⇒ 1,000,000 (auto-detect, when present).
188
- // 4. fallback 200,000.
189
- // homeDir/manifestText overrides keep it testable.
190
- function modelWindow(cwd, homeDir, manifestText) {
182
+ // The LIVE window for a session, published by the statusline. UserPromptSubmit hooks receive no
183
+ // model/context-window data, but the statusline payload carries context_window.context_window_size
184
+ // (resolved by Claude Code, refreshed every render — so it tracks a mid-session /model switch). The
185
+ // statusline writes it to a session-scoped /tmp sidecar; we read it back here by session_id.
186
+ // See statusline.sh and memory `handoff-window-defaults-200k`. Returns a positive number or null.
187
+ function readStatuslineWindow(sessionId) {
188
+ if (!sessionId) return null;
189
+ try {
190
+ const p = path.join(os.tmpdir(), `claude-statusline-ctx-${sessionId}.json`);
191
+ const o = JSON.parse(fs.readFileSync(p, 'utf8'));
192
+ const w = Number(o && o.context_window_size);
193
+ return Number.isFinite(w) && w > 0 ? w : null;
194
+ } catch {
195
+ return null;
196
+ }
197
+ }
198
+
199
+ // Model context window, resolved by an explicit precedence (issue 29 + dynamic statusline bridge).
200
+ // On [1m] sessions lastModelUsage is often EMPTY and the transcript model id lacks the [1m] tag, and
201
+ // the hook payload carries no model — so we lean on the statusline (which DOES know the live window):
202
+ // 1. env WRXN_CONTEXT_WINDOW — a positive finite number wins unconditionally (manual force).
203
+ // 2. statusline sidecar — the live per-session window; tracks mid-session model switches.
204
+ // 3. manifest CONTEXT_WINDOW — a positive finite value (when manifestText is supplied).
205
+ // 4. ~/.claude.json lastModelUsage KEYS — a [1m] tag ⇒ 1,000,000 (auto-detect, when present).
206
+ // 5. self-correcting net — resident already past the 200k default ⇒ window is necessarily larger.
207
+ // 6. fallback 200,000.
208
+ // homeDir/manifestText/sessionId/resident overrides keep it testable.
209
+ function modelWindow(cwd, homeDir, manifestText, sessionId, resident) {
191
210
  // 1. explicit env override.
192
211
  const envWin = Number(process.env.WRXN_CONTEXT_WINDOW);
193
212
  if (Number.isFinite(envWin) && envWin > 0) return envWin;
194
213
 
195
- // 2. manifest CONTEXT_WINDOW (the engine already reads scalar manifest values).
214
+ // 2. statusline sidecar the live, authoritative window (dynamic across /model switches).
215
+ const scWin = readStatuslineWindow(sessionId);
216
+ if (scWin) return scWin;
217
+
218
+ // 3. manifest CONTEXT_WINDOW (the engine already reads scalar manifest values).
196
219
  if (manifestText != null) {
197
220
  const manWin = Number(manifestValue(manifestText, 'CONTEXT_WINDOW'));
198
221
  if (Number.isFinite(manWin) && manWin > 0) return manWin;
199
222
  }
200
223
 
201
- // 3. lastModelUsage [1m] auto-detect.
224
+ // 4. lastModelUsage [1m] auto-detect.
202
225
  try {
203
226
  const home = homeDir || process.env.HOME || os.homedir();
204
227
  const cfg = JSON.parse(fs.readFileSync(path.join(home, '.claude.json'), 'utf8'));
@@ -206,10 +229,13 @@ function modelWindow(cwd, homeDir, manifestText) {
206
229
  const keys = Object.keys(proj.lastModelUsage || {});
207
230
  if (keys.some((k) => /\[1m\]/i.test(k))) return 1000000;
208
231
  } catch {
209
- // fall through to the default.
232
+ // fall through.
210
233
  }
211
234
 
212
- // 4. fallback.
235
+ // 5. self-correcting net: resident past the 200k default ⇒ a larger (1M) window.
236
+ if (Number.isFinite(resident) && resident > 200000) return 1000000;
237
+
238
+ // 6. fallback.
213
239
  return 200000;
214
240
  }
215
241
 
@@ -295,7 +321,7 @@ function compose(root, event) {
295
321
  if (ev.transcript_path) {
296
322
  const resident = readResidentTokens(ev.transcript_path);
297
323
  if (resident != null) {
298
- const window = modelWindow(ev.cwd || root, process.env.HOME || os.homedir(), manifestText);
324
+ const window = modelWindow(ev.cwd || root, process.env.HOME || os.homedir(), manifestText, ev.session_id, resident);
299
325
  const consumed = resident / window;
300
326
  const pct = resolveHandoffPct(manifestText);
301
327
  if (consumed >= pct) out.push(handoffDirective(consumed, pct));
@@ -345,6 +371,7 @@ module.exports = {
345
371
  compose,
346
372
  findInstallRoot,
347
373
  readResidentTokens,
374
+ readStatuslineWindow,
348
375
  modelWindow,
349
376
  resolveHandoffPct,
350
377
  handoffDirective,
@@ -1,132 +1,133 @@
1
1
  ---
2
2
  name: synapse
3
- description: "This skill should be used when users want to understand the SYNAPSE context engine, manage domains, configure context rules, or troubleshoot rule injection. Use when asked about SYNAPSE architecture, domain management, star-commands, context brackets, or the 8-layer processing pipeline."
3
+ description: "Use when someone wants to understand the SYNAPSE context engine, manage its rule domains, tune the token budget or handoff threshold, or troubleshoot why a rule did or didn't inject. Covers the real engine: the three layers (constitution / always-on / keyword-recall), the flat token budget, and the non-blocking handoff directive."
4
4
  ---
5
5
 
6
- # SYNAPSE Context Engine
6
+ # SYNAPSE context engine
7
7
 
8
- ## Overview
8
+ ## What it is
9
9
 
10
- SYNAPSE (Synkra Adaptive Processing & State Engine) is the unified context engine for AIOX. It injects contextual rules into every prompt via an 8-layer processing pipeline, adapting to context window usage through bracket-aware filtering.
10
+ SYNAPSE is the per-prompt context-injection engine. On every `UserPromptSubmit` it assembles the
11
+ install's active rule domains into a single `<synapse-rules>` block and returns it as
12
+ `additionalContext`, so each prompt carries the constitution plus the operational rules that apply.
11
13
 
12
- **What it does:**
13
- - Injects rules per-prompt via Claude Code's `UserPromptSubmit` hook
14
- - Processes 8 layers (L0 Constitution through L7 Star-Commands) sequentially
15
- - Adapts injection volume based on context brackets (FRESH/MODERATE/DEPLETED/CRITICAL)
16
- - Integrates with agent state (active agent, workflow, task, squad)
17
- - Outputs `<synapse-rules>` XML block appended to each prompt
14
+ It is one self-contained hook — `.claude/hooks/synapse-engine.cjs` — that ships into an install and
15
+ reads only the install's own files (`.claude/constitution.md`, the `.synapse/` domains, and the
16
+ `wrxn.install.json` receipt that marks the install root). It imports nothing from the kernel package.
17
+ It is **fail-open**: any fault (unparseable input, missing file, assembly error) emits an empty
18
+ envelope and injects nothing the engine never blocks a prompt.
18
19
 
19
- **What it replaces:** SYNAPSE replaces the legacy CARL system with full feature parity plus 8 new capabilities including agent-scoped domains, workflow activation, and CRUD management commands.
20
+ ## The three layers
20
21
 
21
- **Architecture model:** Open Core the 8-layer engine lives in `aiox-core` (open source), memory integration is feature-gated in `aiox-pro`.
22
+ SYNAPSE assembles each prompt from three layers, in this order:
22
23
 
23
- ## Quick Start
24
+ | Layer | Source | Fires |
25
+ |-------|--------|-------|
26
+ | **L0 — Constitution** | `.claude/constitution.md` | Always. Never trimmed by the budget. |
27
+ | **L1 — Always-on domains** | `.synapse/<domain>` where `<DOMAIN>_ALWAYS_ON=true` | Every prompt. (Seeded: `global`, `pipeline`.) |
28
+ | **L6 — Keyword-recall domains** | `.synapse/<domain>` with a `<DOMAIN>_RECALL=word,...` list | Only when a trigger word appears in the prompt. (Seeded: `routing`.) |
24
29
 
25
- ### Verify SYNAPSE is Active
30
+ The constitution is rendered from `constitution.md` (article headings + their bullets) and sits
31
+ outside the token budget — it is always kept. Every other active domain contributes a numbered rules
32
+ section. See [the layer model](references/layers.md).
26
33
 
27
- SYNAPSE runs automatically via the Claude Code hook. To check status:
34
+ ## How a domain is defined
28
35
 
29
- ```
30
- *synapse status
31
- ```
36
+ Domains are registered in `.synapse/manifest` (flat `KEY=VALUE`) and their rules live in a sibling
37
+ file `.synapse/<domain>` (lowercased) as `<DOMAIN>_RULE_<N>=text` lines:
32
38
 
33
- This shows: active domains, current bracket, session info, and loaded layers.
39
+ ```
40
+ # .synapse/manifest
41
+ GLOBAL_STATE=active
42
+ GLOBAL_ALWAYS_ON=true
34
43
 
35
- ### Basic Commands
44
+ # .synapse/global
45
+ GLOBAL_RULE_0=git push, PR creation, and release tags are deliberate acts held behind a confirmation flag (anti-accidental-push) — `devops` is a dispatch-phase label, not an authority.
46
+ GLOBAL_RULE_1=The unit of work is an issue with explicit acceptance criteria.
47
+ ```
36
48
 
37
- | Command | What it does |
38
- |---------|-------------|
39
- | `*synapse status` | Show current engine state |
40
- | `*synapse domains` | List all registered domains |
41
- | `*synapse debug` | Show detailed debug info (manifest parse, load times, rule counts) |
42
- | `*synapse help` | Show all available synapse commands |
43
- | `*brief` | Switch to brief response mode |
44
- | `*dev` | Switch to developer mode (code-focused) |
45
- | `*review` | Switch to code review mode |
49
+ A domain loads only when `<DOMAIN>_STATE=active`. An always-on domain sets `_ALWAYS_ON=true`; a
50
+ keyword domain sets `_RECALL=word1,word2`. See [the manifest format](references/manifest.md) and
51
+ [domains & rule files](references/domains.md).
46
52
 
47
- ### Create a Custom Domain
53
+ ## The token budget
48
54
 
49
- ```
50
- *synapse create
51
- ```
55
+ Everything except the constitution is trimmable. A single flat budget (`RULES_BUDGET_TOKENS`,
56
+ default 600; override `WRXN_RULES_BUDGET`) caps the trimmable sections; when the assembled rules
57
+ exceed it, whole sections are dropped lowest-priority-first and a visible `[SYNAPSE-RULES-TRIM]`
58
+ marker records what was dropped. One budget, applied flat. See
59
+ [token budget & handoff](references/brackets.md).
52
60
 
53
- This walks you through creating a new domain file + manifest entry. See [references/domains.md](references/domains.md) for the full domain guide.
61
+ ## The handoff directive
54
62
 
55
- ## Architecture
63
+ When real consumed context reaches the handoff threshold (`HANDOFF_PCT`, default 0.40; override
64
+ `WRXN_HANDOFF_PCT`) of the model window, SYNAPSE appends a **non-blocking** `[HANDOFF REQUIRED]`
65
+ directive: finish the current request, run the handoff skill to write the baton, then `/clear` and
66
+ resume in a fresh session. It never refuses work. The math runs on real token usage (resident tokens
67
+ from the transcript ÷ the resolved model window), not an assumed window. See
68
+ [token budget & handoff](references/brackets.md).
56
69
 
57
- SYNAPSE operates as a 4-layer architecture:
70
+ ## Output shape
58
71
 
59
72
  ```
60
- .claude/hooks/synapse-engine.js # Layer 1: Hook Entry (~50 lines)
61
- |
62
- v imports
63
- .aiox-core/core/synapse/ # Layer 2: Engine Modules
64
- |-- engine.js # SynapseEngine class
65
- |-- layers/ # 8 layer processors (L0-L7)
66
- |-- session/session-manager.js # Session state (JSON v2.0)
67
- |-- domain/domain-loader.js # Manifest + domain parser
68
- |-- context/context-tracker.js # Bracket calculation
69
- |-- memory/memory-bridge.js # Pro-gated MIS consumer
70
- |-- output/formatter.js # <synapse-rules> XML
71
- |
72
- v reads/writes
73
- .synapse/ # Layer 3: Runtime Data
74
- |-- manifest # Central domain registry (KEY=VALUE)
75
- |-- constitution, global, context # Core domains (L0, L1)
76
- |-- agent-*, workflow-* # Scoped domains (L2, L3)
77
- |-- commands # Star-command definitions (L7)
78
- |-- sessions/, cache/ # Session state (gitignored)
79
- |
80
- v user-invoked
81
- .claude/commands/synapse/ # Layer 4: CRUD Commands + Skill Docs
82
- |-- manager.md # Router/dispatcher
83
- |-- tasks/ (6 tasks) # create, add, edit, toggle, command, suggest
84
- ```
73
+ <synapse-rules>
85
74
 
86
- **Key principle:** SYNAPSE is a **consumer** of existing systems (UAP for session state, MIS for memories). It never rewrites code from other epics.
75
+ [CONSTITUTION] (NON-NEGOTIABLE)
76
+ Article I — Agent Authority (NON-NEGOTIABLE)
77
+ git push, PR creation, and release tags are deliberate acts held behind a confirmation flag (anti-accidental-push) — `devops` is a dispatch-phase label, not an authority.
78
+ ...
87
79
 
88
- ## References
80
+ [GLOBAL]
81
+ 1. git push, PR creation, and release tags are deliberate acts held behind a confirmation flag ...
82
+ 2. The unit of work is an issue with explicit acceptance criteria ...
89
83
 
90
- ### Reference Guides
84
+ [RECALL: routing]
85
+ 1. git push, PR creation, and release tags are deliberate acts held behind a confirmation flag ...
91
86
 
92
- | Guide | Description |
93
- |-------|-------------|
94
- | [domains.md](references/domains.md) | Domain types (L0-L7), KEY=VALUE format, creation guide |
95
- | [commands.md](references/commands.md) | Star-commands, *synapse sub-commands, CRUD operations |
96
- | [manifest.md](references/manifest.md) | Manifest format specification, all valid keys |
97
- | [brackets.md](references/brackets.md) | Context bracket system, token budgets, layer activation |
98
- | [layers.md](references/layers.md) | 8-layer processor architecture, priority, conflict resolution |
87
+ [SYNAPSE-RULES-TRIM] ROUTING dropped over the 600-token rules budget
99
88
 
100
- ### Assets (Templates)
89
+ [HANDOFF REQUIRED]
90
+ Context is at ~42% of the model window (>= the 40% handoff threshold). NON-BLOCKING — do NOT stop work:
91
+ 1. Finish the current request.
92
+ 2. Run the handoff skill to write the baton (a compact handoff document).
93
+ 3. Tell the operator to /clear and open a fresh session, where the baton injects on resume.
101
94
 
102
- Templates for creating custom domains and manifest entries are maintained at:
95
+ </synapse-rules>
96
+ ```
103
97
 
104
- - **Domain template:** `.claude/commands/synapse/templates/domain-template`
105
- - **Manifest entry template:** `.claude/commands/synapse/templates/manifest-entry-template`
98
+ The trim marker appears only when a section was dropped; the handoff directive only at/above the
99
+ threshold. When no domains are active the engine injects nothing.
106
100
 
107
- See [assets/README.md](assets/README.md) for details.
101
+ ## Configuration
108
102
 
109
- ### CRUD Commands
103
+ | Knob | Where | Effect |
104
+ |------|-------|--------|
105
+ | `RULES_BUDGET_TOKENS` | `.synapse/manifest` | Trimmable-rules token ceiling (default 600). |
106
+ | `HANDOFF_PCT` | `.synapse/manifest` | Handoff threshold as a window fraction (default 0.40). |
107
+ | `CONTEXT_WINDOW` | `.synapse/manifest` | Pin the model window (tokens) for the handoff math. |
108
+ | `WRXN_RULES_BUDGET` | env | Overrides `RULES_BUDGET_TOKENS`. |
109
+ | `WRXN_HANDOFF_PCT` | env | Overrides `HANDOFF_PCT`. |
110
+ | `WRXN_CONTEXT_WINDOW` | env | Forces the model window unconditionally. |
110
111
 
111
- For domain management operations, use the SYNAPSE manager:
112
+ See [invocation & configuration](references/commands.md).
112
113
 
113
- | Command | Purpose |
114
- |---------|---------|
115
- | `*synapse create` | Create new domain + manifest entry |
116
- | `*synapse add` | Add rule to existing domain |
117
- | `*synapse edit` | Edit or remove rule by index |
118
- | `*synapse toggle` | Toggle domain active/inactive |
119
- | `*synapse command` | Create new star-command |
120
- | `*synapse suggest` | Suggest best domain for a rule |
114
+ ## References
121
115
 
122
- Full details: [references/commands.md](references/commands.md)
116
+ | Guide | Covers |
117
+ |-------|--------|
118
+ | [The layer model](references/layers.md) | the L0/L1/L6 assembly, ordering, constitution rendering, output format |
119
+ | [The manifest format](references/manifest.md) | the `.synapse/manifest` keys and scalars |
120
+ | [Domains & rule files](references/domains.md) | the seeded domains, the `RULE_N` format, adding a domain |
121
+ | [Token budget & handoff](references/brackets.md) | the flat budget governor + the non-blocking handoff |
122
+ | [Invocation & configuration](references/commands.md) | how the hook is wired, the env/manifest knobs, troubleshooting |
123
+ | [Templates](assets/README.md) | domain-file and manifest-entry templates |
123
124
 
124
- ## Key Files
125
+ ## Key files
125
126
 
126
127
  | File | Purpose |
127
128
  |------|---------|
128
- | `.claude/hooks/synapse-engine.js` | Hook entry point (UserPromptSubmit) |
129
- | `.aiox-core/core/synapse/engine.js` | SynapseEngine orchestrator |
130
- | `.synapse/manifest` | Domain registry (KEY=VALUE) |
131
- | `.synapse/commands` | Star-command definitions |
132
- | `.claude/commands/synapse/manager.md` | CRUD command router |
129
+ | `.claude/hooks/synapse-engine.cjs` | The engine (UserPromptSubmit hook). |
130
+ | `.claude/constitution.md` | L0 source — the non-negotiable articles. |
131
+ | `.synapse/manifest` | Domain registry + budget/handoff scalars. |
132
+ | `.synapse/global`, `.synapse/pipeline` | Seeded always-on (L1) domains. |
133
+ | `.synapse/routing` | Seeded keyword-recall (L6) domain. |
@@ -1,50 +1,34 @@
1
- # SYNAPSE Assets
1
+ # SYNAPSE templates
2
2
 
3
- Templates for creating custom SYNAPSE domains and manifest entries.
3
+ Templates for adding a SYNAPSE domain by hand. There is no interactive creator — a domain is two
4
+ edits: a rule file in `.synapse/` and a registry entry in `.synapse/manifest`. See
5
+ [domains & rule files](../references/domains.md) and [the manifest format](../references/manifest.md).
4
6
 
5
- ## Templates Location
6
-
7
- Templates are maintained as the single source of truth in the CRUD commands directory:
8
-
9
- | Template | Location |
10
- |----------|----------|
11
- | **Domain template** | `.claude/commands/synapse/templates/domain-template` |
12
- | **Manifest entry template** | `.claude/commands/synapse/templates/manifest-entry-template` |
13
-
14
- These templates are used by the `*synapse create` command to scaffold new domains.
15
-
16
- ## Usage
17
-
18
- To create a new domain using these templates, run:
7
+ ## Domain rule file — `.synapse/<name>`
19
8
 
20
9
  ```
21
- *synapse create
10
+ # Domain: <name> (<always-on L1 | keyword-recall L6>) — <one-line description>
11
+ <NAME>_RULE_0=<first rule>
12
+ <NAME>_RULE_1=<second rule>
22
13
  ```
23
14
 
24
- Or reference the templates directly when creating domains manually.
15
+ `<NAME>` is the uppercase prefix; the file is named for its lowercase form. Rules are numbered
16
+ ascending from 0.
25
17
 
26
- ## Template Formats
18
+ ## Manifest entry — `.synapse/manifest`
27
19
 
28
- ### Domain Template
20
+ Always-on (loads on every prompt):
29
21
 
30
22
  ```
31
- # ==========================================
32
- # SYNAPSE Domain: {DOMAIN_NAME}
33
- # Created: {CURRENT_DATE}
34
- # Description: {DESCRIPTION}
35
- # ==========================================
36
-
37
- # Rules
38
- {DOMAIN_KEY}_RULE_0={FIRST_RULE}
23
+ <NAME>_STATE=active
24
+ <NAME>_ALWAYS_ON=true
39
25
  ```
40
26
 
41
- ### Manifest Entry Template
27
+ Keyword-recall (loads only when a trigger word appears in the prompt):
42
28
 
43
29
  ```
44
- # Layer 6: {domain-name}
45
- {DOMAIN_KEY}_STATE=active
46
- {DOMAIN_KEY}_RECALL={KEYWORDS}
47
- {DOMAIN_KEY}_EXCLUDE=
30
+ <NAME>_STATE=active
31
+ <NAME>_RECALL=word1,word2
48
32
  ```
49
33
 
50
- For the complete KEY=VALUE format specification, see [../references/manifest.md](../references/manifest.md).
34
+ Set `<NAME>_STATE=inactive` (or remove the entry) to stop loading the domain.