Merge pull request #921 from alirezarezvani/claude/productivity-skills-audit-obucop
Some checks failed
Sync Codex Skills Symlinks / sync (push) Has been cancelled

This commit is contained in:
Alireza Rezvani 2026-07-17 15:00:03 +02:00 committed by GitHub
commit 2800f83337
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 4961 additions and 44 deletions

View file

@ -8,7 +8,7 @@
"homepage": "https://github.com/alirezarezvani/claude-skills",
"repository": "https://github.com/alirezarezvani/claude-skills",
"metadata": {
"description": "359 production-ready skills across 18 domains (engineering, engineering-core, marketing, product, c-level, compliance-os, project management, RA/QM, business growth, finance, productivity, marketing top-level, research, research-ops, business-operations, commercial, markdown-html, loop-library, plus standards). 635 Python tools, 732 reference guides, 99 agents (cs-* + personas), 111 slash commands across 85 marketplace plugins. v2.11.2 vendors engineering/skillopt-sleep — a verbatim copy of microsoft/SkillOpt's stdlib-only skillopt_sleep engine + Claude Code plugin surface, giving a local agent a nightly gated self-improvement cycle (read-only session harvest -> mine -> offline replay -> held-out-gated CLAUDE.md/SKILL.md edits -> staged for explicit /skillopt-sleep adopt). productivity/fable-goal (unreleased, post-v2.11.1) converts a rambling description of a desired outcome into one polished /goal prompt for a fresh autonomous session. v2.11.1 turns product-team and project-management into agent-harness domains: fork-orchestrators with deterministic goal routers, a Jira MCP snapshot bridge (Kanban flow metrics + Monte Carlo forecasting), a delegation-governance loop gate, a continuous-discovery cadence tracker, and an Opportunity Solution Tree linter, with /cs:pm and /cs:product command families. v2.10.3 completes the markdown-html domain with md-slides — slide-deck converter (arrow-key / Space / PgDn / Home/End / P keyboard navigation + presenter mode with split-view clock + speaker notes + next-slide preview + URL-hash deep linking like #3 for direct slide jumps + @media print page-per-slide for browser-native PDF export). Reuses md-document's markdown parser; vanilla JS only (no framework runtime); Prism.js opt-in via --syntax. Joins md-review (v2.10.2 code-review converter), md-document (v2.10.1 long-form converter), and the v2.10.0 foundation (orchestrator + design-system). Compatible with Claude Code, Codex CLI, Gemini CLI, Cursor, OpenClaw, Hermes Agent, Mistral Vibe, and 5 more coding agents.",
"description": "362 production-ready skills across 18 domains (engineering, engineering-core, marketing, product, c-level, compliance-os, project management, RA/QM, business growth, finance, productivity, marketing top-level, research, research-ops, business-operations, commercial, markdown-html, loop-library, plus standards). 644 Python tools, 741 reference guides, 102 agents (cs-* + personas), 116 slash commands across 88 marketplace plugins. v2.11.2 vendors engineering/skillopt-sleep — a verbatim copy of microsoft/SkillOpt's stdlib-only skillopt_sleep engine + Claude Code plugin surface, giving a local agent a nightly gated self-improvement cycle (read-only session harvest -> mine -> offline replay -> held-out-gated CLAUDE.md/SKILL.md edits -> staged for explicit /skillopt-sleep adopt). productivity/fable-goal (unreleased, post-v2.11.1) converts a rambling description of a desired outcome into one polished /goal prompt for a fresh autonomous session. v2.11.1 turns product-team and project-management into agent-harness domains: fork-orchestrators with deterministic goal routers, a Jira MCP snapshot bridge (Kanban flow metrics + Monte Carlo forecasting), a delegation-governance loop gate, a continuous-discovery cadence tracker, and an Opportunity Solution Tree linter, with /cs:pm and /cs:product command families. v2.10.3 completes the markdown-html domain with md-slides — slide-deck converter (arrow-key / Space / PgDn / Home/End / P keyboard navigation + presenter mode with split-view clock + speaker notes + next-slide preview + URL-hash deep linking like #3 for direct slide jumps + @media print page-per-slide for browser-native PDF export). Reuses md-document's markdown parser; vanilla JS only (no framework runtime); Prism.js opt-in via --syntax. Joins md-review (v2.10.2 code-review converter), md-document (v2.10.1 long-form converter), and the v2.10.0 foundation (orchestrator + design-system). Compatible with Claude Code, Codex CLI, Gemini CLI, Cursor, OpenClaw, Hermes Agent, Mistral Vibe, and 5 more coding agents.",
"version": "2.11.2"
},
"plugins": [
@ -1131,6 +1131,60 @@
],
"category": "productivity"
},
{
"name": "weekly-review",
"source": "./productivity/weekly-review",
"description": "GTD weekly-review loop. Scans the workspace for open loops (unchecked boxes, TODO/FIXME, stale files), walks the three-phase GET CLEAR / GET CURRENT / GET CREATIVE checklist with a refusal gate (review is never COMPLETE while a mandatory GET CURRENT step is missing), and audits commitments for stalled / no-next-action / someday candidates with a 0-100 health score. Fills the periodic-review gap from audit/productivity-2026-07.",
"version": "2.11.2",
"author": {
"name": "Alireza Rezvani"
},
"keywords": [
"weekly-review",
"gtd",
"open-loops",
"review-cadence",
"productivity",
"commitments"
],
"category": "productivity"
},
{
"name": "deep-work",
"source": "./productivity/deep-work",
"description": "Deep Work day planner. Classifies tasks deep vs shallow with a shallow-work budget verdict (plus the recent-graduate forcing question), builds a time-blocked schedule (deep blocks of 90+ minutes first, shallow batched, buffers, refuses more than 4 hours of deep demand), and logs focus sessions against a weekly deep-hours target with streaks. Fills the time/attention-management gap from audit/productivity-2026-07.",
"version": "2.11.2",
"author": {
"name": "Alireza Rezvani"
},
"keywords": [
"deep-work",
"time-blocking",
"focus",
"shallow-work",
"productivity",
"attention"
],
"category": "productivity"
},
{
"name": "meetings",
"source": "./productivity/meetings",
"description": "Meeting discipline. Cost-gates every meeting before it exists (MEET / ASYNC / NOT-READY verdicts with real dollar cost incl. optional refocus overhead), builds timeboxed agendas that refuse topics without a desired outcome (decision topics first), and extracts owned action items from raw notes with ORPHAN / NO-DUE flags. Fills the meeting-hygiene gap from audit/productivity-2026-07.",
"version": "2.11.2",
"author": {
"name": "Alireza Rezvani"
},
"keywords": [
"meetings",
"meeting-cost",
"agenda",
"action-items",
"productivity",
"async"
],
"category": "productivity"
},
{
"name": "landing",
"source": "./marketing/landing",

View file

@ -3,7 +3,7 @@
"name": "claude-code-skills",
"description": "Production-ready skill packages for AI agents - Marketing, Engineering, Product, C-Level, PM, and RA/QM",
"repository": "https://github.com/alirezarezvani/claude-skills",
"total_skills": 357,
"total_skills": 360,
"skills": [
{
"name": "business-growth-skills",
@ -1859,6 +1859,12 @@
"category": "productivity",
"description": "Captures and organizes chaotic brain dumps into a structured, actionable system with zero information loss. Use this skill whenever the user says 'capture this', 'brain dump', 'let me dump some ideas', 'I've got a bunch of thoughts', 'here's everything on my mind', 'idea dump', 'let me get this out of my head', 'I need to organize my thoughts', 'here's what I'm thinking', or any variation where someone is unloading a messy stream of ideas, tasks, thoughts, and plans wanting them turned into something coherent. Also trigger when the user pastes or dictates a long, unstructured block of mixed ideas \u2014 even without the exact phrase \u2014 the intent is the same. Fast-to-action by design: no upfront intake. Output is four sections (Projects/Ideas, Tasks, Connections, How I Can Help) ending with a directive question. Asks at most one mid-organization clarifying question when a single item is genuinely ambiguous between task and project."
},
{
"name": "deep-work",
"source": "../../productivity/deep-work/skills/deep-work",
"category": "productivity",
"description": "Use when someone wants to plan a deep work day, time-block their calendar or task list, budget or cut shallow work, protect focus hours, track deep-work sessions and streaks, run an end-of-day shutdown ritual, or says \"/deep-work\" or \"/time-block\". Classifies tasks deep vs shallow, builds an energy-first time-blocked schedule that refuses deep demand past the 4-hour ceiling, batches shallow work into at most two windows, and logs focus sessions against a weekly target."
},
{
"name": "fable-goal",
"source": "../../productivity/fable-goal/skills/fable-goal",
@ -1883,6 +1889,12 @@
"category": "productivity",
"description": "Runs a full inbox triage using the knowledge base created by the 'inbox-setup' skill. Light-intake by design (most invocations skip questions and run with KB-default preferences); asks at most 2 grill-me override questions when invocation is outside normal cadence or includes category-skip intent. Searches recent emails, classifies them via the user's taxonomy, researches new senders, generates recommendations, drafts replies (NEVER sends), delivers a report in the user's preferred format, and updates the knowledge base with learnings. Designed to run on a recurring schedule (1-3x daily) or on demand. Use when the user wants their inbox processed, in any variation (e.g., 'triage my inbox', 'inbox triage', 'check my email', 'run email triage', 'process my inbox', 'what's new in my email', 'handle my email', 'email triage'). Requires the inbox-setup skill to have been run first."
},
{
"name": "meetings",
"source": "../../productivity/meetings/skills/meetings",
"category": "productivity",
"description": "Use when someone wants to decide whether a meeting is worth calling, price a meeting in dollars, build a timeboxed agenda with desired outcomes, or turn messy meeting notes into owned action items \u2014 or says \"should this be a meeting\", \"/cs:meeting-prep\", or \"/cs:meeting-actions\". Runs a cost gate (ASYNC / NOT-READY / MEET), builds a decision-first agenda, and extracts an owner + due-date checklist that flags every orphan."
},
{
"name": "reflect",
"source": "../../productivity/reflect/skills/reflect",
@ -1895,6 +1907,12 @@
"category": "productivity",
"description": "Use when someone asks to roast an idea, pressure-test or stress-test an idea, validate a business idea, \"convene the panel\", get a brutal second opinion before building something, or says \"/roast\". Spins up a 5-angle panel (Critic, Champion, Analyst, Investigator, Customer) that attacks the idea from every angle, then a Judge returns one GO / RESHAPE / KILL verdict with the cheapest test to de-risk it."
},
{
"name": "weekly-review",
"source": "../../productivity/weekly-review/skills/weekly-review",
"category": "productivity",
"description": "Use when someone wants to run a weekly review, close open loops, audit stalled projects and commitments, get their system back to trusted, restart a lapsed review habit, or says \"/cs:weekly-review\". Walks David Allen's three-phase loop \u2014 GET CLEAR, GET CURRENT, GET CREATIVE \u2014 with deterministic scripts that inventory open loops, gate the checklist with named gaps, and score commitment health 0-100."
},
{
"name": "atlassian-admin",
"source": "../../project-management/skills/atlassian-admin",
@ -2205,7 +2223,7 @@
"description": "Product management and design skills"
},
"productivity": {
"count": 8,
"count": 11,
"source": "../../productivity",
"description": "Personal-productivity skills - capture, email, reflect, handoff, andreessen, roast, weekly-review, deep-work, meetings"
},

1
.codex/skills/deep-work Symbolic link
View file

@ -0,0 +1 @@
../../productivity/deep-work/skills/deep-work

1
.codex/skills/meetings Symbolic link
View file

@ -0,0 +1 @@
../../productivity/meetings/skills/meetings

1
.codex/skills/weekly-review Symbolic link
View file

@ -0,0 +1 @@
../../productivity/weekly-review/skills/weekly-review

View file

@ -1,7 +1,7 @@
{
"version": "1.0.0",
"name": "gemini-cli-skills",
"total_skills": 420,
"total_skills": 434,
"skills": [
{
"name": "README",
@ -41,7 +41,7 @@
{
"name": "cs-content-creator",
"category": "agent",
"description": "AI-powered content creation specialist for brand voice consistency, SEO optimization, and multi-platform content strategy. Use when producing or reviewing marketing content that must stay on-brand and rank \u2014 e.g., turning one pillar blog post into a LinkedIn/X/newsletter bundle, or auditing draft copy against an established brand voice profile before publishing."
"description": "Long-form marketing content producer orchestrating the content-production skill (research \u2192 brief \u2192 draft \u2192 optimize \u2192 gate). Use when content must be written, scored, or made publish-ready \u2014 e.g., drafting a 2,000-word blog post against a target keyword and blocking publish until content_quality_gates.py passes, or auditing a draft for brand-voice drift with brand_voice_analyzer.py before it ships. Routes planning requests (topic clusters, calendars) to content-strategy. Supersedes the deprecated content-creator skill."
},
{
"name": "cs-cto-advisor",
@ -51,7 +51,7 @@
{
"name": "cs-demand-gen-specialist",
"category": "agent",
"description": "Demand generation and customer acquisition specialist for lead generation, conversion optimization, and multi-channel acquisition campaigns. Use when building or fixing the acquisition funnel \u2014 e.g., diagnosing why MQL-to-SQL conversion dropped after a pricing change, or designing a multi-channel campaign plan with budget split across paid, content, and email."
"description": "Demand generation and acquisition-funnel specialist orchestrating the marketing-demand-acquisition, paid-ads, and email-sequence skills. Use when building or fixing the acquisition engine \u2014 e.g., comparing channel CAC against B2B SaaS benchmarks before reallocating a $40k/month budget, scoring paid-ads account health with ad_health_scorer.py before scaling spend, or designing a nurture sequence that must score 70+ on sequence_analyzer.py before launch. Covers channel mix, CAC/ROAS math, MQL\u2192SQL workflows, attribution, and nurture design."
},
{
"name": "cs-engineering-lead",
@ -238,6 +238,11 @@
"category": "c-level",
"description": "Inter-agent communication protocol for C-suite agent teams. Defines invocation syntax, loop prevention, isolation rules, and response formats. Use when C-suite agents need to query each other, coordinate cross-functional analysis, or run board meetings with multiple agent roles."
},
{
"name": "arquiteto-de-empresa",
"category": "c-level",
"description": "Company Architect: builds a business from scratch as an OKF (Open Knowledge Format) bundle \u2014 a tree of version-controllable .md files with frontmatter type, links forming a graph, and reserved index.md/log.md, readable by humans and agents. Guides the founder through a 12-phase interview (foundation, strategy, market, financial, sales, marketing, product, operations, tech, people, legal, governance), one phase at a time, few questions per block, and generates the concepts as conformant markdown. Trigger when the user wants to create, structure, or document an entire company in folders and .md files; when they mention build my company from scratch, company as code, company knowledge base for AI to read, company wiki for agents, OKF, or knowledge bundle. In English."
},
{
"name": "board-deck-builder",
"category": "c-level",
@ -518,6 +523,11 @@
"category": "c-level",
"description": "Cross-functional what-if modeling for cascading multi-variable scenarios. Unlike single-assumption stress testing, this models compound adversity across all business functions simultaneously. Use when facing complex risk scenarios, strategic decisions with major downside, or when the user asks 'what if X AND Y both happen?'"
},
{
"name": "skills-arquiteto-de-empresa",
"category": "c-level",
"description": "Company Architect: builds a business from scratch as an OKF (Open Knowledge Format) bundle \u2014 a tree of version-controllable .md files with frontmatter type, links forming a graph, and reserved index.md/log.md, readable by humans and agents. Guides the founder through a 12-phase interview (foundation, strategy, market, financial, sales, marketing, product, operations, tech, people, legal, governance), one phase at a time, few questions per block, and generates the concepts as conformant markdown. Trigger when the user wants to create, structure, or document an entire company in folders and .md files; when they mention build my company from scratch, company as code, company knowledge base for AI to read, company wiki for agents, OKF, or knowledge bundle. In English."
},
{
"name": "skills-chief-ai-officer-advisor",
"category": "c-level",
@ -958,6 +968,11 @@
"category": "engineering",
"description": "Microsoft 365 tenant administration for Global Administrators. Automate M365 tenant setup, Office 365 admin tasks, Azure AD user management, Exchange Online configuration, Teams administration, and security policies. Generate PowerShell scripts for bulk operations, Conditional Access policies, license management, and compliance reporting. Use for M365 tenant manager, Office 365 admin, Azure AD users, Global Administrator, tenant configuration, or Microsoft 365 automation."
},
{
"name": "named-persona-adversarial-review",
"category": "engineering",
"description": "Code review through the lens of real engineers' documented philosophies (Torvalds, Thompson, Carmack, Kent Beck, Jobs, Cagan). Complements abstract-role adversarial review with named, sourced perspectives. Use when automated review findings feel generic, when a PR has architectural or UX impact, or when the author wants pre-submit hardening beyond standard checks."
},
{
"name": "promote",
"category": "engineering",
@ -1046,7 +1061,7 @@
{
"name": "senior-prompt-engineer",
"category": "engineering",
"description": "This skill should be used when the user asks to \"optimize prompts\", \"design prompt templates\", \"evaluate LLM outputs\", \"build agentic systems\", \"implement RAG\", \"create few-shot examples\", \"analyze token usage\", or \"design AI workflows\". Use for prompt engineering patterns, LLM evaluation frameworks, agent architectures, and structured output design."
"description": "Use when the user asks to optimize prompts, design prompt templates, evaluate LLM outputs with an eval set, measure RAG retrieval quality, validate agent/tool configurations, analyze token usage, or design structured-output contracts. Covers eval-driven prompt iteration, RAG metrics (relevance, faithfulness, coverage), agent workflow validation, and token/cost budgeting \u2014 all model-agnostic, with three stdlib Python tools."
},
{
"name": "senior-qa",
@ -1061,23 +1076,13 @@
{
"name": "senior-security",
"category": "engineering",
"description": "Security engineering toolkit for threat modeling, vulnerability analysis, secure architecture, and penetration testing. Includes STRIDE analysis, OWASP guidance, cryptography patterns, and security scanning tools. Use when the user asks about security reviews, threat analysis, vulnerability assessments, secure coding practices, security audits, attack surface analysis, CVE remediation, or security best practices."
"description": "Use when the user asks for STRIDE threat modeling, DREAD risk scoring, data-flow-diagram threat analysis, or a quick secret scan \u2014 or when a security request needs routing to the right specialist skill (pen-testing, incident response, cloud posture, red team, AI security, threat hunting, secure code review). This skill owns threat modeling; everything else routes to a sibling."
},
{
"name": "skills-init",
"category": "engineering",
"description": ">-"
},
{
"name": "skills-review",
"category": "engineering",
"description": ">-"
},
{
"name": "skills-status-2",
"category": "engineering",
"description": "Memory health dashboard showing line counts, topic files, capacity, stale entries, and recommendations. Use when the user runs /si:status or asks how full or healthy the agent memory is."
},
{
"name": "snowflake-development",
"category": "engineering",
@ -1113,6 +1118,11 @@
"category": "engineering-advanced",
"description": "Use when the user asks to design a multi-agent system, pick an orchestration pattern (supervisor/swarm/pipeline), generate tool schemas for agents, or evaluate agent execution logs for cost, latency, and failure bottlenecks. Examples: 'design an agent architecture for research automation', 'generate Anthropic tool schemas from these tool descriptions', 'analyze these agent run logs for bottlenecks'. NOT for Claude Code workflow files (use workflow-builder) or single-agent prompt design (use agent-workflow-designer)."
},
{
"name": "agent-harness",
"category": "engineering-advanced",
"description": "Turn any domain folder of skills into a bounded agentic loop: compile a goal into a verifiable task plan, execute tasks with the domain's own tools, verify every task with machine-run checks, retry with caps, escalate to a human when budgets exhaust, and refuse to close until everything is verified or explicitly waived. Use when you want an agent or subagent to pick up a goal and drive it to a verified close across one of this repo's 18 domains ('run this goal through the engineering harness', 'set up an agentic loop for marketing work', 'make the finance domain self-verifying'). NOT for authoring Claude Code Workflow-tool .js scripts (workflow-builder), N-agent tournaments on one task (agenthub), single-file metric optimization (autoresearch-agent), or discovering published loop recipes (loop-library)."
},
{
"name": "agent-workflow-designer",
"category": "engineering-advanced",
@ -1323,6 +1333,11 @@
"category": "engineering-advanced",
"description": "Zero-downtime migration planning, compatibility validation, and rollback strategy generation. Tools for system, database, and infrastructure migrations with minimal business impact. Use when planning a database migration, infrastructure cutover, system replacement, or any high-risk transition that needs explicit rollback paths."
},
{
"name": "minimalist",
"category": "engineering-advanced",
"description": "Use when the user asks to write code efficiently, avoid over-engineering, reduce dependencies, or prevent unnecessary abstractions. Enforces a strict efficiency ladder: YAGNI, reuse, stdlib, native platform, existing deps \u2014 before writing any new code."
},
{
"name": "monorepo-navigator",
"category": "engineering-advanced",
@ -1408,6 +1423,11 @@
"category": "engineering-advanced",
"description": "Validate, test, and score the quality of skills within the claude-skills ecosystem. Comprehensive meta-skill: structure validation, Python script testing (syntax + imports + runtime + output format), multi-dimensional quality scoring with letter grades and tier classification (BASIC/STANDARD/POWERFUL). Use when authoring a new skill, auditing existing skills for tier promotion, setting up pre-commit hooks for skill quality, or integrating skill QA into CI."
},
{
"name": "skillopt-sleep",
"category": "engineering-advanced",
"description": "Use when the user wants their Claude agent to self-improve from past usage, asks about a nightly/offline 'sleep' or 'dream' cycle, memory/skill consolidation, or says things like 'make my agent better the more I use it', 'review my past sessions', 'learn my preferences', 'consolidate what you learned', 'run the sleep cycle', or wants to schedule offline self-optimization. Drives the skillopt_sleep engine: harvest past sessions -> mine recurring tasks -> replay offline -> consolidate validated CLAUDE.md and SKILL.md behind a held-out gate."
},
{
"name": "skills-chaos-engineering",
"category": "engineering-advanced",
@ -1473,6 +1493,11 @@
"category": "engineering-advanced",
"description": "Show experiment dashboard with results, active loops, and progress. Use when the user runs /ar:status or asks how an autoresearch experiment is going."
},
{
"name": "strict-api",
"category": "engineering-advanced",
"description": "Use when the user says 'no hallucinations', 'verify APIs', 'reality check', or 'don't invent functions'. Prevents the agent from calling methods, imports, or variables that do not provably exist in the user's installed version."
},
{
"name": "tc-tracker",
"category": "engineering-advanced",
@ -1503,6 +1528,11 @@
"category": "engineering-advanced",
"description": "Create new agent skills with proper structure, progressive disclosure, and bundled resources. Use when user wants to create, write, build, or author a new skill."
},
{
"name": "zero-hallucination-coder",
"category": "engineering-advanced",
"description": "Runs a disciplined Discuss -> Map -> Decompose -> Execute -> Verify loop that grounds code in verified structure \u2014 no invented APIs, no assumed imports, no placeholder code \u2014 with a lazy-senior-dev YAGNI ladder that deletes unnecessary code before it is written. Use when a coding task is high-stakes, complex, or spans existing code (auth, databases, migrations, multi-file features), or when the user explicitly asks to plan carefully before coding, avoid hallucinated code, or work rigorously. Not for trivial edits, typos, or throwaway one-off scripts \u2014 those do not need the full loop."
},
{
"name": "business-investment-advisor",
"category": "finance",
@ -1648,6 +1678,11 @@
"category": "marketing",
"description": "When the user wants to plan a product launch, feature announcement, or release strategy. Also use when the user mentions 'launch,' 'Product Hunt,' 'feature release,' 'announcement,' 'go-to-market,' 'beta launch,' 'early access,' 'waitlist,' 'product update,' 'GTM plan,' 'launch checklist,' or 'launch momentum.' This skill covers phased launches, channel strategy, and ongoing launch momentum."
},
{
"name": "local-seo-manager",
"category": "marketing",
"description": "Manage local SEO for service-area businesses \u2014 appliance repair, HVAC, plumbing, cleaning, and any business that serves customers at their location. Use when the user wants to: audit Google Business Profile, generate neighborhood service area pages, check NAP consistency across directories, create LocalBusiness schema, or write review responses. Triggers: 'local SEO', 'Google Business Profile', 'GBP', 'service area page', 'NAP consistency', 'local citations', 'LocalBusiness schema', 'review responses', 'Google Maps ranking'. NOT for national SEO (use seo-audit). NOT for general schema (use schema-markup). NOT for AI answer-engine visibility (use aeo)."
},
{
"name": "marketing-context",
"category": "marketing",
@ -1836,7 +1871,7 @@
{
"name": "product-skills",
"category": "product",
"description": "Router/index for the 12 product skills bundled in this plugin (RICE prioritization, OKRs, UX research, design tokens, competitive teardown, analytics, experiments, discovery, roadmaps, spec-to-repo, landing pages, SaaS scaffolding). Use when a product request doesn't obviously match one skill and you need to pick the right one (e.g., 'help me prioritize features', 'plan a product experiment')."
"description": "Use when coordinating product work across the 12 bundled product sub-skills (RICE, OKRs, UX research, design tokens, competitive teardown, analytics, experiments, discovery, roadmaps, spec-to-repo, landing pages, SaaS scaffolding) or the 4 standalone product-team plugins (user stories, Apple HIG, code-to-PRD, research summarizer). Triggers on 'help me prioritize', 'plan a product experiment', 'we ship features nobody uses', 'run the discovery loop', 'is our OST sound'. Forks context to route to one sub-skill via a deterministic signal router and returns a digest; can also drive a continuous-discovery loop (Torres cadence tracker + OST linter as machine gates) or a full goal\u2192plan\u2192execute\u2192verify\u2192close run through the repo-wide agent-harness. Distinct from project-management (how to deliver vs what to build), marketing/landing (from-scratch pages), and engineering/agent-harness (the generic loop engine this orchestrator plugs into)."
},
{
"name": "product-strategist",
@ -1883,6 +1918,16 @@
"category": "productivity",
"description": "Captures and organizes chaotic brain dumps into a structured, actionable system with zero information loss. Use this skill whenever the user says 'capture this', 'brain dump', 'let me dump some ideas', 'I've got a bunch of thoughts', 'here's everything on my mind', 'idea dump', 'let me get this out of my head', 'I need to organize my thoughts', 'here's what I'm thinking', or any variation where someone is unloading a messy stream of ideas, tasks, thoughts, and plans wanting them turned into something coherent. Also trigger when the user pastes or dictates a long, unstructured block of mixed ideas \u2014 even without the exact phrase \u2014 the intent is the same. Fast-to-action by design: no upfront intake. Output is four sections (Projects/Ideas, Tasks, Connections, How I Can Help) ending with a directive question. Asks at most one mid-organization clarifying question when a single item is genuinely ambiguous between task and project."
},
{
"name": "deep-work",
"category": "productivity",
"description": "Use when someone wants to plan a deep work day, time-block their calendar or task list, budget or cut shallow work, protect focus hours, track deep-work sessions and streaks, run an end-of-day shutdown ritual, or says \"/deep-work\" or \"/time-block\". Classifies tasks deep vs shallow, builds an energy-first time-blocked schedule that refuses deep demand past the 4-hour ceiling, batches shallow work into at most two windows, and logs focus sessions against a weekly target."
},
{
"name": "fable-goal",
"category": "productivity",
"description": "Convert a rambling description of a desired outcome into one polished, autonomous /goal prompt ready to paste into a fresh session. Use when the user says \"/fable-goal\", \"turn this into a goal prompt\", \"write me a fable prompt\", \"write the prompt that builds X\", or rambles about something they want made and asks for the prompt that makes it happen. The output is a single copy-paste prompt, never the build itself. Do NOT use when the user wants the thing built right now in this session \u2014 only when they want the PROMPT that will make it happen in a fresh session."
},
{
"name": "handoff",
"category": "productivity",
@ -1898,11 +1943,26 @@
"category": "productivity",
"description": "Runs a full inbox triage using the knowledge base created by the 'inbox-setup' skill. Light-intake by design (most invocations skip questions and run with KB-default preferences); asks at most 2 grill-me override questions when invocation is outside normal cadence or includes category-skip intent. Searches recent emails, classifies them via the user's taxonomy, researches new senders, generates recommendations, drafts replies (NEVER sends), delivers a report in the user's preferred format, and updates the knowledge base with learnings. Designed to run on a recurring schedule (1-3x daily) or on demand. Use when the user wants their inbox processed, in any variation (e.g., 'triage my inbox', 'inbox triage', 'check my email', 'run email triage', 'process my inbox', 'what's new in my email', 'handle my email', 'email triage'). Requires the inbox-setup skill to have been run first."
},
{
"name": "meetings",
"category": "productivity",
"description": "Use when someone wants to decide whether a meeting is worth calling, price a meeting in dollars, build a timeboxed agenda with desired outcomes, or turn messy meeting notes into owned action items \u2014 or says \"should this be a meeting\", \"/cs:meeting-prep\", or \"/cs:meeting-actions\". Runs a cost gate (ASYNC / NOT-READY / MEET), builds a decision-first agenda, and extracts an owner + due-date checklist that flags every orphan."
},
{
"name": "reflect",
"category": "productivity",
"description": "Mid-conversation reflection skill that pauses execution and zooms out from detail-mode to honestly reassess direction, assumptions, and bias. Use when the user says 'reflect', 'take a step back', 'step back', 'zoom out', 'are we missing something', 'bigger picture', 'sanity check this', 'are we on track', 'are we overthinking this', 'forest for the trees', or any variation signaling intent to break out of detail-mode and reassess. Also trigger when the conversation has gone deep on implementation details without strategic check-in, or when the user shows signs of being stuck \u2014 that's often a signal the framing needs a reset, not more detail work. Intentionally low-intake: runs the 5-dimension analysis immediately when prior context is rich enough; asks one forcing clarifier only when invocation context is too thin to reassess from."
},
{
"name": "roast",
"category": "productivity",
"description": "Use when someone asks to roast an idea, pressure-test or stress-test an idea, validate a business idea, \"convene the panel\", get a brutal second opinion before building something, or says \"/roast\". Spins up a 5-angle panel (Critic, Champion, Analyst, Investigator, Customer) that attacks the idea from every angle, then a Judge returns one GO / RESHAPE / KILL verdict with the cheapest test to de-risk it."
},
{
"name": "weekly-review",
"category": "productivity",
"description": "Use when someone wants to run a weekly review, close open loops, audit stalled projects and commitments, get their system back to trusted, restart a lapsed review habit, or says \"/cs:weekly-review\". Walks David Allen's three-phase loop \u2014 GET CLEAR, GET CURRENT, GET CREATIVE \u2014 with deterministic scripts that inventory open loops, gate the checklist with named gaps, and score commitment health 0-100."
},
{
"name": "atlassian-admin",
"category": "project-management",
@ -1931,7 +1991,7 @@
{
"name": "pm-skills",
"category": "project-management",
"description": "Router/index for the 8 project-management skills bundled in this plugin (senior PM quant toolkit, scrum master, Jira/JQL, Confluence, Atlassian admin, Atlassian templates, meeting analyzer, team communications). Use when a PM request doesn't obviously match one skill and you need to pick the right one (e.g., 'our sprints feel off', 'audit our Jira permissions'). Bundles an Atlassian Remote MCP config (.mcp.json) for live Jira/Confluence access."
"description": "Use when coordinating project-delivery work across the 8 project-management sub-skills \u2014 sprint/velocity analytics, portfolio health, Jira/JQL, Confluence, Atlassian admin, templates, meeting analysis, team comms. Triggers on 'our sprints feel off', 'project health report', 'audit our Jira permissions', 'when will it be done', 'run the delivery loop'. Forks context to route to one sub-skill via a deterministic signal router and returns a digest; can also drive a full goal\u2192plan\u2192execute\u2192verify\u2192close delivery loop through the repo-wide agent-harness with Jira MCP data bridged into the domain's analytics tools. Distinct from product-team (what to build vs how to deliver it), business-operations (internal ops), and engineering/agent-harness (the generic loop engine this orchestrator plugs into)."
},
{
"name": "scrum-master",
@ -1948,6 +2008,11 @@
"category": "project-management",
"description": "Write internal company communications \u2014 3P updates (Progress/Plans/Problems), company-wide newsletters, FAQ roundups, incident reports, leadership updates, status reports, project updates, and general internal comms. Use this skill any time the user asks to draft, edit, or format something meant for internal audiences. Trigger on keywords like \"3P\", \"weekly update\", \"newsletter\", \"FAQ\", \"internal comms\", \"status report\", \"company update\", \"team update\", \"incident report\", or any request to summarize work for leadership, teammates, or the broader company. Even casual requests like \"write my update\" or \"summarize what my team did this week\" should trigger this skill."
},
{
"name": "agent-decision-receipts",
"category": "ra-qm",
"description": "Mint a tamper-evident, post-quantum-signed receipt for a consequential agent action (deploy, delete, pay, grant-access, model decision) so it can be verified later from the certificate alone. Use when an autonomous agent takes a side-effecting action that may need to be proven later, or when satisfying EU AI Act Article 12 record-keeping. Three decisions: whether an action needs a receipt, minting it, verifying it. Signing is delegated to the open-source OpenAgentOntology package. Not after-the-fact log analysis; not a hosted notary; not a legal opinion."
},
{
"name": "capa-officer",
"category": "ra-qm",
@ -2038,6 +2103,11 @@
"category": "ra-qm",
"description": "Use when the user asks to prepare for SOC 2 audits, map Trust Service Criteria, build control matrices, collect audit evidence, perform gap analysis, or assess SOC 2 Type I vs Type II readiness."
},
{
"name": "deep-research",
"category": "research",
"description": "Run a disciplined, multi-source research investigation for a high-stakes question or decision \u2014 fan-out web search across many channels, parallel sub-agents, source triangulation (each claim backed by \u22653 independent sources), an adversarial review pass, and every source saved to its own file with verbatim quotes for reuse. Use when a low-quality answer is expensive: strategy work, comparing N products/methods/markets, validating a hypothesis with external data, or mapping how a field works. NOT for quick fact-checks (answer directly), structured 12-dimension competitor scoring (use competitive-teardown), or fast topic overviews where the decision risk is low (use the research router instead)."
},
{
"name": "dossier",
"category": "research",
@ -2051,7 +2121,7 @@
{
"name": "litreview",
"category": "research",
"description": "Academic literature orientation skill that searches papers via Consensus, builds a strategic search plan using PICO (default) or SPIDER / Decomposition / hybrid as fallbacks, and synthesizes findings into a formatted Word (.docx) research guide. Grill-me intake (research question specificity + framework hint + tentative depth) before the recon search; a second forcing checkpoint after Phase 2 confirms framework + sub-areas + depth before searches consume budget. Configurable depth (5/10/20 queries) controls coverage vs. speed. Output is a 'launching pad' \u2014 an orientation guide that lets a researcher dive in confidently, not a finished review. Use when the user starts literature-oriented research (e.g., 'litreview on [topic]', 'literature review on [topic]', 'I'm starting a literature review on X', 'I'm writing a paper on X', 'help me research X', 'I'm doing research on X', 'can you help me research X'). Do NOT use for single one-off paper searches wanting a quick list \u2014 that's a plain Consensus search."
"description": "Academic literature orientation skill that searches papers via free keyless APIs (PubMed E-utilities + OpenAlex) by default \u2014 with the Consensus MCP as an optional enhancement lane when connected \u2014 builds a strategic search plan using PICO (default) or SPIDER / Decomposition / hybrid as fallbacks, and synthesizes findings into a formatted Word (.docx) research guide. Grill-me intake (research question specificity + framework hint + tentative depth) before the recon search; a second forcing checkpoint after Phase 2 confirms framework + sub-areas + depth before searches consume budget. Configurable depth (5/10/20 queries) controls coverage vs. speed. Output is a 'launching pad' \u2014 an orientation guide that lets a researcher dive in confidently, not a finished review. Use when the user starts literature-oriented research (e.g., 'litreview on [topic]', 'literature review on [topic]', 'I'm starting a literature review on X', 'I'm writing a paper on X', 'help me research X', 'I'm doing research on X', 'can you help me research X'). Do NOT use for single one-off paper searches wanting a quick list \u2014 that's a plain PubMed/OpenAlex (or Consensus) query."
},
{
"name": "notebooklm",
@ -2118,7 +2188,7 @@
"description": "Business-operations resources"
},
"c-level": {
"count": 66,
"count": 68,
"description": "C-level resources"
},
"command": {
@ -2134,11 +2204,11 @@
"description": "Compliance-os resources"
},
"engineering": {
"count": 51,
"count": 52,
"description": "Engineering resources"
},
"engineering-advanced": {
"count": 79,
"count": 84,
"description": "Engineering-advanced resources"
},
"finance": {
@ -2150,7 +2220,7 @@
"description": "Markdown-html resources"
},
"marketing": {
"count": 47,
"count": 48,
"description": "Marketing resources"
},
"marketing-top-level": {
@ -2162,7 +2232,7 @@
"description": "Product resources"
},
"productivity": {
"count": 6,
"count": 11,
"description": "Productivity resources"
},
"project-management": {
@ -2170,11 +2240,11 @@
"description": "Project-management resources"
},
"ra-qm": {
"count": 18,
"count": 19,
"description": "Ra-qm resources"
},
"research": {
"count": 8,
"count": 9,
"description": "Research resources"
},
"research-ops": {

View file

@ -0,0 +1 @@
../../../ra-qm-team/skills/agent-decision-receipts/SKILL.md

View file

@ -0,0 +1 @@
../../../engineering/agent-harness/skills/agent-harness/SKILL.md

View file

@ -0,0 +1 @@
../../../c-level-advisor/skills/arquiteto-de-empresa/SKILL.md

View file

@ -0,0 +1 @@
../../../research/deep-research/skills/deep-research/SKILL.md

View file

@ -0,0 +1 @@
../../../productivity/deep-work/skills/deep-work/SKILL.md

View file

@ -0,0 +1 @@
../../../productivity/fable-goal/skills/fable-goal/SKILL.md

View file

@ -0,0 +1 @@
../../../marketing-skill/skills/local-seo-manager/SKILL.md

View file

@ -0,0 +1 @@
../../../productivity/meetings/skills/meetings/SKILL.md

View file

@ -0,0 +1 @@
../../../engineering/minimalist/SKILL.md

View file

@ -0,0 +1 @@
../../../engineering-team/skills/named-persona-adversarial-review/SKILL.md

View file

@ -0,0 +1 @@
../../../productivity/roast/skills/roast/SKILL.md

View file

@ -0,0 +1 @@
../../../engineering/skillopt-sleep/skills/skillopt-sleep/SKILL.md

View file

@ -0,0 +1 @@
../../../c-level-advisor/arquiteto-de-empresa/skills/arquiteto-de-empresa/SKILL.md

View file

@ -0,0 +1 @@
../../../engineering/strict-api/SKILL.md

View file

@ -0,0 +1 @@
../../../productivity/weekly-review/skills/weekly-review/SKILL.md

View file

@ -0,0 +1 @@
../../../engineering/zero-hallucination-coder/skills/zero-hallucination-coder/SKILL.md

File diff suppressed because one or more lines are too long

View file

@ -1,8 +1,8 @@
# Claude Code Skills & Plugins — Agent Skills for Every Coding Tool
**359 production-ready Claude Code skills, plugins, and agent skills for 13 AI coding tools.**
**362 production-ready Claude Code skills, plugins, and agent skills for 13 AI coding tools.**
The most comprehensive open-source library of Claude Code skills and agent plugins — also works with OpenAI Codex, Gemini CLI, Cursor, and 9 more coding agents. Reusable expertise packages covering engineering, DevOps, marketing (incl. AEO — Answer Engine Optimization for LLM citation), security (PreToolUse hooks), compliance, C-level advisory (incl. founder-mode CFO/CMO/CRO/CPO/COO/CHRO/CISO/GC/CDO/CAIO/CCO/VPE personas + 21 /cs:* slash commands), productivity (capture/email/reflect), an academic research stack (litreview/grants/dossier/patent/syllabus/pulse/notebooklm/deep-research + hybrid router), and enterprise Research Operations (clinical-research/research-finance/market-research/product-research, v2.9.0).
The most comprehensive open-source library of Claude Code skills and agent plugins — also works with OpenAI Codex, Gemini CLI, Cursor, and 9 more coding agents. Reusable expertise packages covering engineering, DevOps, marketing (incl. AEO — Answer Engine Optimization for LLM citation), security (PreToolUse hooks), compliance, C-level advisory (incl. founder-mode CFO/CMO/CRO/CPO/COO/CHRO/CISO/GC/CDO/CAIO/CCO/VPE personas + 21 /cs:* slash commands), productivity (capture/email/reflect/weekly-review/deep-work/meetings), an academic research stack (litreview/grants/dossier/patent/syllabus/pulse/notebooklm/deep-research + hybrid router), and enterprise Research Operations (clinical-research/research-finance/market-research/product-research, v2.9.0).
**Works with:** Claude Code · OpenAI Codex · Gemini CLI · OpenClaw · Hermes Agent[^hermes] · Mistral Vibe[^vibe] · Cursor · Aider · Windsurf · Kilo Code · OpenCode · Augment · Antigravity
@ -10,10 +10,10 @@ The most comprehensive open-source library of Claude Code skills and agent plugi
[^vibe]: Mistral Vibe is also **BYO-sync tier**: the repo ships a pre-generated `.vibe/skills/claude-skills/` tree, run `./scripts/vibe-install.sh` once locally to install into `~/.vibe/skills/`. Same agentskills.io SKILL.md standard — no format conversion. Docs: <https://docs.mistral.ai/mistral-vibe/agents-skills>.
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge)](https://opensource.org/licenses/MIT)
[![Skills](https://img.shields.io/badge/Skills-359-brightgreen?style=for-the-badge)](#skills-overview)
[![Agents](https://img.shields.io/badge/Agents-99-blue?style=for-the-badge)](#agents)
[![Skills](https://img.shields.io/badge/Skills-362-brightgreen?style=for-the-badge)](#skills-overview)
[![Agents](https://img.shields.io/badge/Agents-102-blue?style=for-the-badge)](#agents)
[![Personas](https://img.shields.io/badge/Personas-7-purple?style=for-the-badge)](#personas)
[![Commands](https://img.shields.io/badge/Commands-111-orange?style=for-the-badge)](#commands)
[![Commands](https://img.shields.io/badge/Commands-116-orange?style=for-the-badge)](#commands)
[![Stars](https://img.shields.io/github/stars/alirezarezvani/claude-skills?style=for-the-badge)](https://github.com/alirezarezvani/claude-skills/stargazers)
[![SkillCheck Validated](https://img.shields.io/badge/SkillCheck-Validated-4c1?style=for-the-badge)](https://getskillcheck.com)
@ -26,10 +26,10 @@ The most comprehensive open-source library of Claude Code skills and agent plugi
Claude Code skills (also called agent skills or coding agent plugins) are modular instruction packages that give AI coding agents domain expertise they don't have out of the box. Each skill includes:
- **SKILL.md** — structured instructions, workflows, and decision frameworks
- **Python tools** — 635 CLI scripts (all stdlib-only, zero pip installs)
- **Reference docs** — 732 templates, checklists, and domain-specific knowledge files
- **Python tools** — 644 CLI scripts (all stdlib-only, zero pip installs)
- **Reference docs** — 741 templates, checklists, and domain-specific knowledge files
**One repo, thirteen platforms.** Works natively as Claude Code plugins, Codex agent skills, Gemini CLI skills, Hermes Agent skills, Mistral Vibe skills, and converts to more tools via `scripts/convert.sh`. All 635 Python tools run anywhere Python runs.
**One repo, thirteen platforms.** Works natively as Claude Code plugins, Codex agent skills, Gemini CLI skills, Hermes Agent skills, Mistral Vibe skills, and converts to more tools via `scripts/convert.sh`. All 644 Python tools run anywhere Python runs.
### Skills vs Agents vs Personas
@ -150,7 +150,7 @@ Run `./scripts/convert.sh --tool all` to generate tool-specific outputs locally.
## Skills Overview
**359 skills across 18 domains:**
**362 skills across 18 domains:**
| Domain | Skills | Highlights | Details |
|--------|--------|------------|---------|
@ -158,7 +158,7 @@ Run `./scripts/convert.sh --tool all` to generate tool-specific outputs locally.
| **⚡ Engineering — POWERFUL** | 84 | Agent designer, RAG architect, database designer, CI/CD builder, security auditor, MCP builder, AgentHub, Helm charts, Terraform, self-eval, llm-wiki, tc-tracker, autoresearch-agent, **reliability portfolio** (feature-flags-architect, kubernetes-operator, chaos-engineering, slo-architect), ship-gate, security-guidance PreToolUse hook, **Matt Pocock skills** (write-a-skill, caveman, grill-me, handoff, grill-with-docs), **zero-hallucination-coder** (Discuss→Map→Decompose→Execute→Verify), **agent-harness** (goal→plan→execute→verify→close loops over any domain), **skillopt-sleep** (nightly gated self-evolution from real Claude Code sessions, vendored from microsoft/SkillOpt) | [engineering/](engineering/) |
| **🎯 Product** | 17 | Product manager, agile PO, strategist, UX researcher, UI design, landing pages, SaaS scaffolder, analytics, experiment designer, discovery, roadmap communicator, code-to-prd, apple-hig-expert | [product-team/](product-team/) |
| **📣 Marketing** | 48 | 8 pods: Content, SEO + AEO (`aeo` — E-E-A-T audit, citation tracking across 5 LLMs) + local (`local-seo-manager` — GBP/NAP/Map-Pack), CRO, Channels, Growth, Intelligence, Sales + context foundation + orchestration router | [marketing-skill/](marketing-skill/) |
| **🚀 Productivity** | 8 | `capture` (brain-dump-to-action), `email` pair (inbox-setup + inbox-triage), `reflect` (journal), `handoff` (Matt Pocock-inspired), `andreessen` (market-first decision mode), `roast` (5-angle idea panel → GO/RESHAPE/KILL), `fable-goal` (ramble → autonomous /goal prompt) | [productivity/](productivity/) |
| **🚀 Productivity** | 11 | `capture` (brain-dump-to-action), `email` pair (inbox-setup + inbox-triage), `reflect` (journal), `handoff` (Matt Pocock-inspired), `andreessen` (market-first decision mode), `roast` (5-angle idea panel → GO/RESHAPE/KILL), `fable-goal` (ramble → autonomous /goal prompt), `weekly-review` (GTD loop with refusal gate), `deep-work` (time-blocking + shallow-work budget), `meetings` (cost gate + agenda + action items) | [productivity/](productivity/) |
| **🎨 Marketing (top-level)** | 1 | `landing` — single-file HTML landing-page generator (4 design styles, GSAP patterns, brand palette validator) | [marketing/](marketing/) |
| **🔬 Research (academic)** | 9 | `research` orchestrator (hybrid router + fallback) + 8 specialists: `pulse`, `litreview`, `grants` (NIH), `dossier`, `patent`, `syllabus`, `notebooklm`, `deep-research` (rigor-first meta-research) | [research/](research/) |
| **🧪 Research Operations** ✨v2.9.0 | 5 | Enterprise/cross-functional research: orchestrator + `clinical-research` (study design), `research-finance` (R&D program finance), `market-research` (sizing/survey/segmentation), `product-research` (user research) — each with onboarding + customization + opt-in autoresearch bridge | [research-ops/](research-ops/) |
@ -354,7 +354,7 @@ Yes. Skills work natively with 13 tools: Claude Code, OpenAI Codex, Gemini CLI,
No. We follow semantic versioning and maintain backward compatibility within patch releases. Existing script arguments, plugin source paths, and SKILL.md structures are never changed in patch versions. See the [CHANGELOG](CHANGELOG.md) for details on each release.
**Are the Python tools dependency-free?**
Yes. All 635 Python tools use the standard library only — zero pip installs required. Every skill's CLI entry point is verified to run with `--help` (most skills ship one script per tool; a few, like the vendored `engineering/skillopt-sleep` engine, ship a multi-module package behind a single `python -m` entry point).
Yes. All 644 Python tools use the standard library only — zero pip installs required. Every skill's CLI entry point is verified to run with `--help` (most skills ship one script per tool; a few, like the vendored `engineering/skillopt-sleep` engine, ship a multi-module package behind a single `python -m` entry point).
**How do I create my own Claude Code skill?**
Each skill is a folder with a `SKILL.md` (frontmatter + instructions), optional `scripts/`, `references/`, and `assets/`. See the [Skills & Agents Factory](https://github.com/alirezarezvani/claude-code-skills-agents-factory) for a step-by-step guide.

View file

@ -1,7 +1,7 @@
{
"schema": "agent-harness/manifest.v1",
"domain": "productivity",
"skill_count": 7,
"skill_count": 11,
"loop_defaults": {
"max_attempts_per_task": 3,
"max_loop_iterations": 12,
@ -154,6 +154,76 @@
"workspace_detection.md"
]
},
{
"name": "deep-work",
"path": "productivity/deep-work/skills/deep-work",
"description": "Use when someone wants to plan a deep work day, time-block their calendar or task list, budget or cut shallow work, protect focus hours, track deep-work sessions and streaks, run an end-of-day shutdown ritual, or says \"/deep-work\" or \"/time-block\". Classifies tasks deep vs shallow, builds an energy-first time-blocked schedule that refuses deep demand past the 4-hour ceiling, batches shallow work into at most two windows, and logs focus sessions against a weekly target.",
"tools": [
{
"script": "productivity/deep-work/skills/deep-work/scripts/focus_session_logger.py",
"wired": true,
"supports_sample": true,
"verification": [
{
"cmd": "python3 productivity/deep-work/skills/deep-work/scripts/focus_session_logger.py --help",
"expect_exit": 0,
"kind": "smoke"
},
{
"cmd": "python3 productivity/deep-work/skills/deep-work/scripts/focus_session_logger.py --sample",
"expect_exit": 0,
"kind": "sample"
}
]
},
{
"script": "productivity/deep-work/skills/deep-work/scripts/shallow_work_auditor.py",
"wired": true,
"supports_sample": true,
"verification": [
{
"cmd": "python3 productivity/deep-work/skills/deep-work/scripts/shallow_work_auditor.py --help",
"expect_exit": 0,
"kind": "smoke"
},
{
"cmd": "python3 productivity/deep-work/skills/deep-work/scripts/shallow_work_auditor.py --sample",
"expect_exit": 0,
"kind": "sample"
}
]
},
{
"script": "productivity/deep-work/skills/deep-work/scripts/time_block_planner.py",
"wired": true,
"supports_sample": true,
"verification": [
{
"cmd": "python3 productivity/deep-work/skills/deep-work/scripts/time_block_planner.py --help",
"expect_exit": 0,
"kind": "smoke"
},
{
"cmd": "python3 productivity/deep-work/skills/deep-work/scripts/time_block_planner.py --sample",
"expect_exit": 0,
"kind": "sample"
}
]
}
],
"agentic_signals": {
"goal_intake": true,
"refusal_gate": true,
"verification": true,
"loop_discipline": true,
"close_out": false
},
"references": [
"deep_work_canon.md",
"shallow_work_budget.md",
"time_blocking_method.md"
]
},
{
"name": "inbox-setup",
"path": "productivity/email/skills/inbox-setup",
@ -284,6 +354,40 @@
"triage_decision_framework.md"
]
},
{
"name": "fable-goal",
"path": "productivity/fable-goal/skills/fable-goal",
"description": "Convert a rambling description of a desired outcome into one polished, autonomous /goal prompt ready to paste into a fresh session. Use when the user says \"/fable-goal\", \"turn this into a goal prompt\", \"write me a fable prompt\", \"write the prompt that builds X\", or rambles about something they want made and asks for the prompt that makes it happen. The output is a single copy-paste prompt, never the build itself. Do NOT use when the user wants the thing built right now in this session \u2014 only when they want the PROMPT that will make it happen in a fresh session.",
"tools": [
{
"script": "productivity/fable-goal/skills/fable-goal/scripts/goal_prompt_self_check.py",
"wired": true,
"supports_sample": true,
"verification": [
{
"cmd": "python3 productivity/fable-goal/skills/fable-goal/scripts/goal_prompt_self_check.py --help",
"expect_exit": 0,
"kind": "smoke"
},
{
"cmd": "python3 productivity/fable-goal/skills/fable-goal/scripts/goal_prompt_self_check.py --sample",
"expect_exit": 0,
"kind": "sample"
}
]
}
],
"agentic_signals": {
"goal_intake": false,
"refusal_gate": false,
"verification": true,
"loop_discipline": true,
"close_out": true
},
"references": [
"goal_prompt_patterns.md"
]
},
{
"name": "handoff",
"path": "productivity/handoff/skills/handoff",
@ -424,6 +528,76 @@
"redaction_checklist.md"
]
},
{
"name": "meetings",
"path": "productivity/meetings/skills/meetings",
"description": "Use when someone wants to decide whether a meeting is worth calling, price a meeting in dollars, build a timeboxed agenda with desired outcomes, or turn messy meeting notes into owned action items \u2014 or says \"should this be a meeting\", \"/cs:meeting-prep\", or \"/cs:meeting-actions\". Runs a cost gate (ASYNC / NOT-READY / MEET), builds a decision-first agenda, and extracts an owner + due-date checklist that flags every orphan.",
"tools": [
{
"script": "productivity/meetings/skills/meetings/scripts/action_item_extractor.py",
"wired": true,
"supports_sample": true,
"verification": [
{
"cmd": "python3 productivity/meetings/skills/meetings/scripts/action_item_extractor.py --help",
"expect_exit": 0,
"kind": "smoke"
},
{
"cmd": "python3 productivity/meetings/skills/meetings/scripts/action_item_extractor.py --sample",
"expect_exit": 0,
"kind": "sample"
}
]
},
{
"script": "productivity/meetings/skills/meetings/scripts/agenda_builder.py",
"wired": true,
"supports_sample": true,
"verification": [
{
"cmd": "python3 productivity/meetings/skills/meetings/scripts/agenda_builder.py --help",
"expect_exit": 0,
"kind": "smoke"
},
{
"cmd": "python3 productivity/meetings/skills/meetings/scripts/agenda_builder.py --sample",
"expect_exit": 0,
"kind": "sample"
}
]
},
{
"script": "productivity/meetings/skills/meetings/scripts/meeting_cost_calculator.py",
"wired": true,
"supports_sample": true,
"verification": [
{
"cmd": "python3 productivity/meetings/skills/meetings/scripts/meeting_cost_calculator.py --help",
"expect_exit": 0,
"kind": "smoke"
},
{
"cmd": "python3 productivity/meetings/skills/meetings/scripts/meeting_cost_calculator.py --sample",
"expect_exit": 0,
"kind": "sample"
}
]
}
],
"agentic_signals": {
"goal_intake": true,
"refusal_gate": true,
"verification": true,
"loop_discipline": true,
"close_out": true
},
"references": [
"action_item_discipline.md",
"agenda_discipline.md",
"meeting_cost_canon.md"
]
},
{
"name": "reflect",
"path": "productivity/reflect/skills/reflect",
@ -563,6 +737,76 @@
"cheapest_test_canon.md",
"verdict_synthesis_method.md"
]
},
{
"name": "weekly-review",
"path": "productivity/weekly-review/skills/weekly-review",
"description": "Use when someone wants to run a weekly review, close open loops, audit stalled projects and commitments, get their system back to trusted, restart a lapsed review habit, or says \"/cs:weekly-review\". Walks David Allen's three-phase loop \u2014 GET CLEAR, GET CURRENT, GET CREATIVE \u2014 with deterministic scripts that inventory open loops, gate the checklist with named gaps, and score commitment health 0-100.",
"tools": [
{
"script": "productivity/weekly-review/skills/weekly-review/scripts/commitment_auditor.py",
"wired": true,
"supports_sample": true,
"verification": [
{
"cmd": "python3 productivity/weekly-review/skills/weekly-review/scripts/commitment_auditor.py --help",
"expect_exit": 0,
"kind": "smoke"
},
{
"cmd": "python3 productivity/weekly-review/skills/weekly-review/scripts/commitment_auditor.py --sample",
"expect_exit": 0,
"kind": "sample"
}
]
},
{
"script": "productivity/weekly-review/skills/weekly-review/scripts/open_loop_scanner.py",
"wired": true,
"supports_sample": true,
"verification": [
{
"cmd": "python3 productivity/weekly-review/skills/weekly-review/scripts/open_loop_scanner.py --help",
"expect_exit": 0,
"kind": "smoke"
},
{
"cmd": "python3 productivity/weekly-review/skills/weekly-review/scripts/open_loop_scanner.py --sample",
"expect_exit": 0,
"kind": "sample"
}
]
},
{
"script": "productivity/weekly-review/skills/weekly-review/scripts/weekly_review_gate.py",
"wired": true,
"supports_sample": true,
"verification": [
{
"cmd": "python3 productivity/weekly-review/skills/weekly-review/scripts/weekly_review_gate.py --help",
"expect_exit": 0,
"kind": "smoke"
},
{
"cmd": "python3 productivity/weekly-review/skills/weekly-review/scripts/weekly_review_gate.py --sample",
"expect_exit": 0,
"kind": "sample"
}
]
}
],
"agentic_signals": {
"goal_intake": true,
"refusal_gate": true,
"verification": true,
"loop_discipline": true,
"close_out": true
},
"references": [
"gtd_weekly_review_canon.md",
"open_loop_psychology.md",
"review_cadence_design.md"
]
}
]
}

View file

@ -0,0 +1,19 @@
{
"name": "deep-work",
"description": "Plan a deep work day the Cal Newport way: time-block planning + shallow-work budgeting + focus-session tracking. Classifies every task deep vs shallow (recent-graduate heuristic, explicit override wins), audits the shallow share against a 30-50% budget with a WITHIN-BUDGET / OVER-BUDGET verdict, then builds an energy-first time-blocked day — deep blocks of at least 90 minutes in the earliest hours, a hard 4-hour deep ceiling the planner refuses to exceed, shallow work batched into at most two windows, 10-minute attention-residue buffers, a fixed lunch, and a hard stop that never silently extends. A JSON-backed focus-session logger tracks weekly deep hours vs target and consecutive-day streaks, and an end-of-day shutdown ritual closes every open loop. Deterministic stdlib scripts do the arithmetic; refusals (exit 2) name exactly what to cut or defer.",
"version": "2.11.2",
"author": {
"name": "Alireza Rezvani",
"url": "https://alirezarezvani.com"
},
"homepage": "https://github.com/alirezarezvani/claude-skills/tree/main/productivity/deep-work",
"repository": "https://github.com/alirezarezvani/claude-skills",
"license": "MIT",
"skills": [
"./skills/deep-work"
],
"source": {
"build_pattern": "Path-B method skill — Cal Newport time-block discipline preserved + deterministic scheduling/auditing/logging tooling added",
"distinct_from": "productivity/andreessen (the 3x5 card picks WHAT to do today; deep-work plans WHEN and HOW with attention protected — run the card first, then block the day here); project-management capacity planning (team-level capacity and sprint math; this is one person's attention across one day and one week)"
}
}

View file

@ -0,0 +1,62 @@
# deep-work — Time-Block the Day, Budget the Shallow
> Plan a deep work day the Cal Newport way. Attention is the protected resource, not time.
`/deep-work` turns a raw task list into a time-blocked day: every task classified deep vs shallow,
the shallow share audited against a budget, deep blocks of at least 90 minutes placed in the
earliest hours under a hard **4-hour ceiling**, shallow work batched into at most two windows, and
the day closed with a shutdown ritual. A local ledger tracks weekly deep hours and streaks.
## The discipline
| Rule | Enforced by |
|---|---|
| Shallow work gets a budget (default 50%) before anything is scheduled | `shallow_work_auditor.py` — OVER-BUDGET exits 2 |
| Deep blocks: ≥90 minutes, earliest hours, energy-first | `time_block_planner.py` |
| Deep demand past 4 hours is refused, naming what to defer | `time_block_planner.py` — exit 2 |
| Shallow work: at most two batches (late morning + end of day) | `time_block_planner.py` |
| 10-minute buffers drain attention residue between blocks | `time_block_planner.py` |
| The hard stop never silently extends — overflow is refused by name | `time_block_planner.py` — exit 2 |
| Deep hours are measured, not felt | `focus_session_logger.py` — status / streak |
| The day ends with a shutdown ritual, not a fade-out | `assets/shutdown_checklist.md` |
## Quick start
```bash
# 1. Audit the task list — deep vs shallow, share vs budget
python skills/deep-work/scripts/shallow_work_auditor.py --sample
# 2. Build the time-blocked day (refuses >4h deep, refuses overflow)
python skills/deep-work/scripts/time_block_planner.py --sample
# 3. Log real focus sessions; check the weekly target and streak
python skills/deep-work/scripts/focus_session_logger.py log --minutes 90 --label "Write spec"
python skills/deep-work/scripts/focus_session_logger.py status --target 15
```
Or just say **"plan my deep work day: …"**, run `/cs:deep-work` (full workflow), or
`/cs:time-block` (quick plan from a task list).
## What's in the box
- **Skill:** [`skills/deep-work/SKILL.md`](skills/deep-work/SKILL.md)
- **Agent:** [`agents/cs-deep-work.md`](agents/cs-deep-work.md)
- **Commands:** [`commands/cs-deep-work.md`](commands/cs-deep-work.md) — `/cs:deep-work`;
[`commands/cs-time-block.md`](commands/cs-time-block.md) — `/cs:time-block`
- **3 stdlib scripts:** `shallow_work_auditor.py`, `time_block_planner.py`, `focus_session_logger.py`
- **3 references** (6-7 sources each: Newport, Leroy, Mark, Ericsson, Csikszentmihalyi, Graham,
Gollwitzer, Eyal, Parkinson, Zeigarnik, Masicampo & Baumeister, Perlow, RescueTime, Atlassian),
**2 assets** (worked example day + shutdown-ritual checklist)
## Not the same as
- **`productivity/andreessen`** — the 3x5 card picks WHAT matters today. Deep-work plans WHEN and
HOW, with attention protected. Run the card first, then block the day here.
- **`project-management` capacity planning** — team-level capacity and sprint math. This is one
person's attention across one day and one week.
- **`productivity/reflect`** — end-of-week reflection prose. The shutdown ritual here is a daily,
mechanical close.
## License
MIT.

View file

@ -0,0 +1,86 @@
---
name: cs-deep-work
description: Plans a deep work day the Cal Newport way — audits a task list deep vs shallow against a 30-50% shallow budget, builds an energy-first time-blocked schedule (deep blocks of at least 90 minutes in the earliest hours, a hard 4-hour deep ceiling, shallow work batched into at most two windows, 10-minute buffers, an immovable hard stop), logs focus sessions against a weekly target, and closes the day with a shutdown ritual. Respects the scripts' exit-2 refusals: deep demand past the cap and overflow past the hard stop are named and deferred, never squeezed in. Use to plan or re-plan a focused day, cut shallow work, or track deep-hours streaks.
skills: productivity/deep-work/skills/deep-work
domain: productivity
model: opus
tools: [Read, Bash]
---
# Deep Work Agent
## Purpose
The `cs-deep-work` agent orchestrates the `deep-work` skill to turn a raw task list into a day
where attention is the protected resource:
1. **Intake** — collect today's tasks with rough minutes each, plus the day's hard start, hard
stop, and lunch time. Ask one batched round of questions at most; a task list plus "9 to 5" is
enough to proceed.
2. **Audit the shallow** — run `shallow_work_auditor.py` (keyword heuristics; an explicit
`:deep`/`:shallow` suffix always wins). Surface the shallow share vs the budget (default 50%)
and the recent-graduate forcing question for every shallow item. `OVER-BUDGET` (exit 2) means
the user cuts, batches, or delegates *before* any schedule is built.
3. **Block the day** — run `time_block_planner.py` with the surviving tasks: deep blocks ≥90 min
in the earliest hours, 4-hour deep cap, ≤2 shallow batches (late morning + end of day),
10-minute buffers, fixed lunch. Present the markdown schedule and read it back in plain words.
4. **Handle refusals honestly** — an exit-2 refusal (deep cap exceeded / overflow past the hard
stop) is the product, not an error. Relay exactly what the planner says to cut or defer, help
the user choose, then re-run. Never hand-edit a schedule around a refusal.
5. **Close the loop** — after real focus blocks, log them with `focus_session_logger.py log`;
report `status` (weekly deep hours vs target, default 15) and `streak`. At day's end, walk the
shutdown ritual (`../skills/deep-work/assets/shutdown_checklist.md`) to its closing phrase.
## Voice
- Calm and unsentimental about arithmetic. Four hours of deep work is the ceiling, not a challenge.
- Protective of mornings. The best hours go to the hardest work; email does not get 09:00.
- Guilt-free about revision. A broken block means redraw the rest of the day — the plan's value
survives its own destruction.
## Hard rules
1. **Audit before schedule.** No time-block plan is built while the shallow share is over budget.
2. **The refusals stand.** Deep demand past 4 hours and overflow past `--end` are deferred by
name, never squeezed, shrunk below 90 minutes, or pushed into the evening.
3. **The hard stop does not move.** Fixed-schedule productivity: the end time is a constraint,
not a suggestion.
4. **Shallow work is batched, never sprinkled.** At most two windows per day.
5. **Measured, not felt.** Weekly deep hours come from the ledger (`status`), never from vibes.
## Skill Integration
**Skill Location:** `../skills/deep-work/`
### Python Scripts (Stdlib)
1. **Shallow-Work Auditor**`skills/deep-work/scripts/shallow_work_auditor.py` — deep/shallow
classification + shallow share vs `--budget` → WITHIN-BUDGET / OVER-BUDGET (exit 2) + the
recent-graduate forcing question per shallow item.
2. **Time-Block Planner**`skills/deep-work/scripts/time_block_planner.py` — energy-first
schedule with the 4-hour deep cap and overflow refusal (both exit 2, both name what to defer).
3. **Focus-Session Logger**`skills/deep-work/scripts/focus_session_logger.py` — JSON ledger:
`log` / `status` (weekly hours vs target) / `streak`; atomic writes via `os.replace`.
### Knowledge Bases
- `skills/deep-work/references/deep_work_canon.md` — deep vs shallow, the deep work hypothesis, the 4-hour ceiling, attention residue (6 sources)
- `skills/deep-work/references/time_blocking_method.md` — plan every minute, block sizes, buffers, guilt-free revision, the hard stop (6+ sources)
- `skills/deep-work/references/shallow_work_budget.md` — the 30-50% band, saying no, batching, why the shutdown ritual works (6 sources)
## Differentiates From Siblings
- **vs `cs-andreessen`** (productivity): the 3x5 card picks WHAT matters today; deep-work plans
WHEN and HOW with attention protected. Run the card first, then block the day here.
- **vs `project-management` capacity planning**: team-level capacity and sprint math; this is one
person's attention across one day and one week.
- **vs `productivity/reflect`**: end-of-week reflection prose; the shutdown ritual here is a
daily, mechanical close.
## Related Agents
- [cs-andreessen](../../andreessen/agents/cs-andreessen.md) — productivity sibling; picks the day's 3-5 priorities before this agent blocks them
---
**Version:** 1.0.0

View file

@ -0,0 +1,94 @@
---
name: "cs-deep-work"
description: "/cs:deep-work — Plan a deep work day the Cal Newport way: audit the task list deep vs shallow against a budget, build an energy-first time-blocked schedule with a hard 4-hour deep ceiling and an immovable hard stop, then log focus sessions and close with the shutdown ritual."
argument-hint: "[today's task list, with rough minutes per task]"
---
# /cs:deep-work — Audit the Shallow, Block the Day, Bank the Hours
**Command:** `/cs:deep-work [today's task list]`
A calendar full of reactions is not a plan. `/cs:deep-work` runs the full attention-first
workflow: classify every task deep vs shallow, audit the shallow share against a budget, build a
time-blocked day where deep work owns the earliest hours, and close the loop with a focus-session
ledger and a shutdown ritual.
## When to Run
- "Plan my deep work day" / "time-block my day" / "protect my focus hours"
- The task list is drowning in email, meetings, and admin and you want the honest split
- You keep "working all day" and shipping nothing hard — depth is unmeasured
- Start of day (plan), mid-day after the plan broke (re-plan), end of day (log + shutdown)
## When NOT to Run
- You need to pick WHAT matters today → run `/cs:andreessen` (3x5 card) first, then come back
- Team-level capacity or sprint math → `project-management` skills, not personal attention
- You just want a quick schedule from a ready task list with no audit → `/cs:time-block`
## What You Get
1. **A shallow-work audit** — every task classified DEEP/SHALLOW with the basis shown, the shallow
share vs your budget (default 50%), a WITHIN-BUDGET / OVER-BUDGET verdict, and the
recent-graduate forcing question for every shallow item.
2. **A time-blocked day** — deep blocks ≥90 min in the earliest hours (capped at 4 hours), shallow
work in at most two batches, 10-minute buffers, fixed lunch, hard stop. Refusals name exactly
what to cut or defer.
3. **A focus ledger** — sessions logged, weekly deep hours vs target (default 15), streak count.
4. **A shutdown ritual** — open loops captured, tomorrow's first block chosen, "shutdown complete."
## Trigger Phrases (auto-invoke without /cs:)
- "plan my deep work day" / "deep work plan"
- "time-block my day" / "time block my calendar"
- "how much of my day is shallow work"
- "protect my focus time" / "I need focus hours"
## Discipline
- **Audit before schedule** — an OVER-BUDGET day gets cut, batched, or delegated first.
- **The refusals stand** — >4h deep demand and overflow past the hard stop are deferred by name,
never squeezed in or pushed into the evening.
- **The hard stop does not move** — fixed-schedule productivity.
- **Batch, never sprinkle** — shallow work lives in at most two windows.
- **Revise, don't abandon** — when a block breaks, re-run the planner from the current time.
- **Measured, not felt** — the weekly target is checked against the ledger, not memory.
## Workflow
```bash
# 1. Audit the task list — deep vs shallow, share vs budget (OVER-BUDGET exits 2)
python ../skills/deep-work/scripts/shallow_work_auditor.py \
--task "Write investor update:60" --task "Email triage:45" \
--task "Analyze churn cohort:90:deep" --budget 50
# 2. Build the time-blocked day (deep-cap and overflow refusals exit 2, naming deferrals)
python ../skills/deep-work/scripts/time_block_planner.py --start 08:30 --end 17:00 --lunch 12:30 \
--task "Write investor update:90:deep" --task "Analyze churn cohort:90:deep" \
--task "Email triage:45:shallow"
# 3. After each real focus block, log it; check the week and the streak
python ../skills/deep-work/scripts/focus_session_logger.py log --minutes 90 --label "Investor update"
python ../skills/deep-work/scripts/focus_session_logger.py status --target 15
python ../skills/deep-work/scripts/focus_session_logger.py streak
# 4. End of day: walk ../skills/deep-work/assets/shutdown_checklist.md to "shutdown complete"
```
## Stop Conditions
- Plan emitted + user accepts the blocks → done; return at day's end for log + shutdown.
- Planner refuses (exit 2) → user picks what to defer from the named candidates, re-run once; if
it refuses again, the day is overcommitted — cut scope, don't fight the arithmetic.
- User says "stop" → drop it; the ledger keeps whatever was already logged.
## Related
- Agent: [`cs-deep-work`](../agents/cs-deep-work.md)
- Skill: [`deep-work`](../skills/deep-work/SKILL.md)
- Quick variant: [`/cs:time-block`](cs-time-block.md) — schedule only, no audit
- Siblings: `/cs:andreessen` (picks WHAT today; run before this), `/cs:reflect` (weekly reflection)
---
**Version:** 1.0.0

View file

@ -0,0 +1,80 @@
---
name: "cs-time-block"
description: "/cs:time-block — Build today's time-block plan from a task list, fast: deep blocks of at least 90 minutes in the earliest hours under a hard 4-hour ceiling, shallow work batched into at most two windows, 10-minute buffers, fixed lunch, immovable hard stop. The quick variant of /cs:deep-work — schedule only, no audit, no ledger."
argument-hint: "[task list with minutes, plus start/end times]"
---
# /cs:time-block — Just Build Today's Blocks
**Command:** `/cs:time-block [task list + start/end]`
The quick variant of `/cs:deep-work`: skip the shallow audit and the ledger, take a ready task
list, and emit the time-blocked day. Same arithmetic, same refusals — deep work first and
earliest, capped at 4 hours; shallow work batched; the hard stop does not move.
## When to Run
- "Time-block my day" with a task list already in hand
- Mid-day re-plan after a block broke — feed the surviving tasks and the current time as `--start`
- You already know what's deep and what's shallow and just need the schedule
## When NOT to Run
- The task list hasn't been triaged — shallow work will eat the plan → run `/cs:deep-work` (it
audits first)
- You need to pick WHAT matters today → `/cs:andreessen` (3x5 card)
- Team capacity or sprint planning → `project-management` skills
## What You Get
A markdown schedule table from hard start to hard stop with **no unassigned minutes**: deep blocks
(≥90 min, earliest hours), at most two shallow batches (late morning + end of day), 10-minute
buffers, optional fixed 30-minute lunch, and named flex blocks that absorb what the plan didn't
foresee. Or a refusal (exit 2) that names exactly what to cut or defer — which is the plan working,
not failing.
## Trigger Phrases (auto-invoke without /cs:)
- "time-block my day" / "build my time blocks"
- "block out my calendar for today"
- "re-plan the rest of my day"
## Discipline
- **Every task needs minutes and a mode**`"name:minutes:deep|shallow"`. If the user doesn't
know a task's mode, that's the tell to run `/cs:deep-work` instead.
- **Deep demand past 4 hours is deferred by name** — never shrunk below 90 minutes or squeezed.
- **Overflow past `--end` is deferred by name** — the day never silently extends.
- **Revision is normal** — a broken day is re-planned from the current time, same rules.
## Workflow
```bash
# Build the day (markdown table; add --json for machine-readable output)
python ../skills/deep-work/scripts/time_block_planner.py --start 08:30 --end 17:00 --lunch 12:30 \
--task "Write product spec:120:deep" \
--task "Design onboarding flow:90:deep" \
--task "Email sweep:30:shallow" \
--task "Expense report:15:shallow"
# Mid-day re-plan: surviving tasks, current time as --start, same hard stop
python ../skills/deep-work/scripts/time_block_planner.py --start 13:00 --end 17:00 \
--task "Finish product spec:90:deep" --task "Email sweep:30:shallow"
```
## Stop Conditions
- Schedule emitted and accepted → done.
- Refusal (exit 2) → user picks a deferral from the named candidates, re-run once; still refusing
means the day is overcommitted — cut scope.
- User says "stop" → drop it.
## Related
- Full workflow: [`/cs:deep-work`](cs-deep-work.md) — audit + plan + ledger + shutdown
- Agent: [`cs-deep-work`](../agents/cs-deep-work.md)
- Skill: [`deep-work`](../skills/deep-work/SKILL.md)
---
**Version:** 1.0.0

View file

@ -0,0 +1,100 @@
---
name: deep-work
description: Use when someone wants to plan a deep work day, time-block their calendar or task list, budget or cut shallow work, protect focus hours, track deep-work sessions and streaks, run an end-of-day shutdown ritual, or says "/deep-work" or "/time-block". Classifies tasks deep vs shallow, builds an energy-first time-blocked schedule that refuses deep demand past the 4-hour ceiling, batches shallow work into at most two windows, and logs focus sessions against a weekly target.
argument-hint: "[today's task list]"
license: MIT
metadata:
version: 1.0.0
build_pattern: "Path-B method skill — Cal Newport time-block discipline preserved + deterministic scheduling scripts added"
distinct_from: "andreessen 3x5 card (picks WHAT to do today; deep-work plans WHEN and HOW with attention protected); project-management capacity planning (team capacity; this is one person's attention)"
---
# Deep Work — Time-Block the Day, Budget the Shallow
> **Portability:** Reasoning-led skill with 3 stdlib Python scripts. No external APIs, no LLM calls in scripts. Works in Claude Code CLI and Claude.ai web. The scripts fix the arithmetic; you keep the judgment.
## What this does
A calendar full of reactions is not a plan. This skill turns a raw task list into a day where
attention is the protected resource: deep tasks get the earliest hours in blocks of at least 90
minutes, shallow work is batched into at most two windows, buffers absorb attention residue, and the
schedule flatly refuses more than 4 hours of deep demand — the trained daily ceiling. A local ledger
of focus sessions keeps the weekly deep-hours target measured, not felt.
## Step 1 — Classify and budget the shallow
Ask for today's task list with rough minutes per task (or take it from `$ARGUMENTS`). The auditor
classifies each task deep vs shallow (keyword heuristics; an explicit `:deep`/`:shallow` suffix always
wins), computes the shallow share against the budget, and prints the forcing question for every
shallow item — *how long would it take to train a smart recent graduate to do this?*
```bash
python scripts/shallow_work_auditor.py \
--task "Write investor update:60" --task "Email triage:45" \
--task "Analyze churn cohort:90:deep" --budget 50
```
`OVER-BUDGET` (exit 2) means cut, batch, or delegate before any schedule is built.
## Step 2 — Block the day
Feed the surviving tasks to the planner with the day's hard start and hard end (fixed-schedule
productivity: the end time does not move). Deep first and earliest, 10-minute buffers, shallow in
two batches (late morning + end of day), an optional fixed lunch:
```bash
python scripts/time_block_planner.py --start 08:30 --end 17:00 --lunch 12:30 \
--task "Write product spec:120:deep" --task "Email sweep:30:shallow"
```
Two refusals, both exit 2: deep demand past the 4-hour cap (the planner names what to defer), and shallow overflow past `--end` (the planner names what to drop — the day never silently extends).
## Step 3 — Log the session, keep the streak
After each real focus block, log it. `status` shows this week's deep hours against the target (default 15); `streak` counts consecutive days with at least one session:
```bash
python scripts/focus_session_logger.py log --minutes 90 --label "Write product spec"
python scripts/focus_session_logger.py status --target 15
```
## Step 4 — Shutdown ritual
End the day with the shutdown checklist (`assets/shutdown_checklist.md`): capture every open loop, glance at tomorrow, say the closing phrase. An incompletely closed day steals tomorrow's first block.
## Scripts
| Script | Role |
|---|---|
| `scripts/shallow_work_auditor.py` | Deep/shallow classification + shallow share vs budget → WITHIN-BUDGET / OVER-BUDGET (exit 2) + the recent-graduate forcing question per shallow item. |
| `scripts/time_block_planner.py` | Energy-first schedule: deep blocks ≥90 min earliest, 4-hour deep cap (refuses, exit 2), ≤2 shallow batches, 10-min buffers, fixed lunch, overflow refusal. |
| `scripts/focus_session_logger.py` | JSON ledger of focus sessions: `log` / `status` (weekly hours vs target) / `streak`; atomic writes. |
## References
- [`references/deep_work_canon.md`](references/deep_work_canon.md) — deep vs shallow, the deep work hypothesis, the 4-hour ceiling, attention residue (6 sources)
- [`references/time_blocking_method.md`](references/time_blocking_method.md) — plan every minute, block sizes, buffers, guilt-free revision, fixed-schedule productivity (6 sources)
- [`references/shallow_work_budget.md`](references/shallow_work_budget.md) — the 30-50% band, saying no, batching, the recent-graduate heuristic, why the shutdown ritual works (6 sources)
## Assets
- [`assets/example_time_block_plan.md`](assets/example_time_block_plan.md) — a full worked day (audit → plan → mid-day revision → shutdown)
- [`assets/shutdown_checklist.md`](assets/shutdown_checklist.md) — end-of-day shutdown ritual template
## Rules
- **Depth first, earliest.** Deep blocks take the best hours; shallow work gets what is left, never the reverse.
- **Respect the refusals.** More than 4 deep hours is fake depth; overflow past the hard stop is a broken budget, not extra output.
- **Batch, never sprinkle.** Shallow work lives in at most two windows; a sprinkled inbox costs a full refocus each time.
- **Revise, don't abandon.** A broken block means redraw the rest of the day, not "the plan failed."
- **Close the day.** No shutdown ritual, no evening — open loops steal tomorrow's first block.
## Distinct From (don't reach for the wrong skill)
- **`productivity/andreessen`** — the 3x5 card picks WHAT matters today. Deep-work plans WHEN and HOW, with attention protected. Run the card first, then block the day here.
- **`project-management` capacity planning** — team-level capacity and sprint math. This is one person's attention across one day and one week.
---
**Version:** 1.0.0
**Build pattern:** Path-B method skill — Newport discipline preserved + deterministic scheduling scripts added.

View file

@ -0,0 +1,93 @@
# Worked Example — A Full Deep-Work Day (Audit → Plan → Mid-Day Revision → Shutdown)
> A complete pass through the `deep-work` skill for one realistic day, showing the exact script
> invocations and their outputs, including the mid-day revision that every real day eventually needs.
## The raw task list (as the user gave it)
- Write the Q3 product spec — ~2h, the week's most important output
- Design the onboarding flow — ~1.5h
- Email sweep — 30 min
- Team status update — 20 min
- Expense report — 15 min
Day: 08:30 hard start, 17:00 hard stop, lunch 12:30.
## Step 1 — Shallow audit
```bash
python scripts/shallow_work_auditor.py \
--task "Write Q3 product spec:120" \
--task "Design onboarding flow:90" \
--task "Email sweep:30" \
--task "Team status update:20" \
--task "Expense report:15" \
--budget 50
```
Result: `Write Q3 product spec` and `Design onboarding flow` classify DEEP (write/design signals);
the other three classify SHALLOW (email/status/expense signals). Shallow share = 65 of 275 min =
**23.6% vs a 50% budget → WITHIN-BUDGET (exit 0)**. The forcing question printed for each shallow
item confirmed none of them deserves a promotion to deep. Proceed.
## Step 2 — The plan
```bash
python scripts/time_block_planner.py --start 08:30 --end 17:00 --lunch 12:30 \
--task "Write Q3 product spec:120:deep" \
--task "Design onboarding flow:90:deep" \
--task "Email sweep:30:shallow" \
--task "Team status update:20:shallow" \
--task "Expense report:15:shallow"
```
| Start | End | Block | Mode |
|-------|-----|-------|------|
| 08:30 | 10:30 | DEEP — Write Q3 product spec | DEEP |
| 10:30 | 10:40 | Buffer — stand up, reset | BUFFER |
| 10:40 | 12:10 | DEEP — Design onboarding flow | DEEP |
| 12:10 | 12:20 | Buffer — stand up, reset | BUFFER |
| 12:20 | 12:30 | Flex — reset, no inputs | FLEX |
| 12:30 | 13:00 | Lunch — away from the desk | BREAK |
| 13:00 | 13:50 | SHALLOW batch (late morning) — Email sweep · Team status update | SHALLOW |
| 13:50 | 16:45 | Flex — overflow absorber | FLEX |
| 16:45 | 17:00 | SHALLOW batch (end of day) — Expense report | SHALLOW |
Deep 3h30 / 4h cap · Shallow 1h05 · Flex 3h05 · Buffers 20min
Reading the plan: both deep blocks own the morning — the best hours — before the manager-schedule
world wakes up. Shallow work exists in exactly two windows. The big afternoon flex block is not
waste; it is where meetings, overruns, and surprises land without touching the deep blocks.
## The revision (because the day broke, as days do)
At 10:15 an incident call ate the rest of the spec block. The rule: **revise, don't abandon.**
At 11:00, re-plan the remaining day with what actually survives:
```bash
python scripts/time_block_planner.py --start 11:00 --end 17:00 --lunch 12:30 \
--task "Finish Q3 product spec:90:deep" \
--task "Email sweep:30:shallow" \
--task "Team status update:20:shallow" \
--task "Expense report:15:shallow"
```
The onboarding-flow design moved to tomorrow's first block — named and deferred, not silently
squeezed into the evening. The redrawn day still has one protected 90-minute deep block, and the
hard stop did not move.
## Step 3 — Log what actually happened
```bash
python scripts/focus_session_logger.py log --minutes 75 --label "Q3 product spec (pre-incident)"
python scripts/focus_session_logger.py log --minutes 90 --label "Q3 product spec (finish)"
python scripts/focus_session_logger.py status --target 15
```
2h45 banked today. The week's ledger, not memory, decides whether the 15-hour target is on track.
## Step 4 — Shutdown
Run `assets/shutdown_checklist.md`: the deferred design block is already on tomorrow's plan, the
incident follow-up is captured as a task, tomorrow's first block is confirmed. **"Shutdown
complete."** No work thoughts after the phrase.

View file

@ -0,0 +1,51 @@
# End-of-Day Shutdown Ritual — Checklist Template
> Run this at the hard stop, every working day. The ritual exists because unfinished tasks keep
> firing in memory until they either finish or get a concrete plan — and a plan is enough
> (Zeigarnik; Masicampo & Baumeister). Ten minutes here buys a recovered evening and protects
> tomorrow's first deep block. Copy this file into your notes system, or fill it in place.
**Date:** ____________ **Hard stop:** ____________
## 1. Capture every open loop
Sweep every place a commitment can hide. Nothing stays only in your head.
- [ ] Inbox — no unread message that represents an action I haven't captured
- [ ] Chat (Slack/Teams/etc.) — same standard as email
- [ ] Notes and scratch files from today — actions extracted
- [ ] The day's time-block plan — every unfinished block accounted for
Open loops captured (task + where it now lives):
| Open loop | Captured to (tomorrow's list / calendar / project plan) |
|---|---|
| | |
| | |
| | |
## 2. Give every incomplete a plan
For each item above: either it has a **time block tomorrow**, a **calendar date**, or an explicit
**deferred / dropped** decision. "I'll get to it" is not a plan — a plan names when.
- [ ] Every incomplete has a when, or a conscious "not doing this"
## 3. Glance at tomorrow
- [ ] Calendar checked — no surprise that should reshape tomorrow's blocks
- [ ] Tomorrow's **first deep block** is chosen and written down:
**Tomorrow, first block:** ____________________________________________
## 4. Bank today's evidence
- [ ] Focus sessions logged (`focus_session_logger.py log ...`)
- [ ] Weekly status glanced at (`focus_session_logger.py status`) — hours vs target, streak intact
## 5. Close
- [ ] Say the closing phrase, out loud or written: **"Shutdown complete."**
After the phrase: no email, no chat, no "quick checks." If a work thought surfaces tonight, the
answer is already true — *it's captured, it has a plan, shutdown is complete.*

View file

@ -0,0 +1,93 @@
# The Deep Work Canon — Deep vs Shallow, the Hypothesis, the Ceiling, the Residue
> Reference for the `deep-work` skill. This is the conceptual spine behind all three scripts:
> why deep and shallow are different kinds of work, why depth is scarce and valuable, why the
> planner caps deep time at 4 hours, and why buffers between blocks are not optional.
## 1. The deep/shallow distinction
Cal Newport defines the two modes precisely (*Deep Work*, 2016):
- **Deep work** — "professional activities performed in a state of distraction-free concentration
that push your cognitive capabilities to their limit. These efforts create new value, improve
your skill, and are hard to replicate."
- **Shallow work** — "noncognitively demanding, logistical-style tasks, often performed while
distracted. These efforts tend to not create much new value in the world and are easy to
replicate."
The operational test Newport supplies — and the one `shallow_work_auditor.py` prints for every
shallow item — is the **recent-graduate heuristic**: *how long would it take to train a smart
recent college graduate with no specialized training to do this task?* Months or years of training
means the task is probably deep. Days or weeks means it is shallow, however urgent it feels.
The auditor's keyword heuristic (email, slack, status, meeting, expense, scheduling, admin →
shallow; write, design, code, build, research, analyze, study → deep) is a first-pass proxy for
this test — which is why the explicit `:deep`/`:shallow` override always wins. The heuristic
starts the conversation; the recent-graduate question settles it.
## 2. The deep work hypothesis
Newport's central economic claim: **the ability to perform deep work is becoming increasingly rare
at exactly the same time it is becoming increasingly valuable.** As machines and markets absorb
routine cognitive work, the two abilities that compound are (a) quickly mastering hard things and
(b) producing at an elite level of quality and speed — and both are functions of depth. The people
who cultivate deep work "will thrive"; those who default to reactive shallowness compete in the
most crowded segment of the labor market.
This is why the skill treats attention — not time — as the protected resource. Eight hours of
fragmented availability produce less durable value than three well-defended deep hours.
## 3. The 4-hour trained limit
The planner's hard cap (`DEEP_CAP_MIN = 240`) comes from the deliberate-practice literature that
Newport builds on. Ericsson, Krampe, and Tesch-Römer's landmark study of expert violinists (1993)
found that even elite performers sustain roughly **four hours of deliberate, maximally effortful
practice per day** — typically in sessions of about 60-90 minutes with rest between — before the
quality of the work collapses. Newport translates this directly: "for someone new to such practice,
an hour a day is a reasonable limit. For experts, four hours — but rarely more."
Two consequences are encoded in `time_block_planner.py`:
1. Scheduling more than 4 hours of deep blocks is refused (exit 2), not warned about. Depth past
the ceiling is fake depth — the block is on the calendar but the concentration is not in it.
2. The minimum deep block is 90 minutes. Csikszentmihalyi's flow research shows that full
absorption takes time to enter and rewards long uninterrupted stretches; blocks shorter than
about 90 minutes spend most of their length ramping up rather than producing.
## 4. Attention residue — why buffers and batching exist
Sophie Leroy (2009) named the mechanism that fragmented schedules quietly bleed: **attention
residue**. When a person switches from Task A to Task B, part of their attention remains stuck on
A — especially when A was unfinished or left under time pressure — and performance on B measurably
degrades. People who worked on a single task without switching significantly outperformed those
who hopped, even when total time was equal.
Gloria Mark's field studies of information workers quantify the recovery cost: after a significant
interruption, it takes on the order of **23 minutes** to fully re-engage with the original task,
and interrupted workers compensate by working faster at the price of more stress and frustration.
The planner encodes both findings:
- **10-minute buffers** between consecutive work blocks give residue somewhere to drain — stand
up, reset, close the loop — instead of carrying it into the next block.
- **Shallow work is batched into at most two windows.** Every sprinkle of "just one email" between
deep tasks is a residue event plus a ~23-minute refocus tax. Two batches means at most two
context-switch penalties per day instead of a dozen.
## 5. Maker's schedule — why deep blocks go earliest
Paul Graham's essay "Maker's Schedule, Manager's Schedule" (2009) explains why a single meeting
can destroy an afternoon: makers operate in units of half a day at minimum, because "a single
meeting can blow a whole afternoon, by breaking it into two pieces each too small to do anything
hard in." The planner therefore front-loads deep blocks into the earliest hours — before the
manager-schedule world wakes up and starts fragmenting the calendar — and pushes shallow batches
to late morning and end of day, where fragmentation does the least damage.
## Sources
1. Cal Newport, *Deep Work: Rules for Focused Success in a Distracted World* (Grand Central, 2016) — the deep/shallow definitions, the deep work hypothesis, the recent-graduate heuristic, the 4-hour claim.
2. Sophie Leroy, "Why is it so hard to do my work? The challenge of attention residue when switching between work tasks," *Organizational Behavior and Human Decision Processes* 109(2), 2009.
3. Gloria Mark, Daniela Gudith, and Ulrich Klocke, "The Cost of Interrupted Work: More Speed and Stress," *Proceedings of CHI 2008* — the ~23-minute refocus finding popularized from Mark's research program.
4. K. Anders Ericsson, Ralf Th. Krampe, and Clemens Tesch-Römer, "The Role of Deliberate Practice in the Acquisition of Expert Performance," *Psychological Review* 100(3), 1993 — the ~4-hour daily limit on deliberate practice.
5. Mihaly Csikszentmihalyi, *Flow: The Psychology of Optimal Experience* (Harper & Row, 1990) — absorption, the cost of entry into flow, why long blocks outperform fragments.
6. Paul Graham, "Maker's Schedule, Manager's Schedule" (paulgraham.com, 2009) — half-day units of maker time; why meetings fragment afternoons.

View file

@ -0,0 +1,92 @@
# The Shallow-Work Budget — The 30-50% Band, Saying No, and the Shutdown Ritual
> Reference for `shallow_work_auditor.py` and the Step-4 shutdown ritual. Why shallow work gets
> an explicit percentage budget, how to enforce it (batching, refusal), and why the day must be
> closed with a ritual rather than simply stopped.
## 1. The 30-50% band
Newport's prescription in *Deep Work* (Rule #4, "Drain the Shallows") is to confront shallow work
quantitatively: decide, ideally with whoever you answer to, **what fraction of your time should go
to shallow work — and hold the line**. His reported experience: for almost anyone in a
non-entry-level knowledge role, the honest answer lands in the **30-50% band**. Below 30% is
usually fantasy (organizations run on some coordination overhead); consistently above 50% means
the role is being consumed by work "easy to replicate" and the deep output that justifies the
seat is quietly starving.
The auditor's `--budget` default of 50 is deliberately the *top* of the band — the most permissive
defensible line. `OVER-BUDGET` (exit 2) at 50% is not a style warning; it means the day as listed
cannot contain the deep work that matters, so something must be cut, batched, or delegated
*before* a schedule is built. Budgeting after scheduling is how shallow work wins.
## 2. The recent-graduate heuristic
Classifying honestly is the hard part — everything feels important to the person doing it. Newport's
forcing question, printed by the auditor for every shallow item:
> **"How long would it take (in months) to train a smart recent college graduate with no
> specialized training in my field to complete this task?"**
Months of training → the task leverages hard-won expertise → probably deep. Days or weeks → the
task is shallow *regardless of how urgent, social, or visible it is*. The question works because
it strips away urgency and identity and asks only about the skill embedded in the work.
## 3. What time audits actually find
Self-estimates of deep time are systematically inflated, which is why the auditor computes the
share from declared minutes instead of asking for a feeling:
- **RescueTime's** analyses of anonymized knowledge-worker data found users averaging only about
**2 hours 48 minutes of productive device time per day**, checking email or IM roughly every
6 minutes, and getting long uninterrupted focus stretches rarely — most days contained no block
of more than an hour without a communication check.
- **Atlassian's** workplace research aggregates put the average worker at **~62 meetings a month**,
with half of surveyed workers rating many of them unnecessary, and inbox/context-switch overhead
consuming a large share of nominal working hours.
The pattern across both: unbudgeted shallow work does not stay small; it metastasizes to fill
whatever attention is left unclaimed. Hence: budget first, then schedule.
## 4. Saying no and batching — enforcing the budget
Three enforcement moves, in order of leverage:
1. **Refuse.** The budget gives the refusal a number: "that puts me over my shallow budget this
week" is a policy, not a mood. Newport pairs this with fixed-schedule productivity — a hard
stop makes every yes visibly displace something.
2. **Batch.** Shallow work that survives triage is consolidated into at most two windows (the
planner enforces this), converting many context switches into two. Leslie Perlow's field
experiments — from "quiet time" studies with software engineers to the **predictable time off**
program run with Boston Consulting Group teams (*HBR*, "Making Time Off Predictable — and
Required," 2009) — showed that even elite always-on teams could carve out protected,
communication-free periods and see collaboration, satisfaction, and work quality *improve*.
Protection works when it is scheduled and collectively respected, not heroic.
3. **Delegate or automate.** Anything a recent graduate could learn in days is a candidate.
## 5. The shutdown ritual — why closing loops works
Newport's rule: end every workday with a **shutdown ritual** — review every open loop, capture it
into a trusted plan (today's incompletes get a home on tomorrow's list or the calendar), glance at
the next day, and mark completion with a fixed phrase ("shutdown complete"). No work thoughts
after the phrase.
The mechanism is Zeigarnik's classic finding (1927) that **interrupted and unfinished tasks
intrude on memory far more than finished ones** — open loops keep firing. Masicampo and
Baumeister's follow-up (2011) supplies the crucial refinement: unfulfilled goals stop intruding
**not only when completed, but as soon as a specific plan for them is made**. The shutdown ritual
is that plan-making, done in batch at day's end: it purchases a genuinely recovered evening, and
recovery is what refills the capacity the 4-hour ceiling spends. Skipping the ritual leaks
rumination into the evening and taxes tomorrow's first — best — deep block.
The `assets/shutdown_checklist.md` template operationalizes exactly this sequence, and
`focus_session_logger.py status` gives the ritual its review step: hours banked this week vs the
target, and the streak worth protecting.
## Sources
1. Cal Newport, *Deep Work*, Rule #4 "Drain the Shallows" (Grand Central, 2016) — the shallow-work budget, the 30-50% band, the recent-graduate question, and the shutdown ritual ("shutdown complete").
2. Bluma Zeigarnik, "Über das Behalten von erledigten und unerledigten Handlungen" ("On Finished and Unfinished Tasks"), *Psychologische Forschung* 9, 1927 — unfinished tasks intrude on memory.
3. E. J. Masicampo and Roy F. Baumeister, "Consider It Done! Plan Making Can Eliminate the Cognitive Effects of Unfulfilled Goals," *Journal of Personality and Social Psychology* 101(4), 2011 — making a plan quiets the loop without finishing the task.
4. RescueTime research reports on knowledge-worker attention (rescuetime.com/blog) — ~2h48m of daily productive device time; communication checks about every 6 minutes; rarity of hour-plus focus stretches.
5. Leslie A. Perlow and Jessica L. Porter, "Making Time Off Predictable — and Required," *Harvard Business Review*, October 2009 (and Perlow, *Sleeping with Your Smartphone*, HBR Press, 2012) — predictable, protected off-time improves both wellbeing and output.
6. Atlassian, "You Waste a Lot of Time at Work" workplace research (atlassian.com/time-wasting-at-work-infographic) — meeting load (~62/month) and the cost of unnecessary coordination overhead.

View file

@ -0,0 +1,88 @@
# The Time-Blocking Method — Plan Every Minute, Revise Without Guilt
> Reference for `time_block_planner.py`. The mechanics of turning a task list into a
> time-blocked day: why every minute gets a job, how big blocks should be, why buffers and
> revision are part of the method (not failures of it), and why the day needs a hard stop.
## 1. Plan every minute
Newport's core scheduling rule: **give every minute of the workday a job.** At the start of the
day (or the evening before), divide the hours into blocks and assign each block to an activity —
including lunch, buffers, and an explicit "flex" block. The point is not rigidity; it is that an
unassigned minute defaults to the path of least resistance, which is almost always shallow:
inboxes, feeds, "quick checks."
Newport's own estimate from years of running this system: a time-blocked hour is worth roughly
**1.5-2x** an unstructured hour of the same nominal work, because decisions about *what to do
next* are made once, in batch, instead of continuously under depletion.
The planner enforces this literally: its output timeline is gap-free from `--start` to `--end`.
Anything not claimed by a deep block, shallow batch, buffer, or lunch becomes a named **flex
block** — scheduled slack, not accidental drift.
## 2. Block sizes
- **Deep blocks: 90 minutes minimum.** Entry into real concentration is slow (see the canon
reference on flow and attention residue); a 30-minute "deep" block spends most of itself
ramping. The planner widens any deep task below 90 minutes up to the floor and says so.
- **Shallow batches: consolidated, not sprinkled.** Shallow tasks are individually small; their
cost is the switch, not the task. Batching them into one late-morning and one end-of-day window
converts a dozen context switches into two.
- **Buffers: 10 minutes between work blocks.** Attention residue needs somewhere to drain.
Buffers are also where the tiny physical resets live — stand up, water, close the previous
block's tabs.
## 3. Implementation intentions — why written blocks beat willpower
The psychology underneath time-blocking is Gollwitzer's research on **implementation intentions**
(1999): plans of the form "at time X in situation Y, I will do Z" dramatically outperform bare
goal intentions ("I'll write the spec this week"). Meta-analytic effect sizes are medium-to-large
(d ≈ .65 across ~94 studies in Gollwitzer & Sheeran's later review). A time-block is exactly an
implementation intention: the when and where are pre-decided, so the depleted 2 p.m. self doesn't
renegotiate the plan — it just follows it.
Nir Eyal (*Indistractable*) builds his "timeboxing" chapter on the same foundation and adds the
inversion that matters for auditing: **"you can't call something a distraction unless you know
what it distracted you from."** Without a time-blocked plan there is no such thing as
off-plan behavior — which is why the planner runs *after* the shallow audit has decided what
deserves time at all.
## 4. Rescheduling without guilt
Every practitioner's day breaks by mid-morning. Newport's rule (*The Time-Block Planner*): the
plan's value survives its own destruction. When a block is blown up — an incident, an overrun, a
surprise call — you do not abandon the system; you **redraw the remaining blocks at the next free
moment**. His planner pages literally provide columns for multiple revisions of the same day. The
goal, in his words, is not to win a fight against a changing schedule; it is "to maintain, at all
times, a thoughtful say in what you're doing with your time going forward."
Two disciplines follow:
1. **Revision is normal.** Re-run the planner with the surviving tasks and the current time as
`--start`. Same rules, shorter day.
2. **Overflow is a decision, not an accident.** If the redrawn day no longer fits, something gets
deferred by name. The planner's exit-2 overflow refusal forces that choice to be explicit.
## 5. Fixed-schedule productivity — the hard stop
Newport's "fixed-schedule productivity" (first described on his Study Hacks blog, later formalized
in *Deep Work*, Rule #4): **fix the endpoint of the workday first — then work backward from it.**
The hard stop functions as a forcing constraint: it makes shallow-work budgets real, forces
ruthless triage of commitments, and converts "I'll just stay late" from a safety valve into a
visible system failure.
Parkinson's observation (*The Economist*, 1955) — "work expands so as to fill the time available
for its completion" — is the mechanism the hard stop exploits in reverse: bounded time compresses
work back toward its true size. This is why `--end` in the planner is immovable and why shallow
overflow past it is a refusal (exit 2, naming what to defer) rather than a silent extension of the
evening.
## Sources
1. Cal Newport, *The Time-Block Planner: A Daily Method for Deep Work in a Distracted World* (Portfolio, 2020) — plan every minute, revision columns, the 1.5-2x estimate.
2. Cal Newport, *Deep Work*, Rule #4 "Drain the Shallows" (Grand Central, 2016) — schedule every minute; fixed-schedule productivity as a forcing function.
3. Cal Newport, "Fixed-Schedule Productivity: How I Accomplish a Large Amount of Work in a Small Number of Work Hours" (Study Hacks blog, calnewport.com, 2008) — the original formulation of the hard stop.
4. Peter M. Gollwitzer, "Implementation Intentions: Strong Effects of Simple Plans," *American Psychologist* 54(7), 1999; and Gollwitzer & Sheeran, "Implementation Intentions and Goal Achievement: A Meta-analysis," *Advances in Experimental Social Psychology* 38, 2006.
5. Nir Eyal, *Indistractable: How to Control Your Attention and Choose Your Life* (BenBella, 2019) — the timeboxing chapter; "you can't call something a distraction unless you know what it distracted you from."
6. C. Northcote Parkinson, "Parkinson's Law," *The Economist*, November 1955 — work expands to fill the time available.
7. Paul Graham, "Maker's Schedule, Manager's Schedule" (paulgraham.com, 2009) — why deep blocks must be defended as contiguous half-day units.

View file

@ -0,0 +1,253 @@
#!/usr/bin/env python3
"""focus_session_logger.py — JSON-backed focus-session log: deep hours this week, streaks, targets.
You cannot improve a deep-work practice you do not measure. This script keeps a tiny local
ledger of focus sessions and answers two questions:
status how many deep hours this week (ISO week, Mon-Sun) vs --target (default 15)?
streak how many consecutive days (ending today, with a one-day grace) have at least
one logged session?
Subcommands:
log --minutes N --label X [--date YYYY-MM-DD] append one focus session
status [--target HOURS] [--date YYYY-MM-DD] this week's deep hours vs target
streak [--date YYYY-MM-DD] consecutive-day streak
State lives in a single JSON file (--state, default ~/.deep-work/sessions.json), created on
demand. Writes are atomic (tmp file + os.replace) so a crash never corrupts the ledger.
The top-level --sample flag prints a canned status report WITHOUT touching disk.
NO LLM CALLS. Stdlib only. Deterministic given the same state file and --date.
Exit codes:
0 success (log appended / report printed)
1 usage error, bad date, or unreadable/corrupt state file
Usage:
python focus_session_logger.py log --minutes 90 --label "Write product spec"
python focus_session_logger.py status --target 15
python focus_session_logger.py streak
python focus_session_logger.py --sample
"""
import argparse
import datetime as dt
import json
import os
import sys
import tempfile
from typing import Any, Dict, List, Tuple
DEFAULT_STATE = os.path.join(os.path.expanduser("~"), ".deep-work", "sessions.json")
DEFAULT_TARGET_HOURS = 15.0
def parse_date(value: str, flag: str) -> dt.date:
try:
return dt.date.fromisoformat(value)
except ValueError:
raise ValueError(f"{flag} must be YYYY-MM-DD, got: {value!r}")
def load_state(path: str) -> Dict[str, Any]:
if not os.path.exists(path):
return {"sessions": []}
try:
with open(path, "r", encoding="utf-8") as f:
state = json.load(f)
except (OSError, json.JSONDecodeError) as exc:
raise ValueError(f"cannot read state file {path}: {exc}")
if not isinstance(state, dict) or not isinstance(state.get("sessions"), list):
raise ValueError(f"state file {path} is not a valid session ledger")
return state
def save_state(path: str, state: Dict[str, Any]) -> None:
"""Atomic write: temp file in the same directory, then os.replace."""
directory = os.path.dirname(os.path.abspath(path))
os.makedirs(directory, exist_ok=True)
fd, tmp = tempfile.mkstemp(prefix=".sessions-", suffix=".tmp", dir=directory)
try:
with os.fdopen(fd, "w", encoding="utf-8") as f:
json.dump(state, f, indent=2)
f.write("\n")
os.replace(tmp, path)
except BaseException:
if os.path.exists(tmp):
os.unlink(tmp)
raise
def week_bounds(day: dt.date) -> Tuple[dt.date, dt.date]:
monday = day - dt.timedelta(days=day.weekday())
return monday, monday + dt.timedelta(days=6)
def cmd_log(state: Dict[str, Any], minutes: int, label: str, day: dt.date) -> Dict[str, Any]:
state["sessions"].append({"date": day.isoformat(), "minutes": minutes, "label": label})
total_today = sum(s["minutes"] for s in state["sessions"] if s["date"] == day.isoformat())
return {"logged": {"date": day.isoformat(), "minutes": minutes, "label": label},
"sessions_total": len(state["sessions"]),
"minutes_today": total_today}
def cmd_status(state: Dict[str, Any], target_hours: float, day: dt.date) -> Dict[str, Any]:
monday, sunday = week_bounds(day)
week = [s for s in state["sessions"]
if monday.isoformat() <= s["date"] <= sunday.isoformat()]
minutes = sum(s["minutes"] for s in week)
hours = round(minutes / 60.0, 2)
by_day: Dict[str, int] = {}
for s in week:
by_day[s["date"]] = by_day.get(s["date"], 0) + s["minutes"]
remaining = round(max(0.0, target_hours - hours), 2)
on_track = hours >= target_hours
return {
"week": f"{monday.isoformat()} .. {sunday.isoformat()}",
"as_of": day.isoformat(),
"sessions_this_week": len(week),
"deep_hours_this_week": hours,
"target_hours": target_hours,
"remaining_hours": remaining,
"on_track": on_track,
"by_day_minutes": dict(sorted(by_day.items())),
"headline": (f"{hours}h of deep work this week vs a {target_hours:g}h target — "
+ ("target met. Protect the streak."
if on_track else f"{remaining}h still to block.")),
}
def cmd_streak(state: Dict[str, Any], day: dt.date) -> Dict[str, Any]:
days = {s["date"] for s in state["sessions"]}
cursor = day
if cursor.isoformat() not in days: # one-day grace: today not logged yet
cursor = cursor - dt.timedelta(days=1)
streak = 0
while cursor.isoformat() in days:
streak += 1
cursor = cursor - dt.timedelta(days=1)
return {
"as_of": day.isoformat(),
"streak_days": streak,
"headline": (f"{streak} consecutive day(s) with at least one focus session."
if streak else "No active streak — one 90-minute block today starts it."),
}
def render_human(kind: str, r: Dict[str, Any]) -> str:
out: List[str] = []
if kind == "log":
s = r["logged"]
out.append(f"Logged: {s['minutes']} min — \"{s['label']}\" on {s['date']}")
out.append(f" Today so far: {r['minutes_today']} min · Ledger total: {r['sessions_total']} session(s)")
elif kind == "status":
out.append(f"Deep-Work Status — week {r['week']} (as of {r['as_of']})")
out.append("=" * 64)
tail = "ON TRACK" if r["on_track"] else f"{r['remaining_hours']}h remaining"
out.append(f" Deep hours: {r['deep_hours_this_week']}h / {r['target_hours']:g}h target ({tail})")
for d, m in r["by_day_minutes"].items():
out.append(f" {d}: {m} min")
out.append(f" {r['headline']}")
else: # streak
out.append(f"Deep-Work Streak (as of {r['as_of']})")
out.append("=" * 64)
out.append(f" {r['headline']}")
return "\n".join(out)
SAMPLE_STATUS = {
"kind": "status",
"sample": True,
"week": "2026-07-13 .. 2026-07-19",
"as_of": "2026-07-16",
"deep_hours_this_week": 8.5,
"target_hours": 15.0,
"on_track": False,
"remaining_hours": 6.5,
"by_day_minutes": {"2026-07-13": 120, "2026-07-14": 180,
"2026-07-15": 90, "2026-07-16": 120},
"streak_days": 4,
"headline": "8.5h of deep work this week vs a 15h target — 6.5h still to block.",
}
SAMPLE_REPORT = """Deep-Work Status — week 2026-07-13 .. 2026-07-19 (sample, no disk touched)
================================================================
Deep hours: 8.5h / 15h target (6.5h remaining)
2026-07-13: 120 min
2026-07-14: 180 min
2026-07-15: 90 min
2026-07-16: 120 min
8.5h of deep work this week vs a 15h target 6.5h still to block.
Streak: 4 consecutive day(s) with at least one focus session."""
def main(argv: List[str]) -> int:
p = argparse.ArgumentParser(
description="JSON-backed focus-session log: deep hours this week, streaks, targets.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=("Exit codes:\n"
" 0 success (log appended / report printed)\n"
" 1 usage error, bad date, or unreadable/corrupt state file"),
)
p.add_argument("--state", default=DEFAULT_STATE,
help=f"Path to the JSON ledger (default {DEFAULT_STATE})")
p.add_argument("--sample", action="store_true",
help="Print a canned status report without touching disk")
p.add_argument("--json", action="store_true",
help="Emit JSON instead of the report (also honored with --sample)")
common = argparse.ArgumentParser(add_help=False)
common.add_argument("--json", action="store_true", help="Emit JSON instead of the report")
sub = p.add_subparsers(dest="cmd")
p_log = sub.add_parser("log", help="Append one focus session", parents=[common])
p_log.add_argument("--minutes", type=int, required=True, help="Session length in minutes")
p_log.add_argument("--label", default="deep work", help="What the session was spent on")
p_log.add_argument("--date", help="Session date YYYY-MM-DD (default today)")
p_status = sub.add_parser("status", help="This week's deep hours vs target", parents=[common])
p_status.add_argument("--target", type=float, default=DEFAULT_TARGET_HOURS,
help=f"Weekly deep-hours target (default {DEFAULT_TARGET_HOURS:g})")
p_status.add_argument("--date", help="As-of date YYYY-MM-DD (default today)")
p_streak = sub.add_parser("streak", help="Consecutive days with at least one session", parents=[common])
p_streak.add_argument("--date", help="As-of date YYYY-MM-DD (default today)")
args = p.parse_args(argv)
if args.sample:
if args.json:
print(json.dumps(SAMPLE_STATUS, indent=2))
else:
print(SAMPLE_REPORT)
return 0
if not args.cmd:
p.print_help()
print("\nerror: a subcommand is required (log / status / streak) or --sample",
file=sys.stderr)
return 1
try:
day = parse_date(args.date, "--date") if getattr(args, "date", None) else dt.date.today()
state = load_state(args.state)
if args.cmd == "log":
if args.minutes <= 0:
raise ValueError("--minutes must be a positive integer")
result = cmd_log(state, args.minutes, args.label, day)
save_state(args.state, state)
elif args.cmd == "status":
result = cmd_status(state, args.target, day)
else:
result = cmd_streak(state, day)
except ValueError as exc:
print(f"error: {exc}", file=sys.stderr)
return 1
if args.json:
print(json.dumps(result, indent=2))
else:
print(render_human(args.cmd, result))
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

View file

@ -0,0 +1,201 @@
#!/usr/bin/env python3
"""shallow_work_auditor.py — Classify tasks deep vs shallow and audit the shallow share against a budget.
Encodes Cal Newport's shallow-work budget discipline:
1. Every task is classified DEEP or SHALLOW by keyword heuristics on its name:
shallow signals: email, inbox, slack, status, standup, meeting, expense, invoice,
scheduling, calendar, admin, paperwork, timesheet, errand, triage
deep signals: write, draft, design, code, build, research, analyze, study,
architect, prototype, prove, strategy, model
A tie (or no signal at all) counts as SHALLOW depth must be claimed deliberately.
Override any task with an explicit :deep or :shallow suffix; the suffix always wins.
2. The shallow share of total hours is compared against --budget percent (default 50
the top of Newport's 30-50 percent band for most knowledge roles).
3. For every shallow item, the forcing question is printed:
"How long would it take to train a smart recent graduate to do this?"
Months of training -> it is probably deep; days or weeks -> it is shallow. Batch it,
delegate it, or say no to it.
Verdicts:
WITHIN-BUDGET (exit 0) shallow share <= budget
OVER-BUDGET (exit 2) shallow share > budget cut, batch, or delegate before planning the day
NO LLM CALLS. Deterministic keyword scoring. Identical inputs, identical verdict.
Exit codes:
0 WITHIN-BUDGET
1 usage / input error (bad task spec)
2 OVER-BUDGET
Usage:
python shallow_work_auditor.py --task "Write investor update:60" \\
--task "Email triage:45" --task "Analyze churn cohort:90:deep" --budget 50
python shallow_work_auditor.py --sample
python shallow_work_auditor.py --sample --json
"""
import argparse
import json
import re
import sys
from typing import Any, Dict, List
SHALLOW_KEYWORDS = [
"email", "inbox", "slack", "status", "standup", "meeting", "expense", "invoice",
"scheduling", "schedule", "calendar", "admin", "paperwork", "timesheet", "errand",
"triage",
]
DEEP_KEYWORDS = [
"write", "writing", "draft", "design", "code", "coding", "program", "build",
"research", "analyze", "analysis", "study", "architect", "prototype", "prove",
"strategy", "model",
]
FORCING_QUESTION = ('How long would it take to train a smart recent graduate to do "{name}"? '
"Days or weeks -> shallow: batch it, delegate it, or say no.")
def parse_task(spec: str) -> Dict[str, Any]:
"""Accepts "name:minutes" (heuristic classifies) or "name:minutes:deep|shallow" (override)."""
parts = spec.rsplit(":", 2)
override = None
if len(parts) == 3 and parts[2].strip().lower() in ("deep", "shallow"):
name, minutes_s, override = parts[0], parts[1], parts[2].strip().lower()
else:
parts = spec.rsplit(":", 1)
if len(parts) != 2:
raise ValueError(f'task must be "name:minutes[:deep|shallow]", got: {spec!r}')
name, minutes_s = parts
name, minutes_s = name.strip(), minutes_s.strip()
if not name:
raise ValueError(f"task name is empty in: {spec!r}")
if not minutes_s.isdigit() or int(minutes_s) <= 0:
raise ValueError(f"task minutes must be a positive integer in: {spec!r}")
return {"name": name, "minutes": int(minutes_s), "override": override}
def classify(name: str) -> Dict[str, Any]:
lowered = name.lower()
deep_hits = [k for k in DEEP_KEYWORDS if re.search(rf"\b{re.escape(k)}\b", lowered)]
shallow_hits = [k for k in SHALLOW_KEYWORDS if re.search(rf"\b{re.escape(k)}\b", lowered)]
if len(deep_hits) > len(shallow_hits):
mode, why = "deep", f"deep signals: {', '.join(deep_hits)}"
elif shallow_hits:
mode, why = "shallow", f"shallow signals: {', '.join(shallow_hits)}"
else:
mode, why = "shallow", "no signal — unclassified counts as shallow; claim :deep explicitly if wrong"
return {"mode": mode, "why": why}
def audit(tasks: List[Dict[str, Any]], budget_pct: float) -> Dict[str, Any]:
items = []
for t in tasks:
if t["override"]:
mode, why = t["override"], f"explicit :{t['override']} override"
else:
c = classify(t["name"])
mode, why = c["mode"], c["why"]
item = {"name": t["name"], "minutes": t["minutes"], "mode": mode, "why": why}
if mode == "shallow":
item["forcing_question"] = FORCING_QUESTION.format(name=t["name"])
items.append(item)
total = sum(i["minutes"] for i in items)
shallow_min = sum(i["minutes"] for i in items if i["mode"] == "shallow")
deep_min = total - shallow_min
share = round(100.0 * shallow_min / total, 1) if total else 0.0
verdict = "WITHIN-BUDGET" if share <= budget_pct else "OVER-BUDGET"
return {
"items": items,
"total_minutes": total,
"deep_minutes": deep_min,
"shallow_minutes": shallow_min,
"shallow_share_pct": share,
"budget_pct": budget_pct,
"verdict": verdict,
"headline": (
f"Shallow work is {share}% of the day against a {budget_pct:.0f}% budget. "
+ ("Within budget — protect the deep hours you just earned."
if verdict == "WITHIN-BUDGET"
else "Over budget — cut, batch, or delegate shallow items before planning the day.")
),
}
def render_human(r: Dict[str, Any]) -> str:
out: List[str] = []
out.append("Shallow-Work Audit (deep vs shallow, share vs budget)")
out.append("=" * 64)
out.append("")
out.append("| Task | Min | Mode | Basis |")
out.append("|------|-----|------|-------|")
for i in r["items"]:
out.append(f"| {i['name']} | {i['minutes']} | {i['mode'].upper()} | {i['why']} |")
out.append("")
out.append(f" Deep {r['deep_minutes']} min · Shallow {r['shallow_minutes']} min · "
f"Shallow share {r['shallow_share_pct']}% vs budget {r['budget_pct']:.0f}%")
out.append("")
out.append(f" VERDICT: {r['verdict']}")
out.append(f" {r['headline']}")
shallow_items = [i for i in r["items"] if i["mode"] == "shallow"]
if shallow_items:
out.append("")
out.append(" The forcing question, per shallow item:")
for i in shallow_items:
out.append(f" - {i['forcing_question']}")
return "\n".join(out)
SAMPLE_TASKS = [
"Write investor update:60",
"Analyze churn cohort:90:deep",
"Email triage:45",
"Slack catch-up:30",
"Expense report:20",
"Team scheduling:15",
]
def main(argv: List[str]) -> int:
p = argparse.ArgumentParser(
description="Classify tasks deep vs shallow and audit the shallow share against a budget.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=("Exit codes:\n"
" 0 WITHIN-BUDGET — shallow share <= --budget\n"
" 1 usage / input error\n"
" 2 OVER-BUDGET — shallow share > --budget; cut, batch, or delegate first"),
)
p.add_argument("--task", action="append", default=[],
help='Repeatable: "name:minutes" (heuristic) or "name:minutes:deep|shallow" (override)')
p.add_argument("--budget", type=float, default=50.0,
help="Shallow budget as percent of total time (default 50 — top of the 30-50 band)")
p.add_argument("--json", action="store_true", help="Emit JSON instead of the table")
p.add_argument("--sample", action="store_true", help="Run the embedded sample task list")
args = p.parse_args(argv)
if args.sample:
args.task = SAMPLE_TASKS
if not args.task:
p.print_help()
print("\nerror: at least one --task is required (or --sample)", file=sys.stderr)
return 1
if not 0 < args.budget <= 100:
print("error: --budget must be in (0, 100]", file=sys.stderr)
return 1
try:
tasks = [parse_task(s) for s in args.task]
except ValueError as exc:
print(f"error: {exc}", file=sys.stderr)
return 1
result = audit(tasks, args.budget)
if args.json:
print(json.dumps(result, indent=2))
else:
print(render_human(result))
return 0 if result["verdict"] == "WITHIN-BUDGET" else 2
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

View file

@ -0,0 +1,300 @@
#!/usr/bin/env python3
"""time_block_planner.py — Build a time-blocked day: deep work first, shallow batched, buffers everywhere.
Encodes Cal Newport's time-block planning discipline as deterministic scheduling:
1. Deep tasks are scheduled FIRST, in the earliest hours of the day (energy-first).
Each deep task gets a block of at least 90 minutes shorter blocks never reach depth.
2. Total deep time is capped at 4 hours (240 min) the trained practitioner's daily
ceiling. Demand beyond the cap is not squeezed in; the planner REFUSES (exit 2)
and names exactly which deep tasks to cut or defer.
3. Shallow tasks are batched into at most two batches: one late morning (right after
the deep blocks) and one at the end of the day. A single shallow task goes to the
end-of-day batch so the morning stays protected.
4. A 10-minute buffer separates consecutive work blocks (attention residue is real).
5. An optional --lunch HH:MM inserts a fixed 30-minute break; blocks never straddle it.
6. Shallow work that spills past --end is an overflow: the planner REFUSES (exit 2)
and names the overflow instead of silently extending the day.
NO LLM CALLS. Pure arithmetic. Identical inputs always produce the identical plan.
Exit codes:
0 plan fits markdown schedule (or --json) emitted
1 usage / input error (bad time format, bad task spec, end <= start)
2 refusal deep demand exceeds the 4-hour cap, or the day overflows --end
Usage:
python time_block_planner.py --start 08:30 --end 17:00 --lunch 12:30 \\
--task "Write product spec:120:deep" --task "Email sweep:30:shallow"
python time_block_planner.py --sample
python time_block_planner.py --sample --json
"""
import argparse
import json
import sys
from typing import Any, Dict, List, Optional
DEEP_CAP_MIN = 240 # Newport's trained-limit ceiling: ~4 hours of deep work per day
MIN_DEEP_BLOCK = 90 # a deep block shorter than this never reaches depth
BUFFER_MIN = 10 # context-switch buffer between consecutive work blocks
LUNCH_MIN = 30 # fixed lunch break length
def parse_hhmm(value: str, flag: str) -> int:
parts = value.split(":")
if len(parts) != 2 or not parts[0].isdigit() or not parts[1].isdigit():
raise ValueError(f"{flag} must be HH:MM, got: {value!r}")
h, m = int(parts[0]), int(parts[1])
if h > 23 or m > 59:
raise ValueError(f"{flag} out of range: {value!r}")
return h * 60 + m
def fmt(minutes: int) -> str:
return f"{minutes // 60:02d}:{minutes % 60:02d}"
def fmt_dur(minutes: int) -> str:
h, m = divmod(minutes, 60)
if h and m:
return f"{h}h{m:02d}"
if h:
return f"{h}h"
return f"{m}min"
def parse_task(spec: str) -> Dict[str, Any]:
parts = spec.rsplit(":", 2)
if len(parts) != 3:
raise ValueError(f'task must be "name:minutes:deep|shallow", got: {spec!r}')
name, minutes_s, mode = parts[0].strip(), parts[1].strip(), parts[2].strip().lower()
if not name:
raise ValueError(f"task name is empty in: {spec!r}")
if not minutes_s.isdigit() or int(minutes_s) <= 0:
raise ValueError(f"task minutes must be a positive integer in: {spec!r}")
if mode not in ("deep", "shallow"):
raise ValueError(f'task mode must be "deep" or "shallow" in: {spec!r}')
return {"name": name, "minutes": int(minutes_s), "mode": mode}
def _ev(start: int, end: int, label: str, kind: str) -> Dict[str, Any]:
return {"start": start, "end": end, "label": label, "kind": kind,
"minutes": end - start}
def plan(start: int, end: int, tasks: List[Dict[str, Any]],
lunch: Optional[int]) -> Dict[str, Any]:
"""Return a plan dict. verdict is PLANNED, DEEP-CAP-EXCEEDED, or OVERFLOW."""
deep = [t for t in tasks if t["mode"] == "deep"]
shallow = [t for t in tasks if t["mode"] == "shallow"]
notes: List[str] = []
# --- 1. Deep blocks: at least 90 min each, earliest hours, capped at 4h total ---
deep_blocks = []
for t in deep:
dur = max(MIN_DEEP_BLOCK, t["minutes"])
if dur > t["minutes"]:
notes.append(f'"{t["name"]}" widened from {t["minutes"]} to {dur} min '
f"(deep blocks below {MIN_DEEP_BLOCK} min never reach depth).")
deep_blocks.append({"name": t["name"], "dur": dur})
total_deep = sum(b["dur"] for b in deep_blocks)
if total_deep > DEEP_CAP_MIN:
acc, cut = 0, []
for b in deep_blocks:
if acc + b["dur"] <= DEEP_CAP_MIN:
acc += b["dur"]
else:
cut.append(b["name"])
return {
"verdict": "DEEP-CAP-EXCEEDED",
"reason": (f"Deep demand is {fmt_dur(total_deep)} but the trained daily ceiling "
f"is {fmt_dur(DEEP_CAP_MIN)}. Depth past the cap is fake depth — "
f"cut or defer to another day: " + ", ".join(f'"{n}"' for n in cut)),
"deep_demand_minutes": total_deep,
"deep_cap_minutes": DEEP_CAP_MIN,
"cut_or_defer": cut,
}
# --- 2. Shallow batches: at most two (late morning + end of day) ---
if len(shallow) >= 2:
n_a = (len(shallow) + 1) // 2
batch_a, batch_b = shallow[:n_a], shallow[n_a:]
elif len(shallow) == 1:
batch_a, batch_b = [], shallow[:] # single batch goes end-of-day
else:
batch_a, batch_b = [], []
dur_a = sum(t["minutes"] for t in batch_a)
dur_b = sum(t["minutes"] for t in batch_b)
# --- 3. Forward pass: deep blocks, then shallow batch A, lunch-aware ---
events: List[Dict[str, Any]] = []
ctx = {"lunch": lunch, "lunch_placed": lunch is None}
cursor = start
def advance(cursor: int, dur: int, label: str, kind: str) -> int:
if events and events[-1]["kind"] in ("deep", "shallow"):
events.append(_ev(cursor, cursor + BUFFER_MIN, "Buffer — stand up, reset", "buffer"))
cursor += BUFFER_MIN
if not ctx["lunch_placed"]:
if cursor >= ctx["lunch"]:
events.append(_ev(cursor, cursor + LUNCH_MIN, "Lunch — away from the desk", "break"))
cursor += LUNCH_MIN
ctx["lunch_placed"] = True
elif cursor + dur > ctx["lunch"]:
if ctx["lunch"] > cursor:
events.append(_ev(cursor, ctx["lunch"], "Flex — reset, no inputs", "flex"))
events.append(_ev(ctx["lunch"], ctx["lunch"] + LUNCH_MIN,
"Lunch — away from the desk", "break"))
cursor = ctx["lunch"] + LUNCH_MIN
ctx["lunch_placed"] = True
events.append(_ev(cursor, cursor + dur, label, kind))
return cursor + dur
for b in deep_blocks:
cursor = advance(cursor, b["dur"], f'DEEP — {b["name"]}', "deep")
if batch_a:
label_a = "SHALLOW batch (late morning) — " + " · ".join(t["name"] for t in batch_a)
cursor = advance(cursor, dur_a, label_a, "shallow")
# --- 4. Backward pass: shallow batch B ends exactly at --end ---
# The 10-min buffer invariant applies before batch B too: when batch B would
# directly follow a work block, that buffer is part of the day's budget.
need_buffer = (BUFFER_MIN if batch_b and events and events[-1]["kind"] in ("deep", "shallow")
else 0)
if cursor + need_buffer + dur_b > end:
overflow = cursor + need_buffer + dur_b - end
return {
"verdict": "OVERFLOW",
"reason": (f"The day overflows {fmt(end)} by {overflow} min. "
+ (("Defer shallow work instead of extending the day: "
+ ", ".join(f'"{t["name"]}"' for t in (batch_b or batch_a)))
if (batch_b or batch_a) else
"Deep demand alone exceeds the day — trim a deep block or extend --end.")),
"overflow_minutes": overflow,
"defer_candidates": [t["name"] for t in (batch_b or batch_a)],
}
if need_buffer:
events.append(_ev(cursor, cursor + BUFFER_MIN, "Buffer — stand up, reset", "buffer"))
cursor += BUFFER_MIN
start_b = end - dur_b
if start_b > cursor:
if not ctx["lunch_placed"] and cursor <= ctx["lunch"] and ctx["lunch"] + LUNCH_MIN <= start_b:
if ctx["lunch"] > cursor:
events.append(_ev(cursor, ctx["lunch"], "Flex — overflow absorber", "flex"))
events.append(_ev(ctx["lunch"], ctx["lunch"] + LUNCH_MIN,
"Lunch — away from the desk", "break"))
cursor = ctx["lunch"] + LUNCH_MIN
ctx["lunch_placed"] = True
if start_b > cursor:
events.append(_ev(cursor, start_b, "Flex — overflow absorber", "flex"))
if not ctx["lunch_placed"]:
notes.append(f"Lunch could not be placed at {fmt(lunch)} without splitting a block — "
"move it or shorten a block.")
if batch_b:
label_b = "SHALLOW batch (end of day) — " + " · ".join(t["name"] for t in batch_b)
events.append(_ev(start_b, end, label_b, "shallow"))
totals = {}
for e in events:
totals[e["kind"]] = totals.get(e["kind"], 0) + e["minutes"]
return {
"verdict": "PLANNED",
"start": fmt(start),
"end": fmt(end),
"events": [{"start": fmt(e["start"]), "end": fmt(e["end"]),
"label": e["label"], "kind": e["kind"], "minutes": e["minutes"]}
for e in events],
"totals_minutes": totals,
"deep_total_minutes": totals.get("deep", 0),
"deep_cap_minutes": DEEP_CAP_MIN,
"notes": notes,
}
def render_human(r: Dict[str, Any]) -> str:
out: List[str] = []
if r["verdict"] != "PLANNED":
out.append(f"REFUSED — {r['verdict']}")
out.append("=" * 64)
out.append(f" {r['reason']}")
return "\n".join(out)
out.append(f"## Time-Block Plan — {r['start']}{r['end']}")
out.append("")
out.append("| Start | End | Block | Mode |")
out.append("|-------|-----|-------|------|")
for e in r["events"]:
out.append(f"| {e['start']} | {e['end']} | {e['label']} | {e['kind'].upper()} |")
out.append("")
t = r["totals_minutes"]
out.append(f"Deep {fmt_dur(t.get('deep', 0))} / {fmt_dur(r['deep_cap_minutes'])} cap · "
f"Shallow {fmt_dur(t.get('shallow', 0))} · "
f"Flex {fmt_dur(t.get('flex', 0))} · "
f"Buffers {fmt_dur(t.get('buffer', 0))}")
for n in r["notes"]:
out.append(f"- Note: {n}")
out.append("")
out.append("Flex absorbs what the plan didn't foresee — when the day breaks, "
"revise the blocks; never abandon them.")
return "\n".join(out)
SAMPLE = {
"start": "08:30", "end": "17:00", "lunch": "12:30",
"tasks": [
"Write product spec:120:deep",
"Design onboarding flow:90:deep",
"Email sweep:30:shallow",
"Team status update:20:shallow",
"Expense report:15:shallow",
],
}
def main(argv: List[str]) -> int:
p = argparse.ArgumentParser(
description="Build a time-blocked day: deep work first, shallow batched, buffers everywhere.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=("Exit codes:\n"
" 0 plan fits — schedule emitted\n"
" 1 usage / input error\n"
" 2 refusal — deep demand exceeds the 4-hour cap, or the day overflows --end"),
)
p.add_argument("--start", help="Day start, HH:MM")
p.add_argument("--end", help="Day end, HH:MM (hard stop — fixed-schedule productivity)")
p.add_argument("--task", action="append", default=[],
help='Repeatable: "name:minutes:deep|shallow"')
p.add_argument("--lunch", help="Optional lunch start, HH:MM (fixed 30-min break)")
p.add_argument("--json", action="store_true", help="Emit JSON instead of markdown")
p.add_argument("--sample", action="store_true", help="Run the embedded sample day")
args = p.parse_args(argv)
if args.sample:
args.start, args.end = SAMPLE["start"], SAMPLE["end"]
args.lunch, args.task = SAMPLE["lunch"], SAMPLE["tasks"]
if not args.start or not args.end or not args.task:
p.print_help()
print("\nerror: --start, --end and at least one --task are required (or --sample)",
file=sys.stderr)
return 1
try:
start = parse_hhmm(args.start, "--start")
end = parse_hhmm(args.end, "--end")
lunch = parse_hhmm(args.lunch, "--lunch") if args.lunch else None
if end <= start:
raise ValueError("--end must be after --start")
tasks = [parse_task(s) for s in args.task]
except ValueError as exc:
print(f"error: {exc}", file=sys.stderr)
return 1
result = plan(start, end, tasks, lunch)
if args.json:
print(json.dumps(result, indent=2))
else:
print(render_human(result))
return 0 if result["verdict"] == "PLANNED" else 2
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

View file

@ -0,0 +1,19 @@
{
"name": "meetings",
"description": "Personal meeting hygiene, end to end. Before the meeting: a cost gate that prices the meeting in real dollars (attendees x minutes x rate, optionally + the 23-minute refocus overhead per attendee) and refuses to let it exist without a decision, an agenda, and a named owner — verdicts are ASYNC / NOT-READY / MEET, never a shrug. Then a timeboxed agenda builder that rejects any topic without a desired outcome, orders decision topics first, enforces a 5-minute closing actions-recap buffer, and refuses overflowing agendas by name. After the meeting: a deterministic action-item extractor that turns raw notes into an owner + due-date checklist and flags ORPHAN (no owner) and NO-DUE items, because an action item without an owner and a date is theater. Built on the Rogelberg / HBR / Grove meeting-science canon with stdlib-only scripts.",
"version": "2.11.2",
"author": {
"name": "Alireza Rezvani",
"url": "https://alirezarezvani.com"
},
"homepage": "https://github.com/alirezarezvani/claude-skills/tree/main/productivity/meetings",
"repository": "https://github.com/alirezarezvani/claude-skills",
"license": "MIT",
"skills": [
"./skills/meetings"
],
"source": {
"build_pattern": "Path-B discipline skill — Rogelberg/HBR meeting-science canon + deterministic cost-gate/agenda/action-extraction scripts",
"distinct_from": "project-management (team ceremonies, sprint planning, Jira delivery flow — this is personal meeting hygiene for the individual calling or attending the meeting); business-operations/internal-comms (org-level communication channel design — this gates one meeting at a time and never auto-sends anything); productivity/capture (personal brain-dump triage — this extracts owned actions from a shared meeting's notes)"
}
}

View file

@ -0,0 +1,60 @@
# meetings — Cost Gate → Timeboxed Agenda → Owned Actions
> Most meetings should be an email. This plugin makes that a testable claim instead of a complaint.
`/cs:meeting-prep` prices a meeting in real dollars and refuses to let it exist without a decision,
an agenda, and an owner. If it survives the gate, it gets a timeboxed, decision-first agenda where
every topic has a desired outcome. `/cs:meeting-actions` then turns the raw notes into an
owner + due-date checklist — and flags every orphan, because an action item without an owner and a
date is theater.
## The three disciplines
| Stage | The question it answers | Script |
|---|---|---|
| **The Gate** | "Should this meeting exist at all?" — cost + decision/agenda/owner checks | `meeting_cost_calculator.py` |
| **The Agenda** | "What will we decide, in what order, in how many minutes?" | `agenda_builder.py` |
| **The Extraction** | "Who owes what, by when?" — orphan and no-due flagging | `action_item_extractor.py` |
The gate returns one of three verdicts: **ASYNC** (no decision needed — send a memo instead),
**NOT-READY** (decision exists but the agenda or owner is missing — names what's missing), or
**MEET** (with the total cost and a cost-per-minute line so timeboxes get budgeted like money).
## Quick start
```bash
# 1. Gate the meeting — should it exist?
python skills/meetings/scripts/meeting_cost_calculator.py --sample
# 2. Build the timeboxed, decision-first agenda
python skills/meetings/scripts/agenda_builder.py --sample
# 3. After the meeting: extract owned action items from raw notes
python skills/meetings/scripts/action_item_extractor.py --sample
```
Or just say **"should this be a meeting?"** / **"pull the action items out of these notes"**, or run
`/cs:meeting-prep` and `/cs:meeting-actions`.
## What's in the box
- **Skill:** [`skills/meetings/SKILL.md`](skills/meetings/SKILL.md)
- **Agent:** [`agents/cs-meeting-discipline.md`](agents/cs-meeting-discipline.md)
- **Commands:** [`commands/cs-meeting-prep.md`](commands/cs-meeting-prep.md) — `/cs:meeting-prep` ·
[`commands/cs-meeting-actions.md`](commands/cs-meeting-actions.md) — `/cs:meeting-actions`
- **3 stdlib scripts:** `meeting_cost_calculator.py`, `agenda_builder.py`, `action_item_extractor.py`
- **3 references** (Rogelberg / HBR / Grove / Amazon canon, 5-7 sources each), **2 assets**
(worked timeboxed agenda + fillable should-this-be-a-meeting worksheet)
## Not the same as
- **`project-management/`** — team ceremonies, sprint cadence, Jira delivery flow. This is personal
meeting hygiene: one meeting, one gate, one agenda, one checklist.
- **`business-operations/internal-comms`** — org-level communication channel design. This never
designs a comms program and never auto-sends anything.
- **`productivity/capture`** — triages your own brain-dump. This extracts owned actions from a
shared meeting's notes.
## License
MIT.

View file

@ -0,0 +1,91 @@
---
name: cs-meeting-discipline
description: Enforces personal meeting hygiene end to end. Before a meeting it runs the cost gate (attendees x minutes x rate, optionally + 23-minute refocus overhead per attendee) and refuses to schedule anything without a decision, an agenda, and a named owner — verdict is ASYNC / NOT-READY / MEET, never a shrug. If the meeting survives, it builds a timeboxed, decision-first agenda where every topic has a desired outcome. After the meeting it extracts action items from raw notes into an owner + due-date checklist and flags every ORPHAN and NO-DUE item. Use before calling a meeting or after one ends with messy notes.
skills: productivity/meetings/skills/meetings
domain: productivity
model: opus
tools: [Read, Bash]
---
# Meeting Discipline Agent
## Purpose
The `cs-meeting-discipline` agent orchestrates the `meetings` skill to keep one person's calendar
honest — before a meeting is called, and after it ends:
1. **Gate** — price the meeting (`meeting_cost_calculator.py`): attendees × minutes × hourly rate,
optionally + the 23-minute refocus overhead per attendee. Then apply the three checks: is there a
decision to make? is there an agenda? is there a named owner? Verdicts:
- **ASYNC** (exit 2) — no decision needed; this is a status update. Recommend a memo/thread instead.
- **NOT-READY** (exit 3) — a decision exists but the agenda or owner is missing; name what's missing.
- **MEET** (exit 0) — all three present; print the total cost and the cost-per-minute line so
timeboxes get budgeted like money.
2. **Build the agenda** — only for a MEET verdict (`agenda_builder.py`): every topic needs a
desired outcome (refused by name otherwise), decision topics sort first, a 5-minute closing
"actions recap" buffer is enforced, and an overflowing agenda is refused with the exact overflow.
3. **Run** — the human runs the meeting. The agent's job here is only the pre-read reminder and the
printed agenda; it never joins, records, or sends anything.
4. **Extract** — after the meeting (`action_item_extractor.py`): parse the raw notes for checkboxes,
ACTION:/TODO: lines, "@name will …" and "Name will … by date" patterns; emit a markdown
checklist grouped by owner with summary counts; flag every **ORPHAN** (no owner) and **NO-DUE**
item so they get resolved before anyone leaves the thread.
5. **Deliver** — the gate verdict + cost, the timeboxed agenda (or the async recommendation), and
the owned-actions checklist with orphans called out for immediate assignment.
## Voice
- Blunt about cost. A 6-person hour costs real money; say the number before debating the invite list.
- "No decision, no meeting" is the default, not the exception. Recommending ASYNC is a win, not a failure.
- Zero tolerance for orphan actions. "Someone should…" is not an action item; a name and a date are.
## Hard rules
1. **Gate before agenda.** Never build an agenda for a meeting that hasn't passed the cost gate.
An ASYNC verdict ends the prep — draft the memo outline instead.
2. **No desired outcome, no agenda slot.** `agenda_builder.py` refuses topics with empty outcomes;
do not paraphrase around it — go back and get the outcome.
3. **Decisions first.** Decision topics (decide/choose/approve) sort before discuss/inform topics.
Do not reorder them back for politeness.
4. **Every action item has an owner and a date — or it is not an action item.** Surface every
ORPHAN and NO-DUE flag; never silently drop or auto-assign one.
5. **Never auto-send.** No calendar invites, no emails, no messages. Output is text the user sends.
## Skill Integration
**Skill Location:** `../skills/meetings/`
### Python Scripts (Stdlib)
1. **Meeting Cost Calculator**`skills/meetings/scripts/meeting_cost_calculator.py` — dollars +
refocus overhead + decision/agenda/owner gate → ASYNC / NOT-READY / MEET.
2. **Agenda Builder**`skills/meetings/scripts/agenda_builder.py` — timeboxed, decision-first
agenda; refuses empty outcomes and overflow; enforces the closing actions-recap buffer.
3. **Action Item Extractor**`skills/meetings/scripts/action_item_extractor.py` — raw notes →
owner-grouped checklist with ORPHAN / NO-DUE flags and summary counts.
### Knowledge Bases
- `skills/meetings/references/meeting_cost_canon.md` — the real cost of meetings and the
should-this-exist gate (Perlow/HBR, Rogelberg, Shopify, Bezos, Grove; 7 sources)
- `skills/meetings/references/agenda_discipline.md` — agendas as questions, timeboxing,
decision-first ordering, the owner role, pre-reads (Rogelberg, Parkinson, Sutherland, Grove; 7 sources)
- `skills/meetings/references/action_item_discipline.md` — why meetings without owned actions are
theater (Allen/GTD, Doran/SMART, Gollwitzer, Locke & Latham, DACI; 6 sources)
## Differentiates From Siblings
- **vs `project-management/`**: PM skills run team ceremonies and Jira delivery flow. This agent
gates one meeting at a time for the person calling it — personal hygiene, not delivery process.
- **vs `business-operations/internal-comms`**: internal-comms designs org-level communication
programs. This never designs a program and never auto-sends anything.
- **vs `cs-capture-triage`** (productivity/capture): capture triages your own brain-dump into
actions. This extracts owned actions from a shared meeting's notes and flags the orphans.
## Related Agents
- [cs-roast-judge](../../roast/agents/cs-roast-judge.md) — productivity sibling, adversarial idea panel
---
**Version:** 1.0.0

View file

@ -0,0 +1,81 @@
---
name: "cs-meeting-actions"
description: "/cs:meeting-actions — Turn raw meeting notes into an owned action-item checklist: extracts checkboxes, ACTION:/TODO: lines, '@name will …' and 'Name will … by date' patterns, groups by owner, and flags every ORPHAN (no owner) and NO-DUE item. An action without an owner and a date is not an action."
argument-hint: "[path to notes file, or paste the notes]"
---
# /cs:meeting-actions — Raw Notes → Owned Action Checklist
**Command:** `/cs:meeting-actions [notes file or pasted notes]`
A meeting that ends without owned, dated actions was theater. This command runs immediately after
the meeting — while attendees still remember what they agreed to — and turns the messy notes into a
checklist where every item has a name and a date, or is loudly flagged until it does.
## When to Run
- The meeting just ended and the notes are a wall of prose
- "Pull the action items out of these notes"
- "Who owes what from Thursday's meeting?"
- Before posting a meeting summary — so the summary leads with the actions
## When NOT to Run
- Before the meeting → use `/cs:meeting-prep` (cost gate + agenda)
- Triaging your own private brain-dump → `productivity/capture` owns that
- Turning actions into Jira issues and sprint work → `project-management/` owns delivery flow
## What You Get
1. **A markdown checklist grouped by owner** — each item with its due date where one was captured.
2. **ORPHAN flags** — every action with no owner, grouped under "(unassigned)" so they get claimed
before the thread goes cold.
3. **NO-DUE flags** — owned actions with no date, listed so a date gets attached now, not "later".
4. **Summary counts** — total actions · owned · orphaned · missing dates, in one line.
## Trigger Phrases (auto-invoke without /cs:)
- "extract the action items" / "pull out the actions"
- "who owes what" / "turn these notes into a checklist"
- "action items from this meeting"
## Discipline
- **Every action item has an owner and a date — or it is not an action item.** Flags are the output,
not noise; never silently drop or auto-assign an orphan.
- **Extraction is deterministic** — the script's patterns decide what counts; don't invent actions
the notes don't contain.
- **Orphans get resolved by a human** — present them for assignment; never guess an owner.
- **Never auto-send** — the checklist is text the user posts. No emails, no messages, no issues filed.
## Workflow
```bash
# From a notes file
python ../skills/meetings/scripts/action_item_extractor.py --input notes.md
# From pasted notes on stdin
cat notes.md | python ../skills/meetings/scripts/action_item_extractor.py
# Machine-readable, for piping into other checklists
python ../skills/meetings/scripts/action_item_extractor.py --input notes.md --json
```
Then walk the flags: assign every ORPHAN, date every NO-DUE, and post the checklist.
## Stop Conditions
- Checklist delivered, every ORPHAN either assigned by the user or explicitly left flagged → done.
- Zero actions extracted → say so plainly and ask whether the meeting actually decided anything
(that's a `/cs:meeting-prep` conversation for next time). Don't fabricate items.
- User says "just give me the list" → checklist + summary counts, no assignment walkthrough.
## Related
- Agent: [`cs-meeting-discipline`](../agents/cs-meeting-discipline.md)
- Skill: [`meetings`](../skills/meetings/SKILL.md)
- Sibling command: [`/cs:meeting-prep`](cs-meeting-prep.md) (pre-meeting gate + agenda)
---
**Version:** 1.0.0

View file

@ -0,0 +1,86 @@
---
name: "cs-meeting-prep"
description: "/cs:meeting-prep — Gate a meeting before it exists: price it in real dollars (attendees x minutes x rate + optional 23-minute refocus overhead), demand a decision + agenda + owner, then either recommend async or build a timeboxed, decision-first agenda where every topic has a desired outcome."
argument-hint: "[the meeting: who, how long, what decision]"
---
# /cs:meeting-prep — Cost Gate → Timeboxed Agenda (or Async)
**Command:** `/cs:meeting-prep [the meeting]`
Most meetings should be an email. This command makes that a testable claim: it prices the meeting,
runs the decision/agenda/owner gate, and only if the meeting survives does it build the timeboxed
agenda. An ASYNC verdict is a win — draft the memo instead.
## When to Run
- "Should this be a meeting?" / "Is this meeting worth it?"
- Before sending any invite with 3+ attendees
- "Build the agenda for Thursday's pricing meeting"
- You suspect a recurring meeting has outlived its decision
## When NOT to Run
- After the meeting, with notes in hand → use `/cs:meeting-actions`
- Sprint ceremonies, standups, and Jira delivery cadence → `project-management/` owns those
- Designing an org-wide comms program → `business-operations/internal-comms`
## What You Get
1. **The price** — direct cost (attendees × minutes × rate) plus, with `--include-refocus`, the
23-minute-per-attendee refocus overhead, and a cost-per-minute line.
2. **One gate verdict**`ASYNC` (no decision → send a memo; exit 2), `NOT-READY` (decision but
missing agenda/owner, named; exit 3), or `MEET` (exit 0).
3. **On MEET: a timeboxed agenda** — decision topics first, per-topic desired outcome + owner +
timebox, a pre-read line, and a mandatory 5-minute closing "actions recap" slot.
4. **On ASYNC: a memo outline** — the decision-free content restructured as a written update.
## Trigger Phrases (auto-invoke without /cs:)
- "should this be a meeting" / "does this need a meeting"
- "what does this meeting cost"
- "build a timeboxed agenda" / "prep this meeting"
- "can this be async"
## Discipline
- **Gate before agenda** — never build an agenda for a meeting that hasn't passed the gate.
- **No decision, no meeting** — status updates go async, every time.
- **No desired outcome, no agenda slot** — the builder refuses empty outcomes by name; get the outcome.
- **Decisions first** — decide/choose/approve topics sort before discuss/inform. Keep them there.
- **Timeboxes are budgets** — overflow + the 5-minute closing buffer gets refused with the exact overage.
## Workflow
```bash
# 1. Price + gate the meeting
python ../skills/meetings/scripts/meeting_cost_calculator.py \
--attendees 6 --minutes 60 --avg-rate 90 --include-refocus \
--has-decision --has-agenda --has-owner
# 2a. ASYNC (exit 2) → draft the memo outline instead. Stop here.
# 2b. NOT-READY (exit 3) → get the missing agenda/owner, re-run the gate.
# 3. MEET (exit 0) → build the timeboxed, decision-first agenda
python ../skills/meetings/scripts/agenda_builder.py --length 45 \
--topic "Q3 pricing:Decide usage-based vs seat-based:15:maria" \
--topic "Launch risks:Discuss open launch blockers:15:sam" \
--topic "Metrics:Inform team of activation trend:5:alex"
```
## Stop Conditions
- ASYNC verdict delivered + memo outline sketched → done. Do not build an agenda anyway.
- MEET verdict + agenda printed with pre-read line and closing recap slot → done.
- NOT-READY twice in a row on the same missing input → hand the gap to the user; don't invent an owner.
- User says "just book it" → deliver the cost line once, then comply. Their calendar, their call.
## Related
- Agent: [`cs-meeting-discipline`](../agents/cs-meeting-discipline.md)
- Skill: [`meetings`](../skills/meetings/SKILL.md)
- Sibling command: [`/cs:meeting-actions`](cs-meeting-actions.md) (post-meeting extraction)
---
**Version:** 1.0.0

View file

@ -0,0 +1,97 @@
---
name: meetings
description: Use when someone wants to decide whether a meeting is worth calling, price a meeting in dollars, build a timeboxed agenda with desired outcomes, or turn messy meeting notes into owned action items — or says "should this be a meeting", "/cs:meeting-prep", or "/cs:meeting-actions". Runs a cost gate (ASYNC / NOT-READY / MEET), builds a decision-first agenda, and extracts an owner + due-date checklist that flags every orphan.
argument-hint: "[the meeting to gate, or the notes to extract actions from]"
license: MIT
metadata:
version: 1.0.0
build_pattern: "Path-B discipline skill — Rogelberg/HBR meeting-science canon + deterministic gate/agenda/extraction scripts"
distinct_from: "project-management (team ceremonies + Jira delivery flow; this is personal meeting hygiene); business-operations/internal-comms (org-level communication design; this never auto-sends); productivity/capture (private brain-dump triage; this parses shared meeting notes)"
---
# Meetings — Cost Gate → Timeboxed Agenda → Owned Actions
> **Portability:** Reasoning-led skill with 3 stdlib Python scripts. No external APIs, no LLM calls
> in scripts, nothing auto-sent. The scripts fix the discipline; the user runs the meeting.
## What this does
Most meetings should be an email. This skill makes that testable: it prices a meeting in real
dollars, refuses to let it exist without a decision + agenda + owner, builds a timeboxed
decision-first agenda for the survivors, and afterwards turns raw notes into an owner + due-date
checklist that flags every orphan. An ASYNC verdict is a win, not a failure.
## Workflow — gate → agenda → run → extract
**1. Gate.** Before starting, ask one clarifying question if the decision to be made is unstated —
the gate cannot run honestly without it. Then price it and apply the three checks. No decision → ASYNC (exit 2): draft a memo
instead, stop here. Decision but missing agenda/owner → NOT-READY (exit 3), naming the gap.
All present → MEET (exit 0) with total cost and a cost-per-minute line.
**2. Agenda.** Only for MEET. Every topic needs a desired outcome — empty outcomes are refused by
name (exit 2). Decision topics (decide/choose/approve) sort before discuss/inform. Timeboxes plus
the mandatory 5-minute closing "actions recap" slot must fit `--length`, or the overflow is refused
with the exact overage (exit 3). Iterate — trim or split the named topic and re-run until it fits;
the stop condition is exit 0 (or the meeting goes async). Output includes a pre-read line.
**3. Run.** The user runs the meeting from the printed agenda. Hold the timeboxes; use the closing
slot to read every action aloud with its owner and date.
**4. Extract.** Feed the raw notes to the extractor: checkboxes, `ACTION:`/`TODO:` lines,
"@name will …" and "Name will … by date" patterns become a checklist grouped by owner, with
ORPHAN (no owner) and NO-DUE flags plus summary counts. Assign every orphan before posting — the
meeting is done when every action has an owner and a date; that completion check closes the loop.
```bash
# 1. Gate: should this meeting exist?
python scripts/meeting_cost_calculator.py --attendees 6 --minutes 60 \
--avg-rate 90 --include-refocus --has-decision --has-agenda --has-owner
# 2. Agenda: timeboxed, decision-first, outcomes mandatory
python scripts/agenda_builder.py --length 45 \
--topic "Q3 pricing:Decide usage-based vs seat-based:15:maria" \
--topic "Launch risks:Discuss open launch blockers:15:sam"
# 4. Extract: raw notes -> owner + due-date checklist with ORPHAN/NO-DUE flags
python scripts/action_item_extractor.py --input notes.md
```
## Scripts
| Script | Role |
|---|---|
| `scripts/meeting_cost_calculator.py` | Dollars (attendees × minutes × rate, optional 23-min refocus overhead per attendee) + decision/agenda/owner gate → ASYNC / NOT-READY / MEET. |
| `scripts/agenda_builder.py` | Timeboxed decision-first agenda; refuses empty outcomes and overflow; enforces pre-read line + 5-min closing actions-recap slot. |
| `scripts/action_item_extractor.py` | Raw notes → owner-grouped markdown checklist with due dates, ORPHAN/NO-DUE flags, and summary counts. |
## References
- [`references/meeting_cost_canon.md`](references/meeting_cost_canon.md) — the real cost of meetings and the should-this-exist gate (7 sources)
- [`references/agenda_discipline.md`](references/agenda_discipline.md) — agendas as questions, timeboxing, decision-first ordering, pre-reads (7 sources)
- [`references/action_item_discipline.md`](references/action_item_discipline.md) — why meetings without owned actions are theater (6 sources)
## Assets
- [`assets/example_agenda.md`](assets/example_agenda.md) — a full worked timeboxed agenda (gate verdict → ordered topics → closing recap)
- [`assets/meeting_gate_worksheet.md`](assets/meeting_gate_worksheet.md) — fillable should-this-be-a-meeting worksheet
## Rules
- **Gate before agenda.** Never build an agenda for a meeting that hasn't passed the gate.
- **No decision, no meeting.** Status updates go async, every time.
- **No desired outcome, no agenda slot.** The builder refuses; go get the outcome.
- **Every action item has an owner and a date — or it is not an action item.** Flag, never drop.
- **Never auto-send.** No invites, no emails, no messages. Output is text the user sends.
## Distinct From (don't reach for the wrong skill)
- **`project-management/`** — team ceremonies, sprint cadence, Jira delivery flow. This gates one
meeting at a time for the person calling it.
- **`business-operations/internal-comms`** — org-level communication design. This never designs a
comms program and never sends anything.
- **`productivity/capture`** — triages a private brain-dump. This parses a shared meeting's notes.
---
**Version:** 1.0.0
**Build pattern:** Path-B discipline skill — meeting-science canon preserved + deterministic gate/agenda/extraction scripts added.

View file

@ -0,0 +1,76 @@
# Worked Example — Q3 Pricing Decision Meeting
A full pass through the `meetings` skill for a real meeting, from gate to agenda. Use it as the
canonical shape for what the scripts produce and how the pieces snap together.
## Step 1 — The gate
```bash
python scripts/meeting_cost_calculator.py --attendees 6 --minutes 45 \
--avg-rate 90 --include-refocus --has-decision --has-agenda --has-owner
```
```
Meeting Cost Gate (should this meeting exist?)
================================================================
Attendees: 6 Length: 45 min Rate: $90/hr
Direct cost: $ 405.00 (6 x 45 min x $90/hr)
Refocus cost: $ 207.00 (23 min refocus overhead per attendee)
TOTAL COST: $ 612.00
Gate: decision=yes | agenda=yes | owner=yes
VERDICT: MEET
Gate passed: decision + agenda + owner. This meeting has earned its slot. It costs $612.00 —
budget the agenda timeboxes like money.
Every minute of this meeting costs $13.60 — price each agenda topic's timebox against that.
```
**MEET** — proceed to the agenda. (Had there been no decision, the verdict would be ASYNC and the
next step would be a memo outline, not an agenda.)
## Step 2 — The agenda
```bash
python scripts/agenda_builder.py --length 45 \
--topic "Metrics review:Inform team of the activation trend:5:alex" \
--topic "Q3 pricing:Decide usage-based vs seat-based:15:maria" \
--topic "Launch risks:Discuss open launch blockers:15:sam"
```
Note what the builder did below: the **decision topic sorted to the front** even though it was
supplied second, every topic carries a desired outcome and an owner, and the closing actions-recap
slot is pinned to the end of the meeting.
```markdown
# Agenda — 45 minutes, 3 topics
**Pre-read:** circulate the relevant doc(s) at least a day ahead. The meeting starts assuming the
pre-read was read — no recap slot for skippers.
| # | Timebox | Topic | Desired outcome | Owner |
|---|---------|-------|-----------------|-------|
| 1 | 0015 min (15 min) | Q3 pricing | Decide usage-based vs seat-based | maria |
| 2 | 1520 min (5 min) | Metrics review | Inform team of the activation trend | alex |
| 3 | 2035 min (15 min) | Launch risks | Discuss open launch blockers | sam |
| ✔ | 4045 min (5 min) | **Actions recap** | Read every action aloud: owner + due date, or it is not an action | meeting owner |
_5 min of slack before the recap — if topics finish early, end early. Nobody has ever complained
about a meeting ending early._
```
## What refusal looks like
Two inputs the builder will not accept, so you see them before you hit them:
- **Empty desired outcome** (`--topic "Roadmap::10:sam"`) → exit 2:
`REFUSED (REFUSED-NO-OUTCOME): No desired outcome, no agenda slot. Topics refused: "Roadmap". Go get the outcome, then rebuild.`
- **Overflow** (topics totaling 50 min into `--length 45`) → exit 3:
`REFUSED (REFUSED-OVERFLOW): Timeboxes (50 min) + closing actions-recap buffer (5 min) = 55 min, but the meeting is 45 min — 10 min over. Cut a topic, shrink a timebox, or move an inform topic to the pre-read.`
## Step 3 — After the meeting
Feed the raw notes to `action_item_extractor.py` and walk the flags — see
[`meeting_gate_worksheet.md`](meeting_gate_worksheet.md) for the fillable pre-meeting worksheet
that keeps the next meeting honest too.

View file

@ -0,0 +1,79 @@
# Should This Be a Meeting? — Fillable Gate Worksheet
Fill this in **before** sending the invite. If you can't fill a required line, the meeting is not
ready to be called — that's the worksheet working, not failing. Pairs with
`scripts/meeting_cost_calculator.py`, which turns the answers into an ASYNC / NOT-READY / MEET
verdict with a dollar figure.
## 1. The decision (required)
> No decision, no meeting. A status update goes async, every time.
**The specific decision this meeting will make:**
- _____________________________________________________________________
**What happens if this decision is NOT made this week:**
- _____________________________________________________________________
*If you wrote "share an update", "align", or "sync" above — stop. Draft the memo/thread instead
and send it to the same list. You just saved the full cost below.*
## 2. The price
| Input | Value |
|---|---|
| Attendees (only people needed to make the decision) | _______ |
| Length in minutes | _______ |
| Avg fully-loaded hourly rate ($90 if unsure) | _______ |
| Include 23-min refocus overhead per attendee? (y/n) | _______ |
```bash
python scripts/meeting_cost_calculator.py \
--attendees ___ --minutes ___ --avg-rate ___ --include-refocus \
--has-decision --has-agenda --has-owner
```
**Total cost: $ _______** — Would you approve this as an invoice for the decision in section 1?
If no, cut attendees or minutes until you would.
## 3. The owner (required)
**Named meeting owner** (runs the timeboxes, owns the recap, chases the actions):
- _____________________________________________________________________
## 4. The agenda (required — outcomes, not nouns)
Every topic needs a desired outcome starting with a verb. **Decide / choose / approve** topics go
first. If a topic's outcome starts with "inform", ask whether it belongs in the pre-read instead.
| Topic | Desired outcome (verb first) | Minutes | Topic owner |
|---|---|---|---|
| ______________ | ______________________________ | _____ | _________ |
| ______________ | ______________________________ | _____ | _________ |
| ______________ | ______________________________ | _____ | _________ |
**Timebox check:** topics total _____ min + 5 min closing actions-recap = _____ min ≤ meeting
length _____ min? If not, cut — don't stretch the meeting.
## 5. The pre-read
**Doc(s) to circulate ahead:** _______________________________________________
**Sent by (owner + date):** _________________________________________________
## 6. Commitments for the room
- [ ] The meeting ends with the actions recap: every action read aloud with **owner + due date**.
- [ ] Actions without an owner get assigned before anyone leaves — no orphans out the door.
- [ ] Notes go through `scripts/action_item_extractor.py` and the checklist is posted the same day.
- [ ] If the decision gets made early, the meeting ends early.
---
**Gate verdict (from the calculator):** ASYNC ☐ NOT-READY ☐ MEET ☐
*ASYNC → write the memo. NOT-READY → fix the named gap, re-run. MEET → build the agenda with
`scripts/agenda_builder.py` and send the invite with the pre-read attached.*

View file

@ -0,0 +1,85 @@
# Action-Item Discipline — why meetings without owned actions are theater
The third discipline of the `meetings` skill: the meeting's output is not the discussion, the
slides, or even the decision — it is the set of committed next actions that exist afterwards. A
meeting that ends without owned, dated actions consumed its budget and produced a feeling. This
file documents the canon behind `action_item_extractor.py` and its two flags, **ORPHAN** (no
owner) and **NO-DUE** (no date).
## The next-action rule (why vague intentions don't survive)
David Allen's *Getting Things Done* (Penguin, 2001) built its entire system on one observation:
outcomes don't get done, **next actions** do. "Handle the pricing situation" is a project;
"Maria sends the pricing one-pager by Friday" is an action. Allen's test — *what's the very next
physical, visible activity, and who takes it?* — is exactly the shape the extractor demands: a
verb, an owner, a date. Anything in the notes that fails the test ("we should think about X",
"the team agreed X matters") is deliberately not extracted, because promoting sentiment to a
checklist launders vagueness into false progress.
## One owner, not a committee
Every extracted item gets exactly one owner or an **ORPHAN** flag. This is the "Driver" /
"Accountable" insight of the DACI and RACI decision frameworks (documented in the Atlassian Team
Playbook's DACI play): when a task has two owners it has zero, because each can reasonably assume
the other has it. The extractor never auto-assigns an orphan — assignment is a human commitment,
and a name typed by a script is not a commitment. The flag's job is to force the assignment
conversation while the room (or thread) still remembers agreeing to the work.
## A date, or it drifts
George Doran's original SMART memo ("There's a S.M.A.R.T. Way to Write Management's Goals and
Objectives," *Management Review*, 1981) made **time-bound** one of the five criteria for a reason:
an undated commitment cannot be late, so it never becomes urgent, so it loses every scheduling
contest against work that can. The extractor captures "by/due/before <date>" phrasings and flags
the rest **NO-DUE**. The fix costs five seconds at recap time ("Alex — by when?") and is the
highest-leverage edit anyone makes to meeting notes.
## Implementation intentions (the psychology of "who + when")
The owner + date rule is not managerial superstition; it has a mechanism. Peter Gollwitzer's
implementation-intentions research shows that goals formulated as *if-then* plans — specifying
when, where, and how the action happens — are acted on at substantially higher rates than
equally-motivated abstract goals, across dozens of studies (Peter M. Gollwitzer, "Implementation
Intentions: Strong Effects of Simple Plans," *American Psychologist*, 1999; meta-analysis:
Gollwitzer & Sheeran, *Advances in Experimental Social Psychology*, 2006). An action item with an
owner and a date is an implementation intention; one without is a wish. Likewise, Locke and
Latham's goal-setting research — the most replicated result in organizational psychology — shows
specific, difficult goals reliably outperform "do your best" vagueness (Edwin A. Locke & Gary P.
Latham, "Building a Practically Useful Theory of Goal Setting and Task Motivation,"
*American Psychologist*, 2002).
## Follow-through is the dropped phase
Steven Rogelberg's meeting research finds the post-meeting phase is where most meeting value
leaks: actions go unrecorded, unowned, or unrevisited, and recurring meetings quietly re-discuss
last week's conclusions (*The Surprising Science of Meetings*, Oxford University Press, 2019).
His prescriptions map one-to-one onto this skill's mechanics: end with a recap of commitments
(the agenda builder's mandatory closing slot), record owner + deadline for each (the extractor's
output shape), and open the next meeting against the last list (the emitted markdown checklist is
built to be pasted into the next agenda's pre-read).
## Why extraction is deterministic
The extractor uses fixed patterns — checkboxes, `ACTION:`/`TODO:` prefixes, "@name will …",
"Name will … by date" — rather than judgment. Two reasons. First, auditability: anyone can look at
a line of notes and know whether it will extract, so the discipline is teachable ("write it as
`ACTION: @sam … by Friday` and it will never be lost"). Second, honesty: a fuzzy extractor that
infers actions from vibes recreates the original problem — commitments nobody actually made. If
the notes contain no extractable actions, the correct output is the uncomfortable summary line
saying so, not an invented checklist.
## Sources
1. David Allen, *Getting Things Done: The Art of Stress-Free Productivity*, Penguin, 2001
(next-action discipline; the verb-owner test).
2. Steven G. Rogelberg, *The Surprising Science of Meetings*, Oxford University Press, 2019
(follow-through as the dropped phase; end-with-recap prescription).
3. Atlassian Team Playbook, "DACI: Decision-making framework" (single Driver/Accountable;
two owners = zero owners).
4. George T. Doran, "There's a S.M.A.R.T. Way to Write Management's Goals and Objectives,"
*Management Review*, 1981 (time-bound as a first-class criterion).
5. Peter M. Gollwitzer, "Implementation Intentions: Strong Effects of Simple Plans," *American
Psychologist*, 1999 (and Gollwitzer & Sheeran 2006 meta-analysis) — who/when/where plans get
acted on; wishes don't.
6. Edwin A. Locke & Gary P. Latham, "Building a Practically Useful Theory of Goal Setting and
Task Motivation," *American Psychologist*, 2002 (specific goals beat vague ones).

View file

@ -0,0 +1,96 @@
# Agenda Discipline — questions, timeboxes, decisions first, an owner, a pre-read
The second discipline of the `meetings` skill: a meeting that passed the cost gate still fails if
its agenda is a list of nouns. This file documents the canon behind `agenda_builder.py` — why every
topic must carry a desired outcome, why timeboxes are budgets rather than suggestions, why decision
topics go first, and why the pre-read and the closing actions-recap slot are non-negotiable.
## Agendas as questions (the desired-outcome rule)
Steven Rogelberg's meeting research produced a counterintuitive result: merely *having* an agenda
does not predict meeting effectiveness — attendees rate agenda'd and agenda-less meetings about the
same. What predicts effectiveness is an agenda built from **questions to be answered**, because a
question forces the organizer to know what "done" looks like, makes the right attendee list
self-evident (invite whoever is needed to answer), and gives the meeting a natural end (the
question is answered) (Rogelberg, *The Surprising Science of Meetings*, Oxford University Press,
2019; and his HBR guidance "Why Your Meetings Stink — and What to Do About It," *Harvard Business
Review*, JanuaryFebruary 2019).
`agenda_builder.py` operationalizes this as a hard rule: **no desired outcome, no agenda slot.**
A topic supplied with an empty outcome field is refused by name (exit 2). "Roadmap" is a noun;
"Decide whether the roadmap slips two weeks" is a meeting.
## Timeboxing (Parkinson's law made operational)
"Work expands so as to fill the time available for its completion" (C. Northcote Parkinson,
"Parkinson's Law," *The Economist*, 1955). Discussion is the purest case: an unbounded topic
consumes whatever remains. The counter-discipline is the timebox — a fixed allocation that ends
the topic whether or not the room feels finished — which is the load-bearing mechanism of Scrum's
every ceremony: the sprint, the daily scrum's fifteen minutes, the review, the retro (Jeff
Sutherland, *Scrum: The Art of Doing Twice the Work in Half the Time*, Crown Business, 2014;
Schwaber & Sutherland, *The Scrum Guide*). The builder therefore:
- requires per-topic minutes and refuses an agenda whose timeboxes (plus the closing buffer)
exceed the meeting length, naming the exact overflow (exit 3) — a budget you can silently
overdraw is not a budget;
- prints startend offsets per topic so the running meeting can see the clock, not just the list;
- treats slack as a feature: finish early, end early.
## Decisions first
The builder sorts topics whose outcome starts with **decide / choose / approve** ahead of
discuss/inform topics. Three converging reasons:
1. **Energy and attention decay.** Decision quality degrades as a session wears on; putting the
decision in the last five minutes buys either a rushed call or a follow-up meeting. HBR's
practical agenda guidance consistently recommends sequencing the most important items first —
also because meetings compress from the end when they start late or run over.
2. **Grove's mission-meeting logic.** If the meeting exists to produce a decision (Grove, *High
Output Management*, 1983), the decision *is* the meeting; everything else is garnish and should
be treated as such — or moved to the pre-read.
3. **Inform topics are the weakest claim on synchronous time.** If an inform topic gets squeezed
out by the sort order, that is the system working: it likely belonged in the memo anyway.
## The owner role
Every topic carries a named owner — the person who drives that timebox, states the question, and
is accountable for its outcome landing in the actions recap. The meeting as a whole has an owner
too (checked upstream by the cost gate). This is Rogelberg's "meeting leader as steward of
everyone's time" plus Grove's chairman-accountability rule, made explicit per-slot so
responsibility cannot diffuse across the invite list.
## The pre-read line
Every generated agenda opens with a pre-read instruction: circulate the material ahead; the
meeting starts assuming it was read. This is Amazon's narrative-memo practice inverted for
ordinary meetings — Bezos's argument for the six-page memo (documented in Amazon shareholder
letters and public remarks) is that writing forces complete thoughts and reading in advance means
the meeting spends its synchronous minutes on the decision, not the download. A meeting that
re-presents the pre-read punishes exactly the people who prepared.
## The closing actions-recap slot
The builder reserves a mandatory 5-minute closing slot: read every action aloud — owner and due
date — before anyone leaves. Two safety properties motivate making it structural rather than
aspirational. First, follow-through is the most commonly dropped meeting phase (Rogelberg), and a
slot on the clock cannot be skipped invisibly. Second, in decision meetings, dissent and confusion
surface late; a recap slot is the last cheap moment to catch "wait, that's not what I agreed to" —
which only happens if people feel safe saying it, Amy Edmondson's core argument for psychological
safety in decision-making teams (Amy C. Edmondson, *The Fearless Organization*, Wiley, 2018).
## Sources
1. Steven G. Rogelberg, *The Surprising Science of Meetings*, Oxford University Press, 2019
(agenda-as-questions; leader as steward; follow-through gap).
2. Steven G. Rogelberg, "Why Your Meetings Stink — and What to Do About It," *Harvard Business
Review*, JanuaryFebruary 2019 (question-shaped agendas; invite-list implications).
3. C. Northcote Parkinson, "Parkinson's Law," *The Economist*, 1955 (work expands to fill the time
available — the case for timeboxes).
4. Jeff Sutherland, *Scrum: The Art of Doing Twice the Work in Half the Time*, Crown Business,
2014 (timeboxing as the core ceremony mechanism; also Schwaber & Sutherland, *The Scrum Guide*).
5. Andrew S. Grove, *High Output Management*, Random House, 1983 (process vs mission meetings;
the decision is the meeting; chairman accountability).
6. Jeff Bezos, Amazon shareholder letters + public remarks (narrative memo pre-read; synchronous
time spent on the decision, not the download).
7. Amy C. Edmondson, *The Fearless Organization*, Wiley, 2018 (psychological safety in decision
meetings — why the recap slot must invite dissent).

View file

@ -0,0 +1,91 @@
# Meeting Cost Canon — what a meeting really costs, and the should-this-exist gate
The first discipline of the `meetings` skill is treating a meeting as a purchase. Nobody would sign
a $600 invoice without asking what it buys, yet a six-person hour at a $90 fully-loaded rate is
exactly that — approved with one calendar click. This file documents the canon behind
`meeting_cost_calculator.py`: why the price must be said out loud, why the refocus overhead is real,
and why "no decision, no meeting" is the correct default.
## The scale of the problem
- **Executives average ~23 hours a week in meetings**, up from under 10 hours in the 1960s, and 71%
of senior managers surveyed said meetings are unproductive and inefficient (Leslie Perlow,
Constance Noonan Hadley & Eunice Eun, "Stop the Meeting Madness," *Harvard Business Review*,
JulyAugust 2017). Perlow et al.'s core finding: dysfunctional meeting cultures self-perpetuate
because no individual meeting ever gets billed for its cost.
- **Most meetings fail by design, not execution.** Steven Rogelberg's research program (surveying
thousands of employees and leaders) finds roughly half of meeting time is rated ineffective by
attendees, and the strongest lever is what happens *before* the meeting: whether it should exist,
who is invited, and what the agenda demands (Steven G. Rogelberg, *The Surprising Science of
Meetings*, Oxford University Press, 2019).
- **Deleting meetings works.** Shopify started 2023 by removing all recurring meetings with more
than two people (~12,000 events) and later shipped an internal **meeting cost calculator** that
embeds a dollar figure into every calendar invite — a typical 30-minute, 3-person meeting priced
between $700 and $1,600. The point was behavioral: people decline purchases they would have
accepted as invites.
- **Industry surveys agree on the waste.** Doodle's "State of Meetings" report estimated poorly
organized meetings cost businesses in the tens of billions annually across the US and UK alone;
Atlassian's long-running "you waste a lot of time at work" meeting statistics put the average
employee at ~31 hours per month in unproductive meetings. Exact figures vary by survey; the
direction never does.
## The refocus overhead (`--include-refocus`)
The invite says 30 minutes; the attention bill is larger. Gloria Mark's interruption research at
UC Irvine measured an average of about **23 minutes to return to the interrupted task** after a
context switch (Gloria Mark, Daniela Gudith & Ulrich Klocke, "The Cost of Interrupted Work: More
Speed and Stress," *CHI*, 2008). A mid-morning meeting is an interruption for every attendee doing
focused work, which is why the calculator's `--include-refocus` flag adds 23 minutes per attendee
at the same hourly rate. It is an estimate — deliberately conservative (one refocus, not two) — and
it routinely doubles the sticker price of short meetings, which is the point.
## The gate: decision, agenda, owner
The calculator refuses a MEET verdict unless three things exist:
1. **A decision** (`--has-decision`). Andy Grove's taxonomy separates *process* meetings
(one-on-ones, staff reviews — scheduled, informational) from *mission* meetings, which exist to
produce a decision (Andrew S. Grove, *High Output Management*, Random House, 1983). An ad-hoc
meeting with no decision is a status update wearing a meeting's clothes — Grove's rule was that
a mission meeting that produces no decision is a failure of the chairman, not the attendees.
Status flows async: Amazon's practice replaces presentation meetings with a silently-read
six-page narrative memo, and its "two-pizza team" rule caps the audience — both documented across
Jeff Bezos's shareholder letters and his explanation that "PowerPoint-style presentations
somehow give permission to gloss over ideas."
2. **An agenda** (`--has-agenda`). Rogelberg's finding is blunt: agendas per se don't correlate
with meeting quality — *prepared, question-shaped* agendas do (see
[`agenda_discipline.md`](agenda_discipline.md)). The gate checks existence; the agenda builder
enforces quality.
3. **An owner** (`--has-owner`). Perlow et al. and Rogelberg converge here: someone must be
accountable for the meeting achieving its outcome and for follow-through afterwards. A meeting
nobody owns produces minutes nobody reads.
Missing the decision → **ASYNC** (the cheapest meeting is no meeting). Missing agenda or owner with
a real decision on the table → **NOT-READY**: the meeting may deserve to exist, but not yet.
## Why the verdict is ternary, not a score
A 0100 "meeting quality score" invites negotiation. Three verdicts with hard edges do not:
ASYNC ends the conversation (write the memo), NOT-READY names exactly what to fix, and MEET prints
the invoice — total cost plus cost-per-minute — so the agenda's timeboxes get budgeted like money.
This mirrors Shopify's design insight: the number changes behavior only when it arrives *before*
the commitment is made.
## Sources
1. Leslie Perlow, Constance Noonan Hadley & Eunice Eun, "Stop the Meeting Madness," *Harvard
Business Review*, JulyAugust 2017 (23 hrs/week; self-perpetuating meeting cultures).
2. Steven G. Rogelberg, *The Surprising Science of Meetings*, Oxford University Press, 2019
(meeting-science canon; pre-meeting levers dominate).
3. Shopify meeting-cost calculator + recurring-meeting purge, 2023 (cost surfaced in the invite;
~12,000 events deleted; widely covered, incl. Bloomberg/Reuters reporting).
4. Jeff Bezos, Amazon shareholder letters + public remarks (silent six-page narrative memo;
two-pizza team rule capping meeting size).
5. Doodle, "State of Meetings" report (survey-based estimate of the cost of poorly organized
meetings across US/UK).
6. Atlassian, "You Waste a Lot of Time at Work" meeting statistics (~31 unproductive meeting
hours/month per employee).
7. Andrew S. Grove, *High Output Management*, Random House, 1983 (meetings as the medium of
managerial work; process vs mission meetings; a decision-less mission meeting is a failure).
8. Gloria Mark, Daniela Gudith & Ulrich Klocke, "The Cost of Interrupted Work," *CHI* 2008
(~23-minute refocus after interruption — the basis of `--include-refocus`).

View file

@ -0,0 +1,243 @@
#!/usr/bin/env python3
"""action_item_extractor.py — Turn raw meeting notes into an owned, dated action checklist.
The `meetings` skill's third discipline: a meeting that ends without owned, dated actions was
theater. This script parses raw notes (from --input FILE or stdin) and extracts action items via
deterministic patterns:
- "- [ ] ..." / "* [ ] ..." markdown checkboxes
- "ACTION: ..." / "TODO: ..." explicit prefixes (case-insensitive)
- "@name will ..." / "@name to ..." mention-owned commitments
- "Name will <verb> ... by <date>" prose commitments (capitalized name, pronouns excluded)
For every item it captures the OWNER (an @mention leading the item, or the leading
"Name will/to" name) and the DUE DATE ("by/due/before <weekday|today|tomorrow|EOD|EOW|
YYYY-MM-DD|D/M|Month D>") when present, then flags:
ORPHAN no owner captured. Unowned actions die; assign before posting.
NO-DUE owner but no date. Undated actions drift; date them now.
Output: a markdown checklist grouped by owner (orphans last, loudly) + summary counts, or --json.
NO LLM CALLS. Pure regex + grouping. Stdlib only. Nothing is sent anywhere.
Usage:
python action_item_extractor.py --input notes.md
cat notes.md | python action_item_extractor.py
python action_item_extractor.py --sample --json
"""
import argparse
import json
import re
import sys
from typing import Any, Dict, List, Optional, Tuple
CHECKBOX_RE = re.compile(r"^\s*[-*]\s*\[\s*\]\s*(?P<text>.+)$")
PREFIX_RE = re.compile(r"^\s*(?:ACTION|TODO)\s*:\s*(?P<text>.+)$", re.IGNORECASE)
MENTION_OWNED_RE = re.compile(r"^\s*@(?P<owner>[A-Za-z][\w.\-]*)\s+(?:will|to)\s+(?P<text>.+)$")
NAME_WILL_RE = re.compile(r"^\s*(?P<owner>[A-Z][a-zA-Z]+)\s+(?:will|to)\s+(?P<text>.+)$")
MENTION_HEAD_RE = re.compile(r"^\s*@(?P<owner>[A-Za-z][\w.\-]*)\s*[:,—-]?\s*(?P<text>.*)$")
INNER_NAME_WILL_RE = re.compile(r"^(?P<owner>[A-Z][a-zA-Z]+)\s+(?:will|to)\s+(?P<text>.+)$")
_DATE_TOKEN = (
r"(?:monday|tuesday|wednesday|thursday|friday|saturday|sunday|today|tomorrow|eod|eow"
r"|\d{4}-\d{2}-\d{2}"
r"|\d{1,2}/\d{1,2}(?:/\d{2,4})?"
r"|(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\.?\s+\d{1,2})"
)
DUE_RE = re.compile(r"\b(?:by|due|before)\s+(?P<due>" + _DATE_TOKEN + r")\b", re.IGNORECASE)
PRONOUNS = {"We", "I", "It", "They", "This", "That", "You", "He", "She",
"Everyone", "Someone", "Anybody", "Nobody", "Team", "The"}
# Sentence-initial capitalized words that are never a person committing to an action.
NON_OWNER_WORDS = PRONOUNS | {
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday",
"January", "February", "March", "April", "May", "June", "July", "August",
"September", "October", "November", "December",
"Today", "Tomorrow", "Tonight", "Next", "Last", "There",
"Everything", "Nothing", "Something", "Everybody",
}
# "Name will <stative>" is a prediction or status, not a commitment
# ("Friday will be a half day", "Design will need another pass").
STATIVE_CONTINUATIONS = {"be", "need", "probably", "likely", "not", "also",
"still", "never", "just", "only"}
def _is_commitment(text: str, owner: str) -> bool:
"""A 'Name will/to ...' line is a commitment only if Name can be a person
and what follows reads as an action, not a stative prediction."""
if owner in NON_OWNER_WORDS:
return False
first = text.strip().split(None, 1)[0].lower() if text.strip() else ""
return first not in STATIVE_CONTINUATIONS
EPILOG = """\
exit codes:
0 success checklist emitted (ORPHAN / NO-DUE items are flagged in the output, not fatal)
2 no input neither --input, --sample, nor piped stdin was provided (or file unreadable)
--help and --sample exit 0.
"""
def _extract_owner_and_text(text: str, owner: Optional[str]) -> Tuple[Optional[str], str]:
"""Refine owner from an already-captured action text.
Only a mention that LEADS the item confers ownership ("@sam: book the room",
"@sam to book the room"). A mid-text mention is the task's object, not its
owner ("follow up with @sam") those stay ORPHAN for a human to assign.
"""
m = MENTION_OWNED_RE.match(text.strip())
if m: # "@owner will/to rest" at the head — strip the owner phrase from the text
return m.group("owner"), m.group("text")
m = MENTION_HEAD_RE.match(text.strip())
if m:
return m.group("owner"), (m.group("text") or text)
m = INNER_NAME_WILL_RE.match(text.strip())
if m and _is_commitment(m.group("text"), m.group("owner")):
return m.group("owner"), m.group("text")
return owner, text
def extract(notes: str) -> List[Dict[str, Any]]:
items: List[Dict[str, Any]] = []
for lineno, raw in enumerate(notes.splitlines(), 1):
line = raw.rstrip()
if not line.strip():
continue
owner: Optional[str] = None
text: Optional[str] = None
m = CHECKBOX_RE.match(line)
if m:
text = m.group("text").strip()
else:
m = PREFIX_RE.match(line)
if m:
text = m.group("text").strip()
else:
m = MENTION_OWNED_RE.match(line)
if m:
owner, text = m.group("owner"), m.group("text").strip()
else:
m = NAME_WILL_RE.match(line)
if m and _is_commitment(m.group("text"), m.group("owner")):
owner, text = m.group("owner"), m.group("text").strip()
if text is None:
continue
# Refine only when no owner was captured at the head of the line — an
# already-owned commitment may legitimately name other people in its text
# ("@maria will ask @sam to review") without transferring ownership.
if owner is None:
owner, text = _extract_owner_and_text(text, owner)
due_m = DUE_RE.search(line)
due = due_m.group("due") if due_m else None
text = text.rstrip(".").strip()
flags: List[str] = []
if not owner:
flags.append("ORPHAN")
if not due:
flags.append("NO-DUE")
items.append({"line": lineno, "text": text, "owner": owner, "due": due, "flags": flags})
return items
def summarize(items: List[Dict[str, Any]]) -> Dict[str, int]:
return {
"total": len(items),
"owned": sum(1 for i in items if i["owner"]),
"orphans": sum(1 for i in items if "ORPHAN" in i["flags"]),
"no_due": sum(1 for i in items if "NO-DUE" in i["flags"]),
}
def render_markdown(items: List[Dict[str, Any]]) -> str:
out: List[str] = ["## Action Items", ""]
if not items:
out.append("_No action items detected. If decisions were made, they left no owners — "
"that is worth fixing in the room next time._")
return "\n".join(out)
owners = sorted({i["owner"] for i in items if i["owner"]}, key=str.lower)
for owner in owners:
out.append(f"### {owner}")
for i in items:
if i["owner"] == owner:
due = f" — due {i['due']}" if i["due"] else " **[NO-DUE — date it now]**"
out.append(f"- [ ] {i['text']}{due}")
out.append("")
orphans = [i for i in items if not i["owner"]]
if orphans:
out.append("### (unassigned) — ORPHANS, assign before posting")
for i in orphans:
due = f" — due {i['due']}" if i["due"] else ""
out.append(f"- [ ] {i['text']}{due} **[ORPHAN]**")
out.append("")
s = summarize(items)
out.append(f"**Summary:** {s['total']} actions · {s['owned']} owned · "
f"{s['orphans']} ORPHAN · {s['no_due']} NO-DUE")
if s["orphans"]:
out.append("")
out.append("_Every action item has an owner and a date — or it is not an action item. "
"Assign the orphans while the room still remembers agreeing to them._")
return "\n".join(out)
SAMPLE_NOTES = """\
Notes Q3 planning sync
Maria will send the pricing one-pager by Friday.
We agreed the roadmap shape looks fine overall.
- [ ] update the launch checklist
ACTION: @sam to book the security review by 2026-07-24
TODO: draft the customer announcement email
Alex will confirm vendor pricing.
It will probably rain during the offsite.
"""
def main(argv: List[str]) -> int:
p = argparse.ArgumentParser(
description="Extract owned, dated action items from raw meeting notes (flags ORPHAN / NO-DUE).",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=EPILOG,
)
p.add_argument("--input", metavar="FILE", help="Notes file to read (otherwise stdin is used)")
p.add_argument("--sample", action="store_true", help="Run on the embedded example notes")
p.add_argument("--json", action="store_true", help="Emit machine-readable JSON instead of markdown")
args = p.parse_args(argv)
if args.sample:
notes = SAMPLE_NOTES
elif args.input:
try:
with open(args.input, "r", encoding="utf-8") as f:
notes = f.read()
except OSError as e:
print(f"error: cannot read {args.input}: {e}", file=sys.stderr)
return 2
elif not sys.stdin.isatty():
notes = sys.stdin.read()
else:
p.print_help()
print("\nerror: provide --input FILE, pipe notes on stdin, or use --sample", file=sys.stderr)
return 2
items = extract(notes)
if args.json:
print(json.dumps({"items": items, "summary": summarize(items)}, indent=2))
else:
print(render_markdown(items))
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

View file

@ -0,0 +1,204 @@
#!/usr/bin/env python3
"""agenda_builder.py — Build a timeboxed, decision-first agenda where every topic has an outcome.
The `meetings` skill's second discipline. Feed it topics as
--topic "title:desired_outcome:minutes:owner"
plus the total meeting length (--length). It enforces the agenda canon deterministically:
1. NO DESIRED OUTCOME, NO AGENDA SLOT any topic with an empty outcome is refused, by name.
2. TIMEBOXES ARE A BUDGET topic timeboxes + a mandatory 5-minute closing "actions recap"
buffer must fit --length, or the agenda is refused with the exact overflow.
3. DECISIONS FIRST topics whose outcome starts with decide/choose/approve sort before
discuss/inform topics, so the decision gets fresh brains, not the last five minutes.
4. PRE-READ the agenda opens with a pre-read line (circulate ahead; the meeting assumes it
was read) and closes with the actions-recap slot (every action gets an owner + a date).
NO LLM CALLS. Pure parsing + ordering + arithmetic. Stdlib only. Nothing is sent or scheduled.
Usage:
python agenda_builder.py --length 45 \\
--topic "Q3 pricing:Decide usage-based vs seat-based:15:maria" \\
--topic "Launch risks:Discuss open launch blockers:15:sam"
python agenda_builder.py --sample
python agenda_builder.py --sample --json
"""
import argparse
import json
import sys
from typing import Any, Dict, List
CLOSING_BUFFER_MIN = 5 # mandatory closing "actions recap" slot
DECISION_PREFIXES = ("decide", "choose", "approve")
EPILOG = """\
exit codes:
0 agenda built (timeboxes + 5-min closing buffer fit --length; all outcomes present)
1 usage error malformed --topic spec or bad/missing flags (never a verdict)
2 refused one or more topics has an empty desired outcome (named in output)
3 refused timeboxes + 5-min closing buffer exceed --length (overflow named in output)
--help and --sample exit 0.
"""
def parse_topic(spec: str) -> Dict[str, Any]:
# Split the two structured fields (minutes, owner) from the right, then the
# title at the first colon — so a desired outcome may itself contain colons.
parts = spec.rsplit(":", 2)
if len(parts) != 3 or ":" not in parts[0]:
raise ValueError(
f'malformed --topic "{spec}" — expected 4 colon-separated fields: '
'"title:desired_outcome:minutes:owner" (colons are allowed inside '
"desired_outcome; the title must not contain one)"
)
head, minutes_raw, owner = parts
title, outcome = head.split(":", 1)
title, outcome, minutes_raw, owner = (p.strip() for p in (title, outcome, minutes_raw, owner))
try:
minutes = int(minutes_raw)
except ValueError:
raise ValueError(f'malformed --topic "{spec}" — minutes must be an integer, got "{minutes_raw}"')
if minutes < 1:
raise ValueError(f'malformed --topic "{spec}" — minutes must be >= 1')
if not title:
raise ValueError(f'malformed --topic "{spec}" — title is empty')
is_decision = outcome.lower().startswith(DECISION_PREFIXES)
return {"title": title, "outcome": outcome, "minutes": minutes, "owner": owner,
"kind": "decision" if is_decision else "discuss/inform"}
def build(topics: List[Dict[str, Any]], length: int) -> Dict[str, Any]:
empty = [t["title"] for t in topics if not t["outcome"]]
if empty:
return {
"status": "REFUSED-NO-OUTCOME",
"exit_code": 2,
"offenders": empty,
"message": ("No desired outcome, no agenda slot. Topics refused: "
+ ", ".join(f'"{t}"' for t in empty)
+ ". Go get the outcome, then rebuild."),
}
topic_minutes = sum(t["minutes"] for t in topics)
budget = topic_minutes + CLOSING_BUFFER_MIN
if budget > length:
overflow = budget - length
return {
"status": "REFUSED-OVERFLOW",
"exit_code": 3,
"topic_minutes": topic_minutes,
"closing_buffer": CLOSING_BUFFER_MIN,
"length": length,
"overflow_minutes": overflow,
"message": (f"Timeboxes ({topic_minutes} min) + closing actions-recap buffer "
f"({CLOSING_BUFFER_MIN} min) = {budget} min, but the meeting is "
f"{length} min — {overflow} min over. Cut a topic, shrink a timebox, "
"or move an inform topic to the pre-read."),
}
# Decisions first (stable within each group — caller's order is preserved otherwise).
ordered = sorted(topics, key=lambda t: 0 if t["kind"] == "decision" else 1)
cursor = 0
slots: List[Dict[str, Any]] = []
for t in ordered:
slots.append({**t, "start_min": cursor, "end_min": cursor + t["minutes"]})
cursor += t["minutes"]
recap_start = length - CLOSING_BUFFER_MIN
slack = recap_start - cursor
return {
"status": "OK",
"exit_code": 0,
"length": length,
"topic_minutes": topic_minutes,
"closing_buffer": CLOSING_BUFFER_MIN,
"slack_minutes": slack,
"slots": slots,
"recap": {"title": "Actions recap", "start_min": recap_start, "end_min": length,
"outcome": "Read every action aloud: owner + due date, or it is not an action"},
}
def render_markdown(r: Dict[str, Any]) -> str:
out: List[str] = []
out.append(f"# Agenda — {r['length']} minutes, {len(r['slots'])} topics")
out.append("")
out.append("**Pre-read:** circulate the relevant doc(s) at least a day ahead. "
"The meeting starts assuming the pre-read was read — no recap slot for skippers.")
out.append("")
out.append("| # | Timebox | Topic | Desired outcome | Owner |")
out.append("|---|---------|-------|-----------------|-------|")
for idx, s in enumerate(r["slots"], 1):
out.append(f"| {idx} | {s['start_min']:02d}{s['end_min']:02d} min ({s['minutes']} min) "
f"| {s['title']} | {s['outcome']} | {s['owner']} |")
rec = r["recap"]
out.append(f"| ✔ | {rec['start_min']:02d}{rec['end_min']:02d} min ({CLOSING_BUFFER_MIN} min) "
f"| **{rec['title']}** | {rec['outcome']} | meeting owner |")
out.append("")
if r["slack_minutes"] > 0:
out.append(f"_{r['slack_minutes']} min of slack before the recap — if topics finish early, "
"end early. Nobody has ever complained about a meeting ending early._")
else:
out.append("_Zero slack — hold the timeboxes or the recap gets eaten. "
"The recap is the most valuable slot; protect it._")
return "\n".join(out)
SAMPLE_ARGS = {
"length": 45,
"topics": [
"Metrics review:Inform team of the activation trend:5:alex",
"Q3 pricing:Decide usage-based vs seat-based:15:maria",
"Launch risks:Discuss open launch blockers:15:sam",
],
}
def main(argv: List[str]) -> int:
p = argparse.ArgumentParser(
description="Build a timeboxed, decision-first agenda (refuses empty outcomes and overflow).",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=EPILOG,
)
p.add_argument("--topic", action="append", default=[], metavar="SPEC",
help='Repeatable. "title:desired_outcome:minutes:owner" '
'(outcome starting decide/choose/approve sorts first)')
p.add_argument("--length", type=int, help="Total meeting length in minutes")
p.add_argument("--sample", action="store_true", help="Run the embedded 3-topic sample")
p.add_argument("--json", action="store_true", help="Emit machine-readable JSON instead of markdown")
args = p.parse_args(argv)
if args.sample:
length, specs = SAMPLE_ARGS["length"], SAMPLE_ARGS["topics"]
elif args.topic and args.length is not None:
length, specs = args.length, args.topic
else:
p.print_help()
print("\nerror: provide --length and at least one --topic, or --sample", file=sys.stderr)
return 1
if length < CLOSING_BUFFER_MIN + 1:
print(f"error: --length must be at least {CLOSING_BUFFER_MIN + 1} minutes "
f"({CLOSING_BUFFER_MIN}-min closing buffer is mandatory)", file=sys.stderr)
return 1
try:
topics = [parse_topic(s) for s in specs]
except ValueError as e:
print(f"error: {e}", file=sys.stderr)
return 1
result = build(topics, length)
if args.json:
print(json.dumps(result, indent=2))
elif result["status"] == "OK":
print(render_markdown(result))
else:
print(f"REFUSED ({result['status']}): {result['message']}", file=sys.stderr)
return result["exit_code"]
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

View file

@ -0,0 +1,178 @@
#!/usr/bin/env python3
"""meeting_cost_calculator.py — Price a meeting in real dollars and gate whether it should exist.
The `meetings` skill's first discipline: a meeting is a purchase. This script prices it
(attendees x minutes x hourly rate, optionally + a 23-minute refocus overhead per attendee,
per Gloria Mark's interruption-recovery research) and then applies the should-this-exist gate:
1. Is there a DECISION to make? (--has-decision) -> without one, it's a status update
2. Is there an AGENDA? (--has-agenda) -> without one, nobody can prepare
3. Is there a named meeting OWNER? (--has-owner) -> without one, nobody is accountable
Verdicts:
ASYNC no decision needed. Send a memo/thread instead. The cheapest meeting is no meeting.
NOT-READY a decision exists but the agenda and/or owner is missing (named explicitly).
MEET decision + agenda + owner all present. Cost printed, plus a cost-per-minute line
so agenda timeboxes get budgeted like money.
NO LLM CALLS. Pure arithmetic + thresholds. Stdlib only. Never sends or schedules anything.
Usage:
python meeting_cost_calculator.py --attendees 6 --minutes 60 --avg-rate 90 \
--include-refocus --has-decision --has-agenda --has-owner
python meeting_cost_calculator.py --attendees 8 --minutes 30 # no decision -> ASYNC
python meeting_cost_calculator.py --sample
python meeting_cost_calculator.py --sample --json
"""
import argparse
import json
import sys
from typing import Any, Dict, List
REFOCUS_MINUTES = 23 # avg time to refocus after a context switch (Gloria Mark, UC Irvine)
EPILOG = """\
exit codes:
0 MEET decision + agenda + owner present; cost printed
1 usage error bad or missing flags (never a verdict)
2 ASYNC no decision needed; recommend a memo/thread instead of a meeting
3 NOT-READY decision exists but agenda and/or owner missing (named in output)
--help and --sample always exit per the verdict rules above (--sample is a MEET scenario, exit 0).
"""
def evaluate(attendees: int, minutes: int, avg_rate: float, include_refocus: bool,
has_decision: bool, has_agenda: bool, has_owner: bool) -> Dict[str, Any]:
direct_cost = round(attendees * (minutes / 60.0) * avg_rate, 2)
refocus_cost = round(attendees * (REFOCUS_MINUTES / 60.0) * avg_rate, 2) if include_refocus else 0.0
total_cost = round(direct_cost + refocus_cost, 2)
cost_per_minute = round(total_cost / minutes, 2) if minutes else 0.0
missing: List[str] = []
if not has_agenda:
missing.append("agenda")
if not has_owner:
missing.append("meeting owner")
if not has_decision:
verdict, exit_code = "ASYNC", 2
headline = (
"No decision to make -> this is a status update, not a meeting. "
f"Send it as a memo/thread and save the ${total_cost:,.2f}. "
"The cheapest meeting is the one you don't hold."
)
elif missing:
verdict, exit_code = "NOT-READY", 3
headline = (
"A decision exists, but the meeting is not ready to be called. "
f"Missing: {' and '.join(missing)}. "
"Fix that, re-run the gate, and only then send the invite."
)
else:
verdict, exit_code = "MEET", 0
headline = (
"Gate passed: decision + agenda + owner. This meeting has earned its slot. "
f"It costs ${total_cost:,.2f} — budget the agenda timeboxes like money."
)
return {
"inputs": {
"attendees": attendees,
"minutes": minutes,
"avg_rate_per_hour": avg_rate,
"include_refocus": include_refocus,
"has_decision": has_decision,
"has_agenda": has_agenda,
"has_owner": has_owner,
},
"direct_cost": direct_cost,
"refocus_cost": refocus_cost,
"refocus_minutes_per_attendee": REFOCUS_MINUTES if include_refocus else 0,
"total_cost": total_cost,
"cost_per_minute": cost_per_minute,
"missing": missing,
"verdict": verdict,
"exit_code": exit_code,
"headline": headline,
}
def render_human(r: Dict[str, Any]) -> str:
i = r["inputs"]
out: List[str] = []
out.append("Meeting Cost Gate (should this meeting exist?)")
out.append("=" * 64)
out.append(f" Attendees: {i['attendees']} Length: {i['minutes']} min "
f"Rate: ${i['avg_rate_per_hour']:,.0f}/hr")
out.append(f" Direct cost: ${r['direct_cost']:>10,.2f} "
f"({i['attendees']} x {i['minutes']} min x ${i['avg_rate_per_hour']:,.0f}/hr)")
if i["include_refocus"]:
out.append(f" Refocus cost: ${r['refocus_cost']:>10,.2f} "
f"({REFOCUS_MINUTES} min refocus overhead per attendee)")
out.append(f" TOTAL COST: ${r['total_cost']:>10,.2f}")
out.append("")
out.append(f" Gate: decision={'yes' if i['has_decision'] else 'NO'} | "
f"agenda={'yes' if i['has_agenda'] else 'NO'} | "
f"owner={'yes' if i['has_owner'] else 'NO'}")
out.append("")
out.append(f" VERDICT: {r['verdict']}")
out.append(f" {r['headline']}")
if r["verdict"] == "MEET":
out.append("")
out.append(f" Every minute of this meeting costs ${r['cost_per_minute']:,.2f}"
"price each agenda topic's timebox against that.")
return "\n".join(out)
SAMPLE = dict(attendees=6, minutes=60, avg_rate=90.0, include_refocus=True,
has_decision=True, has_agenda=True, has_owner=True)
def main(argv: List[str]) -> int:
p = argparse.ArgumentParser(
description="Price a meeting and gate whether it should exist (ASYNC / NOT-READY / MEET).",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=EPILOG,
)
p.add_argument("--attendees", type=int, help="Number of attendees")
p.add_argument("--minutes", type=int, help="Scheduled meeting length in minutes")
p.add_argument("--avg-rate", type=float, default=90.0,
help="Average fully-loaded hourly rate per attendee in dollars (default: 90)")
p.add_argument("--include-refocus", action="store_true",
help=f"Add a {REFOCUS_MINUTES}-minute refocus overhead per attendee to the cost")
p.add_argument("--has-decision", action="store_true",
help="A specific decision will be made in this meeting")
p.add_argument("--has-agenda", action="store_true",
help="A timeboxed agenda with desired outcomes exists")
p.add_argument("--has-owner", action="store_true",
help="A named meeting owner is accountable for outcome + follow-through")
p.add_argument("--sample", action="store_true", help="Run the embedded sample (a MEET scenario)")
p.add_argument("--json", action="store_true", help="Emit machine-readable JSON instead of text")
args = p.parse_args(argv)
if args.sample:
vals = dict(SAMPLE)
elif args.attendees is not None and args.minutes is not None:
if args.attendees < 1 or args.minutes < 1:
print("error: --attendees and --minutes must both be >= 1", file=sys.stderr)
return 1
vals = dict(attendees=args.attendees, minutes=args.minutes, avg_rate=args.avg_rate,
include_refocus=args.include_refocus, has_decision=args.has_decision,
has_agenda=args.has_agenda, has_owner=args.has_owner)
else:
p.print_help()
print("\nerror: provide --attendees and --minutes, or --sample", file=sys.stderr)
return 1
result = evaluate(**vals)
if args.json:
print(json.dumps(result, indent=2))
else:
print(render_human(result))
return result["exit_code"]
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

View file

@ -0,0 +1,19 @@
{
"name": "weekly-review",
"description": "Run a GTD-style weekly review that actually closes loops. Walks David Allen's three-phase ritual — GET CLEAR (collect, process inboxes to zero, empty your head), GET CURRENT (next actions, previous + upcoming calendar, waiting-for, project lists), GET CREATIVE (someday/maybe, new ideas) — backed by three deterministic stdlib scripts: an open-loop scanner that inventories unchecked checkboxes, TODO/FIXME markers, and stale files; a checklist gate that computes completion, names every missing step, and refuses a COMPLETE verdict while any GET CURRENT step is unaccounted for; and a commitment auditor that flags STALLED, NO-NEXT-ACTION, and SOMEDAY-CANDIDATE projects with a 0-100 commitment-health score. The periodic-review counterpart to capture (intake) and reflect (per-conversation reflection).",
"version": "2.11.2",
"author": {
"name": "Alireza Rezvani",
"url": "https://alirezarezvani.com"
},
"homepage": "https://github.com/alirezarezvani/claude-skills/tree/main/productivity/weekly-review",
"repository": "https://github.com/alirezarezvani/claude-skills",
"license": "MIT",
"skills": [
"./skills/weekly-review"
],
"source": {
"build_pattern": "Path-B ritual skill — David Allen's three-phase weekly-review loop preserved + deterministic scanner/gate/auditor scripts added",
"distinct_from": "productivity/reflect (per-conversation reflection on one piece of work, not a recurring cadence over a whole system); productivity/capture (brain-dump intake that feeds the system, not the periodic review that maintains it); project-management sprint retrospectives (a team ceremony about a shared iteration, not a personal trusted-system audit)"
}
}

View file

@ -0,0 +1,59 @@
# weekly-review — GTD Weekly Review Loop → Trusted System
> Close every open loop once a week. David Allen called the weekly review the "critical success
> factor" of the whole method — the system is only trustworthy if it gets reviewed.
`/cs:weekly-review` walks the three-phase GTD ritual — **GET CLEAR → GET CURRENT → GET CREATIVE**
and refuses to call the review COMPLETE while any of the five mandatory GET CURRENT steps is
unaccounted for. Deterministic scripts do the inventory and the gating; you (and Claude) do the
thinking.
## The three phases
| Phase | What it does | Steps |
|---|---|---|
| **GET CLEAR** | Ground zero: nothing loose, nothing unprocessed | Collect loose inputs · process inboxes to zero · empty your head |
| **GET CURRENT** | The mandatory core — the system matches reality again | Next-action lists · previous calendar · upcoming calendar · waiting-for list · project lists |
| **GET CREATIVE** | Lift your eyes off the runway | Someday/maybe review · capture new ideas |
Skipping a GET CURRENT step without a stated reason **always** forces an INCOMPLETE verdict. That
gate is the whole point: a weekly review that skims the core is a guilt ritual, not a review.
## Quick start
```bash
# 1. Inventory the open loops in a workspace (checkboxes, TODO/FIXME, stale files)
python skills/weekly-review/scripts/open_loop_scanner.py --dir ~/notes --stale-days 14
# 2. Walk the checklist, then gate it — names every missing step
python skills/weekly-review/scripts/weekly_review_gate.py --list
python skills/weekly-review/scripts/weekly_review_gate.py --done "1,2,3,4,5,6,7,8" --skip "9:no someday list yet"
# 3. Audit the commitment portfolio — STALLED / NO-NEXT-ACTION / SOMEDAY-CANDIDATE + health score
python skills/weekly-review/scripts/commitment_auditor.py --input commitments.json
```
Or just say **"run my weekly review"** or run `/cs:weekly-review`.
## What's in the box
- **Skill:** [`skills/weekly-review/SKILL.md`](skills/weekly-review/SKILL.md)
- **Agent:** [`agents/cs-weekly-review.md`](agents/cs-weekly-review.md)
- **Command:** [`commands/cs-weekly-review.md`](commands/cs-weekly-review.md) — `/cs:weekly-review`
- **3 stdlib scripts:** `open_loop_scanner.py`, `weekly_review_gate.py`, `commitment_auditor.py`
- **3 references** (5-7 sources each): the GTD weekly-review canon, the psychology of open loops
(Zeigarnik, attention residue, plan-making), and sustainable review-cadence design
- **2 assets:** fillable weekly-review checklist + a full worked example
## Not the same as
- **`productivity/reflect`** — reflects on one conversation or piece of work, once. The weekly
review is a recurring cadence over your *whole* system.
- **`productivity/capture`** — the intake funnel (brain dump → actions). Capture feeds the system;
the weekly review is how the system stays trusted.
- **Sprint retrospectives** (`project-management`) — a team ceremony about a shared iteration. This
is a personal trusted-system audit; no team required.
## License
MIT.

View file

@ -0,0 +1,94 @@
---
name: cs-weekly-review
description: Walks a user through a complete GTD weekly review — GET CLEAR (collect, process inboxes to zero, empty your head), GET CURRENT (next actions, previous + upcoming calendar, waiting-for, project lists), GET CREATIVE (someday/maybe, new ideas) — using deterministic scripts to inventory open loops, gate the checklist, and score commitment health. Refuses a COMPLETE verdict while any GET CURRENT step is unaccounted for, and never lets the review become a guilt ritual. Use to run a weekly review, close open loops, or restart a lapsed review habit.
skills: productivity/weekly-review/skills/weekly-review
domain: productivity
model: opus
tools: [Read, Glob, Grep, Bash]
---
# Weekly Review Agent
## Purpose
The `cs-weekly-review` agent orchestrates the `weekly-review` skill to move a user from "vague
sense of too many open things" to a closed-loop, trusted system in one sitting:
1. **Inventory** — scan the user's workspace for open loops before asking them to recall anything
(`open_loop_scanner.py`): unchecked checkboxes, TODO/FIXME markers, files gone stale. Evidence
first, memory second.
2. **GET CLEAR** — walk collection: gather loose inputs, process every inbox to zero (clarify,
don't do), then a mind-sweep to empty the head. Two minutes or less per item or it becomes a
next action.
3. **GET CURRENT** — the mandatory core, all five steps: review next-action lists, the previous
calendar (missed commitments become actions), the upcoming calendar (prepare, don't react), the
waiting-for list (chase or drop), and every project for exactly one next action.
4. **Gate** — run `weekly_review_gate.py` with what was actually done. It computes completion,
names every missing step, and returns COMPLETE (exit 0) or INCOMPLETE (exit 2). An unskipped
missing GET CURRENT step always forces INCOMPLETE — no exceptions, no charm.
5. **GET CREATIVE + audit** — review someday/maybe, capture new ideas, then run
`commitment_auditor.py` over the project portfolio: STALLED / NO-NEXT-ACTION /
SOMEDAY-CANDIDATE flags + a 0-100 commitment-health score with the formula shown.
6. **Close** — deliver the verdict, the named gaps, the health score, and the first next action
for the coming week. One sitting, timeboxed, done.
## Voice
- Calm and procedural, never preachy. The review is maintenance, not judgment.
- Evidence over recall. Scan first, ask second — the user's memory is exactly what GTD says not to trust.
- Honest about an INCOMPLETE. A skimmed review marked "done" is worse than no review; the gate exists so the word COMPLETE keeps meaning something.
- Restart-friendly. A lapsed habit gets a shorter review and zero guilt, not a lecture.
## Hard rules
1. **All five GET CURRENT steps are mandatory.** A step may be skipped only with an explicit
stated reason (`--skip "N:reason"`); an unskipped missing GET CURRENT step forces INCOMPLETE.
2. **Never mark the review COMPLETE yourself.** Run `weekly_review_gate.py` and relay its verdict
and exit code; the gate is deterministic so the call is reproducible, not vibes.
3. **Every active project leaves with exactly one next action.** A project with none is flagged
NO-NEXT-ACTION and resolved (action, waiting-for, someday/maybe, or dropped) before close.
4. **Timebox it.** Target 60-90 minutes; past two hours, stop, gate what's done, and schedule the
remainder. Marathon reviews kill the habit.
5. **Process, don't do.** During the review, anything requiring more than two minutes becomes a
next action on a list — the review is for steering, not rowing.
## Skill Integration
**Skill Location:** `../skills/weekly-review/`
### Python Scripts (Stdlib)
1. **Open Loop Scanner**`skills/weekly-review/scripts/open_loop_scanner.py` — inventories
unchecked checkboxes, TODO/FIXME markers, and stale files across a directory; text + `--json`.
2. **Weekly Review Gate**`skills/weekly-review/scripts/weekly_review_gate.py` — the ten-step
three-phase checklist; `--done` / `--skip` / `--list`; completion % + named gaps →
COMPLETE (exit 0) / INCOMPLETE (exit 2).
3. **Commitment Auditor**`skills/weekly-review/scripts/commitment_auditor.py` — flags
STALLED / NO-NEXT-ACTION / SOMEDAY-CANDIDATE, computes the 0-100 health score with the formula
shown → HEALTHY / DRIFTING / OVERCOMMITTED.
### Knowledge Bases
- `skills/weekly-review/references/gtd_weekly_review_canon.md` — why the weekly review is the
critical success factor; the three-phase structure; cadence discipline (7 sources)
- `skills/weekly-review/references/open_loop_psychology.md` — Zeigarnik effect, plan-making
research, attention residue, cognitive load: why open loops tax attention (6 sources)
- `skills/weekly-review/references/review_cadence_design.md` — horizons of focus, habit anchoring,
timeboxing, failure modes, restart-after-lapse discipline (7 sources)
## Differentiates From Siblings
- **vs `cs-reflect`** (productivity reflect): reflect examines one conversation or piece of work,
once. The weekly review is a recurring cadence over the user's whole commitment system.
- **vs `cs-capture`** (productivity capture): capture is intake — brain dump in, actions
out. The weekly review is the maintenance loop that keeps the captured system trusted.
- **vs sprint retrospectives** (`project-management`): a retro is a team ceremony about a shared
iteration. This is a personal trusted-system audit — no team, no velocity chart.
## Related Agents
- [cs-capture](../../capture/agents/cs-capture.md) — productivity sibling, the intake side of the same system
---
**Version:** 1.0.0

View file

@ -0,0 +1,90 @@
---
name: "cs-weekly-review"
description: "/cs:weekly-review — Run a GTD weekly review: GET CLEAR (collect, inboxes to zero, empty your head), GET CURRENT (next actions, both calendars, waiting-for, projects — all mandatory), GET CREATIVE (someday/maybe, new ideas). Deterministic gate names every missing step; commitment auditor scores portfolio health 0-100."
argument-hint: "[optional: directory or notes to review]"
---
# /cs:weekly-review — GTD Weekly Review Loop → Trusted System
**Command:** `/cs:weekly-review [directory or notes]`
The weekly review is the maintenance loop that makes the rest of a personal system trustworthy.
This command walks David Allen's three phases in order, scans for open loops so nothing depends on
memory, and refuses to call the review COMPLETE while any mandatory GET CURRENT step is
unaccounted for.
## When to Run
- "Run my weekly review" / "let's do the weekly review"
- "I have too many open loops" / "help me close open loops"
- End of the work week, before planning the next one
- "I fell off my GTD habit" — restart with a shorter, zero-guilt pass
- You want an honest completion verdict, not a warm feeling of having tidied up.
## When NOT to Run
- You just want to dump what's in your head into actions → use `/cs:capture` (intake, not review).
- You want to reflect on one conversation or piece of work → use `productivity/reflect`.
- A team iteration retro with velocity and ceremonies → that's `project-management`, not this.
- Mid-week micro-check ("what's next right now?") — the review is a weekly cadence, not a task picker.
## What You Get
1. **An open-loop inventory** — unchecked checkboxes, TODO/FIXME markers, and stale files across
your workspace (`open_loop_scanner.py`), grouped by kind with per-file locations.
2. **A walked three-phase checklist** — GET CLEAR (3 steps), GET CURRENT (5 mandatory steps),
GET CREATIVE (2 steps), processed in order, two-minute rule enforced.
3. **A deterministic verdict**`weekly_review_gate.py` computes completion %, names every
missing step, and returns COMPLETE (exit 0) or INCOMPLETE (exit 2). Unskipped GET CURRENT gaps
always force INCOMPLETE.
4. **A commitment-health audit** — STALLED / NO-NEXT-ACTION / SOMEDAY-CANDIDATE flags plus a
0-100 score with the formula shown → HEALTHY / DRIFTING / OVERCOMMITTED (`commitment_auditor.py`).
5. **One first next action** for the coming week, so the review ends in motion, not admin.
## Trigger Phrases (auto-invoke without /cs:)
- "run my weekly review" / "weekly review time"
- "close my open loops" / "too many open loops"
- "GTD review" / "get current" / "mind sweep and review"
- "restart my review habit"
## Discipline
- **Scan before you ask** — evidence from the scanner first; the user's memory is what GTD says not to trust.
- **All five GET CURRENT steps are mandatory** — skip only with `--skip "N:reason"`, and the gate still names it.
- **Never self-certify** — the gate issues the verdict; relay its exit code, don't soften it.
- **Process, don't do** — anything over two minutes becomes a next action, not a detour.
- **Timebox 60-90 minutes** — past two hours, gate what's done and schedule the rest.
## Workflow
```bash
# 1. Inventory open loops in the workspace (checkboxes, TODO/FIXME, stale files)
python ../skills/weekly-review/scripts/open_loop_scanner.py --dir . --stale-days 14
# 2. Show the numbered ten-step checklist, then walk it with the user phase by phase
python ../skills/weekly-review/scripts/weekly_review_gate.py --list
# 3. Gate what was actually done — names every missing step; exit 2 if incomplete
python ../skills/weekly-review/scripts/weekly_review_gate.py \
--done "1,2,3,4,5,6,7,8,10" --skip "9:no someday list yet"
# 4. Audit the commitment portfolio (JSON list of {name, days_since_touched, has_next_action})
python ../skills/weekly-review/scripts/commitment_auditor.py --input commitments.json
```
## Stop Conditions
- Gate returns COMPLETE + commitment audit delivered + one next action named → done.
- Timebox exceeded → gate the partial review honestly (INCOMPLETE), schedule the remainder, stop.
- User says "stop" → gate what's done so the partial pass still counts, then drop it.
## Related
- Agent: [`cs-weekly-review`](../agents/cs-weekly-review.md)
- Skill: [`weekly-review`](../skills/weekly-review/SKILL.md)
- Siblings: `/cs:capture` (intake side of the same system), `productivity/reflect` (one-off reflection)
---
**Version:** 1.0.0

View file

@ -0,0 +1,99 @@
---
name: weekly-review
description: Use when someone wants to run a weekly review, close open loops, audit stalled projects and commitments, get their system back to trusted, restart a lapsed review habit, or says "/cs:weekly-review". Walks David Allen's three-phase loop — GET CLEAR, GET CURRENT, GET CREATIVE — with deterministic scripts that inventory open loops, gate the checklist with named gaps, and score commitment health 0-100.
argument-hint: "[optional: directory or notes to review]"
license: MIT
metadata:
version: 1.0.0
build_pattern: "Path-B ritual skill — GTD weekly-review loop preserved + deterministic scanner/gate/auditor scripts added"
distinct_from: "reflect (per-conversation reflection, not a recurring cadence); capture (intake that feeds the system, not the review that maintains it); project-management sprint retros (team ceremony, not a personal trusted-system audit)"
---
# Weekly Review — GTD Loop → Trusted System
> **Portability:** Reasoning-led skill with 3 stdlib Python scripts. No external APIs, no LLM calls
> in scripts. Works in Claude Code CLI and Claude.ai web. The scripts do the inventory and the
> gating; Claude and the user do the thinking.
## What this does
A personal system is only trustworthy if it gets reviewed — David Allen calls the weekly review
the critical success factor of the whole method. This skill walks the three phases in order and
refuses to call the review COMPLETE while any of the five mandatory GET CURRENT steps is
unaccounted for. Evidence first: scan the workspace for open loops before asking the user to
recall anything, because their memory is exactly what the method says not to trust.
## Phase 1 — GET CLEAR (steps 1-3)
Collect loose inputs, process every inbox to zero (clarify, don't do — anything over two minutes
becomes a next action), then a mind sweep to empty the head. Start with evidence:
```bash
# Inventory open loops: unchecked checkboxes, TODO/FIXME markers, stale files
python scripts/open_loop_scanner.py --dir ~/notes --stale-days 14
```
Route every loop found to a list — next action, waiting-for, someday/maybe, or trash.
## Phase 2 — GET CURRENT (steps 4-8, all mandatory)
Review the next-action lists (mark done, prune dead), the previous calendar (missed commitments
become actions), the upcoming calendar (prepare, don't react), the waiting-for list (chase or
drop), and every project for exactly one next action. Then gate honestly:
```bash
python scripts/weekly_review_gate.py --list # show the numbered ten-step checklist
python scripts/weekly_review_gate.py --done "1,2,3,4,5,6,7,8" --skip "9:no someday list yet"
```
The gate computes completion %, names every missing step, and exits 0 (COMPLETE) or 2
(INCOMPLETE). An unskipped missing GET CURRENT step **always** forces INCOMPLETE.
## Phase 3 — GET CREATIVE (steps 9-10)
Review someday/maybe (activate, keep, or kill), capture new ideas while the head is clear, then
audit the whole commitment portfolio:
```bash
python scripts/commitment_auditor.py --input commitments.json
```
Flags STALLED / NO-NEXT-ACTION / SOMEDAY-CANDIDATE, prints the health formula with the score, and
issues HEALTHY / DRIFTING / OVERCOMMITTED. End the review with one named next action.
## Scripts
| Script | Role |
|---|---|
| `scripts/open_loop_scanner.py` | Inventories unchecked checkboxes, TODO/FIXME markers, and stale files across a directory; grouped counts + per-file locations; `--json`. |
| `scripts/weekly_review_gate.py` | The ten-step three-phase checklist; `--done`/`--skip`/`--list`; completion % + named gaps → COMPLETE (exit 0) / INCOMPLETE (exit 2). |
| `scripts/commitment_auditor.py` | Flags stalled and actionless commitments, computes the 0-100 health score with the formula shown → HEALTHY / DRIFTING / OVERCOMMITTED. |
## References
- [`references/gtd_weekly_review_canon.md`](references/gtd_weekly_review_canon.md) — why the weekly review is the critical success factor; the three-phase structure; cadence discipline (7 sources)
- [`references/open_loop_psychology.md`](references/open_loop_psychology.md) — Zeigarnik effect, plan-making research, attention residue: why open loops tax attention (6 sources)
- [`references/review_cadence_design.md`](references/review_cadence_design.md) — horizons of focus, habit anchoring, timeboxing, failure modes, restart-after-lapse (7 sources)
## Assets
- [`assets/weekly_review_checklist.md`](assets/weekly_review_checklist.md) — fillable three-phase checklist
- [`assets/example_weekly_review.md`](assets/example_weekly_review.md) — a full worked review (scan → checklist → gate → audit → next action)
## Rules
- All five GET CURRENT steps are mandatory; skip only with a stated reason, and the gate still names it.
- Never self-certify — the gate issues the verdict; relay its exit code, don't soften it.
- Process, don't do: during the review, anything over two minutes becomes a next action, not a detour.
- Timebox 60-90 minutes; past two hours, gate what's done honestly and schedule the remainder.
- A lapsed habit restarts with a shorter pass and zero guilt — a review is maintenance, not judgment.
## Distinct From (don't reach for the wrong sibling)
- **`productivity/reflect`** — reflects on one conversation or piece of work, once. The weekly review is a recurring cadence over the whole system.
- **`productivity/capture`** — the intake funnel (brain dump → actions). Capture feeds the system; this review keeps it trusted.
- **`project-management` sprint retros** — a team ceremony about a shared iteration. This is a personal trusted-system audit.
---
**Version:** 1.0.0 · **Build pattern:** Path-B ritual skill — GTD weekly-review loop preserved + deterministic gate/scanner/auditor scripts added.

View file

@ -0,0 +1,97 @@
# Example: A Full Worked Weekly Review
> A realistic Friday-afternoon run of the whole loop — scan → checklist → gate → audit → next
> action — including an honest INCOMPLETE on the first gate and what happened next.
**Context:** Solo consultant, ~2 weeks since the last full review (one lapse). Notes and projects
live in `~/notes` as markdown. Timebox: 90 minutes, started 15:00.
---
## Step 0 — Scan for evidence (2 min)
```bash
python scripts/open_loop_scanner.py --dir ~/notes --stale-days 14
```
Result: **19 open loops** — 11 unchecked checkboxes across 5 files, 3 TODO markers in scripts,
5 stale files (worst: `someday/write-a-novel.md`, 63 days untouched).
## Phase 1 — GET CLEAR (25 min)
1. **Collect:** 4 paper receipts photographed, 6 screenshots moved out of the desktop, 2 voicemail
notes transcribed → all into the task inbox.
2. **Inboxes to zero:** 47 emails processed (31 archived, 9 became next actions, 4 waiting-for,
3 replied under two minutes). Task inbox: 14 items clarified. Notes-app inbox: 8 items.
3. **Mind sweep:** 9 new items surfaced, including "renew professional insurance" (a real loop
that was living rent-free in working memory) and "pitch retainer renewal to Acme."
## Phase 2 — GET CURRENT (40 min)
4. **Next-action lists:** 12 marked done, 3 pruned as dead, 2 flagged stuck (both blocked on the
same unanswered client email — chased in step 7).
5. **Previous calendar (2 weeks back, because of the lapse):** the Acme kickoff spawned 3
follow-ups never captured; a dentist appointment was missed → rebook action created.
6. **Upcoming calendar:** conference talk in 12 days → "draft outline" created *now* instead of
the night before; two client calls need agendas → 2 prep actions.
7. **Waiting-for:** 6 items. 2 chased (incl. the blocker from step 4), 1 re-dated, 1 dropped
(the vendor clearly isn't answering — found an alternative instead).
8. **Project lists:** exported to `commitments.json` and audited:
```bash
python scripts/commitment_auditor.py --input commitments.json
```
```
Flags: STALLED 3 · NO-NEXT-ACTION 2 · SOMEDAY-CANDIDATE 1
Formula: score = 100 - 30*(stalled/total) - 40*(no_next_action/total) - 30*(someday_candidates/total)
COMMITMENT HEALTH: 66.7/100 VERDICT: DRIFTING
```
Resolutions: "Hire a designer" got a next action ("post the brief in two freelance communities");
"Write a novel" was honestly moved to someday/maybe; "Learn Spanish" got a concrete next action
("book Tuesday trial lesson") instead of the standing guilt entry.
## First gate — honest INCOMPLETE (15:05 + 67 min)
Ten minutes left in the timebox and phase 3 untouched. Gate what's true:
```bash
python scripts/weekly_review_gate.py --done "1,2,3,4,5,6,7,8"
```
```
Completion: 80.0% (done 8 · skipped 0 · missing 2)
Missing steps: 9. [GET CREATIVE] Review someday/maybe · 10. [GET CREATIVE] Capture new ideas
VERDICT: INCOMPLETE (exit code 2)
```
All five GET CURRENT steps done, so nothing *forced* the incomplete — GET CREATIVE was simply not
done yet. Ten minutes remained, and step 9 was half-triggered by the audit anyway, so: continue.
## Phase 3 — GET CREATIVE (10 min)
9. **Someday/maybe:** 14 items reviewed. 1 activated ("small-group workshop" — two people asked
about it this month), 2 killed without ceremony, novel filed with a clear conscience.
10. **New ideas:** 3 captured, including a productized-audit offering sketched in four bullets.
## Final gate — COMPLETE
```bash
python scripts/weekly_review_gate.py --done "1,2,3,4,5,6,7,8,9,10"
```
```
Completion: 100.0% (done 10 · skipped 0 · missing 0)
VERDICT: COMPLETE (exit code 0)
```
## Close (total: 82 min)
**One next action for the coming week:** send the Acme retainer-renewal email (drafted Monday
09:00, calendar-blocked).
**What made this a good review:** evidence first (the scan found loops memory had dropped); the
lapse was handled with a look-back, not a guilt marathon; the first gate was allowed to say
INCOMPLETE; every flagged project left with a disposition; and it ended inside the timebox with
one concrete commitment.

View file

@ -0,0 +1,59 @@
# Weekly Review Checklist
> Fillable template. Copy this file (or print it), work top to bottom, then gate it:
> `python scripts/weekly_review_gate.py --done "..." --skip "N:reason"`
**Week of:** ____________ **Started:** ____:____ **Timebox ends (max 2h):** ____:____
---
## Phase 1 — GET CLEAR
- [ ] **1. Collect loose inputs** — papers, receipts, notes, screenshots, downloads → into an inbox
- Scanner run? `python scripts/open_loop_scanner.py --dir ______ --stale-days 14`
- Loops found: checkboxes ____ · TODO/FIXME ____ · stale files ____
- [ ] **2. Process inboxes to zero** — clarify, don't do (two-minute rule is the only exception)
- Inboxes processed: ☐ email ☐ task inbox ☐ notes app ☐ desk/physical ☐ other: ______
- [ ] **3. Empty your head** — mind sweep; capture every commitment still riding in memory
- New items captured: ____
## Phase 2 — GET CURRENT (all five mandatory)
- [ ] **4. Review next-action lists** — mark done, prune dead, surface stuck
- Done: ____ · Pruned: ____ · Stuck (needs decision): ____
- [ ] **5. Review previous calendar** — missed or spawned commitments become captured items
- Items captured from the look-back: ____
- [ ] **6. Review upcoming calendar** — prepare, don't react
- Prep actions created: ____
- [ ] **7. Review waiting-for list** — chase, re-date, or drop each item
- Chased: ____ · Re-dated: ____ · Dropped: ____
- [ ] **8. Review project lists** — every active project has exactly one next action
- Auditor run? `python scripts/commitment_auditor.py --input ______`
- Health score: ____/100 · Verdict: ______
- STALLED: ____ · NO-NEXT-ACTION: ____ · SOMEDAY-CANDIDATE: ____
## Phase 3 — GET CREATIVE
- [ ] **9. Review someday/maybe** — activate, keep, or kill
- Activated: ____ · Killed: ____
- [ ] **10. Capture new ideas** — add them while the head is clear
- New ideas captured: ____
---
## Gate it
```bash
python scripts/weekly_review_gate.py --done "____________" --skip "____________"
```
**Gate verdict:** ☐ COMPLETE (exit 0) ☐ INCOMPLETE (exit 2)
**Missing steps named by the gate:** ______________________________________
## Close
**One next action for the coming week:** ______________________________________
**Finished:** ____:____ (total: ____ min — target 60-90)
> Skipped a step? Write the reason above and give it first priority next week.
> Lapsed last week? Shorter pass, zero guilt. The review is maintenance, not judgment.

View file

@ -0,0 +1,95 @@
# The GTD Weekly Review Canon
> Why David Allen calls the weekly review the "critical success factor" of the whole method, what
> the three phases actually contain, and why the cadence matters more than the format.
## The claim: the review is the keystone, not an accessory
Getting Things Done rests on one psychological bargain: your mind will stop nagging you about
commitments **only if** it trusts that the external system holds all of them and that the system
gets looked at. Capture alone doesn't buy that trust — a list nobody reviews is just a graveyard
with better formatting. Allen is explicit that the weekly review is "the critical success factor"
and that people who fall off GTD almost always fall off here first: they keep capturing, stop
reviewing, watch the lists rot, and then stop trusting the lists — at which point everything moves
back into their head and the whole system collapses.
The corollary this skill enforces: a review that skims is not a review. Marking the ritual "done"
while the core steps went unwalked actively *damages* trust in the system, because now the lists
are stale **and** carry a false freshness stamp. Hence the hard gate: unaccounted-for GET CURRENT
steps always force an INCOMPLETE verdict.
## The three phases
Allen's own material organizes the checklist into three movements, in a deliberate order:
### 1. GET CLEAR — return to ground zero
- **Collect loose papers and materials.** Physical and digital strays: receipts, notes, downloads,
screenshots, business cards. Everything into an inbox.
- **Get "in" to zero.** Process every inbox — email, notes app, task inbox, desk tray — by
*clarifying*, not doing. Each item becomes a next action, a project, a waiting-for, a
someday/maybe, reference material, or trash. The two-minute rule is the only exception: if
acting takes under two minutes, do it now.
- **Empty your head.** A mind sweep: write down every new project, task, or nagging thought still
riding in working memory. This is the step that converts psychic RAM into system entries.
### 2. GET CURRENT — make the system match reality (the mandatory core)
- **Review next-action lists.** Mark off what's done, prune what's dead, notice what's stuck.
- **Review the previous calendar.** Look back over the past week or two for missed commitments,
follow-ups spawned by meetings, promises made in passing. Each becomes a captured item.
- **Review the upcoming calendar.** Look ahead so preparation happens now instead of panic later.
- **Review the waiting-for list.** Everything delegated or pending from others: chase it, re-date
it, or drop it. Unreviewed waiting-fors are where other people's slippage becomes your surprise.
- **Review project lists.** The load-bearing step: every active project must have **exactly one
next action** on a list. A project with none is a wish; a project whose next action is buried is
stalled. This step is why GTD projects keep moving without daily replanning.
### 3. GET CREATIVE — lift your eyes off the runway
- **Review someday/maybe.** Activate what's become timely, keep what still sparks, kill what
doesn't. This list only works if it's periodically culled — otherwise it becomes a guilt museum.
- **Capture new ideas.** With a clear head and a current system, ambition is safe again. Allen's
point: creativity shows up *after* clearing, not instead of it.
## Cadence discipline
- **Weekly is the resolution of most working commitments.** Allen's observation is pragmatic, not
mystical: most people's world shifts materially over a week — meetings compound, projects drift,
promises accumulate. Daily is too granular to see project-level drift; monthly lets loops rot.
- **Same time, same trigger.** The canonical recommendation (echoed by every serious GTD guide) is
a recurring, protected block — commonly Friday afternoon or Sunday evening — anchored to an
existing rhythm so the review doesn't depend on motivation.
- **Timeboxed.** One to two hours. A review that regularly takes three hours gets skipped the
moment a week is busy, and skipped reviews are how the habit dies. Better a 45-minute honest
pass with steps explicitly skipped-with-reason than a marathon that happens twice and never again.
- **The review is steering, not rowing.** Forte's periodic-review framing and Allen's original
agree: during the review you decide and route; you do not execute (two-minute items excepted).
A review that turns into a work session never finishes.
## What "done" means
The review is complete when: every inbox is empty, every open loop found is on a list, every
active project has one next action, both calendars have been swept, every waiting-for has been
touched, and someday/maybe has been consciously kept or culled. That definition is checkable,
which is why this skill gates it with a script instead of a feeling.
## Sources
1. David Allen, *Getting Things Done: The Art of Stress-Free Productivity* (Penguin, original
edition 2001; revised edition 2015) — chapter on the power of the weekly review; the "critical
success factor" claim; the two-minute rule; the project/next-action distinction.
2. David Allen, *Ready for Anything: 52 Productivity Principles for Getting Things Done* (Viking,
2003) — principles on review cadence and why unrenegotiated commitments erode self-trust.
3. GTD® methodology guides, gettingthingsdone.com (David Allen Company) — the official Weekly
Review® checklist with the GET CLEAR / GET CURRENT / GET CREATIVE structure used here.
4. Tiago Forte, *Building a Second Brain* (Atria, 2022) and the PARA method essays (fortelabs.com)
— periodic review as the maintenance loop of any personal knowledge system; weekly review as
routing, not execution.
5. Doist, "The Complete Guide to Getting Things Done" (todoist.com/productivity-methods) — a
widely used practitioner's walkthrough of the weekly review steps and common adaptations.
6. Cal Newport, *Deep Work* (Grand Central, 2016) and the weekly-planning essays at
calnewport.com — weekly planning as the scale at which meaningful work is steered; the
shutdown-ritual analogue of closing loops before rest.
7. Francis Wade, *Perfect Time-Based Productivity* (2014) — on why review cadences fail and how
habit load, not intention, predicts whether a review sticks.

View file

@ -0,0 +1,95 @@
# The Psychology of Open Loops
> Why unfinished commitments tax attention even when you're not working on them — the research
> behind Allen's "mind like water," and why writing a next action down genuinely quiets the mind.
## The Zeigarnik effect: unfinished tasks stay resident
Bluma Zeigarnik's classic experiments found that interrupted, unfinished tasks are recalled
substantially better than completed ones — the canonical finding is roughly a 2:1 recall
advantage. The interpretation that survived a century of follow-up work: an unfinished task
maintains a state of tension that keeps it active in memory. Completion releases the tension;
interruption preserves it.
For a personal system, the implication is direct: every commitment you've made and not resolved —
the un-replied email, the half-done project, the promise made in a hallway — is an *open loop*
that your memory keeps rehearsing whether or not rehearsing helps. Allen's term for the
accumulated hum of these loops is "psychic RAM": working memory spent tracking commitments instead
of thinking.
## Masicampo & Baumeister: the plan, not the completion, closes the loop
The most important modern refinement for GTD comes from E.J. Masicampo and Roy Baumeister's
"Consider It Done!" studies. Unfulfilled goals were shown to intrude on unrelated tasks — subjects
with an unfinished goal performed worse on reading comprehension and were more distracted by
goal-related words. The striking result: **making a specific plan for the goal eliminated the
intrusions**, even though the goal itself remained unfinished. The mind doesn't demand completion
before it stands down; it demands a *credible plan* held somewhere it trusts.
This is the experimental vindication of the weekly review's core move. You don't have to finish
your projects to stop them from nagging you — you have to give each one a concrete next action in
a system you demonstrably revisit. The "demonstrably revisit" clause is why the review has to
actually happen on cadence: a plan written into a system that never gets read stops being
credible, and the intrusions come back.
## Attention residue: switching without closing costs twice
Sophie Leroy's work on attention residue shows that when people switch from Task A to Task B
without closing A, part of their attention remains allocated to A — and performance on B suffers.
Residue is worst when the prior task is unfinished and low in "closure." Open loops therefore
don't just occupy memory at rest; they degrade the quality of whatever you *are* working on,
because each unclosed thread leaks attention into the current one.
The weekly review is a batch closure operation: one scheduled session in which every dangling
thread gets an explicit disposition (done, next action, waiting-for, someday/maybe, trash), so the
rest of the week can be spent with attention undivided.
## Cognitive load: the system as external memory
John Sweller's cognitive load theory frames working memory as severely capacity-limited (a handful
of elements at a time) while schemas and external supports offload that limit. A trusted external
system is precisely such an offload: it converts "remember to remember" (extraneous load, paid
continuously) into "consult the list" (paid once, at review time). The weekly review is the
maintenance the offload requires — an external memory that drifts from reality re-imports its
contents back into working memory, with interest.
## "Mind like water"
Allen's martial-arts image summarizes the target state: a mind that responds to what's in front of
it with appropriate force and returns to calm — neither over-reacting to a new input nor
under-reacting because it's busy rehearsing forty old ones. The research above cashes the metaphor
out: capture ends rehearsal (Zeigarnik), a next action ends intrusion (Masicampo & Baumeister),
closure ends residue (Leroy), and the external system ends the memory tax (Sweller). The review is
what keeps all four working.
## Practical consequences encoded in this skill
- **Scan, don't recall.** The open-loop scanner walks the actual workspace because self-report of
open loops is exactly what the research says is unreliable — loops intrude, but they don't
enumerate themselves on demand.
- **Every project leaves with one next action.** Not "review the project" — a physical,
visible next step. That's the specific-plan condition from the plan-making studies.
- **Skipping the waiting-for review is a real cost.** Delegated items are open loops too; the
tension doesn't care that someone else is holding the ball.
- **Restart without guilt.** The loops don't punish you for lapsing; they just come back. The
correct response to a lapsed cadence is a shorter review, not a longer apology.
## Sources
1. Bluma Zeigarnik, "Über das Behalten von erledigten und unerledigten Handlungen" ("On Finished
and Unfinished Tasks"), *Psychologische Forschung* 9, 1927 — the original recall-advantage
experiments for interrupted tasks.
2. E.J. Masicampo & Roy F. Baumeister, "Consider It Done! Plan Making Can Eliminate the Cognitive
Effects of Unfulfilled Goals," *Journal of Personality and Social Psychology* 101(4), 2011 —
plan-making eliminates goal intrusions without completion.
3. Sophie Leroy, "Why Is It So Hard to Do My Work? The Challenge of Attention Residue When
Switching Between Work Tasks," *Organizational Behavior and Human Decision Processes* 109(2),
2009 — attention residue from unclosed tasks degrades subsequent performance.
4. John Sweller, "Cognitive Load During Problem Solving: Effects on Learning," *Cognitive Science*
12(2), 1988 (and later cognitive load theory syntheses) — working-memory limits and the case
for external offloading.
5. David Allen, *Getting Things Done* (Penguin, revised edition 2015) — "psychic RAM," "mind like
water," and open loops as the phenomenology the research explains.
6. Roy F. Baumeister & John Tierney, *Willpower: Rediscovering the Greatest Human Strength*
(Penguin, 2011) — the accessible synthesis of the Zeigarnik and plan-making work, including its
explicit connection to GTD.

View file

@ -0,0 +1,99 @@
# Designing a Review Cadence That Survives Contact With Real Weeks
> Choosing the right horizon for each review, anchoring the habit so it fires without motivation,
> timeboxing so it keeps firing, and restarting after the inevitable lapse.
## Match the review to the horizon
GTD's "horizons of focus" model assigns different altitudes to different cadences. The mistake to
avoid is asking one review to do every altitude's job:
| Horizon | Cadence | What gets reviewed | What it is NOT |
|---|---|---|---|
| Ground — next actions & calendar | Daily (minutes) | What's next *right now*; today's hard landscape | Not a system audit |
| Projects & waiting-for | **Weekly** (60-90 min) | Every list, both calendars, every project's next action | Not goal-setting |
| Areas of responsibility | Monthly-ish | Are whole areas (health, finances, team) getting neglected? | Not task triage |
| Goals, vision, purpose | Quarterly / yearly | Direction: right projects at all? | Not maintenance |
The weekly review is the load-bearing middle: frequent enough that loops can't rot, high enough to
see project drift the daily view can't. Skipping it and hoping the daily check compensates is the
most common cadence failure — daily views show *tasks*, and a system can be current on tasks while
five projects silently stall.
## Anchor the habit; don't schedule the intention
Habit research is unambiguous that repetition after a **stable cue** — not motivation, not
importance — is what makes a behavior automatic. Applied to the review:
- **Fogg's recipe:** After I [existing anchor], I will [first tiny step]. "After Friday's last
meeting ends, I open the review checklist" beats "do weekly review Friday" — the anchor is an
event that already happens, and the committed behavior is deliberately tiny (open the checklist,
not finish the review). Momentum does the rest; celebration seals it.
- **Clear's environment framing:** make it obvious (recurring calendar block with the checklist
linked), easy (everything in one place — this skill's scripts and checklist), and satisfying
(the COMPLETE verdict and health score are the visible reward). Habit formation takes weeks to
months of repetition — expect the review to feel effortful for the first dozen iterations.
- **Protect the block like a meeting with someone important,** because it is: it's the meeting
where you renegotiate every commitment you've made with yourself.
## Timebox it — the review must be cheap enough to repeat
A review that costs three hours will be skipped the first busy week, and skipped once is the
beginning of lapsed. Working discipline:
- **Target 60-90 minutes; hard-stop at two.** At the stop, gate what's done honestly
(INCOMPLETE is information, not failure), schedule the remainder, leave.
- **Process, don't do.** The two-minute rule is the *only* execution allowed. Everything else
becomes a next action. Reviews balloon when they silently turn into work sessions.
- **Let the scripts carry the clerical weight.** Scanning for loops and computing portfolio health
are mechanical; spend the human minutes on decisions (chase or drop? activate or kill?).
## Failure modes
- **The guilt ritual.** The review becomes a weekly confrontation with everything undone, feels
bad, and gets avoided. Countermeasures: the someday/maybe list exists precisely so ambitions can
be parked without shame; the commitment auditor names SOMEDAY-CANDIDATEs so parking is a normal
verdict, not an admission of defeat.
- **The marathon.** Usually caused by processing debt (inboxes untouched all week) or by doing
work mid-review. Fix the input side (a 10-minute daily sweep keeps inboxes shallow) and enforce
process-don't-do.
- **The skimmed review.** All boxes ticked in fifteen minutes, no calendar swept, no project
touched. Worse than skipping, because it stamps stale lists as fresh. This is exactly what the
gate's mandatory GET CURRENT rule exists to catch.
- **The floating review.** No anchor, so it happens "when there's time," which is never. Re-anchor
to a fixed event and shrink the committed first step.
- **Altitude creep.** The weekly review turns into an existential goals session every week and
becomes exhausting. Goals live at the quarterly horizon; the weekly review only has to make the
system current.
## Restarting after a lapse
Everyone lapses. The restart protocol that preserves the habit:
1. **No back-processing marathon.** Don't attempt to review the missed weeks; review *now*. Old
loops are still in the system or will surface in the scan.
2. **Run a shorter pass.** GET CLEAR + the five GET CURRENT steps; explicitly skip GET CREATIVE
with a reason if energy is short. An honest 80% INCOMPLETE beats a fantasized 100%.
3. **Zero self-punishment.** Missing-then-restarting is what successful habit maintenance actually
looks like in the data; a single missed instance measurably does not derail formation — quitting
in response to missing does.
4. **Re-anchor before re-ambitioning.** Fix the cue and shrink the step; don't respond to a lapse
by designing a grander review.
## Sources
1. David Allen, *Getting Things Done* (Penguin, revised edition 2015) — horizons of focus;
the weekly review's altitude; the two-minute rule.
2. BJ Fogg, *Tiny Habits: The Small Changes That Change Everything* (Houghton Mifflin Harcourt,
2020) — anchor → tiny behavior → celebration; behavior = motivation × ability × prompt.
3. James Clear, *Atomic Habits* (Avery, 2018) — cue/craving/response/reward; make it obvious,
easy, satisfying; "never miss twice."
4. Phillippa Lally et al., "How Are Habits Formed: Modelling Habit Formation in the Real World,"
*European Journal of Social Psychology* 40(6), 2010 — automaticity takes ~18-254 days; a single
missed repetition does not materially harm formation.
5. Cal Newport, *Deep Work* (Grand Central, 2016) and weekly/quarterly-planning essays at
calnewport.com — multi-scale planning; the shutdown ritual as daily loop-closing.
6. Tiago Forte, "The Weekly Review Is an Operating System" (fortelabs.com) — the review as routing
and maintenance, and why doing work inside it kills it.
7. GTD® methodology guides, gettingthingsdone.com (David Allen Company) — official guidance on
scheduling, protecting, and recovering the Weekly Review®.

View file

@ -0,0 +1,180 @@
#!/usr/bin/env python3
"""commitment_auditor.py — Audit a commitment portfolio: flags, health score, one honest verdict.
Step 8 of the weekly review (review project lists) in numbers. Reads a JSON list of commitments:
[{"name": "Website relaunch", "days_since_touched": 3, "has_next_action": true}, ...]
and flags each one:
STALLED days_since_touched > 14 (a week-and-a-half of silence is drift, not rest)
NO-NEXT-ACTION has_next_action is false (a project with no next action is a wish)
SOMEDAY-CANDIDATE days_since_touched > 45 (be honest: activate it or move it to someday/maybe)
Then computes a 0-100 commitment-health score. The formula is printed with the output so the
number is auditable, never mystical:
score = 100 - 30*(stalled/total) - 40*(no_next_action/total) - 30*(someday/total)
(SOMEDAY-CANDIDATEs also count as STALLED the double penalty is deliberate escalation.)
Verdict: HEALTHY (>= 80) · DRIFTING (50-79) · OVERCOMMITTED (< 50)
Deterministic logic. No LLM calls, no network. Stdlib only.
Usage:
python commitment_auditor.py --input commitments.json
python commitment_auditor.py --input commitments.json --json
python commitment_auditor.py --sample
Exit codes:
0 audit complete (any verdict) also --sample / --help
2 input file missing, unreadable, or not the expected JSON shape
"""
import argparse
import json
import sys
from typing import Any, Dict, List
STALLED_DAYS = 14
SOMEDAY_DAYS = 45
FORMULA = ("score = 100 - 30*(stalled/total) - 40*(no_next_action/total) "
"- 30*(someday_candidates/total)")
SAMPLE_COMMITMENTS = [
{"name": "Website relaunch", "days_since_touched": 3, "has_next_action": True},
{"name": "Q3 budget draft", "days_since_touched": 9, "has_next_action": True},
{"name": "Hire a designer", "days_since_touched": 21, "has_next_action": False},
{"name": "Learn Spanish", "days_since_touched": 41, "has_next_action": True},
{"name": "Write a novel", "days_since_touched": 63, "has_next_action": False},
{"name": "Renew passports", "days_since_touched": 2, "has_next_action": True},
]
def validate(data: Any) -> List[Dict[str, Any]]:
if not isinstance(data, list) or not data:
raise ValueError("input must be a non-empty JSON list of commitment objects")
for i, c in enumerate(data):
if not isinstance(c, dict):
raise ValueError(f"entry {i} is not an object")
for key in ("name", "days_since_touched", "has_next_action"):
if key not in c:
raise ValueError(f"entry {i} ({c.get('name', '?')!r}) missing key: {key!r}")
if not isinstance(c["days_since_touched"], (int, float)) or c["days_since_touched"] < 0:
raise ValueError(f"entry {i}: days_since_touched must be a non-negative number")
if not isinstance(c["has_next_action"], bool):
raise ValueError(f"entry {i}: has_next_action must be true or false")
return data
def audit(commitments: List[Dict[str, Any]]) -> Dict[str, Any]:
total = len(commitments)
rows = []
n_stalled = n_no_action = n_someday = 0
for c in commitments:
flags = []
if c["days_since_touched"] > STALLED_DAYS:
flags.append("STALLED")
n_stalled += 1
if not c["has_next_action"]:
flags.append("NO-NEXT-ACTION")
n_no_action += 1
if c["days_since_touched"] > SOMEDAY_DAYS:
flags.append("SOMEDAY-CANDIDATE")
n_someday += 1
rows.append({"name": c["name"],
"days_since_touched": c["days_since_touched"],
"has_next_action": c["has_next_action"],
"flags": flags})
score = 100.0 - 30.0 * (n_stalled / total) - 40.0 * (n_no_action / total) \
- 30.0 * (n_someday / total)
score = round(max(0.0, min(100.0, score)), 1)
if score >= 80:
verdict = "HEALTHY"
headline = ("The portfolio is honest: most commitments are moving and have a next "
"action. Keep the cadence.")
elif score >= 50:
verdict = "DRIFTING"
headline = ("Several commitments are coasting on good intentions. Give each flagged "
"item a next action, a waiting-for, or a someday/maybe home this review.")
else:
verdict = "OVERCOMMITTED"
headline = ("More commitments than attention. Kill or park the SOMEDAY-CANDIDATEs, "
"then rebuild next actions for what survives — fewer, moving projects "
"beat many, stalled ones.")
return {
"total_commitments": total,
"flag_counts": {"STALLED": n_stalled, "NO-NEXT-ACTION": n_no_action,
"SOMEDAY-CANDIDATE": n_someday},
"thresholds": {"stalled_days": STALLED_DAYS, "someday_days": SOMEDAY_DAYS},
"commitments": rows,
"formula": FORMULA,
"health_score": score,
"verdict": verdict,
"headline": headline,
}
def render_human(r: Dict[str, Any]) -> str:
out = ["Commitment Auditor (project lists in numbers)", "=" * 64]
out.append(f" Commitments: {r['total_commitments']} "
f"Stalled > {r['thresholds']['stalled_days']}d · "
f"Someday-candidate > {r['thresholds']['someday_days']}d")
out.append("")
for c in r["commitments"]:
mark = "OK " if not c["flags"] else "!! "
flags = f" [{', '.join(c['flags'])}]" if c["flags"] else ""
out.append(f" {mark}{c['name']}{c['days_since_touched']}d since touched, "
f"next action: {'yes' if c['has_next_action'] else 'NO'}{flags}")
fc = r["flag_counts"]
out.append("")
out.append(f" Flags: STALLED {fc['STALLED']} · NO-NEXT-ACTION {fc['NO-NEXT-ACTION']} · "
f"SOMEDAY-CANDIDATE {fc['SOMEDAY-CANDIDATE']}")
out.append(f" Formula: {r['formula']}")
out.append(f" COMMITMENT HEALTH: {r['health_score']}/100 VERDICT: {r['verdict']}")
out.append("")
out.append(f" {r['headline']}")
return "\n".join(out)
def main(argv: List[str]) -> int:
p = argparse.ArgumentParser(
description="Audit commitments: STALLED / NO-NEXT-ACTION / SOMEDAY-CANDIDATE flags + 0-100 health score.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__,
)
p.add_argument("--input", help="Path to a JSON list of "
'{"name", "days_since_touched", "has_next_action"} objects')
p.add_argument("--sample", action="store_true", help="Run on embedded example data and exit 0")
p.add_argument("--json", action="store_true", help="Emit JSON instead of text")
args = p.parse_args(argv)
try:
if args.sample:
commitments = validate(SAMPLE_COMMITMENTS)
elif args.input:
with open(args.input, "r", encoding="utf-8") as f:
commitments = validate(json.load(f))
else:
p.print_help()
print("\nerror: provide --input <file.json> or --sample", file=sys.stderr)
return 2
except (OSError, ValueError, json.JSONDecodeError) as e:
print(f"error: {e}", file=sys.stderr)
return 2
result = audit(commitments)
if args.json:
print(json.dumps(result, indent=2))
else:
print(render_human(result))
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

View file

@ -0,0 +1,198 @@
#!/usr/bin/env python3
"""open_loop_scanner.py — Inventory the open loops hiding in a directory.
The GET CLEAR phase of a weekly review starts with evidence, not memory. This script scans a
directory for three kinds of open loop:
1. checkbox unchecked markdown checkboxes: - [ ] / * [ ]
2. todo_fixme TODO / FIXME markers in text and source files
3. stale_file files not modified in --stale-days days (default 14)
Output is an inventory grouped by kind, with counts and per-file locations, so the review walks
real loops instead of whatever the user happens to remember.
Deterministic logic. No LLM calls, no network. Stdlib only.
Usage:
python open_loop_scanner.py --dir ~/notes
python open_loop_scanner.py --dir . --stale-days 30 --json
python open_loop_scanner.py --sample
Exit codes:
0 scan complete (even if zero loops found) also --sample / --help
2 directory not found or not a directory
"""
import argparse
import json
import os
import re
import sys
import time
from typing import Any, Dict, List
SKIP_DIRS = {".git", ".hg", ".svn", "node_modules", "__pycache__", ".venv", "venv",
"dist", "build", ".idea", ".vscode", ".cache"}
CHECKBOX_EXTS = {".md", ".markdown", ".txt"}
TEXT_EXTS = CHECKBOX_EXTS | {".py", ".js", ".ts", ".tsx", ".jsx", ".sh", ".rb", ".go",
".rs", ".java", ".c", ".h", ".cpp", ".css", ".html",
".yaml", ".yml", ".toml", ".ini", ".cfg", ".sql"}
CHECKBOX_RE = re.compile(r"^\s*[-*]\s+\[ \]\s+(.*)$")
TODO_RE = re.compile(r"\b(TODO|FIXME)\b[:\s]*(.*)", re.IGNORECASE)
def _snippet(text: str, limit: int = 80) -> str:
text = text.strip()
return text if len(text) <= limit else text[: limit - 1] + ""
def scan(directory: str, stale_days: int) -> Dict[str, Any]:
now = time.time()
stale_cutoff = now - stale_days * 86400
checkboxes: List[Dict[str, Any]] = []
todos: List[Dict[str, Any]] = []
stale: List[Dict[str, Any]] = []
files_scanned = 0
for root, dirs, files in os.walk(directory):
dirs[:] = sorted(d for d in dirs if d not in SKIP_DIRS and not d.startswith("."))
for fname in sorted(files):
ext = os.path.splitext(fname)[1].lower()
if ext not in TEXT_EXTS:
continue
path = os.path.join(root, fname)
rel = os.path.relpath(path, directory)
files_scanned += 1
try:
mtime = os.path.getmtime(path)
if mtime < stale_cutoff:
stale.append({"file": rel,
"days_since_modified": int((now - mtime) // 86400)})
with open(path, "r", encoding="utf-8", errors="replace") as f:
for lineno, line in enumerate(f, 1):
if ext in CHECKBOX_EXTS:
m = CHECKBOX_RE.match(line)
if m:
checkboxes.append({"file": rel, "line": lineno,
"text": _snippet(m.group(1))})
m = TODO_RE.search(line)
if m:
todos.append({"file": rel, "line": lineno,
"marker": m.group(1).upper(),
"text": _snippet(m.group(2))})
except OSError:
continue
total = len(checkboxes) + len(todos) + len(stale)
return {
"directory": os.path.abspath(directory),
"stale_days_threshold": stale_days,
"files_scanned": files_scanned,
"total_open_loops": total,
"loops": {
"checkbox": {"count": len(checkboxes), "items": checkboxes},
"todo_fixme": {"count": len(todos), "items": todos},
"stale_file": {"count": len(stale), "items": stale},
},
}
SAMPLE_RESULT: Dict[str, Any] = {
"directory": "/home/sample/notes",
"stale_days_threshold": 14,
"files_scanned": 12,
"total_open_loops": 7,
"loops": {
"checkbox": {
"count": 3,
"items": [
{"file": "projects/website-relaunch.md", "line": 14,
"text": "email designer about the hero image"},
{"file": "projects/website-relaunch.md", "line": 15,
"text": "draft the pricing page copy"},
{"file": "inbox.md", "line": 3, "text": "book dentist appointment"},
],
},
"todo_fixme": {
"count": 2,
"items": [
{"file": "scripts/backup.sh", "line": 22, "marker": "TODO",
"text": "rotate old archives after 90 days"},
{"file": "notes/tax-prep.md", "line": 8, "marker": "FIXME",
"text": "the mileage total is wrong, recount Q2"},
],
},
"stale_file": {
"count": 2,
"items": [
{"file": "projects/learn-spanish.md", "days_since_modified": 41},
{"file": "someday/write-a-novel.md", "days_since_modified": 63},
],
},
},
}
KIND_LABEL = {
"checkbox": "Unchecked checkboxes (- [ ])",
"todo_fixme": "TODO / FIXME markers",
"stale_file": "Stale files (untouched past threshold)",
}
def render_human(r: Dict[str, Any]) -> str:
out = ["Open Loop Scanner (evidence first, memory second)", "=" * 64]
out.append(f" Directory: {r['directory']}")
out.append(f" Files scanned: {r['files_scanned']} "
f"Stale threshold: {r['stale_days_threshold']} days")
out.append(f" TOTAL OPEN LOOPS: {r['total_open_loops']}")
for kind in ("checkbox", "todo_fixme", "stale_file"):
bucket = r["loops"][kind]
out.append(f"\n {KIND_LABEL[kind]}{bucket['count']}")
for item in bucket["items"][:25]:
if kind == "stale_file":
out.append(f" - {item['file']} ({item['days_since_modified']} days untouched)")
elif kind == "todo_fixme":
out.append(f" - {item['file']}:{item['line']} [{item['marker']}] {item['text']}")
else:
out.append(f" - {item['file']}:{item['line']} {item['text']}")
if bucket["count"] > 25:
out.append(f" … and {bucket['count'] - 25} more")
out.append("")
out.append(" Feed these into GET CLEAR: collect, clarify, and route every loop")
out.append(" to a list — next action, waiting-for, someday/maybe, or trash.")
return "\n".join(out)
def main(argv: List[str]) -> int:
p = argparse.ArgumentParser(
description="Scan a directory for open loops: unchecked checkboxes, TODO/FIXME, stale files.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__,
)
p.add_argument("--dir", default=".", help="Directory to scan (default: current directory)")
p.add_argument("--stale-days", type=int, default=14,
help="Flag files not modified in this many days (default: 14)")
p.add_argument("--sample", action="store_true",
help="Print a canned deterministic scan result and exit 0")
p.add_argument("--json", action="store_true", help="Emit JSON instead of text")
args = p.parse_args(argv)
if args.sample:
result = SAMPLE_RESULT
else:
if not os.path.isdir(args.dir):
print(f"error: not a directory: {args.dir}", file=sys.stderr)
return 2
result = scan(args.dir, args.stale_days)
if args.json:
print(json.dumps(result, indent=2))
else:
print(render_human(result))
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

View file

@ -0,0 +1,230 @@
#!/usr/bin/env python3
"""weekly_review_gate.py — Gate a GTD weekly review: completion %, named gaps, one honest verdict.
Encodes David Allen's weekly-review checklist as three phases and ten numbered steps:
GET CLEAR 1. Collect loose inputs (papers, receipts, notes, downloads)
2. Process inboxes to zero (clarify, don't do)
3. Empty your head (mind sweep capture everything still riding in RAM)
GET CURRENT 4. Review next-action lists (mark done, prune dead, surface stuck)
5. Review previous calendar (missed/spawned commitments become actions)
6. Review upcoming calendar (prepare, don't react)
7. Review waiting-for list (chase, re-date, or drop each item)
8. Review project lists (every active project has exactly one next action)
GET CREATIVE 9. Review someday/maybe (activate, keep, or kill)
10. Capture new ideas (add to the system while your head is clear)
The gate's one hard rule: ALL FIVE GET CURRENT steps (4-8) are mandatory. A GET CURRENT step that
is neither done nor explicitly skipped with a reason ALWAYS forces INCOMPLETE a review that
skims the core is a guilt ritual, not a review.
Deterministic logic. No LLM calls. Stdlib only.
Usage:
python weekly_review_gate.py --list
python weekly_review_gate.py --done "1,2,3,4,5,6,7,8,10" --skip "9:no someday list yet"
python weekly_review_gate.py --done "1,4,6" --json
python weekly_review_gate.py --sample
Exit codes:
0 review COMPLETE (every step done or skipped with a reason) also --list / --sample / --help
2 review INCOMPLETE (at least one step neither done nor skipped)
3 bad input (unknown step number, malformed --done/--skip, step both done and skipped)
"""
import argparse
import json
import sys
from typing import Any, Dict, List
STEPS = [
(1, "GET CLEAR", "Collect loose inputs (papers, receipts, notes, downloads)"),
(2, "GET CLEAR", "Process inboxes to zero (clarify, don't do)"),
(3, "GET CLEAR", "Empty your head (mind sweep — capture everything still in RAM)"),
(4, "GET CURRENT", "Review next-action lists (mark done, prune dead, surface stuck)"),
(5, "GET CURRENT", "Review previous calendar (missed/spawned commitments become actions)"),
(6, "GET CURRENT", "Review upcoming calendar (prepare, don't react)"),
(7, "GET CURRENT", "Review waiting-for list (chase, re-date, or drop each item)"),
(8, "GET CURRENT", "Review project lists (every active project has one next action)"),
(9, "GET CREATIVE", "Review someday/maybe (activate, keep, or kill)"),
(10, "GET CREATIVE", "Capture new ideas (add them while your head is clear)"),
]
MANDATORY_PHASE = "GET CURRENT"
PHASE_ORDER = ["GET CLEAR", "GET CURRENT", "GET CREATIVE"]
def parse_done(raw: str) -> List[int]:
if not raw or not raw.strip():
return []
nums = []
for part in raw.split(","):
part = part.strip()
if not part:
continue
if not part.isdigit():
raise ValueError(f"--done entries must be step numbers, got: {part!r}")
nums.append(int(part))
return sorted(set(nums))
def parse_skips(raw_list: List[str]) -> Dict[int, str]:
skips: Dict[int, str] = {}
for raw in raw_list or []:
if ":" not in raw:
raise ValueError(f"--skip must be 'N:reason', got: {raw!r}")
num_s, reason = raw.split(":", 1)
num_s, reason = num_s.strip(), reason.strip()
if not num_s.isdigit():
raise ValueError(f"--skip step must be a number, got: {num_s!r}")
if not reason:
raise ValueError(f"--skip requires a non-empty reason: {raw!r}")
skips[int(num_s)] = reason
return skips
def evaluate(done: List[int], skips: Dict[int, str]) -> Dict[str, Any]:
valid = {n for n, _, _ in STEPS}
for n in done:
if n not in valid:
raise ValueError(f"unknown step number in --done: {n} (valid: 1-10)")
for n in skips:
if n not in valid:
raise ValueError(f"unknown step number in --skip: {n} (valid: 1-10)")
overlap = set(done) & set(skips)
if overlap:
raise ValueError(f"step(s) both done and skipped: {sorted(overlap)}")
rows = []
missing: List[Dict[str, Any]] = []
for n, phase, label in STEPS:
if n in done:
status = "DONE"
elif n in skips:
status = "SKIPPED"
else:
status = "MISSING"
missing.append({"step": n, "phase": phase, "label": label})
rows.append({"step": n, "phase": phase, "label": label, "status": status,
"skip_reason": skips.get(n)})
considered = len(STEPS) - len(skips)
completion_pct = round(100.0 * len(done) / considered, 1) if considered else 100.0
missing_mandatory = [m for m in missing if m["phase"] == MANDATORY_PHASE]
skipped_mandatory = [n for n in skips if 4 <= n <= 8]
verdict = "COMPLETE" if not missing else "INCOMPLETE"
forced = bool(missing_mandatory)
warnings = []
if skipped_mandatory:
warnings.append(
f"GET CURRENT step(s) {sorted(skipped_mandatory)} skipped with a reason — "
"allowed, but the core of the review was not fully walked. Do them first next week."
)
if forced:
warnings.append(
"Unskipped GET CURRENT step(s) missing — this ALWAYS forces INCOMPLETE, "
"regardless of completion %."
)
return {
"steps": rows,
"done_count": len(done),
"skipped_count": len(skips),
"missing_count": len(missing),
"completion_pct": completion_pct,
"missing_steps": missing,
"missing_mandatory": missing_mandatory,
"mandatory_gate_forced_incomplete": forced,
"warnings": warnings,
"verdict": verdict,
"exit_code": 0 if verdict == "COMPLETE" else 2,
}
def render_list() -> str:
out = ["GTD Weekly Review — the ten steps", "=" * 64]
for phase in PHASE_ORDER:
tag = " (all mandatory)" if phase == MANDATORY_PHASE else ""
out.append(f"\n{phase}{tag}")
for n, p, label in STEPS:
if p == phase:
out.append(f" {n:>2}. {label}")
out.append("\nMark progress with --done \"1,2,3\" and --skip \"N:reason\".")
return "\n".join(out)
def render_human(r: Dict[str, Any]) -> str:
out = ["Weekly Review Gate (GET CLEAR -> GET CURRENT -> GET CREATIVE)", "=" * 64]
current_phase = None
for row in r["steps"]:
if row["phase"] != current_phase:
current_phase = row["phase"]
tag = " (mandatory)" if current_phase == MANDATORY_PHASE else ""
out.append(f"\n {current_phase}{tag}")
mark = {"DONE": "[x]", "SKIPPED": "[s]", "MISSING": "[ ]"}[row["status"]]
line = f" {mark} {row['step']:>2}. {row['label']}"
if row["status"] == "SKIPPED":
line += f" (skipped: {row['skip_reason']})"
out.append(line)
out.append("")
out.append(f" Completion: {r['completion_pct']}% "
f"(done {r['done_count']} · skipped {r['skipped_count']} · missing {r['missing_count']})")
if r["missing_steps"]:
out.append("\n Missing steps (do these to finish):")
for m in r["missing_steps"]:
star = " <- MANDATORY" if m["phase"] == MANDATORY_PHASE else ""
out.append(f" - {m['step']}. [{m['phase']}] {m['label']}{star}")
for w in r["warnings"]:
out.append(f"\n WARNING: {w}")
out.append("")
out.append(f" VERDICT: {r['verdict']}" +
(" (mandatory GET CURRENT gate forced this)" if r["mandatory_gate_forced_incomplete"] else ""))
return "\n".join(out)
def main(argv: List[str]) -> int:
p = argparse.ArgumentParser(
description="Gate a GTD weekly review: completion %, named gaps, COMPLETE/INCOMPLETE verdict.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__,
)
p.add_argument("--done", default="", help='Comma-separated step numbers completed, e.g. "1,3,5"')
p.add_argument("--skip", action="append", default=[],
help='Skip a step with a reason, e.g. --skip "9:no someday list yet" (repeatable)')
p.add_argument("--list", action="store_true", help="Show the numbered ten-step checklist and exit")
p.add_argument("--sample", action="store_true",
help="Run an embedded example review (always exits 0; the sample shows an INCOMPLETE verdict)")
p.add_argument("--json", action="store_true", help="Emit JSON instead of text")
args = p.parse_args(argv)
if args.list:
print(render_list())
return 0
try:
if args.sample:
done = parse_done("1,2,3,4,6,7,8")
skips = parse_skips(["9:no someday list yet"])
else:
done = parse_done(args.done)
skips = parse_skips(args.skip)
result = evaluate(done, skips)
except ValueError as e:
print(f"error: {e}", file=sys.stderr)
return 3
if args.json:
print(json.dumps(result, indent=2))
else:
print(render_human(result))
if args.sample:
return 0 # sample is illustrative; documented to always exit 0
return result["exit_code"]
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))