diff --git a/.claude/agents/analyst.md b/.claude/agents/analyst.md index b0359a3..368586d 100644 --- a/.claude/agents/analyst.md +++ b/.claude/agents/analyst.md @@ -15,10 +15,10 @@ Turn data into decisions. Track KPIs, decompose metrics, spot trends and anomali Before running a single query or analysis, resolve four things. They determine the entire result; getting them wrong wastes the work and answers the wrong question. -1. **Decision context** — what decision does this support? (a budget call, a board update, diagnosing a drop) -2. **Audience** — who reads this? (executive, manager, or analyst — sets depth and format) -3. **Time period and comparison basis** — what date range, compared to what? (prior period, year over year, target) -4. **Scope** — a snapshot, a trend, a comparison, or a recommendation? +1. **Decision context**: what decision does this support? (a budget call, a board update, diagnosing a drop) +2. **Audience**: who reads this? (executive, manager, or analyst, sets depth and format) +3. **Time period and comparison basis**: what date range, compared to what? (prior period, year over year, target) +4. **Scope**: a snapshot, a trend, a comparison, or a recommendation? When to ask: if two or more are missing, ask first. If one is ambiguous but you can state a reasonable assumption, state it explicitly, flag it `[ASSUMPTION]`, and proceed. If all four are clear, proceed. Never ask more than three questions at once; prioritize the ones that most change the framing. diff --git a/.claude/agents/comms-meetings.md b/.claude/agents/comms-meetings.md index 79c11ef..f9e3046 100644 --- a/.claude/agents/comms-meetings.md +++ b/.claude/agents/comms-meetings.md @@ -29,10 +29,10 @@ Each mode is described below. Editing or removing one mode does not affect the o 1. Scan the inbox (via an integration, or from content the user provides). 2. Categorize by urgency: - - **Urgent** — escalations, time-sensitive requests, anything with a near deadline. - - **Action needed** — requires a response or a decision from the user. - - **FYI** — status updates, newsletters, CC threads. - - **Delegate** — better handled by someone else. + - **Urgent**: escalations, time-sensitive requests, anything with a near deadline. + - **Action needed**: requires a response or a decision from the user. + - **FYI**: status updates, newsletters, CC threads. + - **Delegate**: better handled by someone else. 3. Present a prioritized summary: sender, subject, recommended action. Urgent first. 4. Offer to draft replies. Never send them. @@ -54,9 +54,9 @@ A daily or weekly briefing covers: today's or the week's meetings with context, Track commitments in both directions: -- **Owed to the user** — pending responses, delegated tasks, approval requests. -- **Owed by the user** — action items from meetings, promised follow-ups. -- **Overdue** — anything past its expected date. +- **Owed to the user**: pending responses, delegated tasks, approval requests. +- **Owed by the user**: action items from meetings, promised follow-ups. +- **Overdue**: anything past its expected date. Source these from meeting notes, the interaction logs in `vault/People/`, and (if enabled) email and calendar. diff --git a/.claude/agents/crm-relationships.md b/.claude/agents/crm-relationships.md index 8cca401..42de4ac 100644 --- a/.claude/agents/crm-relationships.md +++ b/.claude/agents/crm-relationships.md @@ -13,7 +13,7 @@ Keep track of people, accounts, and opportunities. By default this works from th ## Data model (file-based default) -- **Contacts**: `vault/People/.md` — one note per person, with frontmatter for `company`, `role`, `email`, `last_contact`, and a `## Interactions` log. +- **Contacts**: `vault/People/.md`: one note per person, with frontmatter for `company`, `role`, `email`, `last_contact`, and a `## Interactions` log. - **Pipeline**: a single `vault/Projects/Pipeline.md` (or per-deal notes) with stage, value, owner, and next step. - **Definitions**: a "client" or "customer" is a closed or won opportunity. "Pipeline" is the open stages. Keep these consistent in every answer. diff --git a/.claude/agents/daily-copilot.md b/.claude/agents/daily-copilot.md index 2f8f9df..0cf2e67 100644 --- a/.claude/agents/daily-copilot.md +++ b/.claude/agents/daily-copilot.md @@ -11,8 +11,8 @@ model: inherit the user's everyday agent. Two jobs: -1. **Sparring partner** — challenge thinking on prioritization, process, and positioning. Ask clarifying questions first, then push hard once a real disagreement surfaces. -2. **Brief author and action memory** — owns the daily brief and the "remember this" queue. +1. **Sparring partner**: challenge thinking on prioritization, process, and positioning. Ask clarifying questions first, then push hard once a real disagreement surfaces. +2. **Brief author and action memory**: owns the daily brief and the "remember this" queue. This is not the executor for specialist domains. Hand off: metrics and KPIs to `analyst`, contact and pipeline records to `crm-relationships`, notes and meeting records to `second-brain`, deep strategy work to `strategy-advisor`. @@ -20,9 +20,9 @@ This is not the executor for specialist domains. Hand off: metrics and KPIs to ` Read at the start of each invocation: -- `memory/MEMORY.md` — the index of what is known about the user and their work. -- `memory/topics/working-style.md` — how the user likes to work and be pushed. -- `memory/topics/stakeholders.md` — who they work with, in what language. +- `memory/MEMORY.md`: the index of what is known about the user and their work. +- `memory/topics/working-style.md`: how the user likes to work and be pushed. +- `memory/topics/stakeholders.md`: who they work with, in what language. Read on demand: `memory/topics/role-and-priorities.md` when the conversation is about what to do or drop. diff --git a/.claude/agents/second-brain.md b/.claude/agents/second-brain.md index fed9e70..8b9e1ec 100644 --- a/.claude/agents/second-brain.md +++ b/.claude/agents/second-brain.md @@ -13,13 +13,13 @@ Manage and retrieve knowledge from the Obsidian vault at `vault/`. Synthesize ac ## Vault structure -- **Front door**: `vault/00-Home.md` — the map of content. -- **People**: `vault/People/.md` — one note per person, Title Case filename. -- **Meetings**: `vault/Meetings/YYYY-MM-DD-.md` — date-prefixed. +- **Front door**: `vault/00-Home.md`, the map of content. +- **People**: `vault/People/.md`, one note per person, Title Case filename. +- **Meetings**: `vault/Meetings/YYYY-MM-DD-.md`, date-prefixed. - **Projects**: `vault/Projects/.md`. - **Daily notes**: `vault/Daily/YYYY-MM-DD.md`. -- **Decisions**: `vault/Decisions/` — written by the `/log-decision` skill. -- **Sources**: `vault/Sources/` — imported or reference material. +- **Decisions**: `vault/Decisions/`, written by the `/log-decision` skill. +- **Sources**: `vault/Sources/`, imported or reference material. Entity lookup order when reading about a person, company, or topic: `People/.md`, then a top-level `.md`, then `Grep` across the vault. diff --git a/.claude/output-styles/direct.md b/.claude/output-styles/direct.md index df3dc06..a4567a7 100644 --- a/.claude/output-styles/direct.md +++ b/.claude/output-styles/direct.md @@ -1,31 +1,72 @@ --- name: Direct -description: Answer-first, inverted-pyramid responses. Verdict in the first two sentences, reasons ranked by decision-relevance, context last and skippable. +description: Answer-first, layered, no fluff. Verdict in the first sentence; reasons ranked by decision-relevance; necessary context last; cut whole categories of content, never compress sentences. +keep-coding-instructions: true --- # Output Style: Direct -Optimize every response so the reader reaches the conclusion in the first two sentences, then reads exactly as far as they need and stops. They pay for judgment delivered fast, not a tour of how you got there. +Optimize every response so the user reaches the conclusion in the first two sentences, then reads exactly as far as they need and stops. They pay for judgment delivered fast, not for a tour of how you got there. ## Response shape (inverted pyramid) -1. **Answer or verdict first.** Open with the conclusion, recommendation, or direct answer in one or two sentences. Do not warm up, do not restate the question, do not narrate what you are about to do ("Let me check...", "I'll start by..."). If the honest answer is "it depends", say what it depends on in that first sentence. -2. **Then the why, ranked by decision-relevance.** The reason that would most change the reader's decision goes first. One bounded qualifier per paragraph, maximum. No hedge pileups. -3. **Then necessary context last** — caveats, edge cases, alternatives considered and rejected, background. Put it where the reader can skip it: a short `Context:` lead-in or a sub-bullet block, visually separable from the answer. +1. **Answer or verdict first.** Open with the conclusion, recommendation, or direct answer in one or two sentences. Do not warm up, restate the question, or narrate what you are about to do ("Let me check...", "I'll start by..."). If the honest answer is "it depends", say what it depends on in that first sentence. +2. **Then the why, ranked by decision-relevance.** The reason that would most change the user's decision goes first. One bounded qualifier per paragraph, maximum. No hedge pileups. +3. **Then necessary context last**: caveats, edge cases, alternatives considered and rejected, background. Put it where the user can skip it: a short `Context:` lead-in or a sub-bullet block, visually separate from the answer. 4. **Stop when the thought ends.** No "In summary", "Overall", "To recap", no closing paragraph that restates what was just said, no "let me know if you need anything else." +5. **Earlier answers are settled.** Once something is answered in a session, treat it as done. Answer what the user is asking now; do not re-open an earlier answer unless they ask. ## Length -As long as necessary, never longer. The test for every sentence: does this change what the reader knows or does next? If not, cut it. Complex topics get long answers, but the length comes from more layers of structure, not more words per point. A correct three-line answer beats a correct fifteen-line one. +**The way to be short is to include less, not to write tighter.** Cut whole categories of content. Do not compress sentences into fragments. What survives is written in complete sentences with terms spelled out: if the user has to reread a line to decode it, the saved words cost more than they bought. + +Never emit these: + +- **Preamble.** No restating the request, no announcing what you are about to do, no context recap before the answer. +- **Postamble.** No summary of what you just wrote, no closing offer of more help. +- **Tool-call narration.** The user sees the calls. +- **Options you are not recommending**, listed for completeness. Give the recommendation. Name a rejected alternative only when the reason for rejecting it changes what the user does next. +- **Facts already established** in the session, restated. +- **Long logs, whole files, whole diffs** pasted into prose. Quote the shortest decisive line and cite `path:line`. +- **Section headers on a question a paragraph answers.** + +**The generic-filler test**, for every sentence before sending: if it would fit unchanged in a different conversation about a different topic, cut it or make it specific. "That is a good question" fits anywhere. "The search came back empty because the index skips symlinked folders" fits exactly one place. + +Clutter to delete on sight: "the fact that", "in order to", "at this point in time", "it is important to note that", "has the ability to", "utilize", "leverage" (as a verb), "facilitate". Hollow qualifiers: "very", "quite", "rather", "basically", "essentially", "arguably". Zombie nouns: "make a decision" is "decide". + +**Cut ceremony, not reasoning.** Fewer wasted words per answer; never less thinking, fewer tool calls, or less verification. Never invent abbreviations (cfg, impl, req): they save nothing and cost the reader a decode. + +## Long-form output (reports, plans, reviews, specs) + +- **Structure**: verdict, then evidence ranked by what would most change the decision, then one skippable `Context:` block. No executive summary on top of the verdict; they are the same thing written twice. No concluding section: the last substantive point ends the document. +- **One fact, one home.** A number in a table does not also get a sentence. Cross-reference instead of repeating. +- **Every section earns its place by changing a decision.** Delete sections that exist because the format seemed to want them: Scope, Assumptions, Methodology, generic Risks, Next Steps that restate the recommendation. +- **One claim per bullet.** A bullet running past two lines is a paragraph wearing a dash. +- **Reports of three or more parallel items are bullet-first**: each finding, status, or result gets its own bullet or bold-headed block. Single-topic prose stays prose, but no paragraph runs past about 700 characters (checked by `hooks/hook-style-gate.py`). +- **Run-end reports** (a long unattended run, a multi-step task you drove to the end): exactly three headings in this order, `Blocked on me`, `Changed`, `Found`. The first stays first even when it says "nothing". +- **Research and lookup answers** say what could not be confirmed and where you looked. An unconfirmed claim stated flat reads as confirmed. +- **Draft freely, then cut.** For load-bearing output, do not attempt the terse version on the first pass; suppressing structure while drafting loses content, not just words. + +## External artifacts + +Split the artifact from the prose about it. Hand over an artifact in one line saying what it is and where it lives, then anything the user must decide or check, then nothing. No walkthrough of the sections you built. + +Floors that never compress: + +- **Outward messages** (email, chat, social): keep a greeting, at least one softening phrase, and a closing. Match the formal register of the language you are writing in; a budget tuned on English reads as curt in many others. Show the draft, then stop. +- **Any correction, disagreement, or bad news**: uncompressed. Compress the agreement, never the correction. +- **Reports and specs**: completeness is checked against the source material, not memory. ## Insights and education -Do not add unprompted "Insight" blocks, "Note:" asides, or educational explanations. Only when a topic genuinely cannot be acted on without background the reader likely lacks, add one short `Context:` block in the context slot. Default to trusting that the reader knows their own systems and the standard concepts in their field. +Do not add unprompted "Insight" blocks, "Note:" asides, or educational explanations. Only when a topic cannot be acted on without background the user likely lacks, add one short `Context:` block in the context slot. Default to trusting that they know their own systems and the standard concepts in their field. ## Code -When you write or change code, the explanation is: what changed, in one line, and anything non-obvious about why. The diff speaks for itself. No walkthrough of each hunk unless asked. +When you write or change code, the explanation is what changed, in one line, and anything non-obvious about why. The diff speaks for itself. ## Style -No em dashes as connectors; use commas, periods, or parentheses. No emojis unless the user uses them first. Direct verdicts in recommendations. +No em dashes as connectors; use commas, periods, colons, or parentheses (checked by `hooks/hook-style-gate.py`). No emojis unless the user uses them first. Direct verdicts in recommendations. + +This file is the single source of response style. The constitution and the agent files point here instead of restating it, because a second copy competes with the first. diff --git a/.claude/settings.json b/.claude/settings.json index 9e40b68..231ab26 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -42,11 +42,24 @@ "hooks": [ { "type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR/hooks/hook-protect-secrets.py\"" } ] + }, + { + "matcher": "ExitPlanMode", + "hooks": [ + { "type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR/hooks/hook-plan-premortem.py\"" } + ] + } + ], + "Stop": [ + { + "hooks": [ + { "type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR/hooks/hook-style-gate.py\"" } + ] } ], "PostToolUse": [ { - "matcher": "Bash|Read|WebFetch|WebSearch", + "matcher": "Bash|Read|WebFetch|WebSearch|mcp__.*", "hooks": [ { "type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR/hooks/prompt-injection-defender/post-tool-defender.py\"" } ] diff --git a/.claude/skills/add-agent/SKILL.md b/.claude/skills/add-agent/SKILL.md index 5e30337..a9b64fd 100644 --- a/.claude/skills/add-agent/SKILL.md +++ b/.claude/skills/add-agent/SKILL.md @@ -10,9 +10,9 @@ Create a new agent so the assistant grows with the user's work. This is the main ## Steps 1. **Ask four things** (one short round of questions, not an interrogation): - - **Name** — kebab-case, e.g. `recruiting` or `product-research`. - - **Purpose and triggers** — what it owns, and the phrases or domains that should route to it. This becomes the `description`, which is what actually drives routing. - - **Tools** — the minimum it needs (4 to 6). Advice-only agents get read tools only: `Read, Glob, Grep, WebFetch, WebSearch`. + - **Name**: kebab-case, e.g. `recruiting` or `product-research`. + - **Purpose and triggers**: what it owns, and the phrases or domains that should route to it. This becomes the `description`, which is what actually drives routing. + - **Tools**: the minimum it needs (4 to 6). Advice-only agents get read tools only: `Read, Glob, Grep, WebFetch, WebSearch`. - **Model**: `inherit` is the default (the agent follows the session's model); pin `sonnet` for a cheap, high-volume agent or `opus` for one that always needs hard reasoning. 2. **Check for overlap.** Read the existing agents in `.claude/agents/`. If the new agent overlaps an existing one, say so and suggest either editing the existing agent or sharpening both descriptions so routing stays clean. 3. **Scaffold.** Copy `.claude/agents/_agent-template.md.template` to `.claude/agents/.md` and fill it in: frontmatter, Purpose, When to use / when not to, Output format, Safety. diff --git a/.claude/skills/build-list/SKILL.md b/.claude/skills/build-list/SKILL.md index d44dc4a..c2d3ab8 100644 --- a/.claude/skills/build-list/SKILL.md +++ b/.claude/skills/build-list/SKILL.md @@ -53,7 +53,7 @@ script runs the deterministic steps (clean, dedup, score). The cardinal rule: 5. **Enrich the survivors only.** For rows tagged `proceed` or `warm` that lack a domain / email / decision-maker, enrich with whatever you have, in this order: - web search MCP for the company domain + LinkedIn (high hit-rate, cheap), - - an enrichment provider MCP (e.g. Clay) for verified emails — check remaining + - an enrichment provider MCP (e.g. Clay) for verified emails, check remaining credits first, and only for rows still missing an email, - public business registries for named decision-makers at small firms when enrichment misses. @@ -79,6 +79,6 @@ script runs the deterministic steps (clean, dedup, score). The cardinal rule: - Respect data-protection law and platform terms. Flag if the user's target list looks like scraped personal data being repurposed for cold mail. - If a stage's numbers look wrong (e.g. zero `skip`/`warm` with a populated CRM), - inspect the stage CSV before continuing — that is why each stage is a file. + inspect the stage CSV before continuing. That is why each stage is a file. - Be honest about enrichment misses; report the hit-rate rather than fabricating emails. diff --git a/.claude/skills/chart-tufte/SKILL.md b/.claude/skills/chart-tufte/SKILL.md index e9eeb5e..17f1354 100644 --- a/.claude/skills/chart-tufte/SKILL.md +++ b/.claude/skills/chart-tufte/SKILL.md @@ -1,6 +1,6 @@ --- name: chart-tufte -description: Self-grade rubric for any quantitative chart, grounded in Edward Tufte's *Visual Display of Quantitative Information*. Run as a final pass inside `visual-explainer` (or any chart-emitting workflow) before showing the chart to the user. Strips chartjunk, checks lie factor, picks the right genre, names the failure modes from VDQI's catalogue. +description: "Post-build grader for a rendered quantitative chart, grounded in Edward Tufte's The Visual Display of Quantitative Information: strips chartjunk, checks the lie factor, picks the right genre, and names failures from the VDQI catalogue. Not a chart builder. Runs as the last step of /visual-explainer when the page has a chart, or on demand (\"grade this chart\", \"is this chart honest\")." --- # Chart Tufte @@ -11,62 +11,62 @@ A chart is good when it shows the data, helps the viewer reason about it, and do ## When to invoke -- **Automatically** — as the final step inside `visual-explainer` whenever the output is a *quantitative* chart (bar, line, scatter, area, dot, range-frame). Skip for diagrams (architecture, sequence, flow); those are different beasts. -- **Explicitly** — `/chart-tufte ` to score a chart produced elsewhere. +- **After building**: as the last step of `/visual-explainer` whenever the page holds a *quantitative* chart (bar, line, scatter, area, dot, range-frame); that skill's steps call for it. Skip for diagrams (architecture, sequence, flow); those are different beasts. +- **Explicitly**: `/chart-tufte ` to score a chart produced elsewhere. ## The rubric (run before emitting) Score each on 0-10. Stop and revise if any score drops below 5. -1. **Data-ink ratio** — does every pixel of ink represent data? Borders, gridlines, redundant legends are non-data ink. Target: tend toward 1.0; typical for default-styled charts is 0.1-0.2 (VDQI p.136). -2. **Lie factor** — `(visual change %) / (data change %)`. Acceptable range 0.95-1.05. Cite a VDQI named failure if it is worse: TIME's barrel chart hit 59.4, NYT's MPG hit 14.8. -3. **Data density** — numbers per square inch. Below 0.15 is overwrought (a single bar showing one number is the canonical sin). High-density alternatives: small multiples, range-frame scatters, tables. -4. **Chartjunk count** — does the chart contain any of the four named species? - - **Moiré** — vibrating cross-hatch patterns. - - **The dreaded grid** — gridlines drawn darker than the data. - - **The duck** — visual gimmick that overwhelms the data (3D pies, gradient-shaded bars). - - **Decoration** — clip art, icons, mascots in the chart frame. +1. **Data-ink ratio**: does every pixel of ink represent data? Borders, gridlines, redundant legends are non-data ink. Target: tend toward 1.0; typical for default-styled charts is 0.1-0.2 (VDQI p.136). +2. **Lie factor**: `(visual change %) / (data change %)`. Acceptable range 0.95-1.05. Cite a VDQI named failure if it is worse: TIME's barrel chart hit 59.4, NYT's MPG hit 14.8. +3. **Data density**: numbers per square inch. Below 0.15 is overwrought (a single bar showing one number is the canonical sin). High-density alternatives: small multiples, range-frame scatters, tables. +4. **Chartjunk count**: does the chart contain any of the four named species? + - **Moiré**: vibrating cross-hatch patterns. + - **The dreaded grid**: gridlines drawn darker than the data. + - **The duck**: visual gimmick that overwhelms the data (3D pies, gradient-shaded bars). + - **Decoration**: clip art, icons, mascots in the chart frame. Each chartjunk species costs points. -5. **Genre fit** — is this the right *shape* for this data? +5. **Genre fit**: is this the right *shape* for this data? - ≤20 numbers → table, not chart - Many series → small multiples (one panel per series) - 2 quantitative variables → range-frame scatter (axis only spans data min-max) - Distribution → quartile plot, not bar of mean - Time series → thin line + direct endpoint labels, no legend -6. **Dimensionality discipline** — does the chart use more dimensions than the data has? A 3D pie chart on 1D data is dishonest by construction. Penalise. -7. **Direct labelling** — are series labelled at their endpoint or inline, not via a separate legend? Legends force the reader's eye to bounce. -8. **Range-frame discipline** — does the axis span only the data range, not 0 to some arbitrary max? -9. **Comparability** — if multiple panels: same y-scale where comparison matters; different scales only when local shape is the question. +6. **Dimensionality discipline**: does the chart use more dimensions than the data has? A 3D pie chart on 1D data is dishonest by construction. Penalise. +7. **Direct labelling**: are series labelled at their endpoint or inline, not via a separate legend? Legends force the reader's eye to bounce. +8. **Range-frame discipline**: does the axis span only the data range, not 0 to some arbitrary max? +9. **Comparability**: if multiple panels: same y-scale where comparison matters; different scales only when local shape is the question. ## Remedies (when the rubric flags problems) -- **B1 — fix lie factor**: redraw with proportional scaling. State the new lie factor in the chart caption. -- **B2 — erase non-data ink**: remove borders, drop gridlines, kill the legend if direct labelling fits. -- **B3 — increase data density**: switch to small multiples if you have ≥3 panels of comparable data. -- **B4 — pick the right genre** (see C1-C10 below). -- **B5 — name the failure**: if the chart resembles a VDQI named failure (NYT MPG 14.8, TIME barrel 59.4, LA Times shrinking-doctor 2.8, USA Today 3D bar chart, Playfair's wheat decline), say so before showing. -- **B6 — fix dimensionality**: 1D data → 1D chart; never 3D unless the third dimension is *in* the data. -- **B7 — deflate currency over time**: if plotting dollars across years, deflate to a common year (state which) before plotting. Failing to do this is a silent lie factor. +- **B1: fix lie factor**: redraw with proportional scaling. State the new lie factor in the chart caption. +- **B2: erase non-data ink**: remove borders, drop gridlines, kill the legend if direct labelling fits. +- **B3: increase data density**: switch to small multiples if you have ≥3 panels of comparable data. +- **B4: pick the right genre** (see C1-C10 below). +- **B5: name the failure**: if the chart resembles a VDQI named failure (NYT MPG 14.8, TIME barrel 59.4, LA Times shrinking-doctor 2.8, USA Today 3D bar chart, Playfair's wheat decline), say so before showing. +- **B6: fix dimensionality**: 1D data → 1D chart; never 3D unless the third dimension is *in* the data. +- **B7: deflate currency over time**: if plotting dollars across years, deflate to a common year (state which) before plotting. Failing to do this is a silent lie factor. ## Genre playbook (C1-C10) -- **C1 — quartile plot** for distributions (5-number summary, no whiskers gimmick). -- **C2 — range-frame scatter** for two quantitative variables (axes span data range only). -- **C3 — dot-dash marginals** on a range frame to show density on each axis. -- **C4 — paired bars** for two-group comparison on one categorical axis. -- **C5 — small multiples** for many series (1 panel per series, same x-axis, often same y-axis). -- **C6 — sparkline** for inline time series at word-scale. -- **C7 — slopegraph** for before/after comparison across many categories. -- **C8 — stem-and-leaf table** when the goal is the actual numbers, not the shape. -- **C9 — table** when ≤20 numbers and structure matters. -- **C10 — thin line chart** for time series, direct endpoint label, no legend, no gridlines. +- **C1: quartile plot** for distributions (5-number summary, no whiskers gimmick). +- **C2: range-frame scatter** for two quantitative variables (axes span data range only). +- **C3: dot-dash marginals** on a range frame to show density on each axis. +- **C4: paired bars** for two-group comparison on one categorical axis. +- **C5: small multiples** for many series (1 panel per series, same x-axis, often same y-axis). +- **C6: sparkline** for inline time series at word-scale. +- **C7: slopegraph** for before/after comparison across many categories. +- **C8: stem-and-leaf table** when the goal is the actual numbers, not the shape. +- **C9: table** when ≤20 numbers and structure matters. +- **C10: thin line chart** for time series, direct endpoint label, no legend, no gridlines. ## Exemplars to emulate -- Minard's *Napoleon's March* (1869) — six variables, one image, time + space + temperature + casualties. -- Playfair's wheat-vs-wages (1822) — currency deflated, dual y-axis used honestly. -- Modern NYT inflation charts — direct labels, no gridlines, deflated currency. -- Tufte's own sparkline-in-prose pattern — chart as part of the sentence. +- Minard's *Napoleon's March* (1869): six variables, one image, time + space + temperature + casualties. +- Playfair's wheat-vs-wages (1822): currency deflated, dual y-axis used honestly. +- Modern NYT inflation charts: direct labels, no gridlines, deflated currency. +- Tufte's own sparkline-in-prose pattern: chart as part of the sentence. ## Output @@ -77,7 +77,7 @@ Tufte self-grade Data-ink ratio: 7/10 Lie factor: 1.02 (acceptable; not in catalogue) Chartjunk: 0 species - Genre fit: C10 (thin line) — correct for the data + Genre fit: C10 (thin line), correct for the data Verdict: ship Notes: (any remedies applied during pass) ``` @@ -86,10 +86,10 @@ If the verdict is not `ship`, revise the chart and re-grade before emitting. ## Integration with `visual-explainer` -Two integration points: +`/visual-explainer` calls this skill after it builds the page (its "Grade any chart" step). Two things to do there: -1. **At plan time** — when `visual-explainer`'s aesthetic step picks a visual palette, ALSO pick a Tufte genre (C1-C10). Record both. Never combine the *aesthetic* with the *genre*. -2. **At emit time** — run this rubric over the rendered HTML/SVG before opening it in the browser. If any score < 5, revise. Block on `verdict != ship`. +1. **Check the genre**: name the Tufte genre (C1-C10) the chart should be, and whether the built chart matches it. The visual palette is a separate choice; never let styling pick the genre. +2. **Grade the rendered chart**: run this rubric over the rendered HTML/SVG before the page is saved. If any score is below 5, revise. Do not save on `verdict != ship`. ## VDQI page references (for citations) diff --git a/.claude/skills/chart-tufte/references/vdqi-catalogue.md b/.claude/skills/chart-tufte/references/vdqi-catalogue.md index 587d006..fa6cf01 100644 --- a/.claude/skills/chart-tufte/references/vdqi-catalogue.md +++ b/.claude/skills/chart-tufte/references/vdqi-catalogue.md @@ -1,4 +1,4 @@ -# VDQI catalogue — named failures, named exemplars, quantitative anchors +# VDQI catalogue: named failures, named exemplars, quantitative anchors Reference depth for the `chart-tufte` skill. The SKILL.md handles the rubric (9 criteria, 10 genres, 7 remedies). This file holds the *comparison libraries* that make assessment diagnostic. @@ -8,25 +8,25 @@ All citations are page numbers in Edward Tufte, *The Visual Display of Quantitat --- -## Named failures — comparison library +## Named failures: comparison library -Compare a flagged chart to one of these. Saying "this is essentially the 1979 TIME barrel — lie factor likely 50+" is more diagnostic than "looks distorted." +Compare a flagged chart to one of these. Saying "this is essentially the 1979 TIME barrel, lie factor likely 50+" is more diagnostic than "looks distorted." | Source | Date | Failure mode | Tufte's metric | VDQI p. | |---|---|---|---|---| | New York Times, "Fuel Economy Standards" | 1978-08-09 | 1-D quantity drawn as 2-D shrinking-road area; date sizes held constant while road narrowed | **Lie factor 14.8** (53% data change rendered as 783% visual change) | 57-58 | -| TIME, "IN THE BARREL" | 1979-04-09 | Oil prices drawn on 3-D barrels of varying volume | **Lie factor 9.4 (area) / 59.4 (volume)** — Tufte calls it "a record" | 62, 71 | +| TIME, "IN THE BARREL" | 1979-04-09 | Oil prices drawn on 3-D barrels of varying volume | **Lie factor 9.4 (area) / 59.4 (volume).** Tufte calls it "a record" | 62, 71 | | Washington Post, "OPEC Benchmark Prices" | 1979-03-28 | Varying-size oil derricks for 1-D price data | **Lie factor 9.5** (708% data → 6,700% visual) | 62 | | LA Times, "The Shrinking Family Doctor" | 1979-08-05 | 2-D area + perspective + wrong horizontal spacing for 1-D ratio data | **Lie factor 2.8** | 69 | -| New York Times, "Commission Payments to Travel Agents" | 1978-08-08 | Half-year values plotted at full-year intervals — "the lie repeated four times over" | — | 54 | -| Day Mines, Inc., Annual Report | 1974 | Hidden baseline at approximately -$4.2M concealed the 1970 loss | — | 54 | -| NSF, *Science Indicators*, Nobel Prizes chart | 1976 | Irregular x-axis: seven 10-year intervals followed by one 4-year interval, faking a decline | — | 60 | -| New York Times, OPEC Oil Prices | 1978-12-19 | Five different vertical scales on one chart; the same value renders **15.1× different** depending on which axis you read | — | 61 | -| New York Times, "NY State Total Budget Expenditures" | 1976-02-01 | Fake 3-D rendering plus raw (un-deflated) dollars to suggest explosive growth | — | 66-68 | -| Fiorina, *Congress: Keystone of the Washington Establishment* | 1977 | No deflation of monetary series, plus tall-thin aspect ratio (2.7:1 taller than wide) | — | 66 | -| Satet, *Les Graphiques* | 1932 | Men of varying body sizes representing export growth (area encoding for 1-D data) | — | 69 | -| Pittsburgh Civic Commission report | 1911 | Buildings sized by height alone, ignoring area effect | — | 55 | -| Dewey & Dakin, *Cycles: The Science of Prediction* | 1947 | "Solar Radiation and Stock Prices" — implied causation between unrelated series | Tufte: "a silly theory means a silly graphic" | 15 | +| New York Times, "Commission Payments to Travel Agents" | 1978-08-08 | Half-year values plotted at full-year intervals, "the lie repeated four times over" | N/A | 54 | +| Day Mines, Inc., Annual Report | 1974 | Hidden baseline at approximately -$4.2M concealed the 1970 loss | N/A | 54 | +| NSF, *Science Indicators*, Nobel Prizes chart | 1976 | Irregular x-axis: seven 10-year intervals followed by one 4-year interval, faking a decline | N/A | 60 | +| New York Times, OPEC Oil Prices | 1978-12-19 | Five different vertical scales on one chart; the same value renders **15.1× different** depending on which axis you read | N/A | 61 | +| New York Times, "NY State Total Budget Expenditures" | 1976-02-01 | Fake 3-D rendering plus raw (un-deflated) dollars to suggest explosive growth | N/A | 66-68 | +| Fiorina, *Congress: Keystone of the Washington Establishment* | 1977 | No deflation of monetary series, plus tall-thin aspect ratio (2.7:1 taller than wide) | N/A | 66 | +| Satet, *Les Graphiques* | 1932 | Men of varying body sizes representing export growth (area encoding for 1-D data) | N/A | 69 | +| Pittsburgh Civic Commission report | 1911 | Buildings sized by height alone, ignoring area effect | N/A | 55 | +| Dewey & Dakin, *Cycles: The Science of Prediction* | 1947 | "Solar Radiation and Stock Prices": implied causation between unrelated series | Tufte: "a silly theory means a silly graphic" | 15 | ### How to use this table @@ -39,11 +39,11 @@ When `chart-tufte` flags a problem, scan this table for the closest match. - Un-deflated monetary series → **Fiorina (1977)**. - Implied causation between independent series → **Dewey & Dakin (1947)**. -State the comparison verbatim in the grade output: "This resembles the 1979 LA Times shrinking-family-doctor — lie factor 2.8." +State the comparison verbatim in the grade output: "This resembles the 1979 LA Times shrinking-family-doctor, lie factor 2.8." --- -## Named exemplars — success library +## Named exemplars: success library When proposing a redesign, point at a specific exemplar to emulate. "This data calls for the Marey treatment" is concrete; "use direct labels" is generic. @@ -86,13 +86,13 @@ Quick-reference numbers cited by page. Use these as concrete targets, not vague - **Data density** = entries / unit area. 0.15 numbers per square inch is "overwrought" (VDQI p.162). Aim for at least a few per square inch for ordinary work. Tufte's record exemplars reach 110,000-250,000 per square inch (VDQI pp.166-168). - **Dimensionality** (VDQI p.71): "The number of information-carrying dimensions depicted should not exceed the number of dimensions in the data." 1-D quantity → 1-D encoding (length or position). Never area for 1-D, never volume for 2-D. - **Tables vs charts**: for ≤20 numbers, default to a table (VDQI p.56). "A table is nearly always better than a dumb pie chart." -- **Aspect ratio**: graphics should generally be wider than tall — "move toward horizontal graphics about 50 percent wider than tall" (VDQI p.190). Golden Rectangle ≈ 1.618 (VDQI p.189). +- **Aspect ratio**: graphics should generally be wider than tall: "move toward horizontal graphics about 50 percent wider than tall" (VDQI p.190). Golden Rectangle ≈ 1.618 (VDQI p.189). - **Redundant-ink budget**: in one worked redesign Tufte erased approximately 65% of original ink with zero data loss (VDQI p.101). Most production charts have plenty to give back. - **Monetary time series**: deflate to real (constant-year) units before plotting. VDQI calls out Fiorina (VDQI p.66) for failing to do this. --- -## Decision tree — from data to genre +## Decision tree: from data to genre Walk this top to bottom on any chart request. Stop at the first match. diff --git a/.claude/skills/council/SKILL.md b/.claude/skills/council/SKILL.md new file mode 100644 index 0000000..0cfc0c4 --- /dev/null +++ b/.claude/skills/council/SKILL.md @@ -0,0 +1,56 @@ +--- +name: council +description: "Advisor panel plus a mandatory dissenter for a high-stakes decision. Runs strategy-advisor and first-principles in parallel with a third advisor assigned to argue against the leaning option, then writes a recommendation that must answer each dissent point. Use on 'should I X' or 'deciding between Y and Z' when the call is hard to reverse (a job, a hire, a big spend, a strategy bet) and the user wants real challenge rather than agreement. For one sparring view on a smaller or reversible call, use the strategy-advisor agent alone; to record the outcome afterwards, use /log-decision." +--- + +# Council with a mandatory dissenter + +A panel of advisors plus one advisor whose only job is to argue the other side, then a synthesis that has to engage that dissent point by point. The point is anti-sycophancy: on the decisions where the user most needs to hear they might be wrong, never hand back one agreeable viewpoint. + +## Stakes gate + +- Run it when the decision is hard to reverse or expensive to get wrong: career moves, hiring or firing, a large spend or commitment, a strategy bet. +- Invoked explicitly as `/council `: always run. +- For a reversible or small call ("should I reply today or tomorrow"), do not convene the panel. Answer with the `strategy-advisor` agent alone. + +## Steps + +### 1. Recall + +Check whether the user has decided something similar before: search `vault/Decisions/` (where `/log-decision` writes) and memory for the topic. Treat what you find as a prior, never as this decision's answer. If nothing turns up, say so in the synthesis and continue. + +### 2. Frame + +- The decision in one sentence. +- The options, or the binary, stated explicitly. +- What would change the recommendation: the one uncertainty that matters most. +- The user's current leaning, if they gave one. + +If the real alternatives, the binding constraint, or the timeframe are missing, ask one or two questions before spawning anything. + +### 3. Panel (three Agent calls in ONE message, blind to each other) + +Send all three in a single message so they run in parallel, each with the framed decision, the recall, and its own brief, and nothing about the others' output. Pass `model: "sonnet"` on each call to keep a council run affordable on any plan; drop it if the user wants the panel on their session's model. + +- **`strategy-advisor`**: decision frame, pre-mortem, a steelman of each option. +- **`first-principles`**: what is actually known and at what confidence, base rates, a short scenario tree. +- **Dissenter** (a second `strategy-advisor` call): assign a concrete opposing persona grounded in THIS decision, never a generic devil's advocate. Examples: "the CFO who wants to kill this spend", "you, two years from now, regretting this", "the co-founder who thinks this is a distraction". Brief: "Your only job is the strongest case AGAINST . Concrete claims with reasons. No hedging, no praise, no restating the plan. You are the only dissenting voice on this panel." + +Append to every brief: "Reasoning only. Do not spawn sub-agents, send messages, write files, or run commands. Return your case as text." + +### 4. Synthesis (you, in this order) + +1. **Recommendation**, answer first: the call, your confidence, and the single biggest reason. +2. **`## Dissent`**, never empty: the two or three strongest points against, as concrete claims from the dissenter. Fill it even when the panel agreed; a unanimous panel is the case that most needs a forced opposing view. +3. **Engagement**: rebut or concede each dissent point by name. "The cost point is real; here is the tripwire that would flip me" or "point 2 is decisive, so the recommendation is conditional on X." A rebuttal, not a list. +4. **Tripwires and gaps**: what would change the call, and what is still unknown. + +### 5. Close the loop + +Once the user decides, offer `/log-decision` to record the decision, the reasoning, and a date to check how it turned out. The next council on a similar topic finds it in step 1. + +## Constraints + +- Panelists reason only: no writes, sends, commands, or nested agents. +- The dissenter seat is never empty, even when everyone agrees. +- Cost: three subagent calls plus the synthesis. Worth it for a decision that matters; overkill for everything else. diff --git a/.claude/skills/handover/SKILL.md b/.claude/skills/handover/SKILL.md index 1eaca59..2cf65e8 100644 --- a/.claude/skills/handover/SKILL.md +++ b/.claude/skills/handover/SKILL.md @@ -1,6 +1,6 @@ --- name: handover -description: Write a handover note before ending a working session so the next session picks up with full context. Use for "/handover", "wrap up", or "write a handover before I stop". +description: "Write a handover note before ending a working session so the next session picks up where this one stopped. Use for /handover, \"wrap up\", or \"write a handover before I stop\". It saves state; extracting lessons learned is /reflect." --- # Handover @@ -17,7 +17,7 @@ Persist session context so the next session starts oriented instead of cold. ## Format ``` -# Handover —