@techierathore/techieflow 1.0.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 (151) hide show
  1. package/.claude/commands/TechieFlow/agents/analyst.md +97 -0
  2. package/.claude/commands/TechieFlow/agents/architect.md +84 -0
  3. package/.claude/commands/TechieFlow/agents/flow-master.md +217 -0
  4. package/.claude/commands/TechieFlow/agents/verifier.md +72 -0
  5. package/.claude/commands/TechieFlow/tasks/_metrics-emit-gate.md +138 -0
  6. package/.claude/commands/TechieFlow/tasks/_smoke-test-policy.md +98 -0
  7. package/.claude/commands/TechieFlow/tasks/_status-update-gate.md +78 -0
  8. package/.claude/commands/TechieFlow/tasks/_yolo-mode.md +77 -0
  9. package/.claude/commands/TechieFlow/tasks/advanced-elicitation.md +117 -0
  10. package/.claude/commands/TechieFlow/tasks/amend-docs.md +149 -0
  11. package/.claude/commands/TechieFlow/tasks/author-brd.md +79 -0
  12. package/.claude/commands/TechieFlow/tasks/build-phase.md +231 -0
  13. package/.claude/commands/TechieFlow/tasks/create-deep-research-prompt.md +278 -0
  14. package/.claude/commands/TechieFlow/tasks/create-doc.md +101 -0
  15. package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +507 -0
  16. package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +224 -0
  17. package/.claude/commands/TechieFlow/tasks/devguide.md +224 -0
  18. package/.claude/commands/TechieFlow/tasks/document-project.md +343 -0
  19. package/.claude/commands/TechieFlow/tasks/execute-checklist.md +86 -0
  20. package/.claude/commands/TechieFlow/tasks/facilitate-brainstorming-session.md +136 -0
  21. package/.claude/commands/TechieFlow/tasks/fix-issues.md +134 -0
  22. package/.claude/commands/TechieFlow/tasks/generate-html.md +115 -0
  23. package/.claude/commands/TechieFlow/tasks/handoff-phase.md +126 -0
  24. package/.claude/commands/TechieFlow/tasks/index-docs.md +173 -0
  25. package/.claude/commands/TechieFlow/tasks/kb-mode-interaction.md +75 -0
  26. package/.claude/commands/TechieFlow/tasks/log-miss.md +160 -0
  27. package/.claude/commands/TechieFlow/tasks/metrics-report.md +143 -0
  28. package/.claude/commands/TechieFlow/tasks/mockups.md +122 -0
  29. package/.claude/commands/TechieFlow/tasks/productguide.md +105 -0
  30. package/.claude/commands/TechieFlow/tasks/refresh-status.md +135 -0
  31. package/.claude/commands/TechieFlow/tasks/render-workflow-docs.md +117 -0
  32. package/.claude/commands/TechieFlow/tasks/shard-doc.md +185 -0
  33. package/.claude/commands/TechieFlow/tasks/split-brd.md +131 -0
  34. package/.claude/commands/TechieFlow/tasks/triage-issues.md +157 -0
  35. package/.claude/commands/TechieFlow/tasks/verify-phase.md +579 -0
  36. package/.claude/commands/generate-html.md +124 -0
  37. package/.codex/config.toml +46 -0
  38. package/.codex/hooks.json +63 -0
  39. package/.codex/rules/techieflow.rules +8 -0
  40. package/.opencode/command/generate-html.md +129 -0
  41. package/.opencode/plugin/techieflow.js +350 -0
  42. package/.tfcore/TOKEN-GUIDE.md +59 -0
  43. package/.tfcore/agents/analyst.md +97 -0
  44. package/.tfcore/agents/architect.md +84 -0
  45. package/.tfcore/agents/flow-master.md +217 -0
  46. package/.tfcore/agents/verifier.md +72 -0
  47. package/.tfcore/checklists/architect-checklist.md +438 -0
  48. package/.tfcore/core-config.yaml +47 -0
  49. package/.tfcore/data/brainstorming-techniques.md +36 -0
  50. package/.tfcore/data/elicitation-methods.md +154 -0
  51. package/.tfcore/data/techieflow-kb.md +807 -0
  52. package/.tfcore/data/technical-preferences.md +3 -0
  53. package/.tfcore/enhanced-ide-development-workflow.md +248 -0
  54. package/.tfcore/hooks/block-git.sh +301 -0
  55. package/.tfcore/hooks/codex-adapter.py +248 -0
  56. package/.tfcore/hooks/guard-artifacts.sh +101 -0
  57. package/.tfcore/hooks/guard-status-html.sh +102 -0
  58. package/.tfcore/hooks/guard-status.sh +124 -0
  59. package/.tfcore/hooks/guard-verify.sh +108 -0
  60. package/.tfcore/hooks/metrics-session.sh +129 -0
  61. package/.tfcore/hooks/session-pointer.sh +52 -0
  62. package/.tfcore/hooks/sweep-artifacts.sh +198 -0
  63. package/.tfcore/install-manifest.yaml +231 -0
  64. package/.tfcore/routing.yaml +105 -0
  65. package/.tfcore/tasks/_metrics-emit-gate.md +138 -0
  66. package/.tfcore/tasks/_smoke-test-policy.md +98 -0
  67. package/.tfcore/tasks/_status-update-gate.md +78 -0
  68. package/.tfcore/tasks/_yolo-mode.md +77 -0
  69. package/.tfcore/tasks/advanced-elicitation.md +117 -0
  70. package/.tfcore/tasks/amend-docs.md +149 -0
  71. package/.tfcore/tasks/author-brd.md +79 -0
  72. package/.tfcore/tasks/build-phase.md +231 -0
  73. package/.tfcore/tasks/create-deep-research-prompt.md +278 -0
  74. package/.tfcore/tasks/create-doc.md +101 -0
  75. package/.tfcore/tasks/day1-brownfield.md +507 -0
  76. package/.tfcore/tasks/day1-greenfield.md +224 -0
  77. package/.tfcore/tasks/devguide.md +224 -0
  78. package/.tfcore/tasks/document-project.md +343 -0
  79. package/.tfcore/tasks/execute-checklist.md +86 -0
  80. package/.tfcore/tasks/facilitate-brainstorming-session.md +136 -0
  81. package/.tfcore/tasks/fix-issues.md +134 -0
  82. package/.tfcore/tasks/generate-html.md +115 -0
  83. package/.tfcore/tasks/handoff-phase.md +126 -0
  84. package/.tfcore/tasks/index-docs.md +173 -0
  85. package/.tfcore/tasks/kb-mode-interaction.md +75 -0
  86. package/.tfcore/tasks/log-miss.md +160 -0
  87. package/.tfcore/tasks/metrics-report.md +143 -0
  88. package/.tfcore/tasks/mockups.md +122 -0
  89. package/.tfcore/tasks/productguide.md +105 -0
  90. package/.tfcore/tasks/refresh-status.md +135 -0
  91. package/.tfcore/tasks/render-workflow-docs.md +117 -0
  92. package/.tfcore/tasks/shard-doc.md +185 -0
  93. package/.tfcore/tasks/split-brd.md +131 -0
  94. package/.tfcore/tasks/triage-issues.md +157 -0
  95. package/.tfcore/tasks/verify-phase.md +579 -0
  96. package/.tfcore/telemetry/SCHEMA.md +678 -0
  97. package/.tfcore/telemetry/install-metrics.sh +390 -0
  98. package/.tfcore/telemetry/pre-commit +94 -0
  99. package/.tfcore/telemetry/tf-metrics.sh +1415 -0
  100. package/.tfcore/templates/architecture-tmpl.yaml +650 -0
  101. package/.tfcore/templates/brainstorming-output-tmpl.yaml +156 -0
  102. package/.tfcore/templates/brownfield-architecture-tmpl.yaml +476 -0
  103. package/.tfcore/templates/competitor-analysis-tmpl.yaml +306 -0
  104. package/.tfcore/templates/front-end-architecture-tmpl.yaml +218 -0
  105. package/.tfcore/templates/fullstack-architecture-tmpl.yaml +823 -0
  106. package/.tfcore/templates/market-research-tmpl.yaml +252 -0
  107. package/.tfcore/templates/project-brief-tmpl.yaml +221 -0
  108. package/.tfcore/templates/v4custom/app-agents-md-tmpl.md +61 -0
  109. package/.tfcore/templates/v4custom/app-architecture-tmpl.md +127 -0
  110. package/.tfcore/templates/v4custom/app-brd-tmpl.md +204 -0
  111. package/.tfcore/templates/v4custom/app-checklist-tmpl.md +90 -0
  112. package/.tfcore/templates/v4custom/app-claude-md-tmpl.md +33 -0
  113. package/.tfcore/templates/v4custom/app-devguide-tmpl.md +167 -0
  114. package/.tfcore/templates/v4custom/app-editorconfig-tmpl.editorconfig +44 -0
  115. package/.tfcore/templates/v4custom/app-library-feedback-tmpl.md +31 -0
  116. package/.tfcore/templates/v4custom/app-productguide-tmpl.md +54 -0
  117. package/.tfcore/templates/v4custom/app-project-status-tmpl.md +98 -0
  118. package/.tfcore/templates/v4custom/app-uidesign-tmpl.md +48 -0
  119. package/.tfcore/templates/v4custom/app-usageguide-tmpl.md +67 -0
  120. package/.tfcore/templates/v4custom/build-invocation-ladder.md +190 -0
  121. package/.tfcore/templates/v4custom/html-render-shell.md +502 -0
  122. package/.tfcore/templates/v4custom/metrics-report-template.md +273 -0
  123. package/.tfcore/templates/v4custom/opencode-operating-contract.md +56 -0
  124. package/.tfcore/user-guide.md +633 -0
  125. package/.tfcore/utils/techieflow-doc-template.md +325 -0
  126. package/.tfcore/utils/tf-assets.sh +310 -0
  127. package/.tfcore/utils/tf-codex-bind.py +196 -0
  128. package/.tfcore/utils/tf-codex-telemetry.py +60 -0
  129. package/.tfcore/utils/tf-emit.sh +1075 -0
  130. package/.tfcore/utils/tf-gitignore-audit.sh +281 -0
  131. package/.tfcore/utils/tf-goal.sh +421 -0
  132. package/.tfcore/utils/tf-harness.sh +160 -0
  133. package/.tfcore/utils/tf-mockup-parity.mjs +555 -0
  134. package/.tfcore/utils/tf-mockup-parity.sh +65 -0
  135. package/.tfcore/utils/tf-opencode-audit.py +126 -0
  136. package/.tfcore/utils/tf-perf.sh +241 -0
  137. package/.tfcore/utils/tf-render-html.py +715 -0
  138. package/.tfcore/utils/tf-render-html.sh +28 -0
  139. package/.tfcore/utils/tf-routing-bind.sh +216 -0
  140. package/.tfcore/utils/tf-routing.sh +247 -0
  141. package/.tfcore/utils/tf-yolo.sh +119 -0
  142. package/.tfcore/working-in-the-brownfield.md +606 -0
  143. package/LICENSE +201 -0
  144. package/README.md +1212 -0
  145. package/WORKFLOW.html +1986 -0
  146. package/docs/TechieFlow-Installation.md +299 -0
  147. package/opencode.jsonc +929 -0
  148. package/package.json +60 -0
  149. package/scripts/install.mjs +992 -0
  150. package/scripts/test-install.mjs +505 -0
  151. package/scripts/validate.mjs +155 -0
@@ -0,0 +1,502 @@
1
+ # html-render-shell
2
+
3
+ **Shared HTML scaffold for every rendered MD → HTML output in this framework.**
4
+
5
+ Both `render-workflow-docs.md` and `generate-html.md` MUST use the exact CSS, JS, and structural rules below. Single source of truth. If you find yourself hand-rolling a different palette or skipping the scripts, STOP and re-read this file.
6
+
7
+ This is a **specification + verbatim snippets to inline**. Do NOT load via `<link>` / `<script src=>`. Every rendered HTML is self-contained.
8
+
9
+ > **This spec has an implementation — use it, do not hand-author HTML.**
10
+ > ```bash
11
+ > bash .tfcore/utils/tf-render-html.sh <file.md> [more.md ...]
12
+ > ```
13
+ > `tf-render-html.py` **extracts §2 CSS, §3's theme script and §7 JS out of THIS FILE at render time**, so the shell can never drift from its own documentation: edit the snippets below and every future render picks them up. Python 3 standard library only — no pandoc, no node, no pip.
14
+ >
15
+ > Added 2026-08-27 (TfLens TF-003). Before that, every rendered page was hand-authored by the model from this spec, on every phase — ~75–80k output tokens per render cycle, non-reproducible output, and silent-truncation risk on large files. Read this file to understand or change the shell; you do not need to read it to render.
16
+
17
+ ---
18
+
19
+ ## 0. Output contract
20
+
21
+ Each rendered HTML file is:
22
+
23
+ - A **single self-contained `.html` file**, no external CSS, no external JS except the two `<script>` tags below (mermaid + svg-pan-zoom from jsdelivr CDN).
24
+ - Sibling to the source MD (same filename, `.html` extension).
25
+ - **Light + dark theme with a persisted toggle** (§2 + §3 + §7). Default is chosen by **time of day** on first open — light between 07:00 and 19:00 local, softened dark otherwise — then the user's explicit toggle choice (saved in `localStorage` as `tf-theme`) wins on every later visit. Light is a **warm off-white** (never harsh bright white); dark is a softened near-black. Monospace for code/pre, sans for body.
26
+ - **Only human-readable docs are ever rendered to HTML** (BRD, Architecture, UsageGuide, DevGuide, PROJECT-STATUS). The **checklists are AI-agent working documents — never render them to HTML** (they are read by agents in markdown; an HTML mirror just wastes tokens and drifts). If you are about to emit a `*-Checklist.html`, STOP — that is banned.
27
+ - Working anchor links from any auto-generated or hand-written TOC to its target heading.
28
+ - Copy buttons on every **code** `<pre>` — but NOT on `pre.mermaid` (a button injected there corrupts the diagram source mermaid reads → "Syntax error"; mermaid blocks get copy/export from their own diagram toolbar).
29
+ - Mermaid toolbar on every rendered diagram.
30
+ - Sidebar TOC **only when the document has more than 6 `<h2>` headings**; otherwise full-width main column with no sidebar.
31
+
32
+ If any of the above is missing, the render is broken — go back and fix.
33
+
34
+ ---
35
+
36
+ ## 1. Heading slug rule (CRITICAL — TOC links break when this differs)
37
+
38
+ The same slug function MUST produce the `id="..."` on each heading AND the `href="#..."` on every TOC entry. Otherwise the TOC links don't work — which is exactly the bug the user reported.
39
+
40
+ **Slug algorithm (apply to the raw heading text after stripping markdown):**
41
+
42
+ 1. Lowercase.
43
+ 2. Strip leading numbering like `1.`, `1.1`, `2.3.4`, ` § `, ` — `, ` - ` if it appears before the first letter (so `## 1. Executive summary` → `executive-summary`, not `1-executive-summary`). Keep numbers that are part of a name (e.g. `## REQ-FN-3 Order pipeline` → `req-fn-3-order-pipeline`).
44
+ 3. Replace any run of non-alphanumeric characters with a single `-`.
45
+ 4. Strip leading and trailing `-`.
46
+ 5. If the resulting slug is empty (heading was all punctuation), fallback to `section-{N}` where N is the 1-based H2/H3 index.
47
+ 6. Deduplicate: if the same slug occurs twice, suffix the second with `-2`, third with `-3`, etc.
48
+
49
+ The MD inline TOC and the HTML sidebar TOC use the SAME slug. The heading `id` uses the SAME slug. No exceptions.
50
+
51
+ ---
52
+
53
+ ## 2. CSS (inline at top of `<head>`, verbatim)
54
+
55
+ ```css
56
+ /* LIGHT is the default theme — a warm off-white, easy on the eyes (never bright white). */
57
+ :root{
58
+ --bg:#f4f1e9; --panel:#efe9db; --panel2:#e7e0cd; --line:#d8cfb6;
59
+ --ink:#2e2a22; --muted:#6f6857; --accent:#2f6f9f; --accent2:#3f7d54;
60
+ --warn:#9a6b15; --danger:#b03a52; --ok:#3f7d54;
61
+ --code-bg:#ece5d3; --code-ink:#332f26; --row-alt:#eae2cf;
62
+ --h3:#3a352b; --h4:#2f6f9f; --quote:#4a4537; --cta-bg:#e6eef5;
63
+ --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
64
+ --sans:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
65
+ }
66
+ /* DARK — softened near-black (warmer / less harsh than pure #0f1115). */
67
+ html[data-theme="dark"]{
68
+ --bg:#11141a; --panel:#171b23; --panel2:#1e2430; --line:#2b3240;
69
+ --ink:#dfe3ea; --muted:#99a2b1; --accent:#7cc4ff; --accent2:#a6e3a1;
70
+ --warn:#f9c74f; --danger:#f38ba8; --ok:#a6e3a1;
71
+ --code-bg:#0c1016; --code-ink:#d8dde8; --row-alt:#141821;
72
+ --h3:#cfd5e3; --h4:#7cc4ff; --quote:#cfd5e3; --cta-bg:#0d2030;
73
+ }
74
+ *{box-sizing:border-box}
75
+ html,body{margin:0;padding:0;background:var(--bg);color:var(--ink);font-family:var(--sans);line-height:1.55;font-size:15px}
76
+ a{color:var(--accent);text-decoration:none}
77
+ a:hover{text-decoration:underline}
78
+ code{font-family:var(--mono);font-size:13px;background:var(--code-bg);color:var(--code-ink);padding:1px 5px;border-radius:4px;border:1px solid var(--line)}
79
+ pre{font-family:var(--mono);font-size:12.5px;background:var(--code-bg);border:1px solid var(--line);border-radius:8px;padding:12px 14px;overflow:auto;color:var(--code-ink);position:relative}
80
+ pre code{background:none;border:none;padding:0}
81
+ .copy{position:absolute;top:8px;right:8px;background:var(--panel2);color:var(--muted);border:1px solid var(--line);border-radius:6px;padding:3px 8px;font-size:11px;font-family:var(--sans);cursor:pointer}
82
+ .copy:hover{color:var(--accent);border-color:var(--accent)}
83
+ /* floating light/dark toggle (top-right) */
84
+ .theme-toggle{position:fixed;top:12px;right:14px;z-index:10000;background:var(--panel);color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:6px 12px;font-size:12.5px;font-family:var(--sans);cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.15)}
85
+ .theme-toggle:hover{color:var(--accent);border-color:var(--accent)}
86
+
87
+ .layout{display:grid;grid-template-columns:260px 1fr;min-height:100vh}
88
+ .layout.no-toc{grid-template-columns:1fr}
89
+ nav.side{position:sticky;top:0;height:100vh;overflow-y:auto;border-right:1px solid var(--line);background:var(--panel);padding:18px 14px}
90
+ nav.side h1{font-size:14px;margin:0 0 4px;letter-spacing:.5px;color:var(--ink)}
91
+ nav.side .sub{font-size:11.5px;color:var(--muted);margin-bottom:14px}
92
+ nav.side ol,nav.side ul{list-style:none;padding:0;margin:0}
93
+ nav.side li{font-size:13px;margin:2px 0}
94
+ nav.side li a{display:block;padding:5px 8px;color:var(--ink);border-radius:6px}
95
+ nav.side li a:hover{background:var(--panel2);text-decoration:none}
96
+ nav.side li.h3 a{padding-left:22px;font-size:12.5px;color:var(--h3)}
97
+ nav.side .group{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.7px;margin:14px 8px 4px}
98
+
99
+ main{padding:28px 40px 80px;max-width:1100px}
100
+ .layout.no-toc main{margin:0 auto}
101
+
102
+ h1{font-size:28px;margin:0 0 6px}
103
+ h2{font-size:22px;margin:32px 0 10px;padding-bottom:6px;border-bottom:1px solid var(--line);scroll-margin-top:16px}
104
+ h3{font-size:17px;margin:22px 0 6px;color:var(--h3);scroll-margin-top:16px}
105
+ h4{font-size:14px;margin:14px 0 4px;color:var(--h4);scroll-margin-top:16px}
106
+ .subtitle{color:var(--muted);font-size:14px;margin-top:-4px;margin-bottom:18px}
107
+ p{margin:6px 0 10px}
108
+ blockquote{border-left:3px solid var(--accent);background:var(--panel);margin:8px 0;padding:8px 14px;color:var(--quote)}
109
+ ul,ol{padding-left:22px}
110
+ li{margin:2px 0}
111
+
112
+ table{border-collapse:collapse;width:100%;font-size:13.5px;margin:8px 0}
113
+ th,td{border:1px solid var(--line);padding:8px 10px;text-align:left;vertical-align:top}
114
+ th{background:var(--panel2);color:var(--h3);font-weight:600}
115
+ tr:nth-child(even) td{background:var(--row-alt)}
116
+
117
+ hr{border:none;border-top:1px solid var(--line);margin:24px 0}
118
+ .anchor-link{color:var(--muted);font-weight:400;font-size:0.85em;margin-left:8px;opacity:0;transition:opacity .15s;text-decoration:none}
119
+ h2:hover .anchor-link,h3:hover .anchor-link,h4:hover .anchor-link{opacity:1}
120
+
121
+ /* inline TOC at top of main content */
122
+ .toc-inline{background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:10px 16px;margin:14px 0 24px}
123
+ .toc-inline > div{font-size:11.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px}
124
+ .toc-inline ol,.toc-inline ul{margin:4px 0;padding-left:18px}
125
+ .toc-inline li{font-size:13px;margin:1px 0}
126
+ .toc-inline a{color:var(--h3)}
127
+
128
+ /* mermaid diagram wrapper + toolbar */
129
+ .diagram{position:relative;border:1px solid var(--line);border-radius:8px;background:var(--code-bg);margin:14px 0;overflow:hidden}
130
+ .diagram .mermaid{padding:14px;margin:0;background:transparent;border:none;overflow:auto}
131
+ .diagram .dt{display:flex;gap:6px;justify-content:flex-end;padding:6px 8px;background:var(--row-alt);border-bottom:1px solid var(--line)}
132
+ .diagram .dt button{background:var(--panel2);color:var(--muted);border:1px solid var(--line);border-radius:6px;padding:3px 10px;font-size:12px;font-family:var(--sans);cursor:pointer;min-width:30px}
133
+ .diagram .dt button:hover{color:var(--accent);border-color:var(--accent)}
134
+ .diagram .dt .zlabel{padding:3px 6px;font-size:11px;color:var(--muted);font-family:var(--mono);align-self:center}
135
+ .diagram.fs{position:fixed;inset:0;z-index:9999;border-radius:0;border:none}
136
+ .diagram.fs .mermaid{height:calc(100vh - 40px);display:flex;align-items:center;justify-content:center}
137
+
138
+ @media(max-width:900px){.layout{grid-template-columns:1fr}nav.side{position:static;height:auto}}
139
+ ```
140
+
141
+ ---
142
+
143
+ ## 3. Page skeleton
144
+
145
+ ```html
146
+ <!doctype html>
147
+ <html lang="en">
148
+ <head>
149
+ <meta charset="utf-8">
150
+ <meta name="viewport" content="width=device-width,initial-scale=1">
151
+ <title>{Document title} — {AppName or short context}</title>
152
+ <script>
153
+ /* Resolve the theme BEFORE first paint (no flash). Saved choice wins; otherwise
154
+ pick by time of day — light 07:00–19:00 local, softened dark at night. */
155
+ (function(){
156
+ try{
157
+ var t = localStorage.getItem('tf-theme');
158
+ if(t!=='light' && t!=='dark'){ var h = new Date().getHours(); t = (h>=7 && h<19) ? 'light' : 'dark'; }
159
+ document.documentElement.setAttribute('data-theme', t);
160
+ }catch(e){ document.documentElement.setAttribute('data-theme','light'); }
161
+ })();
162
+ </script>
163
+ <style>{paste §2 CSS verbatim}</style>
164
+ </head>
165
+ <body>
166
+ <button id="themeToggle" class="theme-toggle" title="Toggle light / dark">☾ Dark</button>
167
+ <div class="layout {no-toc if H2-count <= 6}">
168
+ <!-- sidebar TOC: include ONLY when there are MORE THAN 6 H2 headings -->
169
+ <nav class="side">
170
+ <h1>{Document title}</h1>
171
+ <div class="sub">{Short context line — e.g. "Last rendered: 2026-05-27"}</div>
172
+ <div class="group">Contents</div>
173
+ <ol>
174
+ <li><a href="#slug">{H2 text}</a></li>
175
+ <li class="h3"><a href="#slug">{H3 text}</a></li>
176
+ ...
177
+ </ol>
178
+ </nav>
179
+ <main>
180
+ <h1>{Document title}</h1>
181
+ <div class="subtitle">{Subtitle}</div>
182
+
183
+ <!-- inline content listing (always present when H2-count >= 2) -->
184
+ <div class="toc-inline">
185
+ <div>Contents</div>
186
+ <ol>
187
+ <li><a href="#slug">{H2 text}</a></li>
188
+ ...
189
+ </ol>
190
+ </div>
191
+
192
+ {converted body content — see §4–§6}
193
+ </main>
194
+ </div>
195
+
196
+ <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
197
+ <script src="https://cdn.jsdelivr.net/npm/svg-pan-zoom@3.6.1/dist/svg-pan-zoom.min.js"></script>
198
+ <script>{paste §7 JS verbatim}</script>
199
+ </body>
200
+ </html>
201
+ ```
202
+
203
+ Apply the `no-toc` class on `.layout` when the sidebar `<nav class="side">` is omitted (H2 count ≤ 6). When `no-toc`, do not emit the `<nav class="side">` element at all.
204
+
205
+ ---
206
+
207
+ ## 4. Heading conversion (anchor decoration)
208
+
209
+ Every H2/H3/H4 becomes:
210
+
211
+ ```html
212
+ <h2 id="{slug}">{text}<a class="anchor-link" href="#{slug}">#</a></h2>
213
+ ```
214
+
215
+ The trailing `<a class="anchor-link">` lets users grab a deep link by hovering.
216
+
217
+ **Preserve hand-written anchors.** Some source docs place explicit non-heading anchors like `<a id="d-req-ui-001"></a>` directly above a list item, and a table cell links to them with `[view](#d-req-ui-001)`. This is how the Requirements Status table's **Details** column jumps to each REQ's detail entry. Emit every such `<a id="...">` verbatim in the HTML and keep the matching `href="#..."` link intact — do NOT drop them, renumber them, or run them through the heading-slug function. Dropping them silently breaks the Details links (the exact class of bug the user keeps reporting).
218
+
219
+ ---
220
+
221
+ ## 5. Mermaid block conversion
222
+
223
+ Markdown fence:
224
+
225
+ ````
226
+ ```mermaid
227
+ flowchart LR
228
+ A --> B
229
+ ```
230
+ ````
231
+
232
+ Becomes:
233
+
234
+ ```html
235
+ <div class="diagram">
236
+ <div class="dt">
237
+ <button data-zout title="Zoom out">−</button>
238
+ <span class="zlabel">100%</span>
239
+ <button data-zin title="Zoom in">+</button>
240
+ <button data-fit title="Fit to width">Fit</button>
241
+ <button data-one title="Actual size">1:1</button>
242
+ <button data-fs title="Fullscreen">⛶</button>
243
+ <button data-pop title="Open in new tab">↗</button>
244
+ </div>
245
+ <pre class="mermaid">{raw mermaid source verbatim, no escaping}</pre>
246
+ </div>
247
+ ```
248
+
249
+ The toolbar wires up via the JS in §7.
250
+
251
+ ---
252
+
253
+ ## 5.5 Mermaid authoring rules (write VALID Mermaid — read before drafting ANY diagram)
254
+
255
+ Mermaid 11.x is **strict**. The single most common bug in this framework's rendered docs is a flowchart that shows *"Syntax error in text — mermaid version 11.x.x"* in the browser. It is almost always **unquoted special characters in a node or edge label**. Sequence diagrams rarely break (simple participant/message text); flowcharts break constantly because real labels contain `()`, `/`, `&`, etc. These rules eliminate the entire class of error. They apply to **whoever authors the `.md`** (analyst/day-1/architect tasks) AND to anyone hand-editing a diagram — not just the renderer.
256
+
257
+ **Rule 1 — Quote EVERY label.** Wrap all human-readable label text in double quotes, always. Quoting is never wrong, so when in doubt, quote.
258
+ - ✅ `A["Order Service (v2)"]` ❌ `A[Order Service (v2)]` ← parens break it
259
+ - ✅ `B["Azure AD / OAuth2"]` ❌ `B[Azure AD / OAuth2]` ← slash breaks it
260
+ - ✅ `C["Users & Roles"]` ❌ `C[Users & Roles]` ← ampersand breaks it
261
+ - These characters break an **unquoted** label: `(` `)` `/` `\` `&` `:` `,` `;` `[` `]` `{` `}` `#` `@` `<` `>` `"` `|` `` ` `` `%`, plus `-` runs and any markdown. Quoting the label makes all of them safe.
262
+
263
+ **Rule 2 — The quotes go INSIDE the shape brackets.** Combine shape + quoted string:
264
+ - Rectangle `N["text"]` · Round `N("text")` · Stadium `N(["text"])` · Cylinder/DB `N[("text")]` · Parallelogram `N[/"text"/]` · Decision `N{"text"}` · Hexagon `N{{"text"}}`.
265
+
266
+ **Rule 3 — Node IDs are code, not text.** An id is letters/digits only (no spaces, no punctuation) and is never shown. Put ALL human text in the quoted label. Never reuse a reserved word as an id — lowercase **`end`** silently breaks flowcharts; use `End`, `Endpoint`, `fin`. (`subgraph … end` as a block terminator is fine — that's syntax, not a node id.)
267
+
268
+ **Rule 4 — Line breaks:** use `<br/>` inside the quotes (`A["Line 1<br/>Line 2"]`), never a literal newline. No other HTML/markdown in labels (no `**bold**`, no backticks, no links).
269
+
270
+ **Rule 5 — subgraphs:** give an id, then a quoted title — `subgraph BE["Backend (.NET 9)"]`. Reference the subgraph elsewhere by its id (`BE`), not its title.
271
+
272
+ **Rule 6 — Edge labels:** quote them too — `A -->|"yes / no"| B` or `A -- "calls" --> B`.
273
+
274
+ **Rule 7 — One statement per line.** Never chain with `;`.
275
+
276
+ **Rule 8 — Sequence diagrams:** quote any participant alias containing spaces/punctuation — `participant A as "App API"`. `Note over A,B: text` and `alt/loop/opt` blocks are fine.
277
+
278
+ **Self-check before saving:** re-read every diagram; for each `[...]`, `(...)`, `{...}` label, if it contains anything other than letters, digits, and spaces, confirm it is wrapped in `"…"`. The renderer (`generate-html` / `render-workflow-docs`) MUST run this same scan (it is in their checklists) and fix any bare label it finds rather than emitting a diagram it knows will error.
279
+
280
+ ---
281
+
282
+ ## 6. Code block conversion
283
+
284
+ Every non-mermaid fenced block becomes:
285
+
286
+ ```html
287
+ <pre><code class="language-{lang}">{escaped source}</code></pre>
288
+ ```
289
+
290
+ The copy button is added by JS at load time (see §7). Do NOT manually inject `<button class="copy">` per block.
291
+
292
+ Escape `<`, `>`, `&` in the code content. Preserve newlines and whitespace.
293
+
294
+ ---
295
+
296
+ ## 6b. Agent-facing authoring notes NEVER render as visible text
297
+
298
+ The doc templates carry instructions for the DRAFTING AGENT (the "Depth mandate", "Mermaid mandate", "read before drafting" notes, how-to-use-this-template guidance) as **HTML comments** — those pass through invisibly, which is correct. If the source MD still carries such a note as a **visible** blockquote or paragraph (docs generated from pre-2026-07 templates did), **omit it from the rendered HTML** and report the omission in your output summary — the human reading the HTML must never see authoring instructions. The test: is the text addressed to the agent writing the document, or to the document's human reader? Reader-facing blockquotes (a DevGuide's verification-status banner or Purpose note, a UsageGuide's test-user rules, a UIDesign's "What this is") stay.
299
+
300
+ ---
301
+
302
+ ## 7. JavaScript (inline at end of `<body>`, verbatim)
303
+
304
+ ```javascript
305
+ // Light/dark toggle. The initial theme was already applied flash-free by the
306
+ // <head> script (saved choice, else time-of-day). Clicking flips it, saves the
307
+ // choice, and reloads so Mermaid re-renders in the matching theme.
308
+ (function(){
309
+ var btn = document.getElementById('themeToggle');
310
+ if(!btn) return;
311
+ var relabel = function(){
312
+ var dark = document.documentElement.getAttribute('data-theme') === 'dark';
313
+ btn.textContent = dark ? '☀ Light' : '☾ Dark';
314
+ };
315
+ relabel();
316
+ btn.addEventListener('click', function(){
317
+ var next = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
318
+ try{ localStorage.setItem('tf-theme', next); }catch(e){}
319
+ location.reload();
320
+ });
321
+ })();
322
+
323
+ // Copy buttons on every code <pre> — but NEVER on a `pre.mermaid`:
324
+ // mermaid reads the <pre>'s textContent as the diagram source, so a button
325
+ // appended here glues "copy" onto the last line and breaks the parse
326
+ // (flowcharts → "Syntax error"; sequence diagrams silently absorb it).
327
+ // Mermaid blocks get copy/export from their own .diagram toolbar instead.
328
+ document.querySelectorAll('pre:not(.mermaid)').forEach(pre => {
329
+ const btn = document.createElement('button');
330
+ btn.className = 'copy';
331
+ btn.textContent = 'copy';
332
+ btn.addEventListener('click', () => {
333
+ const text = pre.querySelector('code') ? pre.querySelector('code').innerText : pre.innerText;
334
+ navigator.clipboard.writeText(text).then(() => {
335
+ btn.textContent = 'copied';
336
+ setTimeout(() => { btn.textContent = 'copy'; }, 1200);
337
+ });
338
+ });
339
+ pre.appendChild(btn);
340
+ });
341
+
342
+ // Mermaid init — theme follows the resolved light/dark theme
343
+ if (window.mermaid) {
344
+ var mermaidTheme = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'default';
345
+ mermaid.initialize({ startOnLoad: true, theme: mermaidTheme, securityLevel: 'loose' });
346
+ }
347
+
348
+ // Mermaid toolbar wiring — run after mermaid renders
349
+ function wireMermaidToolbars() {
350
+ document.querySelectorAll('.diagram').forEach(dia => {
351
+ if (dia.dataset.wired) return;
352
+ const merm = dia.querySelector('.mermaid');
353
+ const svg = merm && merm.querySelector('svg');
354
+ if (!svg) return;
355
+ dia.dataset.wired = '1';
356
+ let scale = 1;
357
+ const label = dia.querySelector('.zlabel');
358
+ const apply = () => {
359
+ svg.style.transform = `scale(${scale})`;
360
+ svg.style.transformOrigin = 'top left';
361
+ if (label) label.textContent = Math.round(scale * 100) + '%';
362
+ };
363
+ const setScale = (s) => { scale = Math.max(0.25, Math.min(4, s)); apply(); };
364
+ dia.querySelector('[data-zin]').onclick = () => setScale(scale * 1.2);
365
+ dia.querySelector('[data-zout]').onclick = () => setScale(scale / 1.2);
366
+ dia.querySelector('[data-fit]').onclick = () => {
367
+ svg.style.transform = '';
368
+ svg.removeAttribute('width');
369
+ svg.style.maxWidth = '100%';
370
+ svg.style.height = 'auto';
371
+ scale = 1;
372
+ if (label) label.textContent = 'Fit';
373
+ };
374
+ dia.querySelector('[data-one]').onclick = () => {
375
+ svg.style.transform = '';
376
+ svg.style.maxWidth = 'none';
377
+ svg.style.width = '';
378
+ svg.style.height = '';
379
+ scale = 1;
380
+ if (label) label.textContent = '100%';
381
+ };
382
+ dia.querySelector('[data-fs]').onclick = () => {
383
+ dia.classList.toggle('fs');
384
+ if (dia.classList.contains('fs')) {
385
+ document.addEventListener('keydown', escFs);
386
+ } else {
387
+ document.removeEventListener('keydown', escFs);
388
+ }
389
+ };
390
+ function escFs(e) { if (e.key === 'Escape') { dia.classList.remove('fs'); document.removeEventListener('keydown', escFs); } }
391
+ dia.querySelector('[data-pop]').onclick = () => openInNewTab(svg);
392
+ // Ctrl+wheel zoom
393
+ merm.addEventListener('wheel', (e) => {
394
+ if (!e.ctrlKey) return;
395
+ e.preventDefault();
396
+ setScale(scale * (e.deltaY < 0 ? 1.1 : 1/1.1));
397
+ }, { passive: false });
398
+ });
399
+ }
400
+
401
+ function openInNewTab(svg) {
402
+ const svgSource = new XMLSerializer().serializeToString(svg);
403
+ const html = `<!doctype html><html><head><meta charset="utf-8"><title>Diagram</title>
404
+ <style>
405
+ html,body{margin:0;height:100%;background:#0f1115;color:#e6e8ee;font-family:system-ui,sans-serif}
406
+ .bar{position:fixed;top:0;left:0;right:0;padding:8px 12px;background:#161a22;border-bottom:1px solid #2a3142;display:flex;gap:8px;z-index:10}
407
+ .bar button{background:#1c2230;color:#9aa3b2;border:1px solid #2a3142;border-radius:6px;padding:5px 12px;font-size:13px;cursor:pointer}
408
+ .bar button:hover{color:#7cc4ff;border-color:#7cc4ff}
409
+ .bar .hint{margin-left:auto;color:#9aa3b2;font-size:11.5px;align-self:center}
410
+ #stage{position:absolute;top:42px;bottom:0;left:0;right:0;overflow:hidden}
411
+ #stage svg{width:100%;height:100%}
412
+ </style>
413
+ </head><body>
414
+ <div class="bar">
415
+ <button onclick="zpz.zoomIn()">+</button>
416
+ <button onclick="zpz.zoomOut()">−</button>
417
+ <button onclick="zpz.fit();zpz.center()">Fit</button>
418
+ <button onclick="zpz.resetZoom();zpz.center()">1:1</button>
419
+ <button onclick="window.print()">Print</button>
420
+ <button onclick="dlPng()">PNG</button>
421
+ <button onclick="dlSvg()">SVG</button>
422
+ <span class="hint">Drag to pan · Ctrl+wheel to zoom · + / − keys</span>
423
+ </div>
424
+ <div id="stage">${svgSource}</div>
425
+ <script src="https://cdn.jsdelivr.net/npm/svg-pan-zoom@3.6.1/dist/svg-pan-zoom.min.js"><\/script>
426
+ <script>
427
+ const el = document.querySelector('#stage svg');
428
+ const zpz = svgPanZoom(el, { zoomScaleSensitivity: .3, minZoom: .2, maxZoom: 10, controlIconsEnabled: false, fit: true, center: true });
429
+ document.addEventListener('keydown', e => {
430
+ if (e.key === '+' || e.key === '=') zpz.zoomIn();
431
+ if (e.key === '-' || e.key === '_') zpz.zoomOut();
432
+ if (e.key === '0') { zpz.resetZoom(); zpz.center(); }
433
+ });
434
+ function dlSvg() {
435
+ const blob = new Blob([new XMLSerializer().serializeToString(el)], { type: 'image/svg+xml' });
436
+ const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'diagram.svg'; a.click();
437
+ }
438
+ function dlPng() {
439
+ const src = new XMLSerializer().serializeToString(el);
440
+ const img = new Image();
441
+ const url = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(src)));
442
+ img.onload = () => {
443
+ const c = document.createElement('canvas');
444
+ c.width = el.clientWidth * 2; c.height = el.clientHeight * 2;
445
+ const ctx = c.getContext('2d'); ctx.scale(2,2); ctx.drawImage(img, 0, 0);
446
+ c.toBlob(b => { const a = document.createElement('a'); a.href = URL.createObjectURL(b); a.download = 'diagram.png'; a.click(); });
447
+ };
448
+ img.src = url;
449
+ }
450
+ <\/script>
451
+ </body></html>`;
452
+ const w = window.open();
453
+ w.document.write(html);
454
+ w.document.close();
455
+ }
456
+
457
+ // Run wiring after mermaid finishes (it renders async)
458
+ if (window.mermaid) {
459
+ setTimeout(wireMermaidToolbars, 400);
460
+ setTimeout(wireMermaidToolbars, 1200); // safety re-run for slow renders
461
+ }
462
+ ```
463
+
464
+ ---
465
+
466
+ ## 8. Inline (in-document) Table of Contents — applies to BOTH MD and HTML
467
+
468
+ **In MD source files (templates and generated docs):**
469
+
470
+ Place this section immediately after the H1 title block (and any leading metadata like `> Stable IDs:` or `**Last updated:**`), before the first H2:
471
+
472
+ ```markdown
473
+ ## Table of Contents
474
+
475
+ 1. [Section name](#section-slug)
476
+ 2. [Section name](#section-slug)
477
+ - [Sub-section](#sub-slug)
478
+ ```
479
+
480
+ Use the §1 slug rule when writing the links. When you write/update the doc, also update this section so links stay correct.
481
+
482
+ **In HTML output:**
483
+
484
+ The inline `<div class="toc-inline">` mirrors the MD Table of Contents. When there are more than 6 H2s, ALSO emit the sidebar `<nav class="side">` with the same slugs. Sidebar is always identical to inline TOC, just styled differently.
485
+
486
+ ---
487
+
488
+ ## 9. Render checklist (use after generating any HTML)
489
+
490
+ - [ ] CSS palette matches §2 verbatim (themed `:root` light + `html[data-theme="dark"]` overrides; no hardcoded hex colors reintroduced in the body rules)
491
+ - [ ] Page skeleton matches §3 — flash-free theme-init `<script>` in `<head>` + the `#themeToggle` button present; §7 JS wires the toggle and Mermaid's theme follows `data-theme`
492
+ - [ ] This is a human-readable doc (BRD / Architecture / UsageGuide / DevGuide / PROJECT-STATUS) — NOT a checklist (checklists are never rendered to HTML)
493
+ - [ ] Every H2/H3/H4 has `id="{slug}"` matching §1 algorithm
494
+ - [ ] Every TOC entry (MD inline AND HTML inline/sidebar) uses the same slug
495
+ - [ ] Every mermaid fence is wrapped in `<div class="diagram">` with the §5 toolbar
496
+ - [ ] Every mermaid diagram passes the §5.5 self-check — each non-trivial node/edge/subgraph label is double-quoted, no reserved-word (`end`) node ids; bare labels were fixed, not emitted
497
+ - [ ] Every non-mermaid code fence is `<pre><code>...</code></pre>` and JS in §7 adds the copy button
498
+ - [ ] No agent-facing authoring note rendered as visible text (§6b) — leaked mandate blockquotes omitted and reported
499
+ - [ ] Mermaid + svg-pan-zoom CDN scripts loaded
500
+ - [ ] §7 JS pasted at end of body
501
+ - [ ] Sidebar TOC present iff doc has >6 H2 headings
502
+ - [ ] File is self-contained (no missing local assets)