chati-dev 4.3.0 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/chati.js CHANGED
@@ -141,6 +141,26 @@ async function main() {
141
141
  console.log(` Framework sync skipped: ${err.message}`);
142
142
  }
143
143
 
144
+ // 2.6. Re-bundle CLI (Bug 4 part 3, v4.3.1+) — refreshes .chati.dev/_cli/
145
+ // and writes version.json so chati-router.js mismatch detection clears
146
+ // on the next invocation.
147
+ console.log(' Re-bundling CLI...');
148
+ try {
149
+ const { copyCliSource, copyCliDependencies } = await import('../src/installer/core.js');
150
+ const { writeFileSync } = await import('fs');
151
+ const fwDir = resolveFrameworkDir(targetDir);
152
+ const frameworkDir = join(targetDir, fwDir);
153
+ copyCliSource(join(__dirname, '..', 'src'), join(frameworkDir, '_cli'));
154
+ copyCliDependencies(join(__dirname, '..'), join(frameworkDir, '_cli', 'node_modules'));
155
+ writeFileSync(
156
+ join(frameworkDir, '_cli', 'version.json'),
157
+ JSON.stringify({ version: pkg.version, bundled_at: new Date().toISOString() }, null, 2),
158
+ );
159
+ console.log(' CLI re-bundled.');
160
+ } catch (err) {
161
+ console.log(` CLI rebundle skipped: ${err.message}`);
162
+ }
163
+
144
164
  // 2.6. Detect tracked framework files and warn user (non-blocking)
145
165
  try {
146
166
  const { detectTrackedFrameworkFiles } = await import('../src/upgrade/tracked-files-detector.js');
@@ -105,7 +105,7 @@ The `motion-premium` scaffold preset (`scaffold/motion-premium/`) ships referenc
105
105
 
106
106
  **Context**
107
107
 
108
- Tailwind v4 is a major shift: the `@theme` block, CSS-first token declaration, Cascade Layers compilation, and arbitrary-value type inference all changed. The change introduced three silent-failure modes that pass `pnpm lint`, `pnpm typecheck`, and `pnpm build` with zero errors. One was caught by QA-Visual pixel inspection; two slipped past it and bit production in the focus-ai-website saga. All three now have deterministic gates in qa-visual, but dev must avoid them at build time — fixing in code is cheaper than fixing under a correction loop.
108
+ Tailwind v4 is a major shift: the `@theme` block, CSS-first token declaration, Cascade Layers compilation, and arbitrary-value type inference all changed. The change introduced three silent-failure modes that pass `pnpm lint`, `pnpm typecheck`, and `pnpm build` with zero errors. One was caught by QA-Visual pixel inspection; two slipped past it and bit production in a reference-website case study. All three now have deterministic gates in qa-visual, but dev must avoid them at build time — fixing in code is cheaper than fixing under a correction loop.
109
109
 
110
110
  **Decision**
111
111
 
@@ -142,7 +142,7 @@ Prefer the promoted Tailwind utilities (`text-xs..text-hero`, `bg-primary`, etc.
142
142
 
143
143
  *Pitfall 2 — Unlayered global CSS defeats every utility.*
144
144
 
145
- Tailwind v4 places utilities inside `@layer utilities`. Per CSS Cascade Layers (Cascade Level 5), *unlayered* CSS always wins over any named layer — regardless of selector specificity. A single global reset outside `@layer` silently overrides `mx-auto`, `px-*`, `flex`, everything. This broke container centering for a full day on focus-ai-website (2026-04-16 incident) and was invisible at 1280px viewport — the x=0 container alignment only became visible at 1920px where lg:px-12 should have added padding but unlayered `* { margin: 0 }` outranked it.
145
+ Tailwind v4 places utilities inside `@layer utilities`. Per CSS Cascade Layers (Cascade Level 5), *unlayered* CSS always wins over any named layer — regardless of selector specificity. A single global reset outside `@layer` silently overrides `mx-auto`, `px-*`, `flex`, everything. This broke container centering for a full day on a reference-website (a Tailwind v4 incident encountered in production) and was invisible at 1280px viewport — the x=0 container alignment only became visible at 1920px where lg:px-12 should have added padding but unlayered `* { margin: 0 }` outranked it.
146
146
 
147
147
  WRONG — kills every Tailwind margin/padding utility:
148
148
  ```css
@@ -298,7 +298,7 @@ React 19 (stable since late 2024) introduced four patterns that improve ergonomi
298
298
 
299
299
  **Context**
300
300
 
301
- Lenis (virtualized smooth scroll) + GSAP + ScrollTrigger form the premium animation stack. Initialization order is load-bearing: wrong order produces silent bugs (tab-restore snap, ScrollTrigger positions measured against the native scrollbar instead of Lenis, drifting RAF clocks). The `motion-premium` scaffold encodes all 13 "sauce patterns" learned from the focus-ai-website saga; this ADR names the canonical init sequence dev must preserve.
301
+ Lenis (virtualized smooth scroll) + GSAP + ScrollTrigger form the premium animation stack. Initialization order is load-bearing: wrong order produces silent bugs (tab-restore snap, ScrollTrigger positions measured against the native scrollbar instead of Lenis, drifting RAF clocks). The `motion-premium` scaffold encodes all 13 "sauce patterns" learned from a reference-website case study; this ADR names the canonical init sequence dev must preserve.
302
302
 
303
303
  **Decision**
304
304
 
@@ -361,7 +361,7 @@ export async function initLenis() {
361
361
 
362
362
  **Context**
363
363
 
364
- Responsive design done wrong introduces a class of silent bugs that are invisible at 1280px and lethal at 1920px or 1200px. This ADR encodes the 12 principles learned from the focus-ai-website saga. They are non-negotiable for premium UI tasks. Principles 1, 2, 3, 7, 10, and 12 are enforced structurally by the `motion-premium` scaffold; the remaining six are dev discipline that visual-qa validates.
364
+ Responsive design done wrong introduces a class of silent bugs that are invisible at 1280px and lethal at 1920px or 1200px. This ADR encodes the 12 principles learned from a reference-website case study. They are non-negotiable for premium UI tasks. Principles 1, 2, 3, 7, 10, and 12 are enforced structurally by the `motion-premium` scaffold; the remaining six are dev discipline that visual-qa validates.
365
365
 
366
366
  **Decision**
367
367
 
@@ -559,6 +559,7 @@ domains:
559
559
  required:
560
560
  - constitution.yaml
561
561
  - global.yaml
562
+ - rules/human-writing-style.md
562
563
  - agents/brief.yaml
563
564
  - artifacts/handoffs/greenfield-wu-handoff.md # or brownfield-wu-handoff.md
564
565
  ```
@@ -661,6 +662,7 @@ On failure:
661
662
  6. Negative scope is mandatory - explicitly document what will NOT be built
662
663
  7. Adapt question depth to user level (vibecoder = guided, power user = direct)
663
664
  8. Maximum 5 interaction rounds before compiling brief draft
665
+ 9. Apply `rules/human-writing-style.md` to brief-report.md prose, handoff Layer 1, conversation turns. Exempt: visual_references YAML, brief.yaml frontmatter, brief criteria tables.
664
666
 
665
667
  ---
666
668
 
@@ -377,6 +377,7 @@ domains:
377
377
  required:
378
378
  - constitution.yaml
379
379
  - global.yaml
380
+ - rules/human-writing-style.md
380
381
  - agents/brownfield-wu.yaml
381
382
  ```
382
383
 
@@ -474,6 +475,7 @@ On failure:
474
475
  6. Treat all scout call findings as preliminary (agents will re-run in full mode later)
475
476
  7. Preserve the user's terminology when documenting operational context
476
477
  8. Flag security-sensitive patterns (hardcoded secrets, exposed endpoints) as CRITICAL
478
+ 9. Apply `rules/human-writing-style.md` for all human-facing output (WU report prose, handoff Layer 1, user-facing conversation turns). Exempt: YAML blocks, tables, code, debt registry tables.
477
479
 
478
480
  ---
479
481
 
@@ -291,6 +291,7 @@ domains:
291
291
  required:
292
292
  - constitution.yaml
293
293
  - global.yaml
294
+ - rules/human-writing-style.md
294
295
  - agents/greenfield-wu.yaml
295
296
  ```
296
297
 
@@ -443,6 +444,7 @@ On failure:
443
444
  3. Keep questions focused and progressive (don't ask everything at once)
444
445
  4. Adapt question depth to user level (vibecoder = simpler, power user = technical)
445
446
  5. Maximum 4 interaction rounds before generating report
447
+ 6. Apply `rules/human-writing-style.md` for all human-facing output (WU report prose, handoff Layer 1, user-facing conversation turns). Exempt: YAML blocks, tables, scaffold_signals.
446
448
 
447
449
  ---
448
450
 
@@ -325,6 +325,7 @@ Rules:
325
325
  |-------|--------|---------|
326
326
  | L0 | `.chati/session.yaml` | Project type, current pipeline position, mode, agent statuses |
327
327
  | L1 | `chati.dev/constitution.md` | Protocols, validation thresholds, handoff rules |
328
+ | L1.5 | `chati.dev/rules/human-writing-style.md` | Anti-AI prose rules for human-facing PRD sections |
328
329
  | L2 | `artifacts/1-Brief/brief-report.md` | Problems, desired outcomes, constraints, target users |
329
330
  | L3 | `artifacts/handoffs/brief-handoff.md` | Brief agent handoff with decisions and open questions |
330
331
 
@@ -388,6 +389,7 @@ Beyond self-validation (Protocol 5.1), the Detail agent enforces:
388
389
  4. **NFR measurability**: Every non-functional requirement must include a measurable threshold (e.g., "response time < 200ms" not "fast response")
389
390
  5. **Scope boundaries are bilateral**: Both in-scope AND out-of-scope must be explicitly defined - omitting out-of-scope is a validation failure
390
391
  6. **Traceability is bidirectional**: Brief-to-PRD and PRD-to-Brief mappings must both exist with zero orphans in either direction
392
+ 7. **Human writing style**: Apply `rules/human-writing-style.md` to Executive Summary, Goals, Target Users, and Scope narrative prose. Exempt: FR/NFR/BR tables, Given-When-Then criteria, Traceability Matrix.
391
393
 
392
394
  ---
393
395
 
@@ -26,6 +26,15 @@ You are the **QA-Visual Agent**, a specialized quality gate focused exclusively
26
26
  ## Required MCPs
27
27
  - browser (Playwright MCP - mandatory, not optional)
28
28
 
29
+ ## Gateway weight (v4.3.1+)
30
+
31
+ QA-Visual operates with two execution paths:
32
+
33
+ - **Strong gateway (default):** real Playwright screenshots. Required for full verdict authority — score reflects what users actually see.
34
+ - **Weak gateway (`gateway: WEAK`):** source-code analysis fallback when Playwright is unavailable (npm package missing OR chromium binary not installed). Score is capped below 90% to prevent fallback-only verdicts from clearing the threshold autonomously. The fallback exists as a backstop, not a primary path. When triggered, the report MUST surface `gateway: WEAK` so the human-in-the-loop knows the verdict is text-based and warrants extra scrutiny before advancing.
35
+
36
+ Action when fallback triggers: emit a recovery instruction in the QA report — `Run: npx playwright install chromium` (binary missing) or `Run: npm install playwright` (package missing). See visual-qa.js error disambiguation (lines ~373-385).
37
+
29
38
  ---
30
39
 
31
40
  ## Mission
@@ -1,8 +1,8 @@
1
1
  # chati.dev Configuration
2
- version: "4.3.0"
2
+ version: "4.3.1"
3
3
  installed_at: "2026-02-07T10:00:00Z"
4
- updated_at: "2026-04-18T00:00:00Z"
5
- installer_version: "4.3.0"
4
+ updated_at: "2026-04-29T00:00:00Z"
5
+ installer_version: "4.3.1"
6
6
  project_type: greenfield
7
7
  language: en
8
8
  ides: [claude-code]
@@ -3,8 +3,8 @@
3
3
  # and by the Health Check for system integrity validation.
4
4
 
5
5
  metadata:
6
- version: "4.3.0"
7
- last_updated: "2026-04-14T00:00:00Z"
6
+ version: "4.3.1"
7
+ last_updated: "2026-04-29T00:00:00Z"
8
8
  entity_count: 100
9
9
  checksum_algorithm: sha256
10
10
 
@@ -28,6 +28,51 @@ const ROUTER_ARGS = process.argv.slice(2);
28
28
  async function findCliModule(projectDir) {
29
29
  // Resolve framework dir (.chati.dev/ or chati.dev/ for legacy)
30
30
  const fwDir = existsSync(join(projectDir, '.chati.dev')) ? '.chati.dev' : 'chati.dev';
31
+
32
+ // --- VERSION MISMATCH DETECTION (v4.3.1+) ---
33
+ // G8: reset stale state from previous invocations within same process.
34
+ findCliModule._mismatch = null;
35
+
36
+ // Compare canonical version (chati.dev/config.yaml.installer_version) against
37
+ // installed bundle (.chati.dev/_cli/version.json). Pre-v4.3.1 installs lack
38
+ // version.json — silent skip (graceful degradation).
39
+ let installedVersion = null;
40
+ let bundledVersion = null;
41
+ try {
42
+ const configPath = join(projectDir, fwDir, 'config.yaml');
43
+ if (existsSync(configPath)) {
44
+ const raw = readFileSync(configPath, 'utf-8');
45
+ const m = raw.match(/^installer_version:\s*['"]?([^\s'"]+)/m);
46
+ installedVersion = m ? m[1] : null;
47
+ }
48
+ const versionStampPath = join(projectDir, fwDir, '_cli', 'version.json');
49
+ if (existsSync(versionStampPath)) {
50
+ bundledVersion = JSON.parse(readFileSync(versionStampPath, 'utf-8')).version;
51
+ }
52
+ if (installedVersion && bundledVersion && installedVersion !== bundledVersion) {
53
+ // G6: severity-aware warning. Major diff signals breaking schema/behavior changes.
54
+ const installedMajor = parseInt(String(installedVersion).split('.')[0], 10);
55
+ const bundledMajor = parseInt(String(bundledVersion).split('.')[0], 10);
56
+ const majorDiff =
57
+ !Number.isNaN(installedMajor) &&
58
+ !Number.isNaN(bundledMajor) &&
59
+ installedMajor !== bundledMajor;
60
+ const severityNote = majorDiff
61
+ ? ' Major version change — review CHANGELOG before upgrading.'
62
+ : '';
63
+ process.stderr.write(
64
+ `[chati] WARNING: CLI bundle version (${bundledVersion}) ` +
65
+ `does not match installed framework (${installedVersion}).${severityNote} ` +
66
+ `Run: npx chati-dev upgrade\n`
67
+ );
68
+ findCliModule._mismatch = {
69
+ bundle: bundledVersion,
70
+ installed: installedVersion,
71
+ major_diff: majorDiff,
72
+ };
73
+ }
74
+ } catch { /* non-critical — proceed to candidates loop */ }
75
+
31
76
  const candidates = [
32
77
  // PRIMARY: bundled CLI inside framework dir (self-contained install — no npx needed)
33
78
  join(projectDir, fwDir, '_cli', 'orchestrator', 'cli.js'),
@@ -90,9 +135,11 @@ async function routeAdvance(agentName, score) {
90
135
 
91
136
  try {
92
137
  const cliMod = await findCliModule(PROJECT_DIR);
138
+ if (findCliModule._mismatch) result.version_mismatch = findCliModule._mismatch;
93
139
  if (!cliMod) {
94
140
  result.ok = false;
95
141
  result.error = 'cli_module_not_found';
142
+ result.reason = 'Bundled CLI not found in .chati.dev/_cli/. Reinstall with: npx chati-dev upgrade';
96
143
  return result;
97
144
  }
98
145
 
@@ -174,16 +221,21 @@ function parseFlagArgs(argv) {
174
221
  */
175
222
  async function dispatchOrchestrate(subCommand, argv) {
176
223
  const cliMod = await findCliModule(PROJECT_DIR);
224
+ const mismatch = findCliModule._mismatch;
177
225
  if (!cliMod) {
178
- return {
226
+ const out = {
179
227
  ok: false,
180
228
  action: 'error',
181
229
  error: 'cli_module_not_found',
182
- reason: 'Bundled CLI not found in .chati.dev/_cli/. Reinstall with: npx chati-dev init',
230
+ reason: 'Bundled CLI not found in .chati.dev/_cli/. Reinstall with: npx chati-dev upgrade',
183
231
  };
232
+ if (mismatch) out.version_mismatch = mismatch;
233
+ return out;
184
234
  }
185
235
  try {
186
- return await captureOrchestrate(cliMod, subCommand, argv, PROJECT_DIR);
236
+ const result = await captureOrchestrate(cliMod, subCommand, argv, PROJECT_DIR);
237
+ if (mismatch && result && typeof result === 'object') result.version_mismatch = mismatch;
238
+ return result;
187
239
  } catch (err) {
188
240
  return {
189
241
  ok: false,
@@ -327,9 +379,11 @@ async function main() {
327
379
  // -----------------------------------------------------------------------
328
380
  try {
329
381
  const cliMod = await findCliModule(PROJECT_DIR);
382
+ if (findCliModule._mismatch) result.version_mismatch = findCliModule._mismatch;
330
383
  if (!cliMod) {
331
384
  result.action = 'error';
332
- result.error = 'cli_module_not_found: bundled CLI missing in .chati.dev/_cli/. Reinstall with: npx chati-dev init';
385
+ result.error = 'cli_module_not_found';
386
+ result.reason = 'Bundled CLI missing in .chati.dev/_cli/. Reinstall with: npx chati-dev upgrade';
333
387
  } else {
334
388
  result.pipeline = await captureOrchestrate(cliMod, 'next', [], PROJECT_DIR);
335
389
  result.action = result.pipeline.action;
@@ -104,6 +104,7 @@ Parse the JSON output. The `action` field tells you what to do:
104
104
  | `user_preview` | Action: User Preview |
105
105
  | `complete` | Action: Complete |
106
106
  | `error` | Display error, suggest `/chati status` |
107
+ | `version_mismatch` (in any response) | "Your CLI bundle is out of date. Run `npx chati-dev upgrade` to fix." Display once per session, then continue. |
107
108
 
108
109
  **Internal: track context bracket from JSON `context_bracket` but DO NOT display it to the user.** The bracket is internal telemetry - users should never see "Context: FRESH (90%)" or framework jargon like "Initiating handoff protocol". Speak in natural language about what you are doing, not the internal state.
109
110
 
@@ -0,0 +1,47 @@
1
+ # Human Writing Style — Anti-AI Guidelines
2
+
3
+ **Scope**: Apply to all human-facing outputs: WU reports, Briefs, PRDs, handoff Layer 1 summaries, conversation turns with the user.
4
+ **Exemption**: Tasks lists, JSON, YAML, code blocks, qa-*.md structured tables, Given-When-Then criteria — do NOT apply.
5
+
6
+ ## Rules
7
+
8
+ ### 1. No generic opening phrases
9
+ Do not open with "In today's fast-paced world", "In the ever-evolving landscape", "As we navigate", or any variant. Start with the subject matter directly.
10
+
11
+ ### 2. No AI-favored filler verbs
12
+ Avoid: delve, leverage, facilitate, utilize, empower, harness. Use: use, help, run, build, drive, enable — only when the simpler word fits.
13
+
14
+ ### 3. No rhetorical tricolons
15
+ Avoid: "not only X, but also Y, and Z", "robust, scalable, and maintainable". State the one thing that matters. Cut the rest.
16
+
17
+ ### 4. No unnecessary hedging
18
+ Remove: "it's worth noting", "it's important to mention", "it should be noted", "one could argue", "arguably". If it is worth saying, say it directly.
19
+
20
+ ### 5. No meta-closings
21
+ Remove: "In conclusion", "To summarize", "Overall", "Ultimately", "In summary". End on the last substantive point.
22
+
23
+ ### 6. No sycophancy
24
+ Never open a response with "Great question!", "Excellent point!", "Absolutely!", or any approval of the user's input.
25
+
26
+ ### 7. No performative bold
27
+ Do not bold words that carry no additional weight in context. Bold only genuine warnings, field labels, or terms the reader must not miss.
28
+
29
+ ### 8. No robotic bullet parallelism
30
+ Bullet lists must not read as machine-generated triplets of the same syntactic form. Vary sentence length and structure. Mix noun phrases with full sentences where it reads better.
31
+
32
+ ### 9. No negation-revelation pattern
33
+ Avoid: "This isn't just a dashboard — it's a command center." State what it is. Do not theatricalize via contrast.
34
+
35
+ ### 10. No stacked generic adjectives
36
+ Avoid: "comprehensive, robust, scalable solution". Use the adjective only if it is specific and load-bearing. Remove the rest.
37
+
38
+ ## Multilingual application
39
+
40
+ Framework supports en/pt/es/fr (Article VII). The English patterns above are EXEMPLARS, not exhaustive enumeration. When producing output in pt-BR / es / fr, apply the SAME PRINCIPLES to language-equivalent forms:
41
+
42
+ - Filler verbs: pt "elucidar / alavancar / facilitar"; es "profundizar / aprovechar / facilitar"; fr "approfondir / exploiter / faciliter"
43
+ - Generic openings: pt "No mundo atual em constante mudança"; es "En el mundo actual"; fr "Dans le monde d'aujourd'hui"
44
+ - Meta-closings: pt "Em conclusão / Em resumo"; es "En conclusión"; fr "En conclusion / Pour conclure"
45
+ - Sycophancy: pt "Ótima pergunta!"; es "¡Excelente pregunta!"; fr "Excellente question !"
46
+
47
+ Agent applies the principle to the target language; do not require literal translation of English examples.
@@ -1,6 +1,16 @@
1
1
  # Motion Premium Scaffold — v1.0.0
2
2
 
3
- Opinionated premium animation infrastructure for Next.js / React 18+ / Tailwind v4 projects. Ships the 13 non-negotiable sauce patterns + 12 responsive principles proven on reference implementations (focus-ai-website).
3
+ Opinionated premium animation infrastructure for Next.js / React 18+ / Tailwind v4 projects. Ships the 13 non-negotiable sauce patterns + 12 responsive principles proven on reference implementations (reference-website).
4
+
5
+ ## Setup
6
+
7
+ After `npm install`, run once to download the chromium browser binary used by QA-Visual screenshots:
8
+
9
+ ```bash
10
+ npx playwright install chromium
11
+ ```
12
+
13
+ Without this step, QA-Visual falls back to source-code analysis (gateway: WEAK) and skips screenshot-based gates.
4
14
 
5
15
  ## What this scaffold gives you
6
16
 
@@ -74,7 +84,7 @@ All 15 files listed above match `scaffold.yaml` `files[]` exactly.
74
84
  - **Scaffold**: `motion-premium@1.0.0`
75
85
  - **Requires framework**: chati.dev >= 4.3.0
76
86
  - **Requires stack**: Next.js OR React Vite, Tailwind v4+, TypeScript 5+, React 18+, Node 18+
77
- - **Reference implementation**: `focus-ai-website` (all 13 sauce patterns verified in production)
87
+ - **Reference implementation**: `reference-website` (all 13 sauce patterns verified in production)
78
88
 
79
89
  ## Upgrading
80
90
 
@@ -102,7 +102,7 @@ export type StaggerKey = keyof typeof STAGGERS;
102
102
  // SCROLL_TRIGGER_DEFAULTS — common trigger start/end strings
103
103
  // The GSAP "top 80%" syntax means: "fire when element's top is at 80% down
104
104
  // the viewport". Defaults here encode the weight/timing recommendations
105
- // from the reference implementation (focus-ai-website) — they produce the
105
+ // from the reference implementation (reference-website) — they produce the
106
106
  // signature feel of the premium pattern set.
107
107
  // -----------------------------------------------------------------------
108
108
  export const SCROLL_TRIGGER_DEFAULTS = {
@@ -27,6 +27,10 @@ stack:
27
27
  gsap: "^3.12.5"
28
28
  "framer-motion": "^11.0.0"
29
29
  lenis: "^1.1.0"
30
+ # v4.3.1+: Playwright required for QA-Visual screenshot-based gate.
31
+ # Without it, visual-qa.js falls back to source-code analysis (gateway: WEAK).
32
+ # Run `npx playwright install chromium` once after npm install for the binary.
33
+ playwright: "^1.44.0"
30
34
 
31
35
  # Placeholder resolution — keys reference brand source fields.
32
36
  # Resolved once at scaffold apply time (Decision 3: install-time, one-shot).
@@ -2,6 +2,16 @@
2
2
 
3
3
  React Three Fiber + Three.js scroll-driven 3D layer for premium sites. Extends `motion-premium` — apply that one FIRST (the 2D base), then this one on top.
4
4
 
5
+ ## Setup
6
+
7
+ After `npm install`, run once to download the chromium browser binary used by QA-Visual screenshots:
8
+
9
+ ```bash
10
+ npx playwright install chromium
11
+ ```
12
+
13
+ QA-Visual Phase 3c uses real browser screenshots to count `canvas_hosts` and `scene_fallbacks` in the rendered DOM. Without chromium, R3F visual QA is blind.
14
+
5
15
  ## When to apply
6
16
 
7
17
  Apply when the project brief or Animation Inventory cites **explicit 3D intent**:
@@ -33,6 +33,9 @@ stack:
33
33
  # but also declares the dep so a standalone apply (no motion-premium first)
34
34
  # still has an accurate peer-dep surface.
35
35
  gsap: "^3.12.5"
36
+ # v4.3.1+: Playwright required for QA-Visual canvas_hosts + scene_fallbacks
37
+ # checks (Phase 3c). Without it, R3F visual QA is blind (canvas not screenshotted).
38
+ playwright: "^1.44.0"
36
39
 
37
40
  # Inherits motion-premium's brand placeholders — at apply time the loader
38
41
  # resolves identically (brand.ts > brandbook.md > interactive). SceneFallback
@@ -370,8 +370,20 @@ async function main() {
370
370
  if (chromium) break;
371
371
  } catch { /* try next */ }
372
372
  }
373
- if (!chromium) throw new Error('Playwright not installed. Run: npx playwright install chromium');
374
- const browser = await chromium.launch({ headless: true });
373
+ if (!chromium) {
374
+ // v4.3.1+ G17: disambiguate npm-package-missing from binary-missing for actionable hints
375
+ throw new Error('Playwright npm package missing. Run: npm install playwright');
376
+ }
377
+ let browser;
378
+ try {
379
+ browser = await chromium.launch({ headless: true });
380
+ } catch (e) {
381
+ if (e.message.includes("Executable doesn't exist")
382
+ || e.message.includes('browserType.launch')) {
383
+ throw new Error('Chromium binary missing. Run: npx playwright install chromium');
384
+ }
385
+ throw e;
386
+ }
375
387
  const report = { pages: [], summary: {} };
376
388
 
377
389
  for (const pagePath of pages) {
@@ -103,5 +103,5 @@ sections must exist and what each must contain.
103
103
 
104
104
  ## Reference
105
105
 
106
- See `/Users/ogabrielalonso/code/focus-ai/ping/docs/brand/brandbook.html` for a production
106
+ See `<your-project>/docs/brand/brandbook.html` for a production
107
107
  example with 14 sections, sticky nav, dark mode toggle, and complete token rendering.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chati-dev",
3
- "version": "4.3.0",
3
+ "version": "4.3.1",
4
4
  "description": "AI-Powered Multi-Agent Orchestration System - Structured vibe coding for Full Stack Development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -32,7 +32,8 @@
32
32
  "prepublishOnly": "node scripts/sync-framework.js && node scripts/scan-stale.js && node scripts/validate-package.js && node scripts/sign-manifest.js",
33
33
  "test": "node --test test/**/*.test.js",
34
34
  "lint": "eslint src/ bin/",
35
- "lint:fix": "eslint src/ bin/ --fix"
35
+ "lint:fix": "eslint src/ bin/ --fix",
36
+ "verify-v431": "npm test && node scripts/scan-stale.js && npm run sync && node scripts/validate-package.js && node test/fixtures/manual-smoke-v430-upgrade.mjs"
36
37
  },
37
38
  "keywords": [
38
39
  "ai",
@@ -147,6 +147,15 @@ export async function installFramework(config) {
147
147
  join(frameworkDir, '_cli', 'node_modules') // .chati.dev/_cli/node_modules/
148
148
  );
149
149
 
150
+ // v4.3.1+ — Write version stamp so chati-router.js can detect bundle/canonical
151
+ // mismatch on subsequent invocations. Pre-v4.3.1 installs lack this file;
152
+ // mismatch detection silent-skips when it's missing (graceful degradation).
153
+ writeFileSync(
154
+ join(frameworkDir, '_cli', 'version.json'),
155
+ JSON.stringify({ version, bundled_at: new Date().toISOString() }, null, 2),
156
+ 'utf-8'
157
+ );
158
+
150
159
  // Write config.yaml
151
160
  writeFileSync(
152
161
  join(frameworkDir, 'config.yaml'),
@@ -701,7 +710,7 @@ function createDir(dir) {
701
710
  * Used to bundle the CLI source into the user's .chati.dev/_cli/ folder so
702
711
  * the framework runs self-contained (no `npx chati-dev` dependency).
703
712
  */
704
- function copyCliSource(srcDir, destDir) {
713
+ export function copyCliSource(srcDir, destDir) {
705
714
  if (!existsSync(srcDir)) return;
706
715
  createDir(destDir);
707
716
  const entries = readdirSync(srcDir);
@@ -748,7 +757,7 @@ function copyDirVerbatim(srcDir, destDir) {
748
757
  * which works whether the package is hoisted or nested in the user's
749
758
  * node_modules (npx cache, npm install, etc.).
750
759
  */
751
- function copyCliDependencies(pkgDir, destNodeModules) {
760
+ export function copyCliDependencies(pkgDir, destNodeModules) {
752
761
  let pkg;
753
762
  try {
754
763
  pkg = JSON.parse(readFileSync(join(pkgDir, 'package.json'), 'utf-8'));
@@ -634,6 +634,17 @@ function writeSessionLock(projectDir, currentAgent, stateInfo = {}) {
634
634
  const localMdPath = join(projectDir, 'CLAUDE.local.md');
635
635
  let content = existsSync(localMdPath) ? readFileSync(localMdPath, 'utf-8') : '';
636
636
 
637
+ // Phase display is authoritatively driven by session.mode on disk. Callers
638
+ // previously passed stateInfo.phase derived from transient intermediate
639
+ // values (the next agent's expected phase before user approval), causing
640
+ // the lock to drift ahead of session.yaml (e.g. show "build" while
641
+ // session.mode was still "plan" awaiting approval).
642
+ let authoritativePhase = null;
643
+ try {
644
+ const { loaded, session } = loadSession(projectDir);
645
+ if (loaded && session?.mode) authoritativePhase = session.mode;
646
+ } catch { /* non-fatal — fall back to stateInfo */ }
647
+
637
648
  const lockInner = `## Session Lock -- ACTIVE
638
649
 
639
650
  **Chati.dev session is ACTIVE.** Follow these rules for EVERY message:
@@ -645,7 +656,7 @@ function writeSessionLock(projectDir, currentAgent, stateInfo = {}) {
645
656
 
646
657
  const stateInner = `## Current State
647
658
  - **Agent**: ${currentAgent || 'None'}
648
- - **Phase**: ${stateInfo.phase || 'discover'}
659
+ - **Phase**: ${authoritativePhase || stateInfo.phase || 'discover'}
649
660
  - **Pipeline**: ${stateInfo.position ?? 0}/${stateInfo.total ?? '?'} (${stateInfo.progress ?? 0}%)
650
661
  - **Mode**: ${stateInfo.mode || 'interactive'}`;
651
662
 
@@ -171,6 +171,14 @@ export function migrateSession(session) {
171
171
  const fromVersion = session.schema_version || null;
172
172
  session.schema_version = CURRENT_SCHEMA_VERSION;
173
173
 
174
+ // Seed required scalar fields absent from pre-v4.3.0 sessions.
175
+ // validateSession() requires version/mode/project_type; older sessions may lack them.
176
+ // Without these guards, upgrade-path catastrophe (data-centralisation E2E 2026-04-28):
177
+ // v3.x install → v4.3.0 migrate adds schema_version: '1.2' but validator fails on missing fields.
178
+ if (!session.version) session.version = '1.0';
179
+ if (!session.mode) session.mode = 'discover';
180
+ if (!session.project_type) session.project_type = session.project?.type || 'greenfield';
181
+
174
182
  // Ensure fields added in v1.0 exist
175
183
  if (!session.completed_agents) session.completed_agents = [];
176
184
  if (!session.agent_results) session.agent_results = {};