@gcunharodrigues/wrxn 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/wrxn.cjs +60 -2
- package/lib/convert.cjs +215 -0
- package/lib/executor.cjs +1 -1
- package/lib/ingest.cjs +174 -0
- package/manifest.json +10 -0
- package/migrations/002-seeded-honesty.cjs +100 -0
- package/package.json +2 -2
- package/payload/.claude/constitution.md +4 -1
- package/payload/.claude/hooks/enforce-push-authority.cjs +6 -5
- package/payload/.claude/hooks/session-end.cjs +86 -46
- package/payload/.claude/skills/ingest/SKILL.md +72 -0
- package/payload/.claude/skills/synapse/SKILL.md +94 -93
- package/payload/.claude/skills/synapse/assets/README.md +18 -34
- package/payload/.claude/skills/synapse/references/brackets.md +50 -76
- package/payload/.claude/skills/synapse/references/commands.md +43 -100
- package/payload/.claude/skills/synapse/references/domains.md +41 -105
- package/payload/.claude/skills/synapse/references/layers.md +74 -152
- package/payload/.claude/skills/synapse/references/manifest.md +58 -108
- package/payload/.mcp.json +1 -1
- package/payload/.synapse/global +1 -1
- package/payload/.synapse/routing +1 -1
- package/payload/.wrxn/raw/.gitkeep +0 -0
- package/payload/docs/agents/domain.md +8 -13
|
@@ -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
|
|
6
|
-
//
|
|
7
|
-
//
|
|
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({}); //
|
|
37
|
+
return emit({}); // confirmed
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
return emit({
|
|
40
41
|
decision: 'block',
|
|
41
42
|
reason:
|
|
42
|
-
'Remote git
|
|
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
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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.
|
|
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
|
|
155
|
+
/* page write failed → fail-open; leave the trail intact for no-loss */
|
|
120
156
|
}
|
|
121
|
-
}
|
|
122
|
-
|
|
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
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ingest
|
|
3
|
+
description: Distill a dropped source file (PDF/DOCX/HTML/PPTX/XLSX/TXT) into curated memory-wiki pages — a summary page + N note pages, each linked back to the raw source. Use when an operator drops a file in .wrxn/raw/ (or names one) and says "ingest this", "distill this document", or "turn this source into wiki notes".
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# ingest — read → distill → split into notes
|
|
8
|
+
|
|
9
|
+
The distillation half of `wrxn ingest <file>`. Turns a raw source into compounding wiki knowledge,
|
|
10
|
+
following the Karpathy LLM-wiki pattern (Adler's *How to Read a Book*: read → distill → split into
|
|
11
|
+
notes). This is built as the first concrete slice of the Phase-3 `dream` ingest.
|
|
12
|
+
|
|
13
|
+
The work is **split** (the kernel executor pattern):
|
|
14
|
+
|
|
15
|
+
- **The harness is deterministic** (`lib/ingest.cjs` → `wrxn ingest`): convert the source to markdown,
|
|
16
|
+
place/keep the raw under `.wrxn/raw/`, write the pages you produce with a `derived_from:` provenance
|
|
17
|
+
stamp, and enforce the **additive-only** guard. You do NOT re-implement any of that.
|
|
18
|
+
- **You are the distillation step.** You read the converted markdown and produce the *content* — a
|
|
19
|
+
summary page + N note pages. Your job is the curation quality.
|
|
20
|
+
|
|
21
|
+
## Scope (PRD decision E)
|
|
22
|
+
|
|
23
|
+
- **Inspectional + analytical depth**: 1 source → **1 summary page + N note pages**. Divide the source
|
|
24
|
+
into its natural documents (sections / themes), one note page each.
|
|
25
|
+
- **Additive-only.** You CREATE new pages. You never edit an existing wiki page and never synthesise
|
|
26
|
+
across sources — that is the `dream` loop, out of scope. The harness refuses to overwrite, so a slug
|
|
27
|
+
that collides with an existing page is silently skipped: choose fresh, source-specific slugs.
|
|
28
|
+
|
|
29
|
+
## Loop
|
|
30
|
+
|
|
31
|
+
1. **Convert.** Read the converted markdown — either run `wrxn convert <file>` and read its output, or
|
|
32
|
+
read what the harness converted. Do not parse the binary yourself.
|
|
33
|
+
2. **Read for the gist.** One pass for the whole; identify the source's structure and its key claims.
|
|
34
|
+
3. **Summary page.** One page capturing what the source IS and its main points — the inspectional read.
|
|
35
|
+
4. **Note pages.** One page per distinct theme/section — the analytical read. Each note is
|
|
36
|
+
self-contained and titled by its idea, not "Section 3".
|
|
37
|
+
5. **Emit the result** as the structured object below and hand it to the harness.
|
|
38
|
+
|
|
39
|
+
## Result contract
|
|
40
|
+
|
|
41
|
+
The harness consumes this exact shape (the CLI accepts it via `--distillation <result.json>`):
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"summary": { "slug": "paper-summary", "title": "...", "description": "one line", "body": "markdown" },
|
|
46
|
+
"notes": [
|
|
47
|
+
{ "slug": "paper-method", "title": "...", "description": "one line", "body": "markdown" },
|
|
48
|
+
{ "slug": "paper-results", "title": "...", "description": "one line", "body": "markdown" }
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- `slug` — kebab-case (`[a-z0-9-]`), unique, source-specific (prefix with the source name to avoid
|
|
54
|
+
collisions). The harness rejects a non-kebab slug.
|
|
55
|
+
- `summary` is required (`{slug, body}` minimum); `notes` is an array (≥1 in practice).
|
|
56
|
+
- `tier` is optional per page (default `concepts`; may be `concepts|decisions|gotchas|sessions`).
|
|
57
|
+
- The harness adds the `derived_from: .wrxn/raw/<file>` stamp, `role`, and `source: wrxn-ingest`
|
|
58
|
+
frontmatter — you do not write frontmatter into `body`.
|
|
59
|
+
|
|
60
|
+
## Run
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# the harness does convert → raw placement → provenance stamp → additive write:
|
|
64
|
+
wrxn ingest .wrxn/raw/paper.pdf --distillation result.json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Re-running on the same source is safe — existing pages are skipped, never clobbered.
|
|
68
|
+
|
|
69
|
+
## Source
|
|
70
|
+
|
|
71
|
+
WRXN Kernel issue multiformat-distill-06 (PRD decisions D + E). Harness: `lib/ingest.cjs`.
|
|
72
|
+
Converter: `lib/convert.cjs` (slice 05). Wiki: `.wrxn/wiki/` (see the `memory` skill).
|
|
@@ -1,132 +1,133 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: synapse
|
|
3
|
-
description: "
|
|
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
|
|
6
|
+
# SYNAPSE context engine
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## What it is
|
|
9
9
|
|
|
10
|
-
SYNAPSE
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
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
|
-
|
|
20
|
+
## The three layers
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
SYNAPSE assembles each prompt from three layers, in this order:
|
|
22
23
|
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
34
|
+
## How a domain is defined
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
39
|
+
```
|
|
40
|
+
# .synapse/manifest
|
|
41
|
+
GLOBAL_STATE=active
|
|
42
|
+
GLOBAL_ALWAYS_ON=true
|
|
34
43
|
|
|
35
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
53
|
+
## The token budget
|
|
48
54
|
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
61
|
+
## The handoff directive
|
|
54
62
|
|
|
55
|
-
|
|
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
|
-
|
|
70
|
+
## Output shape
|
|
58
71
|
|
|
59
72
|
```
|
|
60
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
84
|
+
[RECALL: routing]
|
|
85
|
+
1. git push, PR creation, and release tags are deliberate acts held behind a confirmation flag ...
|
|
91
86
|
|
|
92
|
-
|
|
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
|
-
|
|
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
|
-
|
|
95
|
+
</synapse-rules>
|
|
96
|
+
```
|
|
103
97
|
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
101
|
+
## Configuration
|
|
108
102
|
|
|
109
|
-
|
|
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
|
-
|
|
112
|
+
See [invocation & configuration](references/commands.md).
|
|
112
113
|
|
|
113
|
-
|
|
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
|
-
|
|
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
|
|
125
|
+
## Key files
|
|
125
126
|
|
|
126
127
|
| File | Purpose |
|
|
127
128
|
|------|---------|
|
|
128
|
-
| `.claude/hooks/synapse-engine.
|
|
129
|
-
| `.
|
|
130
|
-
| `.synapse/manifest` | Domain registry
|
|
131
|
-
| `.synapse/
|
|
132
|
-
| `.
|
|
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
|
|
1
|
+
# SYNAPSE templates
|
|
2
2
|
|
|
3
|
-
Templates for
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
15
|
+
`<NAME>` is the uppercase prefix; the file is named for its lowercase form. Rules are numbered
|
|
16
|
+
ascending from 0.
|
|
25
17
|
|
|
26
|
-
##
|
|
18
|
+
## Manifest entry — `.synapse/manifest`
|
|
27
19
|
|
|
28
|
-
|
|
20
|
+
Always-on (loads on every prompt):
|
|
29
21
|
|
|
30
22
|
```
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
27
|
+
Keyword-recall (loads only when a trigger word appears in the prompt):
|
|
42
28
|
|
|
43
29
|
```
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
{DOMAIN_KEY}_RECALL={KEYWORDS}
|
|
47
|
-
{DOMAIN_KEY}_EXCLUDE=
|
|
30
|
+
<NAME>_STATE=active
|
|
31
|
+
<NAME>_RECALL=word1,word2
|
|
48
32
|
```
|
|
49
33
|
|
|
50
|
-
|
|
34
|
+
Set `<NAME>_STATE=inactive` (or remove the entry) to stop loading the domain.
|