sinapse-ai 1.16.0 → 1.17.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.
Files changed (35) hide show
  1. package/.claude/hooks/doc-first-gate.cjs +156 -0
  2. package/.claude/hooks/enforce-story-gate.cjs +6 -2
  3. package/.claude/rules/hook-governance.md +1 -0
  4. package/.sinapse-ai/constitution.md +1 -1
  5. package/.sinapse-ai/core/atlas/atlas-data.js +278 -0
  6. package/.sinapse-ai/core/atlas/flows-pt.js +253 -0
  7. package/.sinapse-ai/core/atlas/flows.js +266 -0
  8. package/.sinapse-ai/core/atlas/index.js +62 -0
  9. package/.sinapse-ai/core/atlas/render-html.js +216 -0
  10. package/.sinapse-ai/core/atlas/render-markdown.js +313 -0
  11. package/.sinapse-ai/core/atlas/render-research-card.js +164 -0
  12. package/.sinapse-ai/core/orchestration/build-command.js +136 -0
  13. package/.sinapse-ai/core/orchestration/doc-first-resolver.js +322 -0
  14. package/.sinapse-ai/core/orchestration/route-command.js +97 -0
  15. package/.sinapse-ai/data/entity-registry.yaml +59 -14
  16. package/.sinapse-ai/development/agents/snps-orqx.md +9 -0
  17. package/.sinapse-ai/development/tasks/create-doc.md +1 -1
  18. package/.sinapse-ai/development/tasks/create-next-story.md +2 -3
  19. package/.sinapse-ai/development/tasks/spec-gather-requirements.md +11 -0
  20. package/.sinapse-ai/development/templates/approval-table.md +106 -0
  21. package/.sinapse-ai/development/workflows/spec-pipeline.yaml +1 -0
  22. package/.sinapse-ai/install-manifest.yaml +60 -16
  23. package/.sinapse-ai/product/templates/prd-tmpl.yaml +6 -2
  24. package/CHANGELOG.md +18 -2
  25. package/bin/sinapse.js +121 -0
  26. package/docs/framework/atlas/OPERATING-ATLAS.md +537 -0
  27. package/docs/framework/atlas/README.md +34 -0
  28. package/docs/framework/atlas/atlas-data.json +2810 -0
  29. package/docs/framework/atlas/atlas.html +392 -0
  30. package/package.json +1 -1
  31. package/packages/installer/src/wizard/ide-config-generator.js +6 -0
  32. package/scripts/install-chrome-brain.sh +2 -2
  33. package/scripts/validate-no-personal-leaks.js +24 -9
  34. package/squads/squad-copy/knowledge-base/consequence-headline-patterns.md +2 -2
  35. package/docs/guides/hooks-two-layers.md +0 -66
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Atlas → HTML dashboard renderer (visual).
3
+ *
4
+ * Produces a single self-contained HTML file (data embedded) with the same
5
+ * atlas data as the markdown atlas: at-a-glance counts, the operating-model
6
+ * Mermaid diagram, and searchable/filterable tables for workflows, agents,
7
+ * squads, the constitution, and rules. SINAPSE B&W aesthetic. No build step,
8
+ * no server — open the file.
9
+ *
10
+ * @module core/atlas/render-html
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ function esc(s) {
16
+ return String(s == null ? '' : s)
17
+ .replace(/&/g, '&')
18
+ .replace(/</g, '&lt;')
19
+ .replace(/>/g, '&gt;')
20
+ .replace(/"/g, '&quot;');
21
+ }
22
+
23
+ function renderAtlasHtml(d) {
24
+ const c = d.counts;
25
+ const stamp = d.generatedAt ? `Generated ${esc(d.generatedAt)}` : '';
26
+ const dataJson = JSON.stringify(d).replace(/</g, '\\u003c');
27
+
28
+ const stat = (n, label) => `<div class="stat"><b>${n}</b><span>${label}</span></div>`;
29
+
30
+ const flowsSection = (d.flows || [])
31
+ .map(
32
+ (f) => `<h3 style="margin:1.6rem 0 .3rem">${esc(f.title)}</h3>
33
+ <p class="lead">${esc(f.purpose)}</p>
34
+ <div class="mermaid">
35
+ ${esc(f.mermaid)}
36
+ </div>`,
37
+ )
38
+ .join('\n');
39
+
40
+ return `<!doctype html>
41
+ <html lang="en">
42
+ <head>
43
+ <meta charset="utf-8" />
44
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
45
+ <title>SINAPSE · Framework Operating Atlas</title>
46
+ <script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
47
+ <style>
48
+ :root{
49
+ --bg:#0a0a0a; --fg:#f5f5f0; --mut:#8a8a85; --line:#222; --card:#121212; --accent:#f5f5f0;
50
+ --font:'Inter',system-ui,-apple-system,Segoe UI,Roboto,sans-serif;
51
+ --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
52
+ }
53
+ *{box-sizing:border-box}
54
+ body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--font);line-height:1.5;
55
+ background-image:radial-gradient(rgba(255,255,255,.025) 1px,transparent 1px);background-size:3px 3px}
56
+ .wrap{max-width:min(1400px,94vw);margin:0 auto;padding:clamp(1.5rem,4vw,3rem) 0 5rem}
57
+ h1{font-size:clamp(1.6rem,4vw,2.6rem);font-weight:800;letter-spacing:-.02em;margin:0 0 .25rem}
58
+ .sub{color:var(--mut);font-family:var(--mono);font-size:.8rem;margin-bottom:2rem}
59
+ .stats{display:flex;flex-wrap:wrap;gap:.6rem;margin-bottom:2rem}
60
+ .stat{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:.7rem 1rem;min-width:96px}
61
+ .stat b{display:block;font-size:1.5rem;font-weight:800;font-family:var(--mono)}
62
+ .stat span{color:var(--mut);font-size:.72rem;text-transform:uppercase;letter-spacing:.06em}
63
+ nav{display:flex;flex-wrap:wrap;gap:.4rem;margin-bottom:1.5rem;position:sticky;top:0;
64
+ background:linear-gradient(var(--bg),var(--bg) 70%,transparent);padding:.6rem 0;z-index:5}
65
+ nav button{background:transparent;color:var(--mut);border:1px solid var(--line);border-radius:999px;
66
+ padding:.35rem .9rem;font-family:var(--mono);font-size:.78rem;cursor:pointer;transition:.15s}
67
+ nav button:hover{color:var(--fg);border-color:#444}
68
+ nav button.on{background:var(--fg);color:var(--bg);border-color:var(--fg)}
69
+ section{display:none;animation:fade .2s ease}
70
+ section.on{display:block}
71
+ @keyframes fade{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
72
+ h2{font-size:1.2rem;font-weight:700;margin:0 0 1rem;letter-spacing:-.01em}
73
+ .mermaid{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:1.5rem;margin-bottom:1.5rem}
74
+ .ctrl{display:flex;gap:.6rem;flex-wrap:wrap;margin-bottom:1rem}
75
+ input,select{background:var(--card);color:var(--fg);border:1px solid var(--line);border-radius:8px;
76
+ padding:.5rem .8rem;font-family:var(--mono);font-size:.82rem}
77
+ input{flex:1;min-width:200px}
78
+ table{width:100%;border-collapse:collapse;font-size:.82rem}
79
+ th,td{text-align:left;padding:.55rem .7rem;border-bottom:1px solid var(--line);vertical-align:top}
80
+ th{color:var(--mut);font-family:var(--mono);font-size:.7rem;text-transform:uppercase;letter-spacing:.06em;
81
+ position:sticky;top:3.4rem;background:var(--bg);cursor:pointer}
82
+ td code{font-family:var(--mono);font-size:.78rem;color:var(--fg);background:#1c1c1c;padding:.1rem .35rem;border-radius:4px}
83
+ .tag{font-family:var(--mono);font-size:.68rem;padding:.1rem .45rem;border-radius:999px;border:1px solid var(--line);color:var(--mut)}
84
+ .nn{color:#fff;border-color:#555}
85
+ .count{color:var(--mut);font-family:var(--mono);font-size:.75rem;margin-bottom:.6rem}
86
+ p.lead{color:var(--mut);max-width:70ch;margin:0 0 1.2rem}
87
+ a{color:var(--fg)}
88
+ </style>
89
+ </head>
90
+ <body>
91
+ <div class="wrap">
92
+ <h1>SINAPSE · Framework Operating Atlas</h1>
93
+ <div class="sub">How the framework works — routing, models, constitution, workflows, agents, squads. ${stamp}</div>
94
+ <div class="stats">
95
+ ${stat(c.squads, 'squads')}
96
+ ${stat(c.agentsTotal, 'agents')}
97
+ ${stat(c.orqx, 'orchestrators')}
98
+ ${stat(c.workflowsTotal, 'workflows')}
99
+ ${stat(c.articles, 'articles')}
100
+ ${stat(c.rules, 'rules')}
101
+ </div>
102
+
103
+ <nav id="nav"></nav>
104
+
105
+ <section id="s-model" class="on">
106
+ <h2>Operating model</h2>
107
+ <p class="lead">Every briefing flows through the same spine: the Imperator routes to a squad orchestrator or framework agent, the doc-first pipeline turns intent into PRD → Epic → Story → Spec, gates block code without docs, then implementation → QA → ship.</p>
108
+ <div class="mermaid">
109
+ flowchart TD
110
+ U[User briefing] --> I[Imperator · master orchestrator]
111
+ I -->|classify intent + project type| R{Route}
112
+ R -->|domain work| SQ[Squad orchestrator -orqx]
113
+ R -->|framework dev| FW[Framework agents]
114
+ SQ --> SP[Squad specialist]
115
+ FW --> DOC[Doc-first pipeline]
116
+ SP --> DOC
117
+ DOC -->|PRD - Epic - Story - Spec| GATE{{Gates}}
118
+ GATE -->|pass| EXEC[Implementation]
119
+ GATE -->|fail| BLOCK[Blocked: produce docs first]
120
+ EXEC --> QA[Quality gate] --> SHIP[Push / PR]
121
+ </div>
122
+ </section>
123
+
124
+ <section id="s-flows">
125
+ <h2>Framework flows</h2>
126
+ <p class="lead">How each mechanism of the framework runs — routing, models, doc-first enforcement, gates, project classification. The meta-workflows behind the machine.</p>
127
+ ${flowsSection}
128
+ </section>
129
+
130
+ <section id="s-constitution"><h2>Constitution · ${c.articles} articles</h2><div id="t-constitution"></div></section>
131
+ <section id="s-workflows"><h2>Workflows · ${c.workflowsTotal}</h2>
132
+ <div class="ctrl"><input id="q-workflows" placeholder="search workflows…"><select id="f-workflows-src"><option value="">all sources</option><option value="framework">framework</option><option value="squad">squad</option></select></div>
133
+ <div class="count" id="c-workflows"></div><div id="t-workflows"></div></section>
134
+ <section id="s-agents"><h2>Agents · ${c.agentsTotal}</h2>
135
+ <div class="ctrl"><input id="q-agents" placeholder="search agents…"><select id="f-agents-squad"></select><label style="color:var(--mut);font-family:var(--mono);font-size:.78rem;display:flex;align-items:center;gap:.4rem"><input type="checkbox" id="f-agents-orqx" style="flex:none">orchestrators only</label></div>
136
+ <div class="count" id="c-agents"></div><div id="t-agents"></div></section>
137
+ <section id="s-squads"><h2>Squads · ${c.squads}</h2><div id="t-squads"></div></section>
138
+ <section id="s-rules"><h2>Rules · ${c.rules}</h2><div id="t-rules"></div></section>
139
+ </div>
140
+
141
+ <script id="atlas-data" type="application/json">${dataJson}</script>
142
+ <script>
143
+ const D = JSON.parse(document.getElementById('atlas-data').textContent);
144
+ // startOnLoad:false — Mermaid renders diagrams in display:none sections at zero
145
+ // width. We render each section's diagrams the first time its tab is shown.
146
+ try{ mermaid.initialize({startOnLoad:false,theme:'dark',themeVariables:{background:'#121212',primaryColor:'#1c1c1c',primaryTextColor:'#f5f5f0',lineColor:'#555'}}); }catch(e){}
147
+ function renderDiagrams(sectionId){
148
+ try{
149
+ const nodes=[...document.querySelectorAll('#'+sectionId+' .mermaid:not([data-processed])')];
150
+ if(nodes.length&&window.mermaid&&mermaid.run) mermaid.run({nodes});
151
+ }catch(e){}
152
+ }
153
+
154
+ const TABS=[['s-model','Operating model'],['s-flows','Flows'],['s-constitution','Constitution'],['s-workflows','Workflows'],['s-agents','Agents'],['s-squads','Squads'],['s-rules','Rules']];
155
+ const nav=document.getElementById('nav');
156
+ TABS.forEach(([id,label],i)=>{const b=document.createElement('button');b.textContent=label;if(i===0)b.className='on';
157
+ b.onclick=()=>{document.querySelectorAll('nav button').forEach(x=>x.classList.remove('on'));b.classList.add('on');
158
+ document.querySelectorAll('section').forEach(s=>s.classList.remove('on'));document.getElementById(id).classList.add('on');
159
+ renderDiagrams(id);};
160
+ nav.appendChild(b);});
161
+ renderDiagrams('s-model'); // initial visible tab
162
+
163
+ function tbl(cols,rows){return '<table><thead><tr>'+cols.map(c=>'<th>'+c+'</th>').join('')+'</tr></thead><tbody>'+
164
+ rows.map(r=>'<tr>'+r.map(c=>'<td>'+c+'</td>').join('')+'</tr>').join('')+'</tbody></table>';}
165
+ const code=s=>'<code>'+(s||'')+'</code>';
166
+ const sev=s=>'<span class="tag'+(/NON-NEGOTIABLE/.test(s)?' nn':'')+'">'+s+'</span>';
167
+
168
+ document.getElementById('t-constitution').innerHTML=tbl(['Art.','Principle','Severity'],
169
+ D.articles.map(a=>[a.number,a.title,sev(a.severity)]));
170
+
171
+ document.getElementById('t-squads').innerHTML=tbl(['Squad','Name','Agents','Workflows','Orchestrators'],
172
+ D.squads.map(s=>[code(s.id),s.name,s.agents,s.workflows,(s.orqx||[]).map(code).join(' ')||'—']));
173
+
174
+ document.getElementById('t-rules').innerHTML=tbl(['Rule','Governs'],
175
+ D.rules.map(r=>[code(r.id),r.title]));
176
+
177
+ // Workflows (search + source filter)
178
+ function renderWorkflows(){
179
+ const q=(document.getElementById('q-workflows').value||'').toLowerCase();
180
+ const src=document.getElementById('f-workflows-src').value;
181
+ const rows=D.workflows.filter(w=>{
182
+ const isFw=w.source==='framework';
183
+ if(src==='framework'&&!isFw)return false; if(src==='squad'&&isFw)return false;
184
+ return !q||(w.id+' '+w.name+' '+(w.description||'')+' '+(w.squad||'')).toLowerCase().includes(q);
185
+ });
186
+ document.getElementById('c-workflows').textContent=rows.length+' of '+D.workflows.length;
187
+ document.getElementById('t-workflows').innerHTML=tbl(['Workflow','Source','Type','Description'],
188
+ rows.map(w=>[code(w.id),w.squad?code(w.squad):'<span class="tag">framework</span>',w.type||'—',w.description||'—']));
189
+ }
190
+ ['q-workflows','f-workflows-src'].forEach(id=>document.getElementById(id).addEventListener('input',renderWorkflows));
191
+
192
+ // Agents (search + squad filter + orqx toggle)
193
+ const squads=[...new Set(D.agents.map(a=>a.squad))].sort();
194
+ const sel=document.getElementById('f-agents-squad');
195
+ sel.innerHTML='<option value="">all squads</option>'+squads.map(s=>'<option>'+s+'</option>').join('');
196
+ function renderAgents(){
197
+ const q=(document.getElementById('q-agents').value||'').toLowerCase();
198
+ const sq=sel.value; const onlyOrqx=document.getElementById('f-agents-orqx').checked;
199
+ const rows=D.agents.filter(a=>{
200
+ if(sq&&a.squad!==sq)return false; if(onlyOrqx&&!a.isOrqx)return false;
201
+ return !q||(a.id+' '+(a.persona||'')+' '+(a.role||a.title||'')+' '+a.squad).toLowerCase().includes(q);
202
+ });
203
+ document.getElementById('c-agents').textContent=rows.length+' of '+D.agents.length;
204
+ document.getElementById('t-agents').innerHTML=tbl(['Agent','Persona','Squad','Role',''],
205
+ rows.map(a=>[code(a.id),a.persona||'—',code(a.squad),a.role||a.title||'—',a.isOrqx?'<span class="tag nn">orqx</span>':'']));
206
+ }
207
+ ['q-agents','f-agents-orqx'].forEach(id=>document.getElementById(id).addEventListener('input',renderAgents));
208
+ sel.addEventListener('change',renderAgents);
209
+
210
+ renderWorkflows();renderAgents();
211
+ </script>
212
+ </body>
213
+ </html>`;
214
+ }
215
+
216
+ module.exports = { renderAtlasHtml };
@@ -0,0 +1,313 @@
1
+ /**
2
+ * Atlas → Markdown renderer (LLM-readable).
3
+ *
4
+ * Produces a single, dense, navigable document describing how the SINAPSE
5
+ * framework operates — optimized to be read top-to-bottom by an LLM or a human.
6
+ * The narrative "operating model" sections are authored here; the catalogs
7
+ * (constitution, workflows, agents, squads, rules) are generated from the live
8
+ * atlas data so counts and lists never drift (Article VII).
9
+ *
10
+ * @module core/atlas/render-markdown
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ function h(s) {
16
+ return String(s == null ? '' : s).replace(/\|/g, '\\|').replace(/\r?\n/g, ' ').trim();
17
+ }
18
+
19
+ // ───────────────────────────────────────────────────────────────────────────
20
+ // Operating-model narrative (authored — the connective tissue)
21
+ // ───────────────────────────────────────────────────────────────────────────
22
+
23
+ function operatingModel(d) {
24
+ const c = d.counts;
25
+ return `## 1. How SINAPSE works (operating model)
26
+
27
+ SINAPSE is a **meta-framework**: it does not run application code itself — it
28
+ orchestrates AI agents (inside Claude Code / Codex) through a governed pipeline.
29
+ Every user request flows through the same spine:
30
+
31
+ \`\`\`mermaid
32
+ flowchart TD
33
+ U[User briefing] --> I[Imperator · master orchestrator]
34
+ I -->|classify intent + project type| R{Route}
35
+ R -->|domain work| SQ[Squad orchestrator -orqx]
36
+ R -->|framework dev| FW[Framework agents pm/architect/dev/qa/...]
37
+ SQ --> SP[Squad specialist agent]
38
+ FW --> DOC[Doc-first pipeline]
39
+ SP --> DOC
40
+ DOC -->|PRD -> Epic -> Story -> Spec| GATE{{Gates}}
41
+ GATE -->|pass| EXEC[Implementation]
42
+ GATE -->|fail| BLOCK[Blocked — produce docs first]
43
+ EXEC --> QA[Quality gate] --> SHIP[Push / PR · devops only]
44
+ \`\`\`
45
+
46
+ **Two routing decisions happen up front, both deterministic in spirit:**
47
+
48
+ 1. **Agent routing** — the Imperator (master \`-orqx\`) absorbs the briefing,
49
+ diagnoses the domain, and **delegates** to the right squad orchestrator,
50
+ which in turn routes to a specialist. Orchestrators never execute domain work
51
+ themselves (Article VIII — Mandatory Delegation). There are **${c.orqx}
52
+ orchestrators** (${c.squads} squad + 1 master) fronting **${c.agentsTotal}
53
+ agents** across **${c.squads} squads**.
54
+
55
+ 2. **Model routing** — each task picks the cheapest model that solves it
56
+ (haiku → sonnet → opus), escalating only when needed. Documentation/spec work
57
+ spends the tokens; execution then just reads the finished doc (see §4).
58
+
59
+ **Documentation-First is the law** (Article III). No project reaches code without
60
+ its planning documents. A briefing like "build a site" is forced through:
61
+
62
+ \`\`\`
63
+ brief → PRD → Epic → Story (validated, status ≥ Ready) → Spec → implementation
64
+ \`\`\`
65
+
66
+ The pipeline produces scannable approval tables in the terminal (requirements,
67
+ epics, stories) and is guarded so that a brand-new project cannot start coding
68
+ with no PRD / epic / story. Observability: \`sinapse route "<brief>"\` prints,
69
+ for any project, its type → workflow → which documents are missing → whether the
70
+ gate is satisfied — i.e. *exactly where it is breaking*.
71
+
72
+ This atlas is itself generated by \`sinapse atlas\`; its counts are read from disk,
73
+ so they are always exact.`;
74
+ }
75
+
76
+ function modelRouting() {
77
+ return `## 4. Model routing (cheapest model that solves it)
78
+
79
+ The token economy is a consequence of the pipeline: heavy thinking goes into the
80
+ PRD/spec, so execution mostly *reads a finished document* instead of reasoning.
81
+
82
+ | Task | Model | Effort |
83
+ |---|---|---|
84
+ | Cross-system architecture, complex debug, multi-file refactor | **opus** | xhigh |
85
+ | Spec Pipeline COMPLEX (score ≥ 16) | **opus** | max |
86
+ | Feature from spec, code review, bug fix, tests, stories | **sonnet** | high |
87
+ | Single-file analysis, factual question | **sonnet** | medium |
88
+ | Lint, rename, YAML, lookup, bulk | **haiku** | low |
89
+
90
+ Rule of thumb: when in doubt, drop a tier and escalate only on failure. A
91
+ sub-agent is only spawned for real parallel fan-out (≥ 8 tool calls), otherwise
92
+ work runs inline. *(Source of truth: \`.claude/rules/token-economy.md\`.)*`;
93
+ }
94
+
95
+ // ───────────────────────────────────────────────────────────────────────────
96
+ // Generated catalogs
97
+ // ───────────────────────────────────────────────────────────────────────────
98
+
99
+ function constitutionTable(d) {
100
+ const rows = d.articles
101
+ .map((a) => `| ${a.number} | ${h(a.title)} | ${h(a.severity)} |`)
102
+ .join('\n');
103
+ return `## 3. Constitution — the ${d.counts.articles} articles
104
+
105
+ Gates automatically block violations of NON-NEGOTIABLE / MUST articles.
106
+ *(Source of truth: \`.sinapse-ai/constitution.md\`.)*
107
+
108
+ | Art. | Principle | Severity |
109
+ |---|---|---|
110
+ ${rows}`;
111
+ }
112
+
113
+ function rulesTable(d) {
114
+ const rows = d.rules.map((r) => `| \`${h(r.id)}\` | ${h(r.title)} |`).join('\n');
115
+ return `## 5. Non-negotiable rules (${d.counts.rules})
116
+
117
+ Loaded automatically by the harness. *(Source: \`.claude/rules/\`.)*
118
+
119
+ | Rule | What it governs |
120
+ |---|---|
121
+ ${rows}`;
122
+ }
123
+
124
+ function workflowCatalog(d) {
125
+ const fw = d.workflows.filter((w) => w.source === 'framework');
126
+ const sq = d.workflows.filter((w) => w.source !== 'framework');
127
+
128
+ const fwRows = fw
129
+ .map((w) => `| \`${h(w.id)}\` | ${h(w.type) || '—'} | ${h(w.description) || '—'} |`)
130
+ .join('\n');
131
+
132
+ // group squad workflows by squad
133
+ const bySquad = {};
134
+ for (const w of sq) (bySquad[w.squad] = bySquad[w.squad] || []).push(w);
135
+ const sqRows = Object.keys(bySquad)
136
+ .sort()
137
+ .map((s) => `| \`${h(s)}\` | ${bySquad[s].map((w) => `\`${h(w.id)}\``).join(', ')} |`)
138
+ .join('\n');
139
+
140
+ return `## 6. Workflow catalog (${d.counts.workflowsTotal} total)
141
+
142
+ **${d.counts.frameworkWorkflows} framework workflows** + **${d.counts.squadWorkflows}
143
+ squad workflows**. The 13 primary framework workflows have detailed docs in
144
+ \`docs/sinapse-workflows/\`.
145
+
146
+ ### Framework workflows (${fw.length})
147
+
148
+ | Workflow | Type | Description |
149
+ |---|---|---|
150
+ ${fwRows}
151
+
152
+ ### Squad workflows (${sq.length}, grouped by squad)
153
+
154
+ | Squad | Workflows |
155
+ |---|---|
156
+ ${sqRows}`;
157
+ }
158
+
159
+ function agentsSquadsMap(d) {
160
+ const orqxRows = d.agents
161
+ .filter((a) => a.isOrqx)
162
+ .sort((a, b) => a.squad.localeCompare(b.squad))
163
+ .map((a) => `| \`${h(a.id)}\` | ${h(a.persona) || '—'} | ${h(a.squad)} | ${h(a.role || a.title) || '—'} |`)
164
+ .join('\n');
165
+
166
+ const squadRows = d.squads
167
+ .map((s) => `| \`${h(s.id)}\` | ${h(s.name)} | ${s.agents} | ${s.workflows} | ${s.orqx.map((o) => `\`${h(o)}\``).join(', ') || '—'} |`)
168
+ .join('\n');
169
+
170
+ return `## 7. Agents & squads map
171
+
172
+ **${d.counts.agentsTotal} agents** = ${d.counts.frameworkAgents} framework +
173
+ ${d.counts.squadAgents} squad, fronted by **${d.counts.orqx} orchestrators**.
174
+
175
+ ### Orchestrators (${d.counts.orqx}) — the routing layer
176
+
177
+ | Orchestrator | Persona | Squad | Role |
178
+ |---|---|---|---|
179
+ ${orqxRows}
180
+
181
+ ### Squads (${d.counts.squads})
182
+
183
+ | Squad | Name | Agents | Workflows | Orchestrator(s) |
184
+ |---|---|---|---|---|
185
+ ${squadRows}
186
+
187
+ > The full ${d.counts.agentsTotal}-agent index is in the generated
188
+ > \`atlas-data.json\` (one object per agent, with file path and role).`;
189
+ }
190
+
191
+ // ───────────────────────────────────────────────────────────────────────────
192
+ // Routing detail (authored §2)
193
+ // ───────────────────────────────────────────────────────────────────────────
194
+
195
+ function routingDetail() {
196
+ return `## 2. Agent routing — briefing to specialist
197
+
198
+ \`\`\`mermaid
199
+ sequenceDiagram
200
+ participant U as User
201
+ participant I as Imperator (master -orqx)
202
+ participant S as Squad -orqx
203
+ participant A as Specialist agent
204
+ U->>I: briefing (natural language)
205
+ I->>I: classify intent + project type (deterministic)
206
+ alt domain work
207
+ I->>S: delegate (announce plan, await go)
208
+ S->>A: route to specialist
209
+ A-->>S: artifact / result
210
+ S-->>I: consolidated
211
+ else framework dev
212
+ I->>A: route to framework agent (pm/architect/dev/qa)
213
+ end
214
+ I-->>U: business-language result (no internal jargon)
215
+ \`\`\`
216
+
217
+ Rules that make this deterministic, not improvised:
218
+ - **Article VIII — Mandatory Delegation:** an orchestrator that does domain work
219
+ itself is a constitutional violation. Even "just do it yourself" → delegate.
220
+ - **Auto-routing:** the user never types an agent name; the system detects the
221
+ domain and routes. Acknowledgement is business language, never \`@agent-id\`.
222
+ - **Project-type gate:** site / lp / app / platform / saas / api / service with
223
+ no epic → the greenfield workflow is invoked *before* any code.`;
224
+ }
225
+
226
+ // ───────────────────────────────────────────────────────────────────────────
227
+ // Framework flows (authored data → diagrams)
228
+ // ───────────────────────────────────────────────────────────────────────────
229
+
230
+ function frameworkFlows(d) {
231
+ const flows = d.flows || [];
232
+ if (!flows.length) return '';
233
+ const blocks = flows
234
+ .map(
235
+ (f) => `### ${f.title}
236
+
237
+ ${f.purpose}
238
+
239
+ \`\`\`mermaid
240
+ ${f.mermaid}
241
+ \`\`\``,
242
+ )
243
+ .join('\n\n');
244
+ return `## 2b. Framework flows — how each mechanism runs
245
+
246
+ The meta-workflows of the framework itself (not the development workflows in
247
+ \`docs/sinapse-workflows/\`). Each is a single diagram you can read at a glance.
248
+
249
+ ${blocks}`;
250
+ }
251
+
252
+ // ───────────────────────────────────────────────────────────────────────────
253
+ // Public
254
+ // ───────────────────────────────────────────────────────────────────────────
255
+
256
+ /**
257
+ * Render the full LLM-readable atlas markdown from atlas data.
258
+ * @param {object} d - Output of buildAtlasData.
259
+ * @returns {string} Markdown.
260
+ */
261
+ function renderAtlasMarkdown(d) {
262
+ const stamp = d.generatedAt ? `\n> Generated: ${d.generatedAt}` : '';
263
+ return `# SINAPSE — Framework Operating Atlas
264
+
265
+ > Single, generated map of how the SINAPSE framework works: routing, models,
266
+ > constitution, workflows, agents, squads. Regenerate with \`sinapse atlas\`.
267
+ > Counts are read from disk (Article VII — always exact).${stamp}
268
+
269
+ **At a glance:** ${d.counts.squads} squads · ${d.counts.agentsTotal} agents
270
+ (${d.counts.frameworkAgents} framework + ${d.counts.squadAgents} squad) ·
271
+ ${d.counts.orqx} orchestrators · ${d.counts.workflowsTotal} workflows
272
+ (${d.counts.frameworkWorkflows} framework + ${d.counts.squadWorkflows} squad) ·
273
+ ${d.counts.articles} constitutional articles · ${d.counts.rules} rules.
274
+
275
+ ---
276
+
277
+ ${operatingModel(d)}
278
+
279
+ ---
280
+
281
+ ${routingDetail()}
282
+
283
+ ---
284
+
285
+ ${frameworkFlows(d)}
286
+
287
+ ---
288
+
289
+ ${constitutionTable(d)}
290
+
291
+ ---
292
+
293
+ ${modelRouting()}
294
+
295
+ ---
296
+
297
+ ${rulesTable(d)}
298
+
299
+ ---
300
+
301
+ ${workflowCatalog(d)}
302
+
303
+ ---
304
+
305
+ ${agentsSquadsMap(d)}
306
+
307
+ ---
308
+
309
+ *Generated by \`.sinapse-ai/core/atlas\` · data: \`docs/framework/atlas/atlas-data.json\`.*
310
+ `;
311
+ }
312
+
313
+ module.exports = { renderAtlasMarkdown };