- BDR-012 — `/client-handover` cover: white bg + green-forest accents + PNG logo default. Captures iteration (cream → green-dark rejected → white-pure final). Why: light theme matches zenquality.fr without overpowering long client-facing text. Solid green-dark reserved for marketing covers, not deliverables. - LRN-013 — marked CLI 16.x ignores stdin and dumps own cli.js source. Pattern: do not assume marked CLI accepts stdin like awk/jq/sed. Always pass `-i FILE`. Smoke-test MD→HTML output for known content. - Journal — appends bugfix subsection under 2026-05-07 with the three bugs, root causes, fixes, verified output (164 KB / 19 pages). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
30 KiB
30 KiB
| type | entry_prefix | schema | rules | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| decisions_registry | BDR |
|
|
Decisions registry (BDR)
Index
| ID | Date | Title | Status |
|---|---|---|---|
| BDR-001 | 2026-04-22 | Uniform --help helper via session-start hook (option C) | accepted |
| BDR-002 | 2026-04-23 | Move tasks/ + introduce memory + audits under .claude/ | accepted |
| BDR-003 | 2026-04-23 | Gitignore wildcard + negations pattern for .claude/ | accepted |
| BDR-004 | 2026-04-27 | Adopt auto permission mode as default | accepted |
| BDR-005 | 2026-04-27 | motion as default animation library; advisor stays read-only |
accepted |
| BDR-006 | 2026-05-03 | Caveman as 4th always-on plugin (output compression) | accepted |
| BDR-007 | 2026-05-04 | Skill profiles partition gstack by usage (design / dev / qa / audit / minimal) | accepted |
| BDR-008 | 2026-05-04 | Profile system v2: extend to plugins + MCPs + CLIs (web/seo/web-full/backend) | accepted |
| BDR-009 | 2026-05-05 | Mandate caveman format on .claude/memory/ registries | accepted |
| BDR-010 | 2026-05-07 | Gate GEO independently at ≥17/20 in client-handover pipeline | accepted |
| BDR-011 | 2026-05-07 | Client handover deliverable: 4-chapter structure + ZenQuality branded HTML/PDF | accepted |
BDR-001 — Uniform --help helper via session-start hook (option C)
- Date: 2026-04-22
- Status: accepted
- Decision: every skill expose
--helpvia shared snippet injected by session-start hook, not duplicate helper in each SKILL.md. - Why: 25+ skills — keep same helper synced across every file guarantees drift. Single injection point = single source of truth.
- Alternatives rejected:
- Option A (copy helper into each SKILL.md) — rejected: maintenance entropy.
- Option B (external wrapper
/help <skill>) — rejected: breaks "one command = one skill" experience.
- Reference: commit
3968a29.
BDR-002 — Move tasks/ + introduce memory + audits under .claude/
- Date: 2026-04-23
- Status: accepted
- Decision: migrate
./tasks/to.claude/tasks/, create.claude/memory/(5 registries BDR/LRN/BLK/journal/EVAL) and.claude/audits/for AUDIT_* files. Adapt skills/agents/CLAUDE.md. Integrate CAPITALIZE step into completion skills (ship-feature, feat, bugfix, hotfix, commit-change), add/closeskill for session-end ritual. - Why: group all meta-project state (AI config + tasks + memory + audits) under
.claude/isolate Claude governance from real code. Aligned with official Claude Code memory docs. Without integration in completion skills, registries stay empty (aspirational text). - Alternatives rejected:
- Keep
./tasks/at root — rejected: clutters repo, mixes code signal with governance signal. - Use
.claude/agent-memory/for everything — rejected:agent-memory/has distinct role (already used by other tools). - Ritual as aspirational text only in CLAUDE.md — rejected: zero execution guarantee, registries stay empty.
Stophook to ask 3 questions every turn — rejected: too noisy.
- Keep
BDR-003 — Gitignore wildcard + negations pattern for .claude/
- Date: 2026-04-23
- Status: accepted
- Decision: use
.claude/*(wildcard match of immediate children) + negations!.claude/tasks/,!.claude/memory/, etc., not.claude/(recursive ignore). - Why: when parent ignored via
.claude/, git no descend (performance optimization) and negations on children ignored — documented ingitignore(5). With.claude/*, git matches each child individually, negations active. - Alternatives rejected:
.claude/+!.claude/tasks/(naive) — rejected: negations no effect, everything stays ignored.- Drop
.claude/from gitignore entirely — rejected:.claude/settings.local.jsonand.claude/agent-memory/must stay ignored (per-machine). - Track paths via
.gitattributesor external tool — rejected: over-engineering, git handles natively.
- Reference: commit
499cd07,git check-ignore -vverified on 4 paths (2 tracked, 2 ignored).
BDR-004 — Adopt auto permission mode as default
- Date: 2026-04-27
- Status: accepted
- Decision: set
permissions.defaultModeto"auto"in user-scopesettings.json, dropdisableAutoMode: "disable". Auto mode runs classifier on every action, blocks risky operations (curl|bash, prod deploys, force push, IAM grants, mass deletes, exfiltration to external endpoints), auto-approves local edits, lockfile-declared dep installs, read-only HTTP. - Why: prompt fatigue under
defaultmode big on multi-step autonomous work. Auto mode keeps safety net (classifier review) without per-tool friction. Classifier re-evaluates conversation-stated boundaries ("don't push", "wait for review") on every check, verbal constraints carry weight. - Alternatives rejected:
- Keep
default— too many prompts, breaks flow on long tasks. acceptEdits— eliminates prompts but no classifier, blanket trust on Bash beyond filesystem helpers.bypassPermissions— skips all checks, no prompt-injection guard. Only for isolated containers.dontAsk— full denylist, breaks anything not pre-approved. Suited to CI, not interactive work.
- Keep
- Caveats: requires Claude Code v2.1.83+, plan ≠ Pro (Max/Team/Enterprise/API only), Sonnet 4.6 / Opus 4.6 / Opus 4.7, Anthropic API provider. On entering auto mode, blanket allow rules (
Bash(*),Bash(python*), package-manager run,Agent) dropped, restored on exit. - Reference: commit
1421578.
BDR-005 — motion as default animation library; advisor stays read-only
- Date: 2026-04-27
- Status: accepted
- Decision: when project stack supports it, framework installs
motion(ormotion-vfor Vue 3 / Nuxt) as default animation library. Install automatic in/init-projectSTEP 5e (post-scaffold), opt-in in/onboardSTEP 2.5 (existing projects).plugin-advisoronly detects and reports status — never runsnpm installitself. Detection logic inlib/animation-lib-check.sh(sourced by all three layers). - Why: framer-motion rebranded
motionin November 2024 (single package supporting Reactmotion/react, Svelte, vanilla JS;motion-vparallel package for Vue). Bake new name now to avoid legacy-import sprawl across new projects. Split init-vs-onboard behavior follows trust gradient: at init, user just validated entire scaffold so silent install fine; at onboard, touching existingpackage.jsoninvasive without explicit consent. Plugin-advisor kept read-only to preserve "Never modify files" contract (PHASE 4 already mutates plugin state with confirmation; piling npm installs on top blurs responsibility). - Alternatives rejected:
- Pin
framer-motion(legacy name) — rejected: package in maintenance mode, every new project inherits old import path. - Auto-install during
/onboardwithout asking — rejected: silently adds runtime dep + ~50 KB gzip to project user did not ask to modify. - Make
plugin-advisorinstall missing libs — rejected: violates read-only spec, breaks separation of concerns (advisor advises; orchestrators mutate). - React-only scope — rejected: Vue/Svelte teams should benefit;
motion-vmakes Vue case clean.
- Pin
- Eligibility rules (helper output):
eligible|motion: React, Next.js, Remix, Astro+React, Svelte/SvelteKiteligible|motion-v: Vue 3, Nuxtno|-: backend, CLI, embedded, Flutter, static HTML, React Native (usereact-native-reanimated), Astro without UI integration, nopackage.json
- Reference: helper at
lib/animation-lib-check.sh; integration inskills/init-project/SKILL.mdSTEP 5e,skills/onboard/SKILL.mdSTEP 2.5,agents/plugin-advisor.mdPHASE 1/2/3,lib/design-gate.md.
BDR-006 — Caveman as 4th always-on plugin (output compression)
- Date: 2026-05-03
- Status: accepted
- Decision: install
JuliusBrussee/cavemanin always-on tier alongsidesecurity-guidance,superpowers,rtk. "Full" install = plugin (/caveman+ cavecrew agents + plugin-scoped SessionStart/UserPromptSubmit hooks) + standalone hooks (statusline + stats badge in~/.claude/hooks/) +caveman-shrinkMCP scaffold (NOT auto-registered — proxy needs upstream wrapper).install-plugins.shSTEP 5.5 callsenable_plugin "caveman" "caveman"to write intoenabledPlugins. Hook paths insettings.jsonnormalized to~/.claude/hooks/...post-install so user home dir no leak across machines. - Why: caveman compresses Claude output ~75% via caveman-speak, preserves technical substance. Symmetrical with rtk (input compression hook) — rtk shrinks tool I/O, caveman shrinks model output. Both hooks pay zero passive cost in clean session, amortize across long runs. Always-on justified: plugin auto-deactivates with phrases like "stop caveman" / "normal mode", toggle would be friction without benefit.
- Alternatives rejected:
- Toggle plugin (start OFF) — rejected: misses by-default benefit; user need remember
claude plugin enable caveman@cavemanper session, negates auto-compression value. --minimalinstall (plugin only) — rejected: loses standalone stats badge surfacing token-saving telemetry.--allinstall (adds per-repocaveman-rules.mdetc. into$PWD) — rejected: would litter THIS config repo (cwd at install time) with rule files meant for project repos. Let users opt in per-repo when wanted.- Auto-register
caveman-shrinkMCP — rejected: proxy errors with "missing upstream command" without upstream MCP to wrap, fails health checks. Print snippet instead, let user pick which upstream they want compressed (filesystem, github, …).
- Toggle plugin (start OFF) — rejected: misses by-default benefit; user need remember
- Caveats:
- Caveman
hooks/install.shwrites absolute paths ($HOME/.claude/hooks/caveman-*.js) intosettings.json.settings.jsonsymlinked into repo, absolute path commits username. STEP 5.5 runs Python post-process to rewrite to portable~/.claude/hooks/...form (bash expands~before passing tonode). - Caveman hook files materialize in
hooks/(repo dir, not~/.claude/hooks/) because latter is symlink. Added to.gitignoreto prevent accidental commit of user-scope state.
- Caveman
- Reference: install-plugins.sh STEP 5.5, lib/detect-plugins.sh
detect_caveman*+plugin_enabled, doctor.sh caveman block, commit9b20b84.
BDR-007 — Skill profiles partition gstack by usage (design / dev / qa / audit / minimal)
- Date: 2026-05-04
- Status: accepted
- Decision: ship
lib/profile.sh+lib/profiles/*.profileto give user fine-grained, task-shaped activation of skills. Profile = plain-text file listing skill names + types (gstack,external,personal,plugin,mcp).profile set <name>enables listed skills, disables every gstack-origin skill not in profile, by moving symlinks betweenskills/andskills-disabled/.profile resetre-enables all of gstack. Plugin/MCP entries advisory — script prints manualclaude plugin enable/claude mcp addcommand but never runs it. Surface area: one CLI (bash lib/profile.sh), one slash command (/profile), four Makefile targets, section inagents/plugin-advisor.md. - Why: when user works on focused kind of task (design only, qa only, audit only) full gstack (~38 skills) injects irrelevant skill descriptions into every session. Existing
lib/toggle-external.sh enable|disable gstacktoo coarse — disables whole gstack including infrastructure skills user does want (checkpoint, ship, learn). Profiles give curated middle ground: keep gstack repo installed, hide skills not relevant to this session. - Alternatives rejected:
- Fork SKILL.md files to strip ~70-line gstack preamble — rejected: every gstack upgrade needs re-fork, preamble already degrades gracefully (
|| true) whengstack/bin/unavailable. Hiding skill cheaper than rewriting. - Per-skill toggle via
claude plugin enable/disable— rejected: gstack skills not marketplace plugins, symlinks owned byskills-external/gstack/. CLI no reach them. - Disable via removing symlinks (rm + recreate on enable) — rejected: lossy if user has local edits, re-creation requires running gstack own setup. Move-based toggle preserves symlink intact.
- Auto-toggle plugins (
ui-ux-pro-max) and MCPs as part ofset— rejected: those affect global Claude Code state, may carry API keys (magic). Keep advisory; user runs CLI command knowingly. - Build giant
gstack-profileCLI wrappinggstack/bin/*directly — rejected: scope creep into gstack internals. Repo already has own toggle infra (lib/toggle-external.sh); profile.sh sits alongside as finer tool.
- Fork SKILL.md files to strip ~70-line gstack preamble — rejected: every gstack upgrade needs re-fork, preamble already degrades gracefully (
- Caveats:
- Profiles do NOT change
gstack/bin/infrastructure — preamble in disabled skills still references it, re-enabling restores normal behavior. No telemetry/learnings data touched. cmd_setonly auto-disables skills returned bygstack_skills()(those withSKILL.mdunderskills-external/gstack/*/). Personal skills (real dirs inskills/) never auto-disabled byset— only added back if listed in profile.cmd_currentreturns "full" when nothing disabled, even if profile happens to be 100% covered by current state. Active-profile heuristic requires at least onegstack__*entry inskills-disabled/so we no lie about profile being "set" when nosetever ran.- Personal skills use
external-style move (nogstack__prefix) so name-collision with gstack skills cannot happen during disable.
- Profiles do NOT change
- Reference:
lib/profile.sh,lib/profiles/{design,dev,qa,audit,minimal}.profile,skills/profile/SKILL.md,agents/plugin-advisor.md(DETECT block + TOGGLING EXTERNAL TOOLS section),Makefiletargetsprofile*,lib/toggle-external.shheader pointer.
BDR-008 — Profile system v2: extend to plugins + MCPs + CLIs (web/seo/web-full/backend)
- Date: 2026-05-04
- Status: accepted
- Decision: extend
profile.shto actually toggle Claude plugins (claude plugin enable|disable <name>@<marketplace>) and MCP servers (delegated tolib/toggle-external.shformagicMCP, advisory for others), add CLI status reporting. New profile syntax usesplugin@<marketplace>so script knows where to enable from. New profiles shipped:web(frontend website),seo(SEO/GEO/W3C audit),web-full(web + seo combined),backend(API/system dev — no design, no SEO). Reverted v1 decision (BDR-007 alternative #4 "advisory only for plugins/MCPs"): user explicitly asked for actual toggling soset webactively enablesui-ux-pro-max+magic,set seoactively disablesui-ux-pro-max. Always-on plugins (caveman,security-guidance,superpowers) protected by both allowlist (MANAGED_PLUGINS) and denylist (PROTECTED_PLUGINS). - Why: v1 profiles only managed skills (symlink toggle). User feedback: "active TOUT le splugins necessaire pour tel profile et desactive les autre". Pure-skill toggling left ui-ux-pro-max/magic always loaded regardless of profile, passive token cost no drop as much as expected when switching to non-design profile. Auto-toggling plugins shifts design from "show me right skills" to "set up right session" — closer to what user actually wants.
- Alternatives rejected:
- Keep plugins advisory + add
--apply-pluginsflag — rejected: user has to type flag every time, defeats "switch profile to switch context" workflow. - Disable ALL non-listed plugins (including third-party user-installed ones) — rejected: too aggressive. Profile system has no business touching plugins user installed for own reasons. Solution: explicit
MANAGED_PLUGINSallowlist (currently 3 entries) — script touches only those. - Treat MCPs identically to plugins (auto-toggle any MCP) — rejected: MCPs typically need env vars / API keys / specific commands. Auto-registering with wrong config produces broken MCPs (LRN-006). Compromise: auto-toggle ONLY
magicbecause already have its config inlib/toggle-external.sh. Other MCPs stay advisory. - Track plugin state across
set/resetcycles, restore on reset — rejected: complexity not worth it.resetre-enables gstack skills only. To re-enable managed plugin, user runsapply <profile>or explicitclaude plugin enablecommand. Documented ininfoline printed at end ofreset.
- Keep plugins advisory + add
- Caveats:
MANAGED_PLUGINShardcoded — adding new toggle-managed plugin requires editingprofile.sh. Acceptable for now (3 entries, rarely changes); revisit if grows.claude plugin enablereturns success even for already-enabled plugins, parser greps for "enabled|already" in stdout/stderr. Works on current Claude CLI; brittle if CLI rewords messages. Acceptable risk.currentheuristic now countsinstalled(CLI status) as available. Without that, profiles listing CLIs would never reach 100% match. Tiebreaker: when two profiles tie on %, larger total wins (web-full > web > design when all are 100%).cmd_showwidened TYPE column to 30 chars to fitplugin@ui-ux-pro-max-skillwithout breaking alignment.mcp magictoggle delegates tolib/toggle-external.sh enable magicwhich requiresMAGIC_API_KEYin.env. If key missing, profile.sh prints info line and continues — rest of profile still applies.
- Reference:
lib/profile.sh(MANAGED_PLUGINS/PROTECTED_PLUGINSarrays,skill_statusplugin@/cli/mcp branches,enable_skill/disable_skillplugin@ + mcp branches,cmd_setplugin disable loop,cmd_currentavailable-counting),lib/profiles/{web,seo,web-full,backend}.profile, refinedlib/profiles/{design,dev,qa,audit}.profile(useplugin@<marketplace>syntax +clientries),skills/profile/SKILL.md(updated profile table + mechanism table),agents/plugin-advisor.md(extended profile recommendation table).
BDR-009 — Mandate caveman format on .claude/memory/ registries
- Date: 2026-05-05
- Status: accepted
- Decision: all writes to
.claude/memory/*.md(decisions, learnings, blockers, journal, evals) MUST use caveman style — drop articles (a/an/the), drop filler (just/really/basically/actually/simply), fragments OK, short synonyms (big not extensive, fix not "implement a solution for"). Keep technical terms exact, code blocks unchanged, error messages quoted exact, IDs (BDR-XXX, LRN-XXX, BLK-XXX, EVAL-XXX) and dates unchanged. Pattern:[thing] [action] [reason]. [next step].Rule added toCLAUDE.md"Memory registries" section. Applied retroactively to existing 5 registries via/caveman:compress. Pre-compression backups saved as*.original.md(gitignored). - Why: registries loaded every session start (per CLAUDE.md "Session start" step 1) — every token compressed cuts permanent input cost. Measured ~40% input-token reduction across 5 files (164→97 lines on average per registry). Caveman style preserves all technical substance (code, IDs, error strings, refs) while dropping prose padding that no engineer needs at re-read. Rule mirrors English-only rule that already governs registries — both about read-efficiency, not aesthetics.
- Alternatives rejected:
- Compress only on new entries, leave existing prose untouched — rejected: every session-start still pays 40% prose tax on legacy entries (largest part of file). Mixed-style file harder to scan than uniform compressed file.
- Use lighter compression (drop only fillers, keep articles) — rejected: half-measure. Caveman lite saves ~15%, full saves ~40%. Cost identical (one /caveman:compress run).
- Move registries to JSON/YAML for max density — rejected: registries are narrative (BDR rationale, LRN context). YAML/JSON would lose nuance, force schema rigidity. Caveman keeps prose readable, just compressed.
- Skip rule, rely on writers to compress organically — rejected: untested writers (skills, future agents) revert to verbose prose. Explicit rule + caveman-mode-active hook ensures consistency without per-skill enforcement.
- Caveats:
- Code blocks, error strings, commit refs, IDs, dates, file paths MUST stay byte-exact — caveman applies to prose only.
- User-facing CAPITALIZE prompts may stay verbose / mirror user language; rule applies only to written entry.
*.original.mdbackups gitignored (BDR-009 commit639486a) — recoverable via git history of pre-compression commit.- Existing registries entries compressed in commit
e4a9259; new entries written caveman from start (BDR-009 itself is first such entry).
- Reference:
CLAUDE.md"Format — registries ALWAYS caveman" section, commits520188a(rule added),e4a9259(5 registries compressed),639486a(gitignore backups).
BDR-010 — Gate GEO independently at ≥17/20 in client-handover pipeline
- Date: 2026-05-07
- Status: accepted
- Decision: client-handover gates SEO classique AND GEO (IA) independently — both must reach
≥17/20. Was: combined display only, gate fired on first/20line found (de facto SEO classique alone). Now:ALL_PASS = (SEO_AFTER ≥ 17) AND (GEO_AFTER ≥ 17) AND (HARDEN_AFTER ≥ 17) AND (VALIDATE_AFTER ≥ 17 OR SKIPPED). SEO subagent re-dispatched if either axis below threshold (same agent fixes both). Score table + roadmap + client doc §4 split rows accordingly. - Why: handover deliverable claims "site ready" — bar must hold on classical search (Google/Bing) AND AI search (ChatGPT/Perplexity) given AI traffic growth. Combined gate (e.g. global pondéré ≥17) lets GEO stay weak while combined passes — false confidence shipped to client. Independent gates close gap.
- Alternatives rejected:
- Gate on
Score global pondéré ≥17only — rejected: SEO=20 + GEO=10 → global=18 → passes despite GEO=10. Same false-confidence issue. - Keep GEO informational (Phase A initial design) — rejected: breaks "every gated audit ≥17 or stop" rule. Two-tier system (gated vs informational) confuses client + breaks score-table semantics.
- Lower GEO threshold to ≥15 — rejected by user: weakens signal. Real fix is optimize GEO, not lower bar.
- Split into two parallel subagents (one SEO, one GEO) — rejected: /seo skill runs both inside one envelope-merge dispatcher. Splitting at handover layer duplicates context discovery (STEP 0) + doubles wall-clock.
- Gate on
- Caveats:
- GEO ≥17 hard on existing sites — most lack llms.txt, Speakable/QAPage Schema, entity SEO (sameAs/Wikidata @id), TL;DR/Q→A content shape. Expect more fix-loop iterations on GEO than SEO. Override option C still per-axis with explicit user consent.
SCORE_GEO_AFTER = "UNKNOWN"treated as fail — legacy single-score SEO.md triggers re-dispatch with explicit demand for both labeled lines (Score SEO (classique) : X.X / 20+Score GEO (IA) : X.X / 20).- Backward compat split:
extract_score_labeledSEO usesallow_fallback=yes(legacy single-score parses as SEO classique); GEO usesallow_fallback=no(no silent duplicate of SEO score). - Loop logic axis-aware:
while (SEO < 17 OR GEO < 17) AND iter ≤ MAX. Re-dispatch prompt labels both scores with PASS/FAIL + lists axis-specific fixes (SEO: meta/canonical/sitemap; GEO: llms.txt/Schema AI/entity SEO).
- Reference: commit
5569a80,agents/client-handover-writer.md(STEP 3extract_score_labeled, STEP 4 axis-aware loop + re-dispatch prompt, STEP 8 gate rule + score table + threshold strictness, STEP 12 §4 client doc table),skills/client-handover/SKILL.md.
BDR-011 — Client handover deliverable: 4-chapter structure + ZenQuality branded HTML/PDF
- Date: 2026-05-07
- Status: accepted
- Decision: client handover doc restructured to 4 chapters — §1 Ce qu'il fallait faire (et pourquoi) (briefing+motivation, 100–180 words), §2 Ce qui a été fait (lay summary, ≤300 words hard cap, zero jargon, no internal tool/skill names), §3 Ce qui vous reste à faire (action-only checklist with cadences), §4 Détails techniques (pour les curieux) (scores, key choices, phases, optional glossary — internal labels allowed here only). Plus optional §5 (external platforms, web), §6 (build & deploy). Replaces old 9-section structure. Output now triple:
LIVRAISON.md(editable source) +LIVRAISON.html(always, branded) +LIVRAISON.pdf(when PDF engine on host). HTML/PDF use ZenQuality identity — green palette#1A3A25 / #2D5A3D / #4A7C59 / #87A878, cream BG#F5F0EB, fonts Inter (body) + Playfair Display (headings), cover page with logo + tagline "La sérénité numérique, la qualité en plus", running header (project name) + footer (page N/M,ZenQuality — zenquality.fr). Renderer cascade: MD→HTML via pandoc > python markdown >npx marked; HTML→PDF via weasyprint > wkhtmltopdf > chromium > headless Chrome. STEP 15 enforces gates before render: chapter 2 word count ≤300 (wc -w) AND forbidden-token grep on chapters 1–3 (no/seo,/harden,/validate,/cso,seo-analyzer,SEO.md,SCORE_*, etc.). - Why: client reads top-down, may stop after §2 — old 9 sections diluted the read. Bare markdown unreadable by non-tech client. Branded PDF = professional deliverable matching company identity (ZenQuality), suitable to email/print/sign. Per-section gates prevent regression to skill-name leaks or jargon bloat.
- Alternatives rejected:
- Keep 9-chapter structure + bolt PDF wrapper on top — rejected: doesn't fix dilution + leak risk; client still scans through "Lessons learned (optional)" / "Pour aller plus loin" before useful actions.
- Render PDF only (no HTML intermediate) — rejected: no fallback if engine missing; HTML doubles as browser preview + manual print-to-PDF route. Triple output (
md+html+pdf) covers all cases. - Single PDF engine (e.g. weasyprint only) — rejected: assumes Python installed; cascade gives graceful degradation. Order chosen: weasyprint (best CSS), wkhtmltopdf (mature), chromium (always-bundled on dev hosts).
- Pandoc with custom template only — rejected: pandoc often not installed (was missing on this host); shell cascade with multiple converters more portable.
- Soft 300-word target — rejected: without hard
wc -wgate, drift inevitable. Cap+gate forces rewrite when over.
- Caveats:
- Word-count + leak gates run at STEP 15 after synthesis, not during. Worst case: re-write step needed. Acceptable trade-off vs in-flight enforcement (would require word counter inside agent prompt — fragile).
- ZenQuality logo URL hardcoded as
https://zenquality.fr/logo-horizontal.svg;LOGO_URLenv var allows local file override (bake into PDF for offline robustness if branding changes / SVG breaks). - PDF cascade detects via
command -vonly — assumes engines on$PATH. Custom installs need$PATHadjusted before invocation. - Bash heredoc + stdin-pipe collision bug in v1 (silent empty output) — fixed via env-var pass-through (LRN-012).
- Renderer always outputs HTML + tries PDF; on PDF failure exits 2, prints install hints. STEP 16 reports
PDF: NOT GENERATEDwith hints in final report. - Optional glossary in §4.4 listed terms (HSTS, CSP, WCAG, Schema.org, llms.txt, SEO/GEO) — only renders if ≥4 of these appear in §4 body.
- Reference: commit
e06b52a,agents/client-handover-writer.md(STEP 12 4-chapter doc structure + tone rules, STEP 15 word-count + leak gates, STEP 16 RENDER pipeline, STEP 17 final report),skills/client-handover/scripts/handover-to-pdf.sh(cascade renderer),skills/client-handover/resources/branding/zenquality.css(ZenQuality print stylesheet),skills/client-handover/resources/branding/zenquality-template.html(HTML wrapper with placeholders).
BDR-012 — client-handover cover: white bg + green accents + PNG logo default
- Date: 2026-05-07
- Context: original
.coverCSS used cream--white-cream(#F5F0EB) bg + 8mm green stripe top. Washed out. SVG logologo-horizontal.svgblended into cream bg = low contrast. User feedback: "couleur du fond n'est pas bon", "utiliser une icone non white". Tried green-dark bg first (rejected — too heavy for client-facing doc, hurt readability of long meta block). - Choice:
.coverbg →--white-purewith two subtle radial tints (sage top-right rgba(135,168,120,0.18), green-forest bottom-left rgba(45,90,61,0.06)). Body text →--black-deep. Title--black-deep. Eyebrow/meta labels/footer →--green-forest(medium green). Meta border-left + meta-strong →--green-forest. Removed.cover::before8mm stripe. DefaultLOGO_URL→https://zenquality.fr/assets/logo-horizontal-1024.png. - Alt rejected: (a) cream
#F5F0EBbg — washed-out, original problem. (b) solid green-dark bg — too heavy, hurt long-text readability, felt like marketing brochure not deliverable. (c) generic white + black — no brand signal. - Why: light theme with green accents matches zenquality.fr without overpowering. White bg keeps long client-facing text readable. Green-forest on white = WCAG AA contrast + brand cue. Subtle radial gradients add depth without weight.
- Status: shipped.
- How to apply: ZenQuality client-facing print docs default to white bg + green-forest accents. Body interior keeps cream
--white-creamas accent (code blocks, blockquote bg) — not as page bg. Solid green-dark reserved for marketing covers, not deliverables. - Reference:
skills/client-handover/resources/branding/zenquality.css.coverblock (line 71-86 bg, 119-149 typography);scripts/handover-to-pdf.shline 107 (LOGO_URL default);agents/client-handover-writer.mdline 1218-1222 (doc updated).