How it works · feature reference

How MTK works,
feature by feature.

The overview shows the four-stage pipeline. This is the full reference: every skill, agent, hook, and mechanism — what it does, a real example, and how & why it works. Every claim on this page was checked against the toolkit's own source.

152 mechanisms documented 46 skills · 6 review agents checked against source

Entries are grouped by where they sit in the workflow. The first six groups follow a feature's journey from setup to operations; the last two are the deterministic hook layer and the machinery underneath. Use the list on the left to jump, or Ctrl/+F to search.

01 Setup 13 entries

Point MTK at a repo once. It detects your stack, pulls your team's standards, and generates the docs every later workflow reads.

#/mtk-setup

skill

The dedicated setup entry point; /mtk never absorbs setup work, it always redirects here.

Trigger Slash command, invoked directly. The /mtk router redirects every setup/bootstrap/audit/architecture ask here (Routing Rule 7) rather than absorbing it.

What it does

Setup lives at its own explicit entry point, separate from the natural-language router. It covers first-time bootstrap plus re-audit and drift refresh of generated docs. When you ask the /mtk router to 'set up' or 'audit' a repo, it points you here with the appropriate flags instead of trying to do it inline.

Example

/mtk set up this repo hits the router's setup diamond and returns the redirect: "Setup lives at /mtk-setup — run that directly." Direct use: /mtk-setup --audit re-runs only the architecture audit; /mtk-setup --refresh --dry-run previews a drift-scoped refresh of the generated docs; /mtk-setup --check runs the read-only CI staleness gate.

How it works

Flags documented in the router's Help Output and Execution notes: bare /mtk-setup = first-time bootstrap (bootstrap + audit); --audit re-runs the architecture audit; --merge unifies multi-repo audits; --refresh does a drift-scoped refresh of all generated docs (--dry-run previews); --check is the read-only CI staleness gate; --preview shows planned changes; --non-interactive skips the interview; --update-guidelines bumps the pinned coding-guidelines SHA. Routing Rule 7 plus the /mtk-setup row in Route Disambiguation guarantee setup asks are always redirected here and never routed to fix/implement.

Why it works

Setup is structural (it bootstraps and audits a repo and generates project docs). Keeping it behind a separate explicit command prevents the natural-language router from silently bootstrapping or overwriting a repo when the engineer only asked for a small change.

#/mtk-setup

skill

Single setup entry point: parses flags and dispatches to bootstrap, audit, merge, refresh, converge, or check.

Trigger Slash command (the one user-invocable entry point for setup; all other setup skills route through it)

What it does

The one command engineers run for any setup task. It parses the argument string into a mode, enforces flag-combination rules, and either runs an inline workflow (--check, --update-guidelines) or reads and follows the matching target skill in full. Default (no flags) is bootstrap.

Example

Running /mtk-setup --audit on a repo with no .claude/tech-stack triggers an AskUserQuestion warning ("No tech stack detected — this looks like a first-time setup. Audit alone won't generate CLAUDE.md... Proceed with audit only? [y/N]"). If confirmed it follows setup-audit; on success it prints ✅ MTK Setup: audit complete in [duration]. See [output file(s)].

How it works

STEP 0 parses flags against a mutual-exclusion matrix (--update-guidelines is exclusive with all others; --refresh/--check exclusive with --audit/--merge and each other; --dry-run requires --refresh; --no-verify-commands is bootstrap-only). STEP 1 maps the flag to a target skill (setup-refresh / setup-converge / setup-audit --merge / setup-audit / setup-bootstrap) or runs the inline --check / --update-guidelines workflow. STEP 2 resolves the target SKILL.md via the MTK File Resolution rules ($CLAUDE_PLUGIN_ROOT prefix, local fallback, then a plugin-cache find) and executes every step, including that skill's own Verification. STEP 3 prints the one-line summary. Precondition guards hard-stop --refresh/--converge on un-bootstrapped repos.

Why it works

Engineers shouldn't memorize five skill names or their preconditions. Centralizing dispatch, flag validation, and precondition guards prevents mistakes like running audit-only on a fresh repo (which wouldn't generate CLAUDE.md) or forcing a refresh where a full bootstrap is required.

#setup-bootstrap

skill

One-time repo onboarding: detect stack, pull pinned guidelines, audit, and generate a lean CLAUDE.md + rules.

Trigger Routed via /mtk-setup with no flags (default mode); not directly user-invocable

What it does

Prepares a repo for MTK workflows. Detects the tech stack, pulls pinned external coding guidelines, audits the codebase, interviews the team, and generates a project-tailored CLAUDE.md (60–80 lines target, 120 hard cap), .claude/rules/*, references, AGENTS.md, product/decisions docs, and supporting config. It is additive and merge-only — it never deletes a file it did not generate.

Example

In a .NET repo, /mtk-setup writes .claude/tech-stack = dotnet, fetches CodingStyle.md at the pinned SHA (aborting on sha256 mismatch), asks 3–7 interview questions, then prints ✅ MTK INIT COMPLETE with a report showing CLAUDE.md ([N] lines — under 120 ✓), the rule files generated, Command verification: N verified, N unverified, N skipped, and Updated / Created / Preserved (untouched) / Needs review counts.

How it works

Modes --preview / --non-interactive / --no-verify-commands. STEP 0 runs scripts/setup-detect.sh --json and writes .claude/tech-stack; STEP 1 pulls guidelines pinned by SHA+sha256 and auto-generates architecture-principles.md if absent (delegating repomap+provenance to setup-audit). STEP 1.5 opens a resumable scan ledger via scripts/workflow-artifact.sh. STEP 2 runs the tech-stack scan recipes; STEP 2.5 the interview → .claude/setup-answers.json; STEP 2.7 ingests existing AI configs per config-ingestion.md. STEP 3 generates CLAUDE.md + rules (merge mode via regen-diff-contract.md when files exist). Write gates: STEP 3.5a verify-references.sh + command verification via verify-commands.sh; 3.5b preview gate + unconditional 120-line ceiling; 3.5c secret-scan.sh before every write. STEP 3.6 prunes stack references against detected-tools.json; STEP 3.8 writes product.md/decisions.md; STEP 4 sets up settings, git pre-commit hook, .mtkignore/.claudeignore, cross-agent configs; STEP 4.5 per-package monorepo CLAUDE.md; STEP 4.8 seeds .claude/.mtk-cache/; STEP 4.9 build-references-index.sh; STEP 4.95 seeds CODE_INDEX.md; STEP 5 report.

Why it works

A tailored, evidence-anchored, lean CLAUDE.md is the source of truth every downstream implement/review agent uses. Doing it once — from actual code plus team knowledge (not aspiration), with hard non-destructive guarantees — avoids both hallucinated rules and clobbering hand-authored files.

#setup-audit

skill

Read-only audit that regenerates evidence-tagged architecture-principles.md and conventions.md from actual code.

Trigger Routed via /mtk-setup --audit (or --audit-only); not directly user-invocable

What it does

Documents what the codebase ACTUALLY does (descriptive, not aspirational). It builds a ranked symbol graph, runs scan recipes, extracts conventions, and writes architecture-principles.md + conventions.md with confidence tags and mandatory evidence citations. Read-only except for its output docs.

Example

/mtk-setup --audit on a .NET service produces lines like [EXTRACTED] All controllers return \ApiResponse<T>\ envelope. Evidence: \src/Api/Controllers/*.cs\ (23 of 23 hits)., appends a ## Provenance table of evidencing symbols, stamps the doc with audited-against: <sha>, runs verify-claims.sh (downgrading zero-hit claims in place), and prints ⚠️ Weak claims surfaced: [N].

How it works

STEP -1 versioned re-run detection (migration path / re-run merge via regen-diff-contract.md). STEP 0.5 runs scripts/repomap.sh <stack> --budget=4000 --out=.claude/.mtk-cache/repomap.json for the ranked symbol backbone; each principle must cite a symbol. STEP 0.9 resumable ledger. STEP 1 scan recipes + verbatim version extraction; STEP 2.5 majority-verified conventions → conventions.md; STEP 2.6 emits .claude/detected-tools.json; STEP 3 principles with S1.15 tags [EXTRACTED]/[INFERRED:N]/[AMBIGUOUS] (each evidenced); [AMBIGUOUS] lines also land in .claude/.mtk-cache/ambiguities.json. STEP 3.5 mandatory Provenance; STEP 3.7 stamp + rule tags [ENFORCED]/[CONVENTION]/[ASPIRATIONAL] + verify-claims.sh with a one-pass retry loop, per audit-grounding.md. Shrink-guarded write (S3.16). Optional --mine-prs via pr-review-mine.sh.

Why it works

Downstream review agents need a factual, drift-checkable model of the architecture. Grounding every claim in a symbol/path and surfacing (not hiding) weak claims prevents the ~20-factual-error failure mode a real client run produced.

#setup-audit --merge

skill

Unify per-repo architecture audits into one team-wide principles doc, flagging where repos agree vs. diverge.

Trigger Routed via /mtk-setup --merge (MERGE MODE of the setup-audit skill; implies audit)

What it does

Combines architecture audits collected from multiple repos under .claude/references/audits/ into a single unified architecture-principles.md. It classifies each principle as team-wide standard, needs-alignment, or project-specific, and merges confidence tags across sources.

Example

A hub repo holds .claude/references/audits/payfac.md and .claude/references/audits/collection-system.md. /mtk-setup --merge produces a unified doc with ## Team-Wide Standards, ## Recommended Alignments (⚠️ where repos disagree, with a recommendation), and ## Project-Specific Patterns per repo. If the audits directory is empty it prints step-by-step instructions to run --audit per repo and copy the outputs in first.

How it works

STEP 0 lists .claude/references/audits/. STEP 1 compares each doc section across audits into consistent / different / unique buckets and applies the Confidence Tag Merge Rules: all-agree [EXTRACTED] stays; mixed tags → [INFERRED:min(confidences)] citing both repos; contradiction → [AMBIGUOUS] with both pointers; single-repo → keep tag, mark project-specific. STEP 2 writes the unified doc (AskUserQuestion before overwriting an existing one). Read-only except the unified output; input audit files are never modified.

Why it works

Teams running many services need one agreed set of standards without manually diffing N audits. Surfacing divergences as explicit team decisions (never auto-deciding) keeps the standardization call with engineers.

#.claude/references/config-ingestion.md

reference

Rules for ingesting existing Cursor/Copilot/Windsurf/Cline/Gemini/CLAUDE.md configs as interview-grade, read-only input.

Trigger Loaded on-demand by setup-bootstrap STEP 2.7 (Ingest Existing AI-Assistant Configs)

What it does

Tells bootstrap how to treat AI-assistant configs a migrating repo already carries. It's read-only ingestion: extract concrete rule/convention candidates as evidence-anchored input to CLAUDE.md generation, deduped against the scan, with conflicts surfaced for review. Source files are never modified, moved, or deleted.

Example

A repo migrating to MTK has a .cursorrules and .github/copilot-instructions.md. During /mtk-setup, STEP 2.7 reads them, extracts a rule like "money is decimal(18,4)" anchored Evidence: migrated from .cursorrules, skips generic boilerplate, and — if that rule contradicts a scan finding — emits a Needs review item citing both. STEP 5 reports Ingested AI configs: .cursorrules, .github/copilot-instructions.md.

How it works

Detection list covers .cursorrules/.cursor/rules/*.mdc, .github/copilot-instructions.md, .windsurfrules, .clinerules, GEMINI.md, root AGENTS.md, and a non-MTK root CLAUDE.md body; any file carrying the Auto-generated by MTK marker is skipped (MTK wrote it). Each adopted candidate carries Evidence: migrated from <path> (resolves under verify-claims). Dedup: a rule the scan already derived is not duplicated. Conflict: a rule contradicting a scan finding → Needs review citing both.

Why it works

Teams don't start from zero — discarding their existing Cursor/Copilot rules loses real institutional knowledge, but blindly importing (or overwriting) them is unsafe. Treating them as read-only, evidence-anchored interview input captures the value without the risk.

#.claude/references/bootstrap-customization.md

reference

Per-stack substitution tables that narrow generic reference files to the one tool the scan actually found.

Trigger Loaded on-demand by setup-bootstrap STEP 4 (Reference File Customization)

What it does

Provides the concrete find/replace tables bootstrap uses to turn generic, multi-tool reference guidance into project-specific guidance — but only when the scan found exactly one tool in a category. It prevents shared references from contradicting the repo-specific rules while keeping guidance for tools the team might adopt later.

Example

In a .NET repo where the scan found only xUnit, STEP 4 opens .claude/references/dotnet/testing-supplement.md, replaces xUnit, NUnit, or MSTest — match the project's existing choice. with xUnit only. Do not introduce NUnit or MSTest., and stamps the file <!-- Customized by setup-bootstrap on [date]. Detected: xUnit. -->. If both xUnit and NUnit were found, the generic line is left intact.

How it works

One table per stack (dotnet/typescript/python) mapping Category → file to patch → generic pattern → example replacement (e.g. mocking library, integration test base, ORM, validation). Procedure: for each category, substitute only on unambiguous single-tool evidence; if the generic pattern isn't found, skip silently (never force). It never removes sections for tools in a different category unless clearly irrelevant (e.g. TanStack Query guidance in a no-React repo), and never removes guidance for tools not used YET.

Why it works

Generic references either bloat the repo or contradict the tailored .claude/rules/. Narrowing them mechanically — only when evidence is unambiguous — gives project-specific guidance without deleting still-useful future-adoption content.

#Post-scan interview (setup-answers.json)

concept

3–7 focused questions (plus up to 3 adaptive) capturing team knowledge, persisted to setup-answers.json.

Trigger setup-bootstrap STEP 2.5 via AskUserQuestion; skippable with --non-interactive

What it does

Auto-detection captures what's in the code; the interview captures the implicit team knowledge that makes CLAUDE.md useful — top failure modes, hard nevers, invisible conventions, compliance constraints, definition of done, product purpose. Answers are persisted and become authoritative human input that regeneration never silently overrides.

Example

During /mtk-setup, MTK asks "What should an AI never do in this repo without explicit approval?"; the answer "never modify financial state without an audit trail" becomes a top Critical Rule (IMPORTANT: prefix) with Evidence: engineer interview — .claude/setup-answers.json (hard_nevers), and the full set is written to .claude/setup-answers.json. An adaptive follow-up may ask about a split the scan found: "handlers split on {Verb}{Entity}Handler (21/31) vs {Entity}Handler (10/31): which is standard?"

How it works

7 static questions max + up to 3 adaptive from .claude/.mtk-cache/ambiguities.json (10 total ceiling), ranked by hit count. Answers map to Critical Rules / rules files / project-specific.md / security.md / product.md, each with an interview evidence anchor that verify-claims.sh never auto-downgrades (it checks the path exists before content-grep). Persisted to .claude/setup-answers.json (committed) through the STEP 3.5c secret-scan gate. Re-runs reuse answers and only ask empty/new keys; --non-interactive reuses silently or skips with a notice. Interview answers are authoritative — a contradicting scan emits Needs review, never a silent flip.

Why it works

The ETH Zurich benchmark showed LLM-generated CLAUDE.md files perform worst; the missing ingredient is team knowledge no scan can infer. A short, persisted, evidence-anchored interview is how MTK gets project-specific rules that are true rather than guessed.

#Reference pruning against detected-tools.json

concept

Ships only the stack reference files whose declared tools intersect the tools actually detected.

Trigger setup-bootstrap STEP 3.6; re-run at refresh time as a ship-time filter

What it does

Stops MTK from dumping every stack reference into a repo. Bootstrap reads the machine-readable detected-tools.json the audit emitted and skips any reference file whose frontmatter tools: array doesn't intersect the detected tool set. It's a ship-time filter only — it never deletes an on-disk reference.

Example

In a Next.js repo using Prismic, STEP 3.6 builds detected = framework ∪ data_layer ∪ test_framework ∪ additional ∪ {stack}, sees the Drizzle/Prisma/TanStack-Query data-layer references don't match, and skips them — printing references: shipped 4, pruned 3 (no detected tool match) and listing the pruned filenames so you can override. A reference with no tools: declared always ships.

How it works

If detected-tools.json is missing, ship everything (with a warning). Otherwise, for each candidate reference under .claude/references/{stack}/ (and every secondary stack, F11): no tools:→ship; tools: ∩ detected non-empty→ship; empty intersection→SKIP (not copied in). Placeholder-status references are excluded earlier. Override by adding the tool to detected-tools.json and re-running, or cp the file from the plugin manually. setup-refresh re-runs this as a ship-time filter that can newly ship a previously-pruned file but never deletes one.

Why it works

Shipping a Drizzle data-layer checklist into a Prismic repo actively misleads downstream agents and bloats context. Pruning to detected tools keeps distributed guidance relevant without ever removing something the team placed by hand.

#Instruction-budget discipline (120-line CLAUDE.md cap)

concept

Generated CLAUDE.md targets 60–80 lines and hard-caps at 120; mechanizable rules move to hooks/rules/references.

Trigger Enforced during setup-bootstrap STEP 3 generation and STEP 3.5b (unconditional ceiling check)

What it does

A research-backed constraint on what bootstrap generates. Claude's compliance with CLAUDE.md rules degrades past ~150 total instructions, so MTK keeps the root file lean, pushes detail into .claude/rules/ and references, mechanizes what it can into hooks/deny-lists, and refuses to emit aspirational rules.

Example

If generation would push CLAUDE.md over 120 lines, STEP 3.5b refuses to proceed and prints "Generated CLAUDE.md exceeds 120 lines — move <section> to .claude/rules/<name>.md", aborting. The STEP 5 report confirms CLAUDE.md ([N] lines — under 120 ✓). The generated AGENTS.md carries the same 60–120 line budget.

How it works

Constraints: root CLAUDE.md target 60–80, hard cap 120; prefer trigger-action / negative phrasing; mechanize into hooks and settings deny-lists rather than duplicate; no aspirational rules (every rule from an actual pattern or failure mode); no list-of-everything. The 120-line ceiling check fires unconditionally (even without --preview); only the confirmation prompt is preview-gated. Live references over restated facts (F8) — point at package.json rather than inlining versions. Cache-stable ordering keeps invariants near the top so the prompt prefix stays warm.

Why it works

A bloated CLAUDE.md is counterproductive — the ETH Zurich benchmark across 1,188 runs showed LLM-generated files performed worst, and Anthropic's own cookbook file is ~80 lines. Enforcing a hard budget is what keeps the generated rules actually followed.

#Cross-agent config generation (AGENTS.md + mirrors)

concept

Generates a portable AGENTS.md and, on request, Cursor/Copilot/Windsurf/Gemini/Cline mirrors from the same references.

Trigger setup-bootstrap STEP 4 via scripts/generate-agents-md.sh and optional scripts/generate-tool-configs.sh --all

What it does

After generating CLAUDE.md and rules, bootstrap produces portable configs so non-Claude tools read the same standards. AGENTS.md is always generated (for Codex and other AGENTS.md-aware tools); the other-tool mirrors are opt-in because some teams deliberately run Claude-only. Custom sections are preserved across regeneration.

Example

On "All tools", bootstrap runs bash scripts/generate-tool-configs.sh --all and writes .cursor/rules/mtk-*.mdc, .github/copilot-instructions.md, .windsurfrules, GEMINI.md, and .clinerules. If AGENTS.md is git-ignored it warns ⚠️ AGENTS.md is git-ignored — generated but will NOT be committed. --non-interactive defaults to AGENTS.md only.

How it works

scripts/generate-agents-md.sh extracts guidelines/security/testing/architecture from the distributed references into a 60–120 line AGENTS.md; ## Custom: sections are preserved, and a marker-less (hand-curated) AGENTS.md is never overwritten. STEP 4 asks once via AskUserQuestion (AGENTS.md only / all tools / none); on all-tools it runs generate-tool-configs.sh --all. Existing configs are regenerated/preserved per their markers — an already-adopted tool is never orphaned. setup-refresh regenerates these deterministically with the same marker-refusal guards.

Why it works

Teams increasingly mix Claude Code, Cursor, Copilot, and Codex. Generating one set of standards into every tool's native format — from a single source, without clobbering hand-curated files — keeps all agents aligned without duplicated manual upkeep.

#.claude/references/preview-gate.md

reference

setup-bootstrap --preview confirmation gate — shows a plan table and full CLAUDE.md before writing.

Trigger Read on-demand by setup-bootstrap STEP 3.5b when --preview is set; auto-attaches on files matching .claude/skills/setup-bootstrap/**

What it does

The approval-before-write gate for setup-bootstrap --preview (a setup concern, adjacent to the spec/plan approval philosophy). It holds all generated content in memory, prints a table of the proposed files with line and token counts, prints the full CLAUDE.md body inline, and asks via AskUserQuestion whether to write. Nothing is written until the engineer confirms.

Example

setup-bootstrap --preview stages CLAUDE.md and the .claude/rules/*.md files, prints "📋 PROPOSED CHANGES (preview — nothing written yet)" with per-file ~TOKENS. If CLAUDE.md exceeds 120 lines it shows "(cap 120 ← FAIL)" and refuses to proceed. Otherwise AskUserQuestion offers "Yes, write all" / "Yes, but skip CLAUDE.md" / "Cancel".

How it works

Stages each pending file to a temp path and computes size via scripts/count-tokens.sh, prints a fixed-column table plus a Critical-Rules / tech-stack summary block, prints the CLAUDE.md body inside a fenced block, then calls AskUserQuestion. A FAIL row (CLAUDE.md > 120 lines) aborts — and that 120-line cap fires even without --preview. "Cancel" leaves the repo untouched; "skip CLAUDE.md" writes everything except root CLAUDE.md; "Yes, write all" proceeds to STEP 4.

Why it works

Setup writes several governing files at once. A preview-before-write gate lets the engineer catch an oversized CLAUDE.md or unwanted rules before they land, and deterministically enforces the 120-line CLAUDE.md budget that keeps the always-loaded prefix small.

#hooks/merge-settings.sh

script

Merges MTK's settings.json into an existing repo's settings.json by unioning arrays without clobbering.

Trigger Bash utility run during setup: hooks/merge-settings.sh <source> <target> → merged JSON on stdout

What it does

A jq-free merge tool used when installing MTK into a repo that already has its own .claude/settings.json. It unions the permission arrays and hook lists from both files so the toolkit's settings are added without overwriting the repo's existing configuration. If either file doesn't parse as JSON, it exits with an error rather than guessing.

Example

Setup combines the new MTK settings with the repo's existing ones:

hooks/merge-settings.sh mtk-settings.json .claude/settings.json > merged.json

allowedTools and deny become the union of both files; hook entries are unioned per event type (all events, matchers, and timeouts preserved), duplicates (matched by command) skipped.

How it works

If the target is empty it just emits the source. It sanity-checks that both files parse as JSON (else exits 2 with no stdout). The merge core is python3 (json module, within the S3.3 baseline): a deep merge that unions permission arrays (allowedTools, deny) sorted-unique, unions hook groups per event type — all event types, matchers, and timeouts preserved, duplicates matched by command — keeps target-only keys, and lets the target win scalar conflicts. No jq dependency; python3 is hard-required with a clear exit-2 message when absent.

Why it works

Installing a shared toolkit must never silently destroy a repo's own Claude Code configuration. A union-merge (with a diff fallback for exotic layouts) makes adoption non-destructive, which is essential for the organic, no-mandate rollout strategy.

02 Plan 10 entries

Before a line of code: turn an ambiguous ask into an approved, hash-sealed spec and a plan broken into verifiable batches.

#spec-driven-development

skill

Writes an approval-gated implementation spec (markdown + JSON sidecar) before any code is written.

Trigger Loaded automatically for new features / multi-file / breaking changes (routed via /mtk or the implement workflow); frontmatter user-invocable: false, so not a direct slash command

What it does

Forces the spec to be written before code. It classifies scope, surfaces assumptions, runs an ambiguity gate, drafts the spec sections (including EARS requirements), and persists both a markdown spec and a machine-parseable JSON sidecar under docs/specs/. Implementation never begins inside this skill — it hard-stops at the approval gate. The skill is phase-locked (a written Tool discipline note) to write only spec artifacts, never source or test code.

Example

Engineer runs /mtk add a webhook retry queue. The skill reads CLAUDE.md + the tech-stack coding guidelines + security/testing references, detects two plausible designs, calls AskUserQuestion, then writes docs/specs/2026-07-09-webhook-retry-queue.md and its .json sidecar, printing Writing spec → docs/specs/2026-07-09-webhook-retry-queue.md. It runs prior-work-check (PASS) and bash scripts/lint-ears.sh (0 violations), then STOPS: "hand to approval gate. Do NOT implement."

How it works

Runs a decision graph (trivial? multi-file? longer than a short session?) then a workflow: (1) load standards; (4b) dirty-worktree scan via git status --porcelain — unlisted modified/untracked paths are recorded as dirty-worktree risks and added to out_of_scope; (6) ambiguity gate via AskUserQuestion, batched for 1-2 ambiguities (cap 4 questions) or Socratic interview mode for ≥3 ambiguities (one question per round, cap 5 rounds); (7) draft Summary/Success criteria/Architecture/Security impact/Change manifest/Test manifest/Batches/Risks/Open questions/Requirements; (8) elegance check; (8b) Claude-Ready (INVEST+C) check against .claude/references/claude-ready-checklist.md (specs scoring ≤13/16 sent back to draft); Constitution Check via bash scripts/constitution-digest.sh; (8c) prior-work-check; (9) persist with version-suffix detection (-v2, -v3); (9.5) bash scripts/lint-ears.sh; (10) hand to Phase 2.5 approval gate. On approval the implement gate records a SHA-256 approval seal via scripts/workflow-artifact.sh seal. (9.6) After the spec is on disk, it additively publishes the spec as a capability-gated Claude Artifact — the first section of one browsable per-workflow URL (keyed to workflow_uuid) that later phases update in place. Disk stays the source of truth; the step is a silent no-op when the Artifact tool is unavailable or MTK_ARTIFACT_PUBLISH=0.

Why it works

Code without a spec is guessing. The approval gate catches bad direction early, before expensive work. Writing the spec after coding is documentation, not specification — the value is in deciding before coding.

#planning-and-task-breakdown

skill

Breaks an approved spec into dependency-ordered, verifiable batches and writes tasks/todo.md + the plan handoff.

Trigger Loaded automatically after spec approval, before multi-file implementation (routed via the implement workflow / /mtk); frontmatter user-invocable: false

What it does

Converts the approved change manifest into batches of 2-4 related files, each with acceptance criteria, a verification step, a Boundary (what it must not leak into), a Depends list, and cited governing-constraint ids. Writes tasks/todo.md and appends a plan section to the JSON handoff. Runs a fresh prior-work-check if the spec is stale, and dispatches plan-gap-reviewer before the approval gate. Like spec-driven, it is phase-locked to write only plan/todo artifacts.

Example

After a spec is approved, the skill emits batches B1..B3, writes tasks/todo.md with checkboxes, and appends "plan": { "batches": [...] } to docs/specs/2026-07-09-webhook-retry-queue.json. It then dispatches plan-gap-reviewer, which returns a BLOCKING finding ("batch B2 touches src/Auth, not in the change manifest"); planning revises before surfacing the plan for approval.

How it works

Starts from the approved change/test manifests; (1a) prior-work freshness check re-runs prior-work-check if the spec's ## Prior Work Check section is missing or was generated more than one session ago (different branch HEAD, different day); (4b) package-legitimacy gate for any new third-party dependency — runs the stack's registry-verification (dependency-intake-checklist.md criterion 0), tags the package [ASSUMED], and inserts a checkpoint:human-verify step that blocks MTK_AUTO_PROCEED; (8) appends the plan section to the JSON handoff (every batch files entry must already exist in the top-level change_manifest, else re-plan); (10) records plan/todo paths on the workflow artifact and leaves plan_trust_gate at pending — only the engineer's Phase 2.5 approval flips it to pass; (11) dispatches plan-gap-reviewer with the request + plan path + spec md + sidecar + todo for the cross-artifact consistency check. After recording plan/todo paths it updates the same workflow Artifact in place (adding the Plan section, reusing the recorded results.artifact_url rather than minting a new link) per .claude/references/artifact-publishing.md — additive, capability-gated, silent no-op when unavailable or MTK_ARTIFACT_PUBLISH=0.

Why it works

Hidden plans drift fastest, oversized batches hide breakage, and a task without verification is a wish. Dependency-ordered vertical slices keep the system buildable at every checkpoint, and re-planning is mandatory the moment scope grows beyond the approved manifest.

#brainstorming

skill

Explores 2-3 concrete design alternatives with honest tradeoffs before committing to a spec.

Trigger Loaded on-demand before spec writing when the approach is unclear (routed via /mtk or mtk:brainstorming); frontmatter user-invocable: false, required-toolsets: [read-only]

What it does

Explores the design space before committing to one approach. The default light path explores context, asks clarifying questions one at a time, proposes 2-3 approaches with pros/cons/effort/fits-when, recommends one, gets approval, and persists the decision. For architecture-shaping decisions it switches to divergence mode, generating approaches in isolated parallel subagents followed by a critic pass. No implementation happens during brainstorming — not even a quick prototype.

Example

Engineer asks "how should we store idempotency keys?" Brainstorming reads the affected code and recent git history, asks one constraint question, then presents three approaches (Redis TTL / DB unique index / in-memory cache) with concrete pros/cons and a recommendation, waits for approval, and saves docs/specs/2026-07-09-idempotency-keys-brainstorm.md.

How it works

Default single-context workflow is 6 steps ending in explicit approval + persistence. Divergence mode (entered for architecture-shaping decisions — new bounded context, persistence target, cross-slice contract, irreversible migration — or on explicit wide-exploration request; self-declares its ~6-10 agent-call cost and confirms first): Phase 1 Diverge picks 3-5 frames from .claude/references/divergence-frames.md (always ≥1 adversarial fintech frame — F-REG/F-FRD/F-AUD — when money/auth/audited state is touched), spawns one isolated subagent per frame in a single parallel message, each banning the first three obvious answers and returning JSON only. Phase 2 Focus is an orchestrator-side critic pass that scores novelty/viability/fit, clusters by angle, flags traps, deepens the top 2-3 survivors, and produces a mandatory Trap Register that carries forward into the spec's "Rejected alternatives".

Why it works

Prevents the most expensive mistake: building the wrong thing correctly. A single context anchors every idea to the first obvious one; isolated branches plus a separate critic avoid that, and the Trap Register makes rejected designs durable knowledge instead of lost work.

#prior-work-check

skill

Runs three deterministic queries to confirm no existing code, constraint, or lesson already covers the proposed work.

Trigger Loaded on-demand at spec-driven-development step 8c and again before planning opens a batch; frontmatter user-invocable: false, effort: high, required-toolsets: [read-only]

What it does

Before code is written, three deterministic queries run against the repo: search_prior_work (find an existing implementation under any name), get_constraints (surface the rules and lessons that govern the area), and get_risk_profile (map the change to a risk surface). It reports findings with severity and emits a verdict; a BLOCK finding holds the approval gate. It only reports — it never edits files.

Example

For a spec adding a JWT issuer, Query 1 greps and finds src/Auth/TokenService.cs:42 already issues JWTs → "Reuse, do not reimplement." Query 3 sees src/Auth/*.cs is a regulated path but the spec declares security_impact: none → a BLOCKING mis-classification finding. Verdict is BLOCKED, and the spec is not eligible for approval until revised.

How it works

Query 1 extracts 2-4 capability keywords from the spec summary/success_criteria and runs parallel grep -rn -i across the active stack's source roots plus bash scripts/query-code-index.sh find (when CODE_INDEX.md exists) or an MCP code-index tool such as mcp__gitnexus__query / mcp__ast-index__* when available. Query 2 greps tasks/lessons.md, .claude/lessons/personal.md, architecture-principles.md, CLAUDE.md, and matching .claude/rules/*.md, grading each hit block/flag/note. Query 3 classifies every change_manifest[].path as regulated/boundary/shared/isolated and compares against the declared security_impact. Verdict: any BLOCK → BLOCKED, any FLAG → PROCEED_WITH_CHANGES, only NOTE (or none) → PASS. Output is appended under a ## Prior Work Check section in the spec.

Why it works

The single most common cause of waste in mature codebases is re-implementing a capability that already exists under a different name. A grep miss is not a confirmed absence, so generic capabilities (validator, formatter, retry) get re-searched with synonyms before PASS.

#plan-gap-reviewer

agent

Anti-anchored reviewer that challenges a saved plan against the actual codebase before implementation starts.

Trigger Dispatched by the planning flow before the Phase 2.5 approval gate to review a saved plan; forked context, model: sonnet, effort: high, read-only (allowed-tools: Read, Glob, Grep only).

What it does

Reviews a saved plan as if it has never seen it before, deliberately refusing to read lessons, prior reviewer output, planner rationale, or the workflow artifact so it is not anchored by the planner's confidence. It verifies the plan against the real repo and cross-checks the spec sidecar and todo for consistency. It returns findings only — it never approves, edits, or rewrites the plan.

Example
## Verdict
FINDINGS

### repo_mismatches/BLOCKING: plan names a slice that doesn't exist
- evidence: plan step 2 → "extend src/Billing/InvoiceService.cs"
- problem: src/Billing/ does not exist in the repo

Every BLOCKING finding sends the plan back for revision before the Phase 2.5 gate reopens.

How it works

sonnet / read-only. Mandatory anti-anchoring rules forbid reading tasks/lessons.md, .claude/lessons/personal.md, .mtk/workflows/{uuid}.json, or prior reviewer/planner notes. Verifies every named path exists (or the plan says 'create'), every assumed import is in the manifest, integration points are addressed, and execution order never imports a later phase's output. Cross-artifact check maps spec change_manifest ↔ plan batches ↔ tasks/todo.mdsuccess_criteria/test_manifest. Dirty-worktree rejection: a batch touching an out_of_scope dirty-worktree path is always BLOCKING. Uses exactly the fixed finding categories (repo_mismatches, missing_surfaces, execution_order_issues, hidden_assumptions, under_scoped_integrations, open_decisions_presented_as_settled, cross_artifact_inconsistencies, dirty_worktree_overlap) with BLOCKING/ADVISORY severity; verdict PASS | FINDINGS.

Why it works

The planner has an incentive to defend its own plan and downstream reviewers read its confidence as evidence. A fresh, unanchored pass catches repo mismatches, wrong execution order, and silent scope drift before a single line of code is written.

#.claude/references/divergence-frames.md

reference

A catalogue of 10 analytical lenses (incl. fintech frames) that seed isolated brainstorming subagents.

Trigger Loaded on-demand by brainstorming's isolated-divergence mode

What it does

Provides distinct frames, each an entire vantage a divergence subagent must reason from and nothing else. It includes fintech-specific adversarial frames — F-REG (Regulator), F-AUD (Auditor), F-FRD (Fraudster), F-BOQ (Back Office at Quarter End) — and general ones: F-3AM (3 AM On-Call), F-INV (Inversion), F-Z0B ($0 Budget), F-RLA (Remove the Load-Bearing Assumption), F-HCP (Hostile Competitor), and F-10Y (10-Year Maintenance). Each frame states what it optimises for and a trap to register.

Example

Brainstorming a payment-refund flow selects F-REG, F-FRD, and F-3AM; three isolated subagents each return one JSON approach from their lens. The critic pass finds F-FRD surfaced a replay-double-credit risk the others missed, and registers F-REG's record-keeping-vs-performance tradeoff as a trap.

How it works

Brainstorming picks 3-5 maximally-distinct frames (always ≥1 adversarial fintech frame when money/auth/audited state is touched) and spawns one isolated subagent per frame with only the problem statement plus its frame prompt. The orchestrator critic then scores each proposal 1-5 on Novelty and Viability with Fit binary, clusters by angle, deepens the top 2-3, and must register a trap for any framing where two selected frames collapse to the same analysis.

Why it works

A single context anchors all ideas to the first answer. Forcing distinct, isolated vantages — especially adversarial fintech ones — surfaces failure modes and options a uniform view would never reach, and the mandatory trap register preserves the rejected space.

#EARS requirements + ANT check (scripts/lint-ears.sh)

concept

Requirements written in EARS notation and screened by an anti-tautology check, enforced by lint-ears.sh.

Trigger Authored in the spec's ## Requirements section; enforced by bash scripts/lint-ears.sh at spec-driven step 9.5

What it does

Every requirement-bearing bullet in a spec uses one of five EARS templates — Ubiquitous, Event-driven, State-driven, Optional, Unwanted — so each requirement has a testable trigger-and-response shape. The ANT (Anti-Null-Tautology) check then rejects any requirement that cannot be falsified. lint-ears.sh enforces both, scoped only to the ## Requirements section.

Example

A bullet "The system shall be reliable" fails the ANT check ("be reliable" is a flagged phrase, and there is no describable counter-example); it's rewritten as "When a payment fails three times, the system shall lock the account." Running bash scripts/lint-ears.sh docs/specs/2026-07-09-webhook-retry-queue.md must return 0 before the spec reaches the approval gate.

How it works

spec-driven step 7 requires EARS bullets in ## Requirements; step 9.5 runs lint-ears.sh with zero violations required before handing to the gate. The lint's ANT phrase list (case-insensitive substring match) flags tautologies including "be reliable / be performant / be scalable / be secure / be robust / be clean / be maintainable / be efficient", "follow best practices", "be production-ready", and vague "where appropriate / as needed / if necessary". It scopes only to the ## Requirements subsections; narrative sections (Summary, Risks, Goals) are not checked.

Why it works

Flowery prose hides untestable requirements. Forcing a predicate-with-trigger shape and asking "what would a counter-example look like?" for each bullet leaves vague aspirations nowhere to hide, making every requirement verifiable.

#Rigor Score (Continuous Ceremony Scaling)

concept

A points score from the spec sidecar that scales ceremony to blast radius: LIGHT / STANDARD / HIGH / MAX.

Trigger Computed once by implement after Phase 2 from the spec JSON sidecar; surfaced on the Phase 2.5 gate line.

What it does

Instead of a binary 'small vs big change', MTK computes a numeric rigor score from the approved plan and maps it to one of four levels. The level dials three things: whether Phase 3 runs inline or via subagents, which Stage-2 reviewers run, and whether MTK_AUTO_PROCEED is even eligible.

Example

A change with 3 implementation batches (+3), 8 files (+3), and security_impact new-auth-path (+3) scores 9 → HIGH. implement prints Rigor: HIGH (score 9 — 3 batches, 8 files, security_impact=new-auth-path) at the gate, routes Phase 3 to subagent-implementation, and forces both test-reviewer + architecture-reviewer in Stage 2 regardless of the per-reviewer conditions.

How it works

Points: +1 per batch, +1 per 3 non-mechanical files (rounded up), +3 if security_impact != none, +1 per public contract added/modified (cap +4), +3 if scope == breaking-change. Bands: <=3 LIGHT, 4-7 STANDARD, 8-11 HIGH, >=12 MAX. A hard-trigger floor forces at least HIGH whenever plan.batches.length >= 3, >= 6 non-mechanical change_manifest entries, or security_impact != none — the score scales ceremony continuously between those long-standing triggers but never relaxes them. HIGH/MAX force the subagent path, add reviewers (MAX adds silent-failure-hunter), and make MTK_AUTO_PROCEED ineligible.

Why it works

Ceremony that's all-or-nothing either over-burdens small fixes or under-protects big changes. Proportional rigor gives a one-line justification the engineer sees at the gate for why this particular change is getting the apparatus it's getting.

#Phase 2.5 Approval Gate

concept

The always-mandatory go/no-go gate: spec, plan, and todo rendered inline, then AskUserQuestion before any code is written.

Trigger Mandatory stop in implement between planning (Phase 2) and building (Phase 3); asked via the AskUserQuestion tool.

What it does

The single hard stop that separates planning from building. It renders the plan and todo inline in the terminal (not just file paths), shows the rigor level and the full downstream gate sequence, and then asks the engineer how to proceed via AskUserQuestion. Until they answer, only read-only Bash is allowed — no Edit/Write on source, no Phase 3.

Example

The engineer sees a one-line header (scope, batch count, file count, rigor), the full tasks/todo.md checklist, a per-batch breakdown, a Gate sequence: 3 batches → Phase 3.5 drift check → Stage 1 compliance-reviewer → Stage 2 [test-reviewer, architecture-reviewer] → Phase 6 cleanup → Phase 7 compound line, and clickable file paths. AskUserQuestion offers: Approve & run until done, Approve (interactive), Edit first, Revise, Show full plan & spec in terminal.

How it works

On approval it records scripts/workflow-artifact.sh gate "$MTK_WF_UUID" plan_trust_gate pass and then seals the approved artifacts via scripts/workflow-artifact.sh seal "$MTK_WF_UUID" — binding the exact approved bytes of the recorded spec/plan (the mutable todo is not sealed) so a later edit can't silently keep the approval (verification-before-completion re-checks with verify-seal, and hooks/spec-approval-trigger.sh re-queues this gate on any post-approval edit to a sealed artifact); Revise/Edit first leave the gate pending and do not seal. Approve & run until done sets autonomous mode — Phases 3-7 never call AskUserQuestion again (the loop stops and reports instead), except the structural Phase 2.5 re-open forced by non-auto-fixable drift. If AskUserQuestion is deferred it's loaded via ToolSearch select:AskUserQuestion; if the harness (Cursor/Copilot CLI/Gemini CLI) can't expose it, the loop prints one line and waits rather than proceeding. Full gate semantics live in .claude/references/orchestration-gates.md.

Why it works

It's the one place a human commits to a fully-informed plan — which is why the spec skill's ambiguity gate must resolve open questions in Phase 1, not defer them here. Inferring approval from the original request, or answering by prose prompt instead of AskUserQuestion, are explicit Red Flags. Harness file-write/Bash prompts are a separate layer — autonomous mode does not bypass them.

#.claude/references/claude-ready-checklist.md

reference

INVEST+C spec-readiness gate — 16 Claude-Ready items a spec must pass before it is approved for implementation.

Trigger Loaded on-demand (frontmatter globs it to docs/specs/**/*.md); applied as a gate by spec-driven-development during Phase 1, before spec approval

What it does

A spec-quality gate that extends Bill Wake's INVEST with a Claude-Ready ('+C') dimension: a spec must be self-sufficient enough for an LLM to execute without asking clarifying questions. spec-driven-development applies it during Phase 1, and a spec failing 4+ items is revised before approval. It is effectively a diagnostic scorecard for specs — the plan-time sibling of the repo-health scorecard.

Example

While drafting a spec, spec-driven-development scores it against the checklist. It fails +C #4 because out_of_scope is empty and +C #3 because a success criterion says "verify it works" instead of a runnable command. With 12/16 passing it lands in 'needs tightening' (11–13), so the author fixes those items before the spec is approved and handed to planning. A reviewer later cites "fails +C #4" by its stable number.

How it works

The reference defines two parts. Classic INVEST is six story-level checks (Independent, Negotiable, Valuable, Estimable, Small, Testable), each with pass criteria. The +C dimension is 16 numbered Claude-Ready items: concrete file paths, named pattern references, runnable verification commands, explicit out-of-scope, a security_impact enum value, dependency pins, data-model deltas, enumerated public_contracts, boundary preserved, local pattern verified, no 'obvious' steps, lessons consulted, prior-work checked, risks named, rollback plan, and test-manifest matching change-manifest. Scoring bands: 14–16 = approved-ready (hand to planning), 11–13 = needs tightening, ≤10 = back to draft. plan-gap-reviewer and compliance-reviewer cite items by their stable numbers.

Why it works

Specs that read fine to a human routinely omit the concrete paths, verification commands, and out-of-scope boundaries an agent needs — and those silent gaps cause the most expensive implementation drift and rework. Gating approval on a falsifiable 16-item checklist forces the spec to be executable-without-questions before any code is written.

03 Build 7 entries

Execute the approved plan in batches that each compile, test, and stay inside the manifest — evidence gathered, drift checked.

#implement (via /mtk)

skill

Orchestrates the full feature loop: context, spec, plan, approval gate, batched build, drift, review, cleanup, compound.

Trigger Routed by the /mtk router when the request is substantial feature work (new feature, breaking change, multi-file work); the skill itself is user-invocable:false, not a standalone slash command.

What it does

The user-facing entry point for substantial work. It is intentionally thin — it sequences a fixed set of phases and delegates the real behavior to the underlying workflow skills (context-engineering, spec-driven-development, planning-and-task-breakdown, the two implementation paths, verification, drift, review, code-simplification). It threads a durable workflow artifact through every phase so the run survives compaction or a crash.

Example

An engineer types /mtk add OAuth device-flow login. implement inits a BUILD workflow artifact, loads CLAUDE.md and the active tech stack, writes an executable spec to docs/specs/2026-07-09-oauth-device-flow.md and a plan to docs/plans/..., prints the rigor line Rigor: HIGH (score 9 — 3 batches, 8 files, security_impact=new-auth-path), and STOPS at the Phase 2.5 AskUserQuestion gate. On Approve & run until done it builds batch-by-batch, runs the spec-drift check, then a two-stage review, and closes with a 'What compounded' section.

How it works

Phases run in order: Phase 0 loads context + inits/resumes a workflow artifact via scripts/workflow-artifact.sh init BUILD (progressive reference disclosure, --terse/--verbose flags); Phase 0.5 optional brainstorming; Phase 1 produces the executable spec (spec-driven-development) persisted to docs/specs/YYYY-MM-DD-<slug>.md + JSON sidecar; Phase 2 writes tasks/todo.md and docs/plans/... (planning-and-task-breakdown); the Rigor Score is computed from the sidecar; Phase 2.5 is the mandatory AskUserQuestion approval gate that also seals the approved spec/plan bytes (scripts/workflow-artifact.sh seal); Phase 3 forks — subagent-implementation at rigor HIGH/MAX or any hard trigger, else incremental-implementation inline; Phase 3.5 runs spec-drift-detection; Phase 4 is two-stage review (Stage 1 compliance-reviewer, Stage 2 test-reviewer/architecture-reviewer/silent-failure-hunter in parallel); Phase 5 fixes findings (max 3 iterations); Phase 6 code-simplification; Phase 7 compound (append to tasks/lessons.md); Phase 7.5 delta sync-back via scripts/spec-archive.sh only on clean drift + no open Critical. Gate decisions (plan_trust_gate, phase_exit_gate, memory_sync_gate) are recorded on the artifact throughout.

Why it works

Prevents the classic failure modes it lists as Red Flags: code started before a spec exists, spec/plan not written to disk, the approval gate skipped or merged into implementation, files touched outside the change manifest, no behavioral diff, review omitted for substantial work. Ceremony scales to blast radius rather than being all-or-nothing.

#fix (via /mtk)

skill

Lightweight bounded loop for 1-3 file changes that self-escalates to implement the moment scope grows.

Trigger Routed by the /mtk router when the user says 'fix', 'bug', 'broken', or similar; user-invocable:false.

What it does

A stripped-down fix loop for bug fixes, validation tweaks, query fixes, small config changes, and narrow refactors. It composes context-engineering, debugging-and-error-recovery, targeted TDD, and a scope guard — without the full spec/plan/approval overhead. Its distinguishing feature is the scope guard: the instant the work needs a 4th file, a new handler/entity/slice, or architectural re-planning, it stops and hands off rather than sprawling.

Example

/mtk fix pagination returns duplicate rows on page 2. fix loads only what the fix needs, reproduces the root cause first (debugging-and-error-recovery), edits the query file, adds a regression test, runs build + the relevant tests, and reports files/root-cause/tests/build result. If it discovers the fix actually needs a new repository class, it stops and calls Skill(skill: "mtk", args: "<original description> — escalated from fix: <reason>").

How it works

A DOT decision graph makes the escalation triggers explicit: three red diamonds (4th file required / new handler-entity-slice / needs architectural re-planning) each route to a STOP-and-escalate node. A red-flag table pre-empts rationalizations ('just one more file', 'I'll add the handler quickly, it's still a fix'). Lessons are pulled via scripts/learnings.sh query --phase implement --files <paths> --max 8 (5-layer filter) with a fallback to scanning tasks/lessons.md. Verification is the minimum: run build + the changed area's tests from the tech stack skill.

Why it works

Stops a quick fix from silently becoming a hidden multi-file feature project. Escalation is framed as faster than a half-broken sprawl — the router re-routes to implement so the work gets a real spec and approval instead of unreviewed scope creep.

#incremental-implementation

skill

Inline thin-slice executor — each batch compiles, tests, and stays inside the approved manifest before the next begins.

Trigger Loaded on-demand by implement Phase 3 as the inline path (rigor LIGHT/STANDARD, below the subagent threshold); user-invocable:false.

What it does

Implements an approved multi-file change in thin slices in the main context. Each batch touches only its listed files, adds/updates its own tests, runs the batch checkpoint, and gets ticked off in tasks/todo.md before the next batch starts. It is the cheaper sibling of subagent-implementation for smaller features.

Example

For a 2-batch change: batch B1 edits its files, runs dotnet build 2>&1 | tee /dev/tty | hooks/parse-build-diagnostics.sh > .mtk/analyzer-output.json, all green, records scripts/workflow-artifact.sh gate "$MTK_WF_UUID" phase_exit_gate pass --reason "batch B1 green". After B2, git diff --stat shows 340 net lines, so it triggers an early pre-commit-review pass; after all batches it runs the full test suite and appends the implement section to the JSON handoff sidecar.

How it works

Per-batch loop: re-read rules, ask the simplicity question, implement only the batch's files, apply TDD (and source-driven-development when framework behavior is uncertain), run the checkpoint, optionally pipe build output through hooks/parse-build-diagnostics.sh, run .claude/references/pre-commit-review-list.md, tick the todo, record the phase_exit_gate. A churn check runs after each batch — >300 cumulative net lines forces an early review checkpoint, >500 without a review halts to run compliance-reviewer. At the end it writes the implement block (actual_files, completed_batches, deviations, behavioral_diff) to docs/specs/<date>-<slug>.json per .claude/schemas/handoff.schema.json and runs scripts/validate-handoff.sh.

Why it works

Makes late failure cheap by front-loading verification into every slice — you never continue across batches on a failing build, and unplanned scope shows up as churn mid-run instead of as a surprise at the end. Honest deviation recording feeds the drift check that follows.

#subagent-implementation

skill

Per-batch context isolation — one fresh implementer subagent per batch, with all drift/persistence judged orchestrator-side.

Trigger Loaded on-demand by implement Phase 3 as the subagent path when any hard trigger holds (plan.batches.length >= 3, non-mechanical change_manifest entries >= 6, security_impact != "none") or rigor score >= 8; user-invocable:false.

What it does

A branch of incremental-implementation for large features. The orchestrator (main context) holds the spec, plan, and JSON sidecar; each batch's actual editing happens inside a fresh subagent that receives only what it needs and returns a structured JSON result. The orchestrator never edits source and the implementer never spawns further subagents.

Example

A 4-batch, 8-file, new-auth-path feature. The orchestrator asks once via AskUserQuestion whether the implementer should be Sonnet (policy default) or Opus, then for each batch builds a context bundle (spec excerpt, batch object, prior batches' summaries — never full diffs), dispatches Agent(subagent_type: general-purpose, model: <chosen>), and parses a fenced JSON result {batch_id, status, actual_files, build, tests, behavioral_diff, deviations}. A batch returning 'done!' with no JSON is recorded inconclusive and respawned once with narrowed scope; a second inconclusive halts.

How it works

Two execution paths share the same implementer prompt template (TASK / DELIVERABLE / SCOPE / VERIFY headers) and the same orchestrator-side gates: the preferred dynamic-workflow path generates a JS script from templates/workflows/subagent-implementation.workflow.js run via the Workflow tool (runtime handles retries + schema validation), and the manual Agent-loop fallback dispatches by hand. Either way, after each batch the orchestrator runs the build/test/inconclusive gate (<=2 retries), the drift micro-check (actual_files ⊆ batch.files? contracts ⊆ planned?), auto-fixes in-package drift or re-opens Phase 2.5 for cross-package/new-contract/security drift, persists to sidecar.implement.completed_batches[], and runs the 300/500-line churn check. Dispatch hardening: large prompts go to .mtk/workflows/<uuid>/batch-<id>.prompt.md (never argv), args bound as real JSON, bundles compressed via scripts/mtk-compress.sh. Phase 3.5 drift and Phase 4 review run unchanged afterward.

Why it works

Keeps the orchestrator's context clean on big features (context contamination is an invisible cost) while refusing to trust a subagent's self-assessment — drift is judged where the subagent can't launder it, inconclusive is never silently upgraded to pass, and a failing batch is never skipped because it would poison every later batch's assumptions. The v7.14 'args-unbound' crash and stall failures are pre-empted by the dispatch-hardening rules.

#test-driven-development

skill

Write the smallest failing test that proves the behavior change before the implementation makes it pass.

Trigger Loaded on-demand inside every batch of implement/fix when behavior changes; user-invocable:false. Advisory keyword triggers: regression, flaky, untested.

What it does

Treats tests as proof of intent, not cleanup after coding. It identifies the behavioral contract that's changing, writes a failing test first, chooses the right test level (unit for pure logic, integration for framework-dependent behavior), then writes the minimum production code to pass — and strengthens any test that only proves 'does not throw'.

Example

Fixing a rounding bug in an interest calculation: first add a test asserting Calculate(1000m, 0.05m) == 50.00m and watch it fail (red), then apply the fix so it passes (green). The skill rejects the shortcut 'a unit test is enough for this query' when the risk actually lives in ORM translation or relational semantics — it points to the tech stack's ORM guidance for the correct level.

How it works

Seven-step workflow: identify contract → write smallest failing test → pick test level (defers stack-specific rules to the active tech-stack skill's ## Test Level Guidance and ## ORM & Data Layer Guidance) → use the project-standard provider → implement the minimum → strengthen weak assertions → run relevant then broader tests. Its Verification checklist requires the test to have failed first, now pass for the right reason, and be able to catch a realistic regression.

Why it works

A test written after the code proves your implementation, not the intended behavior. Writing it failing first is what makes it a real specification of intent, and the assertion-strength rule prevents the green-but-meaningless test (asserts only 'no exception').

#source-driven-development

skill

Verify unfamiliar or version-sensitive framework/SDK behavior against authoritative local sources before implementing.

Trigger Loaded on-demand inside implement/fix Phase 3 (and incremental/subagent batches) when framework, SDK, or library behavior is uncertain; user-invocable:false.

What it does

When framework behavior matters, memory is not enough. The skill makes you check whether the repo already demonstrates the same API safely, and if not consult authoritative sources — then explicitly distinguish 'verified from source' vs 'inferred from local pattern' vs 'still uncertain' before writing code.

Example

About to use an EF Core execution-strategy + explicit transaction combination you're unsure about: instead of guessing, you grep the repo for an existing safe usage or read the SDK source, then state 'verified: retrying execution strategy requires wrapping the transaction in an ExecuteAsync delegate' before implementing. If it can't be verified, you say so plainly rather than presenting a guess as fact.

How it works

Five-step workflow: identify the uncertain API → check for a proven repo pattern → consult authoritative docs → classify the evidence into verified/inferred/uncertain → implement only after the uncertainty is resolved or explicitly surfaced. It explicitly hands off forward-looking 'current best way' questions to research-context (web) rather than trying to answer them from source.

Why it works

Similar-looking API names cause production bugs — the skill forces verification of the actual contract for the actual version, especially for transaction and security behavior where 'I'll fix it if it breaks' is far more expensive than a cheap up-front source check.

#debugging-and-error-recovery

skill

Reproduce the failure first, state a hypothesis, make the smallest root-cause fix, then verify with build/test evidence.

Trigger Loaded on-demand by the fix loop and whenever a test fails, a runtime error occurs, or a regression is reported; user-invocable:false. Advisory keyword triggers: stacktrace, traceback, exception, segfault, nullreference, crashes, panicked.

What it does

A disciplined debugging loop that refuses guesswork. It starts from the actual failure output, localizes the root cause to the smallest responsible area, states a hypothesis before editing, applies the minimal correction, adds a regression guard, and verifies with fresh build/test evidence. It fixes the root cause, not the visible symptom.

Example

A test throws NullReferenceException. The skill first captures the failing test output (it even renders the current git branch and working tree via a dynamic git status block), reads the failing file and neighbors, writes 'hypothesis: the DTO mapper doesn't null-guard the optional address', applies the guard, adds a regression test, and reruns the tech stack's build/test commands. It escalates to the full implementation workflow if the fix grows past 3 files.

How it works

Seven ordered steps — Reproduce, Localize, Reduce, Hypothesize, Fix, Guard, Verify — plus an explicit escalation step (step 8) when scope grows past 3 files or needs new architecture. Red flags include editing on guesswork without reproduction, touching a 4th file without escalating, and multiple failed attempts with no updated hypothesis. It shares the fix loop's scope discipline.

Why it works

Guesswork is not debugging: reproducing first and stating the hypothesis up front means the fix is judged against the predicted root cause, not merely against 'the error went away' — which is how symptom-patches that leave the real bug intact slip through.

04 Review 22 entries

Adversarial, isolated-context reviewer agents plus deterministic linters. Findings are structured, cited to a rule, and block merge.

#verification-before-completion

skill

No 'done' without fresh execution evidence — verify criterion-by-criterion and state results as a binary evidence table.

Trigger Loaded on-demand before any completion claim — before reporting a batch/fix done, before handing to review (implement Phase 4 runs it first); user-invocable:false. effort: high.

What it does

Bars any completion claim that isn't backed by fresh output from an actual command run. 'Should work', 'probably fixed', and 'looks correct' are explicitly not verification. It verifies the spec's frozen success_criteria one at a time — each carrying an evidence_channel and a pre-declared observable — and states results as a criterion | verdict | evidence table where every verdict is binary.

Example

Before reporting a new /health endpoint done, it reads success_criteria[] from the sidecar, runs the checks, and emits:

| criterion | verdict | evidence | |---|---|---| | SC1 | verified | dotnet test --filter Batch2 → 14/14 pass | | SC2 | verified | curl :5080/health → 200, body {"status":"ok"} |

Because SC2 is behavior-shaped, test-run alone is rejected — the channel must include a real execution surface like smoke-boot or http-probe. Editing a file after this table re-arms all criteria and the claim is rejected until re-verified.

How it works

Enforces a verification contract: each criterion has an evidence_channel from a fixed taxonomy (test-run, build-output, http-probe, cli-stdout, db-state-diff, browser, smoke-boot, log-capture, script-output) and an observable declared before execution. Criteria are frozen at Phase 2.5; a tamper check (git diff <approval-ref>..HEAD on the criteria block) is fail-closed and re-opens Phase 2.5 if a goalpost moved (invariant F15). When the workflow carries an approval_seal, scripts/workflow-artifact.sh verify-seal "$MTK_WF_UUID" is the authoritative pre-completion check — stronger than the criteria diff because it binds the exact approved bytes of spec+plan+todo — and a STALE seal (exit 1) is fail-closed and re-opens Phase 2.5. The re-arm rule resets criteria to re-armed after any post-verification edit. A Claim Extraction procedure reconciles every upstream-agent claim to VERIFIED/CONTRADICTED/UNVERIFIABLE; hand-checked criteria persist a golden baseline under docs/specs/<slug>.baselines/<SCn>.txt. A wiring check runs scripts/validate-toolkit.sh --task-scoped <paths>. The browser channel follows .claude/references/evidence-capture.md; repeated failures drive the scripts/workflow-artifact.sh remediation circuit-breaker.

Why it works

Stops the single most common agent failure: declaring success on stale or absent evidence. Criterion-by-criterion checking beats a prose 'all good' because each row is pinned to a re-runnable command; frozen criteria + tamper check + approval seal prevent a run from editing what measures it to force a pass; and for behavior-shaped changes tests alone never prove done — something has to actually boot and answer.

#spec-drift-detection

skill

Diff the actual change against the approved spec sidecar — files, contracts, security impact, scope — before review begins.

Trigger Loaded on-demand at implement Phase 3.5 — after implementation, before Phase 4 review; user-invocable:false. effort: high, required-toolsets: [read-only].

What it does

Verifies the implementation delivered exactly what the spec promised: nothing more, nothing less. It compares the real diff against the JSON sidecar's change_manifest, public_contracts, security_impact, and out_of_scope, emitting structured findings. In regulated environments drift means the Phase 2.5 approval didn't actually cover the shipped code, so it's caught before review, not after.

Example

After a batch touches src/Auth/TokenService.cs (not in the change_manifest) while the sidecar says security_impact: none, the skill emits two critical findings — an extra-file drift (95-100 confidence) and a security-impact-understated drift (95+) — and returns NEEDS_CHANGES. The engineer must either fix the implementation or re-open the spec through Phase 2.5; silent drift repair is forbidden.

How it works

Loads the sidecar (scope, change_manifest, public_contracts, success_criteria, out_of_scope, security_impact), prefers the deterministic scripts/validate-handoff.sh for file/security drift (falls back to git diff --name-status), runs scripts/lint-ears.sh on the spec markdown (EARS/ANT lint), and checks the drift-axis table — file-list, public contract, security impact, out-of-scope, success criteria, ownership/cross-slice creep, dependency shift (lockfile diff), and usage drift (grepping call-sites of renamed contracts). It also runs scripts/monorepo-ripple.sh and compares against tagged principles in .claude/references/architecture-principles.md (S1.15: [EXTRACTED] blocks, [INFERRED:>=0.7] flags). Findings follow .claude/references/review-finding-schema.md with source: "drift"; any critical → NEEDS_CHANGES and flips phase_exit_gate fail.

Why it works

The approval gate only means something if the shipped code matches what was approved. Because findings come from diffing two structured lists rather than judgment, they're high-confidence by construction — and 'the extra file was basically in scope' / 'the auth change is tiny' are exactly the rationalizations that turn into compliance failures.

#stub-detection.md

reference

Deterministic pattern catalog for catching stubs, placeholder/mock data, and unwired components that compile but aren't implemented.

Trigger Cited by code-review-and-quality's correctness axis and the per-stack reviewers; a deterministic grep-pattern catalog applied to the diff during review.

What it does

Treats stubs, placeholders, and unwired components as incomplete implementation, not 'almost done.' It catalogs grep patterns for comment markers (TODO/FIXME/PLACEHOLDER), empty function bodies (NotImplementedException / NotImplementedError), suspect return values (null/[]/{}/""/0 on contracts that should produce data), mock/placeholder data in production code, empty React/JSX components, and — the silent class — handlers/routes/subscribers added but never registered where the system would call them.

Example

src/api/profile.ts:42 returns [] but getProfile() is the only path the spec lists for fetching profile data → Critical stubbed-implementation finding (pattern: empty-array literal return on a declared data path). A new MediatR handler with no DI registration is raised as under_scoped_integrations.

How it works

Per-language grep patterns plus an unwired-component table mapping each surface (HTTP route, MediatR/CQRS handler, background job, React route, CLI subcommand, migration, feature flag) to the registry file where wiring must be verified. Severity guidance: empty body / NotImplementedException on an exposed public contract or mockData reachable from production routes → Critical (block); TODO added by this diff → High; pre-existing TODOs touched but not introduced → Low advisory. Explicit carve-outs (Python except: pass → route to silent-failure-hunter; @abstractmethod / interface declarations; nullable return types; test-directory fixtures) are not flagged.

Why it works

Stubs slip through review because they look syntactically correct and compile — a function returning null compiles, a component rendering a div compiles, a test asserting nothing passes. Mapping to F2 (hardcoded-success/stub returns), this catalog forces reviewers to surface incomplete work rather than approve a diff because it builds.

#evidence-capture.md

reference

Capture procedure for the browser evidence channel — persist screenshot/console/network artifacts, with an explicit degraded fallback.

Trigger Read on-demand by verification-before-completion whenever a success criterion uses the browser evidence channel.

What it does

Defines how a browser-based completion check produces real evidence instead of an unbacked assertion. When Playwright MCP is available it captures three artifacts around the observed behavior and persists them next to the spec; when MCP is unavailable it mandates an explicit textual fallback note — never a silent claim.

Example

Verifying a checkout cart badge, the agent drives the browser, then saves screenshot.png, console.log, and network.json under docs/specs/2026-07-01-checkout.evidence/SC3/ and cites that directory in the completion table. With no MCP, it instead writes browser (no MCP): described only — cart badge updated to "2" after add; no artifact captured.

How it works

With Playwright MCP: browser_navigate to the state, then browser_take_screenshot, browser_console_messages, and browser_network_requests, persisted under docs/specs/<slug>.evidence/<criterion-id>/. Sensitive content is scrubbed before persisting (strip Authorization/Cookie/Set-Cookie headers and tokens from network.json, mask real account data in screenshots, scan console.log for leaked secrets); unscrubbable artifacts stay untracked and gitignored. The rule is absolute: a browser criterion cites either its artifact directory or an explicit no-MCP fallback note — there is no third option.

Why it works

Unlike test-run or script-output, a browser check has no artifact by default — it's just a claim. Forcing a persisted screenshot/console/network trail (or an honest 'described only' note) closes the gap where 'I checked it in the browser' would otherwise be unverifiable.

#code-review-and-quality

skill

Adversarial, multi-agent code review that scores 5 dimensions and blocks merge on any weak axis.

Trigger Auto-dispatched after a verified implementation, and routed via /mtk for PR / merge-safety review; not a direct slash command (frontmatter user-invocable: false, context: fork, effort: max).

What it does

Reviews the changed diff as an adversary, not a collaborator, across correctness, readability, architectural fit, security, performance, and test quality. It fans work out to specialized reviewer agents, merges and dedupes their findings, then scores five dimensions 1-10 and issues a PASS or NEEDS_CHANGES verdict. It is read-only except for recording scores to the workflow artifact.

Example

After a feature adds a try/catch around a ledger write, the skill runs post-verification:

# diff matches \b(catch)\b → dispatches silent-failure-hunter + compliance-reviewer in parallel
# scores → correctness 8, security 8, test_coverage 6, architecture_fit 8, simplicity 7
Verdict: NEEDS_CHANGES  (test_coverage 6 < 7 — cancellation path untested, tests/LedgerTests.cs:1)

Any single dimension below 7 auto-fails regardless of finding count.

How it works

Optionally checks CI first via hooks/ci-status.sh and cached .mtk/analyzer-output.json. Loads CLAUDE.md, the active .claude/tech-stack skill's ## Reference Files, security-checklist.md, testing-patterns.md, performance-checklist.md, and the F1-F16 catalog in ai-failure-modes.md. Reviews 6 axes including stub detection (stub-detection.md). Routes agents: compliance-reviewer, test-reviewer, architecture-reviewer, silent-failure-hunter (when the diff matches an error-handling regex such as \b(catch|except|finally)\b, \.catch\(, \?\?, \|\|, @ts-ignore, it\.skip), and context-miner at HIGH/MAX rigor — run in parallel, findings merged and deduped by (file, line, rule). Categorizes findings per review-finding-schema.md after applying its False-Positive Exclusion List, then scores 5 dimensions with a file:line evidence quote each. Circuit-breaker: scripts/workflow-artifact.sh remediation "$MTK_WF_UUID" review_<dimension> --score <n> returns ESCALATE at MTK_MAX_REMEDIATION_ITERS (default 3) or on score plateau → stop and escalate to a human (orchestration-gates.md failure_stop_gate). Writes results.review_scores.<dimension> when MTK_WF_UUID is set.

Why it works

An author is blind to its own assumptions, so AI self-review is not review. A hostile, evidence-scored second pass with a numeric gate (any dimension < 7 blocks) stops 'mostly style' rubber-stamps and forces real correctness and security scrutiny before merge.

#pre-commit-review

skill

Fast, security-focused gate on staged changes — deterministic linters plus a narrow AI pass, before every commit.

Trigger Routed via /mtk before a commit as a fast staged-diff gate; not a direct slash command (user-invocable: false).

What it does

Runs a fast, security-focused review on the staged diff only, checking a short list of critical rules. It runs a deterministic linter pass first, then a narrow AI pass for context-sensitive issues, and merges both into one schema-conformant verdict. It is built to run in seconds so engineers can run it before every commit.

Example

A clean commit produces the compact form only — no table, no JSON:

✅ Pre-commit review passed — 0 findings across 8 rules (8 files, 214+/37−)

Checked: secrets · SQL injection · PII in logs · auth · audit trail · env secrets · IAM scope · dependency intake

When findings exist it emits the full markdown table + JSON block instead.

How it works

Resolves MTK files via $CLAUDE_PLUGIN_ROOT or a plugin-cache find. Steps: git diff --cached; run hooks/pre-commit-linters.sh (source: "linter", confidence 100); merge cached .mtk/analyzer-output.json filtered to staged files (only if modified in the last 10 minutes — never runs the build itself); optional Roslyn DetectAntiPatterns via dotnet-claude-kit on .NET; dependency-introduction gate against dependency-intake-checklist.md (two Poor ratings block, one requires override); monorepo ripple via scripts/monorepo-ripple.sh; then the AI pass (source: "ai"). Checks 8 named rules (secrets, SQL injection, PII in logs, missing auth, missing audit, env secrets, IAM blast radius, dependency intake). Any critical at threshold → NEEDS_CHANGES.

Why it works

Catches secrets, SQL injection, PII leakage, and missing auth before they enter history. Running deterministic linters first keeps it fast and trustworthy, and the checklist beats the 'I already reviewed this mentally' rationalization the skill explicitly warns against.

#security-and-hardening

skill

Security-and-compliance lens applied as a design constraint when a change touches auth, secrets, audited state, or infra.

Trigger Routed via /mtk or auto-loaded when a change touches auth/secrets/audited state/infra; keyword-trigger-hinted (triggers: auth, jwt, secret, oauth, pii, hardcoded, ...). user-invocable: false, effort: max, context: fork.

What it does

Treats security and compliance as design constraints, not final-review polish. It identifies trust boundaries and checks that a change preserves confidentiality, integrity, auditability, and least privilege. It stops and asks rather than guessing when a requirement is unclear.

Example

A change adds a new endpoint plus a NuGet package. The skill identifies the request as a user-input trust boundary, checks for an auth path and PII-safe logging, confirms state mutations write an audit-trail entry in the same transaction, and walks the new dependency through the six-criteria intake gate — blocking if it earns two Poor ratings (citing CVE id / last-release date / license SPDX).

How it works

Reads CLAUDE.md and security-checklist.md first, then a domain supplement (e.g. domain-finance.md) if present. Identifies trust boundaries (user input, external service input, internal privileged ops); checks auth/authz, PII/secrets/logging, audit requirements for state changes, query safety / transaction boundaries / error exposure, and infra least-privilege / blast-radius / secret scope. Supply-chain step walks any new third-party dep through dependency-intake-checklist.md (two Poor block, one requires written override).

Why it works

Security bugs are cheapest to prevent at design time. Internal boundaries move, and audited-state mutations without an audit trail are compliance failures — so the skill refuses the 'this is internal' and 'this doesn't look like regulated data' rationalizations.

#code-simplification

skill

Behavior-preserving cleanup pass — remove dead code and premature abstraction without changing behavior.

Trigger Routed via /mtk after a fix or feature is verified and passing; not a direct slash command (user-invocable: false).

What it does

Simplifies only after behavior is proven: removes debug artifacts and dead code, collapses premature abstractions, and improves clarity without broadening scope or changing behavior. It walks a YAGNI 'less-code ladder' and stops at the first rung that satisfies the requirement. It has hard safety carve-outs it must never simplify away.

Example

After a verified fix, the skill deletes an unused private field and an obsolete helper, but leaves a delete-guard confirmation in place: lazy ≠ broken. Because delete/destructive-operation guards are a safety carve-out, a guard it suspects is dead becomes a security-and-hardening question to surface, not a cleanup call to delete.

How it works

Starts from verified working code; finds the smallest simplifications; walks the less-code ladder (does it need to exist → stdlib/built-in → existing codebase capability → existing dependency → one line → minimum readable block). Never removes the safety carve-outs (auth/authz checks, secret handling, input validation, migrations, delete-guards, audit logging — all presumed load-bearing). Asks before deleting ambiguous dead code; re-runs build and tests. Directly counters F5 (defensive-guard bloat) and F10 (dead-code accretion).

Why it works

AI accretes dead code and defensive-guard bloat as it generates variations; every rung of unnecessary abstraction is volume a future reader carries. A scoped, behavior-preserving pass trims it without letting cleanup become a hidden rewrite or strip a load-bearing guard.

#code-simplification --audit-duplicates

skill

Periodic sweep that clusters near-duplicate capabilities across the repo so they can be consolidated.

Trigger Optional mode flag on the code-simplification skill; run as a periodic sweep (e.g. after multiple modules merged into one repo, when a reviewer flags duplication, or CODE_INDEX.md is >30 days old with >50 commits since).

What it does

Scans the repo's CODE_INDEX.md for near-duplicate capabilities and groups them by stem so they can be consolidated before they multiply. For each cluster of 2+ entries it emits the capability name, all entry points (path:symbol), and a consolidate / keep-separate recommendation. Output is a findings list only, never edits.

Example

Run after two legacy modules are merged into one repo, it reports a cluster under the stem CalculateTotal — entries Orders/OrderService.cs:CalculateTotal and Billing/Invoicer.cs:CalculateTotalAsync (the Async suffix is dropped during stem normalization, so both fall in one cluster) → recommendation: consolidate on the Orders implementation (follows local idioms). Any actual consolidation goes through the normal spec flow.

How it works

Reads CODE_INDEX.md (template code-index-template.md), extracts all capability rows, and normalizes stems (lowercase, drop suffixes like Async / _v2 / Impl) to group near-duplicates. Uses bash scripts/query-code-index.sh find "<keyword>" for targeted follow-up lookups (falls back to grep on older repos). Cross-checks the active stack's coding guidelines to pick the canonical implementation.

Why it works

AI favors cloning over reuse (F4 copy-from-similar drift), so duplicate capabilities multiply silently. An index-guided periodic sweep catches the duplicates that slipped past the 'check before you write' reflex during spec writing.

#compliance-reviewer

agent

Hostile senior bank reviewer pinned to opus — the strictest lane, scores 5 dimensions and blocks on any.

Trigger Dispatched by code-review-and-quality for security/compliance-sensitive diffs (and by the implement orchestrator as Stage 1); runs in a forked context, pinned to model: opus, effort: high, read-only (required-toolsets: [read-only]).

What it does

An adversarial reviewer persona ('hostile senior code reviewer at an investment bank') that gets no credit for approvals. It reviews the diff against CLAUDE.md, the coding guidelines, architecture principles, and neighboring files, enumerates untested public methods, and scores the five dimensions with file:line evidence. It emits the canonical schema output plus an untested-paths list and a short 'what's good' note.

Example

On a payment handler it flags a missing [Authorize] as Critical (§1.1), lists FinalizePayment as a new public method with no test (Critical — handles money), and scores security 4. Because any dimension < 7 → NEEDS_CHANGES, the verdict is NEEDS_CHANGES even though only two findings surfaced — the score is the gate, not the count.

How it works

opus / effort high / read-only. Step 1 loads the full standards stack: CLAUDE.md, the tech-stack skill + its ## Reference Files, all .claude/rules/*.md, the coding guidelines, architecture-principles.md, conventions.md, the security/testing/performance checklists, and the security-and-hardening skill. Reads the behavioral diff (a mismatch with actual code is Critical). Runs security/architecture/data-layer/performance/test checklists; Step 4 lists every new/modified public method and flags untested ones (Critical if money/auth/mutation). Step 5 scores 5 dimensions with auto-fail rules (any < 7 blocks; uniform scores rejected as non-discriminating; a score with no file:line evidence treated as 0). Escalates to a human after 2 failing iterations on the same blocking dimension. Model tier per model-routing.md (Compliance review agent → opus).

Why it works

The highest-stakes lane needs the strongest model and a reviewer with no incentive to approve. The scored gate and 'if you find zero issues, look again' discipline stop sycophantic sign-off on security-sensitive code.

#architecture-reviewer

agent

Focused reviewer for slice boundaries, dependency direction, and structural drift.

Trigger Dispatched by code-review-and-quality for slice-boundary / dependency-direction concerns; forked context, model: sonnet, effort: high, read-only.

What it does

A single-lens reviewer that finds boundary violations, wrong-direction dependencies, leaky abstractions, misplaced responsibilities, and speculative abstractions. It gets no credit for approving code that 'works but is in the wrong place,' and names the correct layer/slice for each finding rather than just saying 'wrong place.'

Example

A handler in Slice A calls a service in Slice B directly. The agent flags it Critical — a wrong-direction cross-slice dependency — cites the file:line, names the shared/common module it should route through, and rejects the 'it's just one more dependency across slices' rationalization from its own table.

How it works

sonnet / read-only. Loads architecture-principles.md, the rule files, the stack's framework-patterns reference (e.g. mediatr-slice-patterns.md, fastapi-patterns.md), the code-simplification skill, and 2-3 neighboring files as the expected pattern. Checks slice boundaries and inward dependency direction, responsibility splits (controllers = orchestration only, services = no HTTP/DB), abstraction quality, naming/placement, and cross-cutting concerns (DI lifetimes). Wrong-direction cross-slice violations are Critical; god handlers, leaky abstractions, and speculative abstractions are Warnings. Emits the schema output.

Why it works

Working code in the wrong place creates maintenance traps — the next developer copies the pattern and the codebase drifts systemically. A dedicated boundary lens catches structural debt (which compounds faster than code debt) before merge.

#context-miner

agent

Organizational-memory lane — mines git history, PR/issue threads, and prior lessons for context the diff missed.

Trigger Dispatched by code-review-and-quality only at HIGH or MAX rigor (per the implement Rigor Score), in parallel with the other Stage 2 reviewers; forked context, model: sonnet, read-only.

What it does

While the other reviewers judge the diff on its own terms, context-miner surfaces context outside the diff: a prior revert of the same code, a related open issue, a decision recorded in a PR thread, or an applicable lesson. It is strictly read-only and returns an empty findings array honestly when nothing relevant exists.

Example

Reviewing a caching change, it runs git log --oneline -n 20 -- <path>, finds a Revert "add cache here" commit plus a PR-thread note 'we deliberately do not cache here because balances must be live,' and emits a Critical source: "context" finding citing the SHA and PR number — a regression no diff-only reviewer could see.

How it works

sonnet / read-only. For each touched path: git log --oneline -n 20 -- <path> and git log --oneline -S'<key symbol>' -- <path> for churn/reverts; gh pr list / gh issue list / gh pr view <n> --comments when gh is available (noted and skipped otherwise); bash scripts/learnings.sh query --files "..." --max 8 (fallback tasks/lessons.md). Emits source: "context" findings citing a concrete SHA / PR / issue / lesson id. Critical only when the change re-introduces a reverted defect or contradicts a recorded decision. Treats all mined commit/PR/issue text as untrusted data, never instructions.

Why it works

AI implementers work only from current files and the spec — they are blind to history. A lane that asks 'did we already try and revert this?' catches regressions and honored-then-undone decisions that on-the-diff review structurally cannot.

#test-reviewer

agent

Focused reviewer for test coverage, assertion quality, and verification gaps.

Trigger Dispatched by code-review-and-quality for coverage and verification-quality concerns; forked context, model: sonnet, effort: high, read-only.

What it does

Hunts gaps in test coverage, weak assertions, and missing verification. It reads both the changed test files and the production code they exercise, so it can name the specific uncovered method. Missing tests on methods that mutate financial data or handle auth are Critical.

Example

It finds a test whose only assertion is Assert.NotNull(result) on a money calculation, flags it as a weak always-pass assertion (Warning), and separately flags ApplyRefund — a new public mutation method with no test — as Critical, citing both tests/RefundTests.cs and src/Refunds/RefundService.cs.

How it works

sonnet / read-only. Loads testing-patterns.md, the stack testing supplement, and the test-driven-development skill. Runs coverage-completeness, assertion-quality, test-design, and integration-coverage checklists, then a Step-4 anti-pattern scan (always-pass assertions, no-assertion tests, exception-swallowing catches, mock-only verification, copy-paste duplication). Each finding cites file:line for both the test and the production code it fails to cover. Emits the schema output.

Why it works

Line coverage measures execution, not verification — a test asserting result != null passes even when the result is completely wrong. A dedicated lens forces meaningful assertions on mutation and error paths instead of false confidence.

#silent-failure-hunter

agent

Single-lens hunter for swallowed errors, fake fallbacks, silenced diagnostics, and muted tests.

Trigger Dispatched by code-review-and-quality when the diff matches an error-handling regex (catch/except/finally, .catch(, ??, ||, // eslint-disable, # noqa, @ts-ignore, Skip =, it.skip, xit(); runs in parallel with compliance-reviewer. Forked context, model: sonnet, read-only.

What it does

Finds places where code silently swallows, masks, or short-circuits a failure that should have surfaced. It applies one decision rule — if removing the silent handler would surface a real bug at runtime, flag it; otherwise skip it. Severity is set by the path the handler sits on: audited paths (auth, money, permissions, audit-log writes) are automatic Critical.

Example

It flags catch { return 0m; } in a GetBalanceAsync method as Critical with failure_mode: F9 (fake fallback — the caller cannot tell a real zero balance from a failure), and emits category: "error-handling". It skips catch (e) { logger.warn(e); } and catch { return Result.Error(e) } as false positives (logging-only / Result-pattern propagation).

How it works

sonnet / read-only. Scope is only lines in the diff. Works an exhaustive Step-3 pattern catalogue (empty catch/except, catch-then-default-return, lossy rethrow, broad catch of a narrow op, .catch(() => {}), unawaited audited side effect, ?./?? auth or money fallbacks, unjustified // eslint-disable/# noqa/@ts-ignore, added it.skip/xit). Step-4 path-based severity table, Step-5 FP discipline (logging-only catches, Result-pattern returns, defensive render ?., top-level daemon handlers are non-findings). Maps to F1/F2/F9 in ai-failure-modes.md and cites the F-code; the fixed category: "error-handling" makes merge-dedupe by (file, line, rule) straightforward.

Why it works

Swallowed exceptions and fake fallbacks are among the most damaging AI-generated patterns — the caller believes the operation succeeded. A narrow, path-aware lens surfaces them in audited paths without drowning the review in benign defensive code.

#ai-failure-modes.md

reference

Research-cited catalog of 16 recurring AI-coding failure modes (F1-F16) with bad/good examples and reviewer rules.

Trigger Loaded on-demand during review at any rigor level; reviewers cite its F-codes in a finding's failure_mode field.

What it does

A stable catalog of the patterns where AI-assisted development produces incorrect or misleading output — from catch-all error swallowing (F1) and hardcoded-success stubs (F2) to hallucinated packages (F3), fake fallbacks (F9), frozen-replay evidence (F15), and unverified prose claims (F16). Each mode carries a stable id, a citation anchor, a severity, a C#-flavored bad/good pair, and an imperative rule for reviewers.

Example

F3 (Hallucinated APIs or Packages) cites Spracklen et al., 'We Have a Package for You' (USENIX Security 2025), which measured ~19.7% of generated dependency references pointing at non-existent packages — the 'slopsquatting' supply-chain risk where the dangerous case is the one that installs successfully.

How it works

F1-F16 each carry severity (must-fix / should-fix / worth-noting) and a rule reviewers apply. Reviewers cite the F-code in the schema's failure_mode field so patterns aggregate across reviews: silent-failure-hunter maps to F1/F2/F9, code-simplification's ladder counters F5/F10, and the core/docdrift.txt linter pack mechanically catches a subset of F12. Citations anchor modes in external research (e.g. GitClear's 'Coding on Copilot' copy-paste analysis for F4).

Why it works

It gives reviewers a shared, evidence-backed vocabulary for the specific ways AI code fails, so findings are consistent and aggregatable rather than ad-hoc, and so the review catches the failure modes AI is statistically most likely to introduce.

#pre-commit-review-list.md

reference

Curated checklist of critical pre-commit rules, checked inline after every batch and before every commit.

Trigger Read by the implement and fix loops after each batch, and by pre-commit-review before each commit; regenerated with tech-stack items during setup-bootstrap.

What it does

A short, curatable checklist of the critical rules to verify before committing. In this repo the items are toolkit-specific (manifest entry per file, CSO single-sentence descriptions, version sync, skill anatomy, executable bash with set -euo pipefail, no hardcoded secrets, validate-toolkit passes, AGENTS.md routing). Teams add checks they care about and remove irrelevant ones.

Example

After a batch that adds a new skill, the implement loop walks this list and catches that the new SKILL.md is missing a ## Verification section and that AGENTS.md does not yet route to it — before the commit rather than in a later full review.

How it works

Consumed inline by the implement/incremental and fix loops after each batch and by pre-commit-review before each commit. The generated version is augmented with the active stack's ## Pre-Commit Review Items (S2.13) so the list is repo-specific rather than generic.

Why it works

An inline, repo-tuned checklist keeps every batch honest without waiting for a full review — it is the fast, deterministic memory of 'what always breaks here,' curated by the team.

#dependency-intake-checklist.md

reference

Six-criteria intake gate for every new third-party dependency — existence/legitimacy first, then scope, maintenance, size, security, license; two Poors block.

Trigger Invoked by pre-commit-review (step 4.7, when a manifest changes) and security-and-hardening (step 9, supply-chain step) to score every newly added third-party dependency.

What it does

A gate that runs whenever a dependency manifest changes. Criterion 0 (Existence & Legitimacy) runs first — a package must be verified against its live registry, and a Poor there is an immediate block (typosquat / dead repo / not found). Then criteria 1-5 (scope, maintenance, size, security, license) are each scored Good / Acceptable / Poor with an evidence-backed one-line rationale. Two Poors across criteria 1-5 block; any single Poor requires explicit engineer override.

Example

An AI-recommended NuGet package fails dotnet package search <id> --exact-match or resembles a popular package with suspiciously different scope → criterion 0 Poor → immediate block; the finding is reported with the exact registry output and the dep is marked [ASSUMED] in the plan with a checkpoint:human-verify step before any install.

How it works

Per-stack registry verification commands: dotnet package search <id> --exact-match, pip index versions <pkg>, npm view <pkg>, go list -m <module>@latest, cargo search <crate> --limit 5. Each Poor must cite concrete evidence (CVE/GHSA id for security, last-release/commit date for maintenance, du -sh or unpacked size for size, SPDX identifier for license) — a Poor without evidence is downgraded to Acceptable. Emits a dep_gate object into the workflow artifact results. Skips version bumps within the same major (vuln scan only) and internal/monorepo workspace deps; test/dev-only deps auto-pass a single size or license-acceptable Poor.

Why it works

Adding a dependency is a long-lived decision, and criterion 0 directly defends against F3 (hallucinated / slopsquat packages) — 'the dangerous case is the one that installs successfully.' Scoring maintenance, size, security, and license before merge stops abandoned or incompatibly-licensed code entering the supply chain.

#Scored adversarial evaluator (5-dimension scores)

concept

The numeric merge gate: score correctness/security/test_coverage/architecture_fit/simplicity 1-10; any < 7 blocks.

Trigger Automatic inside code-review-and-quality and compliance-reviewer; scores are recorded to the workflow artifact when MTK_WF_UUID is set.

What it does

Every full review scores five named dimensions 1-10, each with a file:line evidence quote, and the scores — not the finding count — are the gate. Any dimension below 7 forces NEEDS_CHANGES; uniform scores across all five are rejected as 'non-discriminating'; a score with no evidence is treated as 0.

Example

A review returns scores {correctness 8, security 9, test_coverage 6, architecture_fit 8, simplicity 7}. Even with zero critical findings, test_coverage 6 < 7 → verdict NEEDS_CHANGES. If a reviewer instead returns all 8s, the review is rejected as non-discriminating and must be redone.

How it works

Dimensions are correctness, security, test_coverage, architecture_fit, simplicity; rubric 9-10 exemplary / 7-8 acceptable / 4-6 blocks / 1-3 severe. Auto-fail rules are enforced by the reviewers, not the schema. The remediation loop runs through scripts/workflow-artifact.sh remediation "$MTK_WF_UUID" review_<dimension> --score <n>, which returns ESCALATE at MTK_MAX_REMEDIATION_ITERS (default 3) or when a dimension's score plateaus (the compliance-reviewer and schema cap the same-dimension loop at 2 failing iterations, escalating a third). On ESCALATE, the workflow stops and escalates to a human per orchestration-gates.md failure_stop_gate. Scores persist to results.review_scores.<dimension> and results.review_iteration.

Why it works

Finding-count reviews are gameable — approve with zero nits and call it clean. A scored gate with mandatory evidence and an escalation cap forces a discriminating judgment on every axis and stops an automated fix loop from grinding indefinitely on the same weak dimension.

#hooks/ci-status.sh

script

Wraps the gh CLI to fetch CI check/run status as structured JSON for review skills.

Trigger Bash helper run on demand (e.g. bash hooks/ci-status.sh); called by the code-review-and-quality skill

What it does

A small, dependency-tolerant helper that reports the CI status of the current branch as JSON, for the review workflow to fold into its assessment. If the gh CLI is missing or not authenticated, it exits cleanly with an 'available:false' result so review can proceed without CI context.

Example

During review the skill runs it:

bash hooks/ci-status.sh
# → {"available":true,"branch":"feat/payments","pr":128,"checks":[{"name":"build","status":"completed","conclusion":"success"}]}

With no gh installed it returns {"available":false,"reason":"gh CLI not installed"}.

How it works

Checks command -v gh and gh auth status; resolves the branch (arg 1 or git branch --show-current). If a PR exists for the branch (gh pr view <branch> --json number --jq '.number'), it returns gh pr checks <pr> --json name,status,conclusion; otherwise it falls back to gh run list --branch <branch> --limit 1 --json status,conclusion,name,headSha. Output is a single JSON object. Every failure path degrades to a clean 'available:false' object rather than erroring.

Why it works

Review quality improves when the reviewer can see whether CI actually passed, but the toolkit must work on machines without gh. Emitting structured JSON with graceful degradation lets the review skill use CI context when present and ignore it when not.

#hooks/api-compat-check.sh

script

Scans the diff against main for API breaking changes and emits review-finding-schema JSON.

Trigger Bash linter run on demand against the branch diff: bash hooks/api-compat-check.sh [base-branch] (default main)

What it does

A deterministic backward-compatibility linter. It reads the diff between the current branch and a base branch and looks only at removed lines to flag things that would break external consumers: deleted HTTP routes, removed public properties/fields on contract types, removed public methods, and touched OpenAPI/Swagger specs. Findings come out in the same schema the review workflow consumes.

Example

You delete a controller route and run:

bash hooks/api-compat-check.sh main
# → {"findings":[{"id":"BC001","severity":"critical","confidence":100,"rule":"API-ROUTE-REMOVED","source":"linter","file":"src/PaymentsController.cs","line":0,"rationale":"API route removed: [HttpGet(\"/payments\")] — existing consumers will get 404",...}]}
How it works

Takes the diff via git diff <base>...HEAD, isolates removed lines, and branches on the active stack from .claude/tech-stack. For dotnet it regex-matches removed [HttpGet/Post/Put/Patch/Delete("...")] routes (critical), removed public properties on dto/request/response/model/contract/event files and removed public methods on controller/endpoint/handler/service files (warning). Python matches @app/@router decorators and dataclass/pydantic fields; TypeScript matches route handlers and exported interface/type properties. Any modified openapi/swagger file adds an API-SPEC-CHANGED warning. Findings carry source:"linter", confidence:100, and ids BC001, BC002, … escaped without jq per S3.3.

Why it works

Breaking an API surface is a high-cost, easy-to-miss mistake in a codebase with external consumers. A deterministic linter catches removed routes/contracts objectively — before the AI reviewer even reasons about the change — and phrases each as an actionable deprecate-first fix.

#hooks/parse-build-diagnostics.sh

script

Turns dotnet/ruff/tsc/biome build output into review-finding-schema JSON with source:analyzer.

Trigger Bash parser fed build output via pipe or --file; called by incremental-implementation and the tech-stack-* skills

What it does

Converts raw compiler/linter output into structured, machine-checkable findings the review layer can trust at 100% confidence. It auto-detects the tool format, maps rule codes to severities, and normalizes everything into the shared finding schema — so a build error becomes a first-class review finding rather than a wall of text.

Example

The implementation loop pipes a build through it:

dotnet build 2>&1 | tee /dev/tty | hooks/parse-build-diagnostics.sh > .mtk/analyzer-output.json
# .mtk/analyzer-output.json → {"findings":[{"id":"A001","severity":"critical","confidence":100,"rule":"CS0103","source":"analyzer",...}]}
How it works

Accepts --format (msbuild|ruff|tsc|biome), --stack, --file, else reads stdin and auto-detects: JSON with "code" → ruff, other JSON → biome, (line,col): warning|error → msbuild, .tsx?(line,col) → tsc. Severity comes from hooks/analyzer-severity/<stack>.txt (tab-separated rule→severity, default warning); msbuild 'error' level is forced to critical. Each match is emitted with source:"analyzer", confidence:100, sequential ids A001, A002, …, escaped without jq per S3.3.

Why it works

AI review is probabilistic; compiler and linter output is ground truth. Normalizing that ground truth into the same finding schema lets the review workflow treat build errors deterministically (confidence 100) and gives a consistent fallback even when heavier stack-specific tooling isn't installed.

#hooks/verify-behavioral-diff.sh

script

Warns when a recent spec is missing (or has an empty) behavioral_diff field before review.

Trigger Bash advisory check run on demand (e.g. before review); not wired into settings.json event hooks

What it does

A tiny advisory guard for spec quality. It finds the most recently modified spec and checks that it declares a behavioral_diff — the field reviewers rely on to verify implementation intent. If the field is missing or empty, it prints a one-line nudge; it never blocks.

Example

You finish coding against a spec that has no behavioral_diff and run the check:

bash hooks/verify-behavioral-diff.sh
# → ⚠ Advisory: Spec 2026-07-08-payments.json has no behavioral_diff field. Consider adding one before review — reviewers use it to verify implementation intent.
How it works

Scans docs/specs for *.json modified in the last day (most recent first). If the file lacks a "behavioral_diff" key it prints the 'no behavioral_diff' advisory; if the key exists but is empty ('""') it prints the 'empty behavioral_diff' advisory. Uses grep only, always exits 0 — non-blocking per the team's advisory-not-gate adoption strategy.

Why it works

Reviewers verify that an implementation matches its declared behavior change; a spec with no behavioral_diff quietly removes that check. A gentle pre-review reminder keeps specs review-ready without gating engineers who are moving fast.

05 Learn 12 entries

Capture corrections and hard-won approaches as durable lessons, then promote the best of them team-wide — with a validated contribute-back path.

#correction-capture

skill

Turns an engineer's in-the-moment correction into a reusable, retrievable lesson so it never has to be given twice.

Trigger Loaded automatically when the engineer corrects or redirects the agent (frontmatter user-invocable: false; trigger: engineer-correction|no|stop|not-like-that|redirect)

What it does

When the engineer says "no", "not like that", "stop", or redirects the approach, this skill captures that correction as a durable lesson instead of losing it at session end. It extracts the general rule (not just the specific instance), records the "why", and decides whether the lesson is personal or team-wide. The stated goal: the engineer should never need to give the same correction twice.

Example

Engineer: "No, don't wire up AutoMapper here — this codebase hand-maps DTOs." The agent replies "Understood — I'll hand-map instead of using AutoMapper because the codebase avoids reflection-based mapping," greps both lesson files for prior entries, then runs:

bash scripts/learnings.sh add --scope team --source correction \
  --decision-origin claude-recommended-rejected --severity warn --phase implement \
  --files "src/Mapping/OrderMapper.cs" --title "Hand-map DTOs, no AutoMapper" \
  --body "Engineer rejected AutoMapper" --rule "Hand-map DTOs in this codebase" \
  --applies-when "phase=implement AND mapping code" --evolution-actions none
bash scripts/learnings.sh regen-markdown

and adjusts the current work immediately.

How it works

Workflow: (1) recognize the correction (negation/redirection/frustration signals); (2) acknowledge without performative agreement; (3) decide destination — .claude/lessons/personal.md (gitignored, default) vs tasks/lessons.md (committed, team-wide), first-person language leans personal; (4) grep both files for prior lessons — a true reversal is captured with --supersedes <old-id> (old entry stays for audit but learnings.sh query stops surfacing it), a duplicate just bumps recurrence.count; (5) capture structured via scripts/learnings.sh add (flags include --scope, --source correction, --decision-origin, --severity, --phase, --files, --title, --body, --rule, --applies-when, --wrong-turns, --time-cost, --evolution-actions, --memory-type) then learnings.sh regen-markdown, with a markdown-append fallback for repos without the script; (6) escalate 3+ repeats toward a CLAUDE.md rule; (7) apply the correction now. Replacing (not appending) a lesson uses a temp file promoted via mtk_guarded_write so a partial regen can't truncate the file. --decision-origin is required for correction-sourced entries and feeds the sycophancy index.

Why it works

Corrections are the highest-density signal in a session and are normally lost at session end, so the same mistake recurs across sessions. Capturing the general rule plus the "why" (not a cargo-cult instruction) and routing it into a store that reloads at the next spec/fix makes correction knowledge compound instead of evaporate.

#golden-path-capture

skill

Captures the agent's own struggle-then-success arc — the dead ends plus the fix — as a lesson, no engineer correction needed.

Trigger Loaded automatically when the agent resolves its own repeated failure (user-invocable: false; trigger: self-struggle-then-success|repeated-failure-same-signature|trial-and-error-resolved|found-working-approach)

What it does

When the agent hits the same failure signature 2+ times, then switches to a materially different approach that works, that arc holds exactly what a future session needs: the wrong turns to avoid and the golden path that resolved them. This skill captures it in-session at the moment of resolution. It is the self-driven sibling of correction-capture — no human redirect is involved.

Example

A test keeps failing with bash -c reporting a piped command as success. The agent tries two fixes that don't work, then finds bash -o pipefail -c fixes it, and records:

bash scripts/learnings.sh add --scope team --source golden-path \
  --decision-origin system-inferred --severity warn --phase implement \
  --files "hooks/verify-commands.sh" --title "Child shells need -o pipefail" \
  --body "false | tee exited 0 under bash -c" \
  --rule "Run assembled cmds via bash -o pipefail -c" \
  --wrong-turns "set -o pipefail in parent — not inherited,quoting the cmd — no effect" \
  --time-cost 15 --evolution-actions reference
bash scripts/learnings.sh regen-markdown
How it works

Requires the full arc before capturing: same signature failed 2+ times, NO engineer correction (else it's correction-capture), and a materially different fix. Reuses correction-capture's destinations and the same learnings.sh add path but with --source golden-path and --decision-origin system-inferred. The distinctive payload fields are --wrong-turns (each dead end + why) and --time-cost (minutes lost) — a golden-path lesson with empty wrong_turns teaches only half. Optionally attaches the v7.25 executable-contract fields (--confidence, --output-contract, --prefinal-checklist, --source-evidence-refs) when the golden path has a checkable outcome. Must be captured in-session — deferring to Phase 7 or a later lesson-mining sweep is the exact loss the skill prevents.

Why it works

The moment the agent gets itself unstuck is where the richest lesson lives, yet it's normally uncaptured until a mining sweep happens to notice it — if ever. Recording the failure signature → golden-path mapping and the dead ends stops the next session from re-paying the same expensive trial-and-error loop.

#/promote-lesson

skill

Explicit, audited path to move a gitignored personal lesson into the committed team file — and optionally share it across all repos.

Trigger Slash command (user-invocable: true; argument-hint: [keyword to filter lessons]); allowed-tools: Read, Write, Edit, Bash, AskUserQuestion

What it does

Personal lessons in .claude/lessons/personal.md are gitignored and belong to the individual engineer. When one matures into a rule every contributor should follow, this skill promotes it to the committed tasks/lessons.md, rewording first-person phrasing for team applicability. Promotion is always explicit and per-lesson — never auto-heuristic. It can also open a validated contribute-back PR to the central toolkit.

Example

Engineer runs /promote-lesson EF. The skill lists personal lessons matching "EF", asks via AskUserQuestion which to promote, shows the reworded team text ("I prefer…" → "engineers/the codebase…") for confirmation, appends it to tasks/lessons.md with a Promoted from personal: <original date> note, deletes it from personal.md, and then offers (default no) to open a one-file contribute-back PR.

How it works

Workflow: locate personal file (resolve to main worktree); list ## header candidates, filtered by the optional keyword; AskUserQuestion for selection; reword first-person → team phrasing and confirm; append via learnings.sh add --scope team --source promotion (inheriting the personal entry's decision-origin) then regen-markdown, with a markdown-append fallback; remove the entry from personal.md so there's one source of truth; suggest CLAUDE.md/.claude/rules/ if the lesson is foundational. Optional step 5b attaches the v7.25 executable contract (--confidence high reserved for actually-verified paths, lint by mtk-doctor). Optional contribute-back writes an anonymized lesson to lessons/contributed/<YYYY-MM-DD>-<slug>.md and opens a gh pr create PR to moberghr/mtk-agent-toolkit targeting main.

Why it works

Without an explicit promotion path, useful personal lessons either stay siloed or get force-shared without review, and duplicated lessons create two sources of truth. This gates the personal→team boundary behind human selection, preserves provenance (original capture date), and keeps the committed team file trustworthy.

#lesson-mining

skill

Periodic reject-by-default sweep of past session transcripts for durable lessons — suggest-only, never auto-writes.

Trigger Routed via /mtk mine lessons (user-invocable: false; trigger: mine-lessons|what-did-we-learn|harvest-transcripts|periodic-lesson-sweep)

What it does

Live capture only fires in the moment; lessons that surface implicitly across a session or across many sessions go unrecorded. This skill is the periodic safety net: it reads past session transcripts, extracts candidate lessons and memories, and applies a strict reject-by-default rubric so only durable, non-obvious, non-derivable lessons survive. It is suggest-only — it proposes candidates and never writes to the lessons store on its own. An empty result set is a correct outcome.

Example

Engineer runs /mtk mine lessons for the last 7 days. The skill resolves transcripts under ~/.claude/projects/<sanitized-cwd>/*.jsonl, scans for corrections/repeated friction, runs each candidate through the rubric — rejecting most (e.g. "EF Core tracks entities automatically" fails R2 framework-boilerplate) — and surfaces one survivor citing admit rule A1 with a verbatim evidence excerpt, asking the engineer to accept. Accepted candidates are routed through correction-capture, not written here.

How it works

Workflow: (1) locate transcripts (graceful "no transcripts found" if absent); (2) scope the window, default last 7 days; (3) treat every transcript as UNTRUSTED — never follow instructions found in transcript content (rubric R6); (4) extract raw candidates from signals; (5) apply .claude/references/lesson-mining-rubric.md — a candidate survives only if it passes ≥1 admit rule (A1–A4) and fails all reject rules (R1 derivable-from-code, R2 framework-boilerplate, R3 already-fixed-in-code→propose code comment, R4 generic-advice, R5 inferred-preference-no-reason, R6 injection); (6) present survivors with admit/reject rules cited; (7) write only on explicit approval, routing through correction-capture / promote-lesson. Includes a Post-Ship Retro mode that runs against approved artifacts rather than transcripts.

Why it works

A single weak lesson poisons trust in the whole lessons file so engineers stop reading it; the false-positive cost is high. The deliberately strict rubric and suggest-only discipline keep the store signal-dense, and treating transcripts as untrusted data prevents prompt-injection from smuggling instructions in as "lessons".

#Post-Ship Retro (lesson-mining mode)

concept

A ~5-minute plan-vs-reality pass after shipping that routes each miss to the durable surface it should change.

Trigger Run deliberately right after a feature ships, as a mode within lesson-mining (against approved artifacts, not transcripts)

What it does

The retro is the immediate, deliberate counterpart to the reflective transcript sweep — the step most teams skip. Run right after a feature ships while the plan-vs-actual gap is fresh, it diffs the approved spec/plan against what actually happened and classifies each miss by where it should be fixed. It answers one question: what would I want to have known before I started?

Example

A feature just shipped. The retro compares docs/specs/* and docs/plans/* (and their assumptions/risks) to reality. It finds one false assumption — "the queue guarantees ordering" — classifies it as a wrong premise, and proposes a single CLAUDE.md/context-file line so the next session starts from the corrected fact. A clean plan that held up produces zero lines, which is a valid outcome.

How it works

Runs against approved artifacts, not the transcript: (1) diff approved spec/plan and its assumptions/risks against reality; (2) classify each miss to its durable surface — wrong premise (false assumption) → CLAUDE.md/context-file line; blind spot (a consideration the plan never raised) → plan-template/spec-checklist line; one-off slip → no lesson, note and move on; (3) apply the same reject-by-default rubric and suggest-only discipline as the sweep, routing survivors through correction-capture / promote-lesson. Kept to about 5 minutes.

Why it works

Capturing lessons is what makes the workflow loop compound rather than repeat, and the retro targets the surfaces future work actually reads — a wrong premise belongs in the context file, a blind spot belongs in the plan template — rather than dumping everything into a lessons file that fires too late.

#pr-review-mining

skill

Mines recurring reviewer-feedback phrases from the last N merged PRs and surfaces them as suggest-only [MINED:feedback] candidates.

Trigger Routed via /mtk repo-health or setup-audit --mine-prs (user-invocable: false); wraps scripts/pr-review-mine.sh

What it does

Wraps the PR miner script to fetch merged PR review threads via gh and cluster repeated reviewer-feedback phrases. Output is always advisory: phrases tagged [MINED:feedback] and never auto-edited into architecture-principles.md. It surfaces what reviewers ask for over and over so a team can decide whether it should become a written rule.

Example

Engineer asks "what are reviewers asking for repeatedly?" The skill confirms gh auth status, runs bash scripts/pr-review-mine.sh --prs 10, and prints a table like | add a regression test | 4 | #21, #22, #23 |. The engineer opts in per-phrase, manually adding a [MINED:feedback] line citing those PRs to architecture-principles.md. If gh is unauthenticated the output says Skipped: <reason> rather than fabricating phrases.

How it works

Workflow: (1) pre-flight gh auth status >/dev/null 2>&1 and stop with a warning if unauthenticated (never fixes gh auth itself); (2) run bash scripts/pr-review-mine.sh --prs 10 (--prs N default 10, range 1..50; --json for machine output); (3) consult .claude/references/pr-mining-patterns.md for phrase meaning and the ## Denylist; (4) present each candidate with its PR citations, suggest-only — never auto-edit architecture-principles.md, the engineer opts in per-phrase with the [MINED:feedback] tag; (5) when invoked from repo-health, the parent emits a "PR mining" section into .claude/repo-health-latest.md. A phrase needs ≥2 distinct merged PRs to become a candidate.

Why it works

Auto-promoting mined phrases would amplify one reviewer's preference or a one-time disagreement into a team-wide rule; requiring ≥2 distinct PRs plus human opt-in and the [MINED:feedback] tag keeps mined feedback distinguishable from deliberately-authored principles and keeps humans as the filter.

#Two-tier lessons store (tasks/lessons.md + .mtk/learnings.jsonl)

concept

Institutional memory split across a committed human-readable markdown file and a gitignored machine-readable JSONL mirror.

Trigger Read at the start of every /mtk workflow; tasks/lessons.md is committed, .mtk/learnings.jsonl and .claude/lessons/personal.md are gitignored

What it does

Lessons live in two synchronized tiers. tasks/lessons.md is committed, team-canonical, and human-readable — the source of truth for cross-machine sharing, read at the start of every /mtk workflow. .mtk/learnings.jsonl is a gitignored, machine-readable JSON Lines mirror indexed for retrieval. Personal, individual lessons live separately in gitignored .claude/lessons/personal.md. The markdown file also carries an Auto-generated section and a Manual additions section that survive regeneration.

Example

A team lesson captured via correction-capture writes one JSONL line to .mtk/learnings.jsonl and appears as a bullet under ## Auto-generated (do not edit by hand) in tasks/lessons.md after regen-markdown. A manually written note under ## Manual additions (preserved across regen) is preserved and, on the next learnings.sh migrate, re-emitted as a source: "manual" entry. A personal preference instead stays in .claude/lessons/personal.md until explicitly promoted.

How it works

JSONL (one entry per line) is used rather than a single JSON array so pure-bash tooling can append and grep without an external parser (S3.3). Scope (personal vs team) controls who loads an entry; promote-lesson flips personal → team. The markdown view is regenerated from the store; manual markdown additions remain canonical for the team and are re-emitted on migrate (which is idempotent — a re-run is a no-op once the store is seeded). The committed markdown is the cross-machine source of truth; the JSON store is rebuildable.

Why it works

Machines need a queryable, indexable store for 5-layer retrieval while humans need a readable, committable, reviewable file — one format can't serve both well. Splitting them (and keeping personal lessons out of git) lets retrieval be fast and structured without sacrificing a trustworthy team-canonical record, and keeps the JSON store disposable/rebuildable.

#Executable lesson contract (v7.25)

concept

Optional fields that turn a prose lesson into a checkable one — the shape of a correct outcome plus the checks that confirm it.

Trigger Optional fields attached at capture or promote time via learnings.sh flags; well-formedness linted by mtk-doctor

What it does

Four optional fields (added in v7.25) let a lesson carry the shape of a correct result and the checks that verify it, rather than only advice. A lesson that omits them is fully valid and renders as before; when present they make the lesson verifiable next time, not just readable. Promotion is the natural point to attach them, and confidence is only rated high when the golden path was actually verified.

Example

Promoting a golden-path lesson, the engineer attaches a contract: --confidence high --output-contract '{"required_files":["planning_note.md","summary.json"],"json_fields":["title","status"]}' --prefinal-checklist '[{"check_id":"required_files_exist","description":"Both files exist before finishing.","verification_method":"file_exists","blocking":true}]' --source-evidence-refs "evidence:trace-step-002". A future session applying the lesson can now confirm the outcome instead of trusting prose.

How it works

The four fields are confidence (low|medium|high — high reserved for actually-verified paths), output_contract (a {...} object describing required_files/json_fields), prefinal_verification_checklist (a [...] array of {check_id, description, verification_method, blocking} entries), and source_evidence_refs (provenance strings tying the lesson to its evidence). learnings.sh add guards only the outer shape (object/array) to stay JSON-parser-free (S3.3); mtk-doctor performs deep well-formedness linting, verifying each checklist entry carries the required keys. Borrowed from an agent-apprenticeship runtime_training schema.

Why it works

A prose lesson tells a future session what to do but can't confirm it was done; encoding the correct-outcome shape and blocking checks lets the lesson be enforced rather than merely re-read, closing the gap where an agent "follows" a lesson yet still ships a wrong result. Gating high confidence on real verification keeps the contract trustworthy.

#Contribute-back PR (lessons/contributed/)

concept

Optionally shares a team-promoted lesson across all repos via an anonymized, CI-validated, human-merged PR to the central toolkit.

Trigger Optional final step of /promote-lesson (default no); opens a single-file gh PR to moberghr/mtk-agent-toolkit, validated by CI

What it does

After a lesson is promoted to the team file, promote-lesson can offer to share it beyond one repo by opening a contribute-back PR to the central MTK toolkit — so every repo using MTK benefits. It is opt-in (default no, never auto-pushed), requires the lesson to be anonymized of any client/secret material first, and is gated by CI plus a human merge.

Example

After promoting a lesson, the engineer accepts the contribute-back offer. The skill runs the anonymization checklist (no client/repo names, no URLs/tickets/employee names, no credentials, no private paths), writes lessons/contributed/2026-07-09-hand-map-dtos.md, and runs gh pr create targeting main touching only that one file. The .github/workflows/validate-lesson-pr.yml workflow validates path/size/secret/injection constraints and labels the PR lesson-validated — then a human merges it.

How it works

Step 8 of promote-lesson: ask via AskUserQuestion (default no). If accepted: (a) run the mandatory anonymization checklist — every item must pass or the lesson stays local; (b) write the anonymized lesson to a single lessons/contributed/<YYYY-MM-DD>-<slug>.md following lessons/contributed/README.md; (c) open a gh pr create PR to moberghr/mtk-agent-toolkit targeting main, one file only; (d) CI .github/workflows/validate-lesson-pr.yml checks path/size/secret/injection and labels lesson-validated — it never auto-merges. One lesson per PR keeps the audit trail clean.

Why it works

Lessons are most valuable when shared across every repo, but pushing raw client lessons to a shared repo risks leaking identifying or secret material and amplifying one team's noise everywhere. Mandatory anonymization, one-file PRs, automated validation, and a required human merge make cross-repo sharing safe and auditable.

#hooks/capture-learnings.sh

hook

After a substantial session with no lessons recorded, reminds the agent to capture them; flags promotion candidates.

Trigger Automatic Stop hook, wired in .claude/settings.json

What it does

At session end it checks whether the session was substantial and whether any lesson was written recently. If real work happened but tasks/lessons.md was not touched, it prompts the agent to capture corrections or golden paths. Separately, if lessons have piled up around a recurring theme, it suggests promoting the pattern to a permanent rule.

Example

You end a 30-edit session where the engineer corrected your approach twice but nothing was written down. The hook emits:

LEARNING CHECK: Substantial session (57 operations, 30 modifications) with no lessons captured. If the engineer corrected your approach... capture it in tasks/lessons.md using the correction-capture workflow; if YOU struggled 2+ times... use golden-path-capture.
How it works

Computes the per-day session-state path itself from pwd | cksum as ${TMPDIR:-/tmp}/mtk-context-budget-<pwd-cksum>-<YYYYMMDD> — it does NOT source hooks/lib/hook-io.sh or call mtk_session_file, so it lines up with the file context-budget.sh writes (which keys on the git repo root) only when the working directory is the repo root. 'Substantial' = ops >= 20 OR mods >= 5. It treats lessons as captured if tasks/lessons.md was modified within the last 120 minutes (find -mmin -120). For promotion it counts '## ' lesson headers (>= 3) and tokenizes 'Rule:' lines, surfacing keywords (length > 4) that appear 3+ times as a PROMOTION CANDIDATE nudge pointing at CLAUDE.md or .claude/rules/. Advisory only; always exits 0.

Why it works

Lessons only compound if they are written down; an unrecorded correction just gets repeated next session. And corrections that merely accumulate in lessons.md without being promoted don't compound either — so the hook pushes both capture and, eventually, promotion to a durable rule.

#claude-md-audit

skill

Re-grades an existing CLAUDE.md against a quality rubric and proposes minimal append-only fixes — never a rewrite.

Trigger Routed via /mtk (e.g. "CLAUDE.md feels stale", "audit CLAUDE.md"); user-invocable: false.

What it does

A periodic health check for CLAUDE.md. It grades what is already documented against a quality rubric and cross-checks it against the codebase, then proposes minimal, append-only diffs for stale or broken content. It is a re-grade loop, not a regeneration — it honors rule S1.5 (CLAUDE.md is protected): it never overwrites and never rewrites whole sections without approval. Distinct from setup-bootstrap (which creates CLAUDE.md) and setup-audit (which refreshes architecture-principles.md).

Example

You say "CLAUDE.md feels off" and /mtk routes here. A dynamic-context block first inventories every CLAUDE.md / .claude.local.md and the active tech stack; Phase 2 cross-checks documented build/test commands against the repo — a dotnet build that now fails, a referenced file that was deleted, a convention that changed. It returns graded findings and proposes appended fixes as diffs. Nothing is written until you approve.

How it works

Front-matter routes it (user-invocable: false; trigger: claude-md-audit|memory-rot|stale-claude-md|claude-md-quality; skip_when: bootstrap|first-time-setup|no-existing-claude-md). A !-prefixed dynamic-context block runs on load to list target CLAUDE.md files plus the tech stack. Phase 1 (Discovery) confirms the inventory and — if the project root has no CLAUDE.md — stops and defers to /mtk-setup; Phase 2 (Currency Cross-Checks) grades commands, paths, and conventions against reality; the remaining phases emit append-only diffs applied only on approval.

Why it works

CLAUDE.md is the always-loaded prompt, so when it drifts — broken commands, references to deleted files, missing recent conventions — every session silently inherits the rot. Auditing on a cadence catches that, and the append-only-with-approval discipline (plus S1.5) keeps the engineer's hand-written intent intact instead of letting a blind regeneration clobber it.

#claude-md-capture

skill

At session end, distills newly-discovered commands, gotchas, and patterns into minimal append-only CLAUDE.md additions.

Trigger Routed via /mtk at session end (e.g. "save what we learned", "update CLAUDE.md"); user-invocable: false.

What it does

A session often surfaces context CLAUDE.md was missing — a build command you had to discover, a gotcha you hit, an env quirk, a pattern the codebase follows. This skill reflects on the finished session, drafts minimal additions, shows them as diffs, and applies only what you approve — so the next session starts where this one ended. It is distinct from claude-md-audit (which fixes rot in what already exists) and correction-capture (which records engineer corrections as lessons rather than project facts).

Example

After a session where you discovered the app only boots with DOTNET_ENVIRONMENT=Local, you say "save what we learned." A dynamic block shows the writable CLAUDE.md files, whether .claude.local.md exists, and the root CLAUDE.md line budget (cap 120). It drafts a one-line append to the Build section as a diff; team-wide facts go to CLAUDE.md, personal-only notes to the gitignored .claude.local.md. Nothing lands without approval.

How it works

Front-matter (user-invocable: false; trigger: capture-claude-md|session-learnings|update-claude-md|what-did-we-learn|revise-claude-md; skip_when: bootstrap|first-time-setup|mid-task|nothing-learned). Phase 1 (Reflect) asks what context, had it been in CLAUDE.md at the start, would have helped. It drafts minimal append-only additions using Edit never Write (honoring S1.5), routes personal notes to .claude.local.md, and respects the root CLAUDE.md line budget. Additions are applied only with approval; if nothing durable surfaced, it says so and stops rather than manufacturing content.

Why it works

Knowledge that stays in one engineer's head evaporates at session end, so the next session re-discovers the same command or re-hits the same gotcha. Compounding it into the always-loaded prompt is high-leverage — and the append-only / approval / line-budget guards stop that from bloating or overwriting the file.

06 Operate 10 entries

Keep the install and the repo healthy: PASS/WARN/FAIL diagnostics, an AI-readiness scorecard, and honest usage signals.

#setup-refresh

skill

Drift-scoped re-run of all generators that proposes hunk-by-hunk diffs for engineer-edited files, not overwrites.

Trigger Routed via /mtk-setup --refresh [--dry-run]; not directly user-invocable

What it does

Re-runs setup's generators on an already-bootstrapped repo, but only for artifacts a deterministic plan marked stale/missing. It never re-audits blind and never silently overwrites a hand-edited file — engineer-edited files go through the diff-proposal contract. Refresh brings the docs back in line with the code (the inverse of converge).

Example

After a big merge, /mtk-setup --refresh --dry-run prints the setup-refresh-plan.sh table (e.g. .claude/references/architecture-principles.md | stale | 4 claim(s) touch changed files) plus a paragraph per stale row, and writes nothing. Running without --dry-run regenerates only the stale sections into a temp file, then asks via AskUserQuestion "MTK wants to make N change(s) to CLAUDE.md..." and applies only the hunks you approve, ending with Updated / Created / Preserved / Needs review counts.

How it works

STEP 0 preconditions (.claude/tech-stack + .claude/mtk-version.json must exist, else hard stop). STEP 1 scripts/setup-refresh-plan.sh --json is the sole staleness source. STEP 2 dry-run gate. STEP 3 scoped regeneration to temp paths: docs re-derive only claim lines whose anchors point into the git diff <stamp>..HEAD changed set (full re-audit only if the stamp is missing or >~30% of tracked source files changed); detected-tools.json re-run of audit STEP 2.6; reference re-pruning (ship-time only, never deletes); deterministic AGENTS.md/tool-config/index regen via generate-agents-md.sh, generate-tool-configs.sh --all, refresh-derived.sh (marker-refusal guards, never --force). STEP 4 applies regen-diff-contract.md per file; STEP 5 re-stamps + runs verify-claims.sh with the retry loop; STEP 6 four-count report. Writes to architecture-principles.md go through the shrink guard (S3.16).

Why it works

Generated docs rot as code changes. A blind re-audit would either clobber engineer edits or churn stable sections and blow prompt-cache prefixes. Scoping to real drift plus proposing diffs keeps refreshes small, honest, and non-destructive.

#setup-converge

skill

Judges the code against the agreed principles docs and reports drift as graded, read-only work items — never auto-fixes.

Trigger Routed via /mtk-setup --converge; read-only outside .claude/.mtk-cache/; not directly user-invocable

What it does

The inverse of refresh: it treats architecture-principles.md/conventions.md as normative and reports where the codebase has drifted from what the team already agreed to. It produces graded (blocking/flag/note) work items, each traceable to a failing claim. Read-only outside .claude/.mtk-cache/.

Example

/mtk-setup --converge on a repo whose code moved away from an [EXTRACTED] principle writes .claude/.mtk-cache/converge-report.md with a ### Blocking item quoting the principle line, its failed anchor, the changed path, and a one-line "consider whether X still applies" direction, plus a per-doc summary table. Interactively it may offer via AskUserQuestion to append the N blocking items to tasks/todo.md; non-interactive runs never append.

How it works

STEP 0 preconditions (tech-stack + mtk-version + at least one <!-- mtk-stamp doc, else stop). STEP 1 runs verify-claims.sh against temp copies under /tmp/mtk-converge/ (never the tracked doc, since the engine rewrites in place), checking each exit code. STEP 2 pairs weak claims with changed paths via audit-drift-check.sh <doc> --json. STEP 3 grades from the ORIGINAL tag captured in each weak entry: [EXTRACTED]/[ENFORCED]→blocking, [INFERRED:N≥0.7]→flag, [INFERRED:N<0.7]/[AMBIGUOUS]→note. STEP 4 writes the report; STEP 5 optional todo append behind a non-skippable interactive gate. It never grades anything not traceable to a weak-claims entry.

Why it works

Once a team agrees on principles, the risk flips: the code drifts from the doc. Converge surfaces that drift as reviewable, evidence-cited work items without ever touching source or the docs — keeping the fix decision with engineers.

#/mtk-setup --check

concept

Read-only CI staleness gate: runs the refresh plan in check mode and propagates its exit code, writing nothing.

Trigger Slash-command flag (inline workflow in mtk-setup); also runnable directly as bash scripts/setup-refresh-plan.sh --check

What it does

A fast, read-only gate that answers "are the generated docs still accurate?" It runs the staleness plan and translates the exit code into a pass/fail verdict. Suitable for CI or a quick manual check; it never writes anything.

Example

/mtk-setup --check prints the per-artifact plan table and, on a clean repo, reports ✅ Generated docs are fresh. (exit 0). If any non-informational row is stale/missing it reports ⚠️ Staleness detected — run /mtk-setup --refresh to reconcile. (exit 1).

How it works

mtk-setup's inline --check workflow runs bash scripts/setup-refresh-plan.sh --check, prints its output verbatim, and propagates exit codes 0/1/2 into the three report lines. The plan script (read-only) evaluates seven artifact rows and fails (exit 1) if any non-informational row is stale or missing; the .claude/setup-answers.json row is informational only and never affects the gate.

Why it works

Drift is invisible until someone trusts a stale doc. A cheap, deterministic gate lets teams catch drift in CI (fail the PR) or on demand, without the cost or write-risk of a full refresh.

#/mtk-setup --update-guidelines

concept

Bumps the pinned coding-guidelines SHA + recorded sha256 hashes to current HEAD; does not re-run bootstrap.

Trigger Slash-command flag (inline workflow in mtk-setup); mutually exclusive with every other flag

What it does

Advances the pinned moberghr/coding-guidelines revision recorded in the manifest/version file to the remote HEAD and refreshes each file's sha256. It's an all-or-nothing pin update — it never applies the new guidelines itself; the engineer decides when to re-run setup.

Example

/mtk-setup --update-guidelines resolves HEAD via git ls-remote; if it differs from the pin it re-fetches every file in coding-guidelines.files, computes all sha256s before writing anything, prints an old→new SHA diff, updates the pin in .claude/mtk-version.json (marketplace install) or .claude/manifest.json (toolkit repo), and reports "Guidelines pin updated in <file>. Run /mtk-setup --audit or /mtk-setup to apply." If already current it says "Already at HEAD ($SHA). Nothing to do."

How it works

Inline workflow. Validates the resolved SHA is 40-char hex (stops on network failure). Reads the pin from ${CLAUDE_PLUGIN_ROOT:-.}/.claude/manifest.json (fallback .claude/mtk-version.json). Fetches each file at the new SHA via curl -fsSL … | sha256sum, checks ${PIPESTATUS[0]} per file, and computes ALL hashes before any write (no partial pin updates). Write rule: never write into the plugin cache — a plugin-prefixed pin writes to the repo-local .claude/mtk-version.json coding-guidelines block, leaving other top-level keys untouched. Does not auto-invoke bootstrap.

Why it works

Bootstrap fetches guidelines by pinned SHA (never main) so every setup is reproducible and auditable. This is the one sanctioned way to move that pin — atomically, with hash verification — decoupled from applying it, so guideline upgrades stay deliberate.

#MTK_AUTO_PROCEED=1

concept

Opt-in env var that lets the orchestrator default the recommended gate option — only when the plan is provably safe.

Trigger Environment variable, typically set in .claude/settings.local.json env; read by implement at the Phase 2.5 gate.

What it does

An opt-in that skips the AskUserQuestion round-trip at Phase 2.5 by defaulting to Approve & run until done — but only when a strict set of preconditions all hold. If any condition fails, it MUST fall back to AskUserQuestion; it never overrides open decisions, unresolved assumptions, or the failure-stop gate.

Example

An engineer sets MTK_AUTO_PROCEED=1 for a fast-moving repo. On a small config-tweak spec with zero open decisions, no [ASSUMED] claims, no blocking plan-gap findings, skill_precedence_gate: pass, and rigor STANDARD, implement records plan_trust_gate pass --reason "AUTO_PROCEED — all preconditions met" and proceeds. On the next spec, which carries one [ASSUMED] claim, it falls back to the interactive gate because an assumption made on the engineer's behalf is an open decision in disguise.

How it works

Applied only when ALL hold: spec open_decisions empty; zero unresolved [ASSUMED] claims ([VERIFIED:path]/[CITED:url] don't block); no plan-gap-reviewer BLOCKING findings; no open checkpoint:human-verify for an externally-recommended package; skill_precedence_gate is pass; scope is not breaking-change or high security_impact; and rigor level is LIGHT or STANDARD (HIGH/MAX always get a human).

Why it works

Reduces approval fatigue on genuinely low-risk changes without weakening the gate where it matters — the eligibility rules are precisely the signals that a plan has no unresolved human judgment left in it.

#/mtk-doctor

skill

Holistic MTK install health check — PASS/WARN/FAIL across core files, components, hooks, integrity, lessons, and context.

Trigger Slash command /mtk-doctor, or run the engine directly: bash scripts/mtk-doctor.sh (with --json --strict in CI, --fix to auto-repair)

What it does

mtk-doctor is the holistic health check for an MTK installation. Where validate-toolkit.sh checks structural integrity, the doctor catches runtime rot: deprecated model IDs in agents, an oversized CLAUDE.md, missing gitignore entries, stale analytics, and dangling hook registrations. It reports PASS/WARN/FAIL and attaches a detail field to each check pointing at the fix.

Example

After upgrading MTK in a target repo, the engineer runs the doctor to catch drift:

$ bash scripts/mtk-doctor.sh
COMPONENTS
  ✓ version sync — 7.25.0
  ✗ deprecated model — .claude/agents/foo.md → claude-3-opus
...
Summary: 24 PASS, 1 WARN, 1 FAIL

The deprecated-model FAIL makes it exit 1. Running bash scripts/mtk-doctor.sh --fix then auto-adds missing .gitignore entries and chmod +x's non-executable hooks — but never edits source and never touches a FAIL.

How it works

The skill (allowed-tools: Read, Bash; argument-hint [--json] [--fix] [--strict]) runs scripts/mtk-doctor.sh, which records checks in six categories: CORE (required files, manifest resolution with a CLAUDE_PLUGIN_ROOT fallback, CLAUDE.md 200-line budget), COMPONENTS (manifest==plugin version sync, skill count, skill-name/directory match, a DEPRECATED_MODELS scan of agent frontmatter), HOOKS (registered hooks in .claude/settings.json exist and are executable, valid event names, every hooks/ script has set -euo pipefail), INTEGRITY (manifest paths exist, .gitignore coverage, analytics freshness >30 days, release-checksum verification via scripts/generate-checksums.sh, optional Ed25519 signature check when MTK_RELEASE_PUBLIC_KEY is set, and a composite run of validate-toolkit.sh), LESSONS (lints executable lesson contracts in .mtk/learnings.jsonl), and CONTEXT (always-on token baseline = CLAUDE.md + rules/INDEX.md + alwaysApply references, MCP schema count from .mcp.json, ENABLE_TOOL_SEARCH state — all report-only). Flags: --json (CI dashboards), --strict (WARN also exits 1), --fix (only gitignore additions + chmod, never FAILs, never source). Exit 1 on any FAIL.

Why it works

A structural validator proves the toolkit is well-formed but not that it is healthy after an upgrade — a deprecated model alias, version skew between manifest and plugin, a hook whose script was deleted, or a CLAUDE.md grown past budget all pass structural checks yet silently degrade behavior. The doctor is the holistic catch, and --fix repairs the known-safe rot without any risk of source edits.

#/mtk repo-health

skill

Periodic AI-readiness report — 12-asset scorecard + medal, PR-review mining, and a top-3 actions list.

Trigger Routed via /mtk repo-health (or natural language like "is this repo AI-ready?"); the scorecard alone runs via bash scripts/repo-health-score.sh

What it does

A single periodic command that reports how ready a repo is as an AI work surface. It combines the 12-asset readiness scorecard (with a medal), PR-review mining of recurring reviewer feedback from recent merged PRs, and a derived top-3 actionable-changes list. It writes the combined report to disk and echoes the medal plus top-3 in chat.

Example

During a monthly retro someone asks "is this repo AI-ready?". The skill runs the scorecard and PR mining, then writes .claude/repo-health-latest.md with a Medal: line, a 12-row asset table, and a "Top 3 actionable changes" section. In chat it reports e.g. "🥈 silver — 6 pass / 3 partial / 3 fail" plus the three highest-priority fixes (like "add tasks/lessons.md — asset #4 is failing"). --prs 20 widens the mining window; --no-mining skips it.

How it works

Parses --prs N (1..50, default 10), --json, --no-mining. It always runs bash scripts/repo-health-score.sh (the scorecard). It runs a best-effort audit-drift check (scripts/audit-drift-check.sh) on CLAUDE.md, architecture-principles.md, and conventions.md — reading each doc's audited-against: SHA stamp and annotating the AI Context bucket with '🟨 audit drift: N citations' if cited files changed (a warning, never flipping pass to fail). Unless --no-mining, it runs bash scripts/pr-review-mine.sh --prs N, which is fail-soft (emits a Skipped: block when gh is missing or unauthenticated). It derives the top-3 by ranking ⬜ fails AI Context > Dev Workflow > Onboarding > mined phrases with ≥3 PR citations. It writes .claude/repo-health-latest.md always and .claude/repo-health-latest.json with --json (both gitignored). Mining is always suggest-only. After writing .claude/repo-health-latest.md it publishes the report as a Claude Artifact (standalone when run outside a workflow, else updating the active REVIEW workflow's artifact) per .claude/references/artifact-publishing.md — additive, capability-gated, silent no-op when unavailable or MTK_ARTIFACT_PUBLISH=0.

Why it works

Repo AI-readiness drifts silently — lessons stop being captured, specs age out, a version bump breaks manifest sync — and nobody notices until an agent produces drift. A bounded, medal-graded scorecard plus mined reviewer patterns turns "how healthy is this repo for AI work?" into a repeatable artifact with a concrete next-3-actions list, while never auto-promoting the mined phrases.

#toolkit-health

skill

Adoption analytics — reads analytics.json, computes usage ratios, and flags anomalies with next actions.

Trigger Routed via /mtk on prompts like "how are we using MTK?" / "toolkit health" / "usage stats" (user-invocable: false; trigger: toolkit-health-check|adoption-audit|analytics-review)

What it does

Reports how the team is actually using MTK — distinct from repo-health, which reports readiness of the repo itself. It reads the session analytics file plus on-disk artifacts, computes usage ratios, flags anomalies against tunable thresholds, and prints one concrete next action per flag. It is strictly read-only and refuses to draw conclusions from too little data.

Example

During a retro someone asks "how are we using MTK?". The skill reads .claude/analytics.json and reports e.g. "42 sessions, 0.03 specs/session — Very few specs, team may skip /mtk implement for features," alongside a fenced JSON block of the raw metrics. With fewer than 3 sessions it says "Insufficient data (fewer than 3 sessions)" rather than inventing percentages.

How it works

It loads data sources in parallel: .claude/analytics.json (persisted by hooks/session-analytics.sh — sessions, total_operations, total_modifications, specs_created, lessons_captured, scope_guard_warnings, benchmarks_run, benchmark_last_score), the tasks/lessons.md '## ' count, docs/specs and docs/plans listings, and an MTK-scoped git log. It computes ops/session, specs/session, lessons/spec, and scope-guard rate, then flags anomalies against a threshold table (e.g. scope_guard_warnings/sessions < 0.3 is healthy; a mod/op ratio of 0.1–0.6 is healthy). It also runs bash scripts/learnings.sh metrics to surface decision_origin totals (user-directed, claude-recommended-approved/modified/rejected, system-inferred) and the π sycophancy index (warn only when the approved+modified+rejected denominator is ≥10), and bash scripts/skill-eval/coverage.sh --json for behavioral eval coverage_pct and overlap_candidates. It degrades gracefully on missing/corrupt/small samples and never writes analytics/lessons/specs.

Why it works

A toolkit can look adopted while quietly being bypassed — sessions accrue but no specs get written, lessons stagnate, or the model's recommendations are rubber-stamped (high π). Turning the raw session counters into ratios, with honest 'insufficient data' guards and concrete next actions, makes real adoption and its failure modes visible without pretending to be a formal KPI dashboard.

#context-report

skill

Diagnostic snapshot of active MTK config — tech stack, references, linter packs, domains, hooks, rules, agents.

Trigger Routed via /mtk on prompts like "what MTK config is active?"; commonly run after /mtk-setup to verify configuration (user-invocable: false)

What it does

A diagnostic snapshot of the MTK toolkit's currently-active configuration — what's loaded, what's not, and why. It shows the pinned tech stack, active domains, enabled linter packs with rule counts, rule files, hook registrations, review agents, reference coverage and footprint, and active specs. It's the tool to reach for when a review missed something or a linter pattern fired unexpectedly.

Example

After running /mtk-setup, the engineer asks what config is active. context-report runs embedded bash that prints sections like --- Tech Stack --- (from .claude/tech-stack), --- Linter Packs --- with per-file rule counts, --- Hooks --- listing registered events, and --- Reference Footprint --- estimating ~13 tokens/line — then flags issues such as a tech stack set with no matching stack skill, a domain listed with no pack, or a hook missing / not executable.

How it works

STEP 1 gathers state via dynamic ! bash blocks: tech stack (.claude/tech-stack, .claude/tech-stack-pm), domains (.claude/domains), linter packs under hooks/linter-patterns/{core, stack-<stack>, domain-<domain>, project} with non-comment rule counts, rules (.claude/rules/*.md line counts), hooks (events grepped from .claude/settings.json and hooks/hooks.json, plus git pre-commit executability), review agents (name + model from .claude/agents/*.md), references (shared + stack) and a footprint estimate at ~13 tokens/line, and active specs grouped by slug/version from docs/specs. STEP 2 presents a clean summary and warns on misconfiguration. It points to bash scripts/mtk-savings.sh for the full always-on/deferred/compression breakdown.

Why it works

MTK's behavior depends on layered, mostly-invisible configuration — which references glob-matched, which linter pack loaded, which hooks are registered. When a review misses an issue or a linter false-positives, the fastest question is "what was actually active?" context-report answers it from disk instead of guesswork, and surfaces config that is set-but-broken (a stack with no skill, a domain with no pack).

#scripts/workflow-dashboard.sh

script

Renders a read-only HTML dashboard of all workflows — status, phase, gates, results, event timeline.

Trigger bash script — scripts/workflow-dashboard.sh renders once; --watch regenerates on an interval and serves a live page

What it does

A visual, read-only view over the workflow artifacts. It reads the same {uuid}.json and {uuid}.events.jsonl files the workflow-artifacts skill writes and renders them as an HTML page showing status, current phase, gate decisions, results, and the event timeline. In --watch mode it serves a self-refreshing page so a tech lead can watch a multi-batch run live.

Example
$ scripts/workflow-dashboard.sh --watch 10 --port 9000

regenerates the dashboard every 10 seconds and serves it at http://127.0.0.1:9000/ (meta-refresh). A tech lead opens the page and watches batches complete and phase_exit_gate decisions land in real time — without touching the running workflow. To share with a non-CLI stakeholder they expose the port with any tunnel tool (ngrok, cloudflared).

How it works

It only reads the artifact files — it never mutates them, so it is safe to run during an active workflow. A plain run renders once to .mtk/workflows/dashboard/index.html and opens it. --watch regenerates on an interval (default 5s) and serves a static page over http://127.0.0.1:8787/ by default (meta-refresh, no WebSocket); --watch 10 --port 9000 overrides interval and port. Requires python3 (the same dependency the helper already uses). The script intentionally stays a self-contained static renderer and embeds no tunnel.

Why it works

Durable workflow state is only useful if a human can see it. A read-only renderer gives live visibility into long, multi-batch runs and an after-the-fact trace, without risking the artifact integrity the skill depends on.

07 Deterministic layer 40 entries

Hooks the harness runs for you — not the model. Advisory where a nudge is enough, blocking where a rule must hold 100% of the time.

#scripts/run-fixtures.sh

script

Structural validator for router-decision fixtures: checks shape, gate names, and cross-rules, no Claude run.

Trigger Bash script: bash scripts/run-fixtures.sh (no args). Run in CI / before commit.

What it does

Validates the router-decision fixtures under tests/fixtures/. It is a structural validator, not an agent runner: it asserts each fixture parses as JSON and matches the documented shape, uses only known gate names and actions, and obeys the documented cross-rules. It does NOT run Claude or check rationale text — that is human review.

Example

bash scripts/run-fixtures.sh iterates tests/fixtures/*.json, printing OK <name>.json per valid router-decision fixture (e.g. OK build-happy-path.json, whose expected.next_action is advance_phase and gate_to_record is phase_exit_gate: pass) and finishing with run-fixtures: all fixtures OK.. The abort cross-rule is exercised by failure-stop-triggered.json (next_action: abort, gate_to_record: failure_stop_gate: fail); a fixture that set next_action to abort without that exact gate makes the embedded Python check print FAIL: <path>: next_action=abort requires gate_to_record='failure_stop_gate: fail', got ... to stderr and the script exits 1. (There is no run-fixtures: FAIL: prefix on per-fixture failures — the defined fail() helper is unused; that prefix only appears on the no-fixtures skip and the final all-OK line.) If no fixtures exist it prints run-fixtures: no fixtures under <dir> — skipping. and exits 0.

How it works

For each JSON fixture a Python check enforces: id matches the filename stem; workflow_type is one of BUILD|DEBUG|REVIEW|PLAN|FIX; required top-level fields description/input/expected are present; every gate in input.gates and expected.gate_to_record is one of the five gates from .claude/references/orchestration-gates.md (plan_trust_gate, phase_exit_gate, failure_stop_gate, memory_sync_gate, skill_precedence_gate); expected.next_action is one of advance_phase|remediate|block|request_engineer|resume_existing|abort; gate results are pending|pass|fail. Cross-rules: next_action==abort requires gate_to_record=='failure_stop_gate: fail'; expected.rationale must be a non-trivial sentence (>=20 chars). Fixtures whose fixture_type is not router-decision are checked only for JSON well-formedness plus id==stem, so handoff/schema fixtures can coexist in the same directory.

Why it works

The router's decision contract (which gate to record, when to abort, what next action is legal) is easy to drift as fixtures accrue. A deterministic, Claude-free validator catches malformed or self-contradictory fixtures in CI without any API spend, keeping the router's expected behavior pinned.

#scripts/run-evals.sh

script

Runs (or lists) per-skill eval scenarios; read-only by default to avoid surprise API spend.

Trigger Bash script: bash scripts/run-evals.sh [--list | --skill <name> | --eval <path>].

What it does

Discovers and lists the eval scenarios under evals/, grouped by skill. By default it is read-only: for each scenario it points you at the eval file (which holds the setup and prompt) so an engineer, or a separate Claude session, can run it explicitly. You opt into automation by pointing it at an executor and grader via environment variables.

Example

bash scripts/run-evals.sh --list prints scenarios grouped by skill — e.g. a == fix == block listing [positive] evals/fix/eval-01-real-fix.md with its title Single-file null check fix with reproduction on the next line. bash scripts/run-evals.sh --skill fix walks that skill's eval-*.md files; with EVAL_EXECUTOR unset it stays in manual mode — printing Manual mode. Read the eval file and run the prompt yourself., the eval file path, and where to record .output.md/.verdict.md — which 'avoids surprise API spend'. (--skill names an existing directory under evals/; a name with no such directory, e.g. a nonexistent mtk, prints ERROR: no such skill: mtk and exits 1.)

How it works

Modes: --list (default), --skill <name>, --eval <path>, plus --run-id <string> and -h/--help. Results are written under evals/results/${RUN_ID} (RUN_ID defaults to a timestamp). Manual mode (no EVAL_EXECUTOR) prints Manual mode. Read the eval file and run the prompt yourself., the eval file path, the evals/$skill/grader.md grader path, and the record paths for <name>.output.md and <name>.verdict.md — it does not print the prompt text itself. Automated mode extracts the fenced ``prompt block from the scenario file, pipes it to $EVAL_EXECUTOR, and — when $EVAL_GRADER is set and evals/$skill/grader.md` exists — assembles a grader payload (grader.md + eval file + actual output) and records the returned verdict.

Why it works

Router and skill behavior need behavioral regression checks, but running agents costs money. A read-only-by-default runner lets engineers inspect and deliberately execute scenarios, with an explicit opt-in executor/grader path for automation, so evals never fire API calls by accident.

#scripts/setup-detect.sh

script

Read-only detector for tech stack, package manager, React Native/Expo, and monorepo layout — emits one JSON object.

Trigger Bash script (bash scripts/setup-detect.sh --json); called by setup-bootstrap STEP 0/4.5 and setup-audit STEP 0

What it does

The single source of truth for repo detection used across setup. It scans marker files to determine the stack(s), picks the package manager by lockfile priority, detects React Native/Expo (including nested workspace apps), and classifies monorepo layout with an enumerated package list. It writes nothing.

Example

bash scripts/setup-detect.sh --json in a pnpm monorepo emits {"stacks":["typescript"],"primary_candidate":"typescript","package_manager":"pnpm","react_native":{"detected":false,"expo":false},"monorepo":{"is_monorepo":true,"ambiguous":false,"signals":["pnpm-workspace.yaml",...],"packages":["apps/web","packages/ui"],"packages_skipped":0},"go_detected":false,"multiple_lockfiles":false,"parse_errors":[]}. The calling skill reads those fields to write .claude/tech-stack and decide monorepo handling.

How it works

Pure Python3 (hard-required, exit 2 if absent) over os.walk. Stack markers: *.csproj/*.sln/*.slnx→dotnet, pyproject.toml/setup.py/requirements.txt/Pipfile→python, package.json→typescript, go.modgo_detected (unsupported). Package-manager priority bun>pnpm>yarn>npm; multiple_lockfiles flags conflicts. Monorepo signals: lerna/pnpm-workspace/turbo/nx/rush/workspaces, csproj≥4, sln≥2, pyproject≥2, or conventional dirs (apps/packages/services/libs) with >1 marked sub-project; ambiguous is true on mixed signals so the skill asks the engineer. Packages enumerated from workspaces globs + csproj/pyproject dirs + conventional dirs, deduped, sorted, capped at 20. A malformed package.json falls back to a scoped dependencies/devDependencies text scan and is listed in parse_errors.

Why it works

Inlining detection bash in the skills made it un-testable and prone to drift between bootstrap and audit. One deterministic, read-only script gives both skills identical answers and keeps marker logic in a single place (F2).

#scripts/verify-commands.sh

script

Runs the build/test/format commands bootstrap will publish in CLAUDE.md; reports verified/failed/skipped as JSON.

Trigger Bash script fed name<TAB>command lines on stdin (or --file); called by setup-bootstrap STEP 3.5a command verification

What it does

Actually executes the commands bootstrap intends to document (sourced from the tech-stack skill, never from repo content) and reports whether each works. This is how CLAUDE.md's Tech Stack section earns a <!-- verified: ... --> stamp or [UNVERIFIED] annotations instead of guessed commands.

Example

printf 'build\tdotnet build\ntest\tdotnet test --list-tests\n' | bash scripts/verify-commands.sh emits {"results":[{"name":"build","command":"dotnet build","status":"verified","detail":""},{"name":"test","command":"dotnet test --list-tests","status":"failed","detail":"exit 1: <first stderr line>"}]}. Bootstrap uses that to mark the failing command [UNVERIFIED] and note it in the STEP 5 report.

How it works

Reads name<TAB>command lines; an empty command half → skipped. Each command runs under a portable timeout probe (timeout, else gtimeout, else unbounded with a note) via bash -o pipefail -c, with stdin from /dev/null so it can't steal the loop's input. Exit 124 under the timeout binary → failed: timeout after <N>s; other non-zero → failed: exit <code>: <first non-blank stderr/stdout line>. The script always exits 0 itself (per-command outcomes live in the JSON body); usage errors exit 2. --timeout <secs> defaults to 300. Bootstrap's --no-verify-commands bypasses it entirely.

Why it works

Documented commands that don't actually run are worse than none — they mislead every future agent. Executing them at generation time, with graceful degradation on a sandboxed runner, keeps CLAUDE.md's commands honest (F7).

#scripts/setup-refresh-plan.sh

script

Read-only per-artifact staleness plan for the refresh/check loop; one row per artifact plus a summary and CI exit code.

Trigger Bash script (--json / --check); the sole staleness source for setup-refresh and the engine behind /mtk-setup --check

What it does

Computes, deterministically, which generated setup artifacts have drifted. It emits one status row per artifact (fresh/stale/missing/unstamped/unknown/present/absent) with a reason, so refresh can scope its work and check can gate CI. It writes nothing to the repo.

Example

bash scripts/setup-refresh-plan.sh prints a markdown table and a summary line like 3 fresh, 1 stale, 0 missing, 1 unstamped/unknown; a stale row might read CLAUDE.md | stale | CLAUDE.md footer stamped v7.20.0, installed v7.25.0. With --check, if any non-informational row is stale/missing it prints run /mtk-setup --refresh to reconcile to stderr and exits 1.

How it works

Runs from the git top-level (exit 2 if not a git repo). Seven rows: architecture-principles.md and conventions.md via audit-drift-check.sh --json (drift→stale, no stamp→unstamped); CLAUDE.md via footer-version-vs-.claude/mtk-version.json plus a dependency rescan cross-referencing backtick tokens against package.json/*.csproj/pyproject.toml; detected-tools.json via a 7-day mtime TTL; AGENTS.md by checking the Auto-generated by MTK marker then regenerating into a temp file and diffing; path-references via verify-references.sh (exit 3→stale, absent→row omitted); setup-answers.json informational only. Sibling helpers resolve via SCRIPT_DIR so it works from a plugin cache; a missing helper degrades that row to unknown rather than failing.

Why it works

Refresh must not re-derive staleness ad hoc, and CI needs a stable exit code. One read-only plan script gives both a single, testable verdict — and degrades gracefully when run from a target repo that never received the helper scripts.

#Confidence tags + verify-claims.sh grounding loop

concept

Every generated claim carries an evidence-graded tag; verify-claims.sh greps each anchor and downgrades failures.

Trigger Enforced by setup-audit STEP 3.7 / setup-bootstrap STEP 3.5a; consumed by setup-converge and setup-refresh

What it does

MTK's evidence-first mechanism. Descriptive principle tags ([EXTRACTED]/[INFERRED:N]/[AMBIGUOUS]) say what the code does; prescriptive rule tags ([ENFORCED]/[CONVENTION]/[ASPIRATIONAL]) say what reviewers should do. A deterministic verifier re-greps every cited anchor and downgrades zero-hit claims in place, then surfaces the weak ones instead of hiding them.

Example

An audit writes [EXTRACTED] Uses EF Core. Evidence: \Program.cs:61 UseSqlServer\. If a refactor removed that call, bash scripts/verify-claims.sh .claude/references/architecture-principles.md finds zero hits and rewrites the line to [INFERRED:0.5 unverified], records it in .claude/.mtk-cache/weak-claims-architecture-principles_md.json, and the audit prints ⚠️ Weak claims surfaced: N. setup-converge later reads that original tag to grade the drift blocking/flag/note.

How it works

Tags require a real anchor (file:line, glob+hit-count, or symbol). verify-claims.sh parses tagged lines, grep/AST-checks each anchor against the working tree, downgrades [EXTRACTED]→[INFERRED:0.5 unverified] and [ENFORCED]→[ASPIRATIONAL unverified] in place, and writes per-doc weak-claims-<doc>.json (full) + .md (top-5, paste-ready). A one-pass retry loop re-derives or deletes (only self-generated) failing lines, then re-verifies once — never re-upgrading a tag without a resolving anchor. It also runs a transient-state lint and terminology denylist. The scheme is codified in audit-grounding.md.

Why it works

LLM audits confidently assert things that aren't true (dead imports as live capabilities, single examples as laws). Grading by evidence and mechanically falsifying each anchor is what makes MTK's claim-verification distinctive — a passing audit surfaces its weak claims rather than pretending they don't exist.

#hooks/spec-approval-trigger.sh

hook

PostToolUse hook that queues planning when a spec transitions to approved, and re-queues approval when a sealed artifact is edited after approval.

Trigger Automatic PostToolUse hook on Edit|Write (wired in .claude/settings.json, timeout 5). Tier-2 (skill-invoking); respects MTK_HOOKS_TIER2 via mtk_queue_enabled.

What it does

After an edit to a spec or plan, this hook does two things. First, if an active workflow sealed that file at approval and its bytes no longer match, it flags the approval as stale and queues the planning skill to re-approve. Second, when a spec markdown file transitions to 'status: approved' (and was not already approved at HEAD), it queues planning-and-task-breakdown so the next turn moves into task breakdown. It is advisory (exit 0) — it never blocks; it defers work to a queue.

Example

The engineer flips docs/specs/2026-07-09-refund-flow.md to - Status: approved and saves. The hook confirms this is a real transition (HEAD did not say approved), then queues a skill. Next prompt, the dispatcher surfaces: planning-and-task-breakdown, reason spec docs/specs/2026-07-09-refund-flow.md transitioned to status: approved. If instead an already-sealed spec is edited after approval, it queues with reason approval seal STALE — ... re-approve at Phase 2.5 before continuing.

How it works

Gated by mtk_queue_enabled (the MTK_HOOKS_TIER2 kill-switch). Fires only for docs/specs/*.md, docs/plans/*.md, or tasks/todo.md. Stale-seal path: scans .mtk/workflows/*.json (via python3) for active workflows whose results.approval_seal lists this file, runs bash scripts/workflow-artifact.sh verify-seal <uuid>, and on rc=1 (stale) calls queue_skill (from lib/skill-queue.sh) with source_hook/dedup key spec-approval-seal-stale-<uuid>. Approved-transition path (spec-only): matches the approved marker as a bold list item (- Status: approved) or YAML frontmatter (status: approved), compares against the HEAD version to confirm a true transition, then queues planning-and-task-breakdown with source_hook/dedup key spec-approval-trigger-<slug>.

Why it works

Approval is the gate between planning and building; a human flipping a status line shouldn't require the agent to remember to advance. Auto-queuing planning makes the workflow move forward deterministically. The seal check closes a subtler hole: if an approved spec/plan is quietly edited after sign-off, the earlier approval no longer describes reality, so the hook forces re-approval rather than letting stale approval carry.

#.claude/references/orchestration-gates.md

reference

The five fail-closed gates the implement workflow records before advancing; plan_trust_gate governs approval.

Trigger Loaded on-demand by the implement workflow and orchestration skills; gate decisions recorded via scripts/workflow-artifact.sh gate

What it does

Defines the only five places /mtk implement may advance, retry, or stop: plan_trust_gate, phase_exit_gate, failure_stop_gate, memory_sync_gate, and skill_precedence_gate. Every gate decision is persisted on the workflow artifact. Gates are fail-closed: pending means "not evaluated yet" and only pass permits the next phase. For this subsystem, plan_trust_gate is the Phase 2.5 spec/plan/todo approval gate.

Example

At Phase 2.5 the engineer chooses "Approve & run until done"; the orchestrator records scripts/workflow-artifact.sh gate "$MTK_WF_UUID" plan_trust_gate pass --reason "approve & run until done", which writes gates.plan_trust_gate in {uuid}.json and appends a gate_decided event to {uuid}.events.jsonl. Choosing "Revise" records fail and returns to Phase 1; "Edit first" leaves the gate pending.

How it works

Each gate carries pass/fail/pending semantics and a responsible skill. plan_trust_gate passes only if the engineer approved (Approve & run until done / Approve interactive), the spec+plan+todo trio is all on disk, and no open decisions remain. The workflow-artifact.sh gate helper writes gates.{name} in {uuid}.json AND appends a gate_decided event — reading either surface alone is incomplete. Advancing a phase without recording its gate is treated the same as advancing on fail; a missing gate event may be treated as drift by validators.

Why it works

Fail-closed gates make "have we actually approved this?" a deterministic, audited fact rather than a vibe. Because both the state and the event are written, an auditor can reconstruct exactly why the workflow advanced.

#approval seal (scripts/workflow-artifact.sh seal / verify-seal)

concept

SHA-256 seal over approved spec/plan bytes so editing an approved scope artifact invalidates approval.

Trigger Recorded automatically by the implement Phase 2.5 gate on approval; checked by verification-before-completion and the spec-approval-trigger.sh hook

What it does

On approval, the implement gate records a SHA-256 approval seal over the approved spec/plan bodies (the todo is progress state and is not sealed). Because the seal binds the exact approved bytes, editing an approved artifact afterward invalidates the approval deterministically — a STALE seal blocks completion and re-opens the Phase 2.5 gate, rather than trusting a stale status: approved marker.

Example

A spec is approved and sealed. Later a success criterion is edited. verification-before-completion runs verify-seal, sees STALE, and refuses to accept the completion claim; independently, the spec-approval-trigger.sh hook detects the byte mismatch and re-queues the approval step. Moving a goalpost post-approval therefore requires re-opening Phase 2.5 — the seal will not match.

How it works

scripts/workflow-artifact.sh seal records the seal (files + hashes) under results.approval_seal on the workflow artifact and emits an approval_sealed event. verify-seal returns rc 1 on mismatch (stale, printing sealed=/current=), 0 on match, 2 uncheckable, 3 no-seal. verification-before-completion refuses completion on a STALE seal; the spec-approval-trigger.sh hook fires its advisory only on rc 1 and ignores 0/2/3.

Why it works

A status: approved marker is trivially staleable by editing the file after approval. Binding approval to exact bytes makes tampering detectable and deterministic, enforcing the "what was approved is what ships" contract instead of a self-asserted status.

#hooks/verify-completion + hooks/context-budget.sh (Stop hook)

hook

The runtime guardrail behind verification-before-completion: checks completion claims cite fresh command output.

Trigger Automatic — a Stop hook fires when the agent finishes responding; freshness/re-arm tracking is wired in settings.json.

What it does

The deterministic backstop that makes the verification-before-completion contract enforceable rather than merely documented. Hooks track the latest edit and latest verification in the session and warn when a 'done' claim is stale, missing evidence, or based on criteria that got re-armed by a later edit.

Example

The agent edits a file, then says 'the fix is complete' without rerunning tests. Because last_edit_seq > last_verification_seq, hooks/verify-completion emits VERIFICATION GAP: criteria re-armed — all success criteria reset to re-armed and the completion claim is rejected until verification reruns after the edit.

How it works

hooks/context-budget.sh records the latest edit time and the latest verification command run in the session; hooks/verify-completion compares those timestamps/sequence numbers and warns with VERIFICATION GAP: when evidence is stale or missing, and with VERIFICATION GAP: criteria re-armed when an edit landed after the last verification. The Stop hook is the actual enforcement mechanism — the skill text is the contract, the hook is the guardrail.

Why it works

An agent has every incentive to declare success; a documented contract alone can be rationalized past. Comparing edit-vs-verification event order deterministically catches the exact 'I ran the tests a few minutes ago (before I touched the code)' staleness that prose review misses.

#command-verification.md (F7)

reference

Runs the build/test/format commands MTK is about to publish and annotates any that fail as [UNVERIFIED].

Trigger Read on-demand by setup-bootstrap STEP 3.5a, unless --no-verify-commands was passed.

What it does

The F7 command-verification procedure: before writing a repo's Tech Stack section into CLAUDE.md, it actually runs the commands it's about to advertise and applies verified/failed/skipped/unavailable outcomes — so the generated docs never promise a command that doesn't work. (It sits in the broader verification family; it runs at setup time, not inside the implement loop.)

Example

During bootstrap, printf 'build\t%s\ntest\t%s\nformat\t%s\n' ... | bash scripts/verify-commands.sh --timeout 300 runs dotnet build, dotnet test --list-tests, and dotnet format --verify-no-changes. Build passes, format fails — so CLAUDE.md gets <!-- verified: build ✓ (2026-07-03) --> and the format line is written with [UNVERIFIED — <detail>] appended. A failed command is never dropped and never appears unannotated.

How it works

Assembles the three commands (test uses the stack's list/collect-only variant; format uses check-mode, not the mutating form used by hooks/format-on-edit.sh), pipes them as name<TAB>command lines to scripts/verify-commands.sh. A tree-mutation guard snapshots git status --porcelain before and git checkout -- any tracked file the verification run dirtied. Outcomes: verified → normal line + a single <!-- verified: ... --> stamp; failed → line kept + [UNVERIFIED — ...]; skipped → line kept, noted in the report; verifier unavailable (missing/exit 127) → every command annotated [UNVERIFIED — verify-commands.sh unavailable]. Non-interactive runs never block on a failing command.

Why it works

Setup docs that advertise a broken build/test command send every future session down a dead end. Verifying before publishing — and never letting a failure appear unannotated — keeps the generated CLAUDE.md trustworthy even when the verifier itself is absent.

#Sycophancy Index (π) — learnings.sh metrics

concept

A computed metric — approved / (approved + modified + rejected) — that flags when the agent's proposals are being rubber-stamped.

Trigger bash scripts/learnings.sh metrics (aggregates last 30 days; --window-days N); surfaced by toolkit-health

What it does

A single number derived from the decision_origin of captured lessons that estimates how often the agent's recommendations are accepted unchanged versus modified or rejected. A high value suggests the engineer is rubber-stamping proposals (a sycophancy risk); it excludes decisions that weren't model recommendations at all.

Example

bash scripts/learnings.sh metrics returns totals over 30 days and "pi": 0.667 with "warn_threshold": 0.70 and "status": "ok". If accepted-unchanged proposals climb so π reaches 0.70, status flips to warn, and toolkit-health surfaces it as a signal that recommendations may be getting insufficient scrutiny.

How it works

learnings.sh metrics aggregates decision_origin across a window (default 30 days, --window-days) and computes pi = approved / (approved + modified + rejected), using claude-recommended-approved/-modified/-rejected. It excludes user-directed and system-inferred because those aren't model recommendations. status: "warn" when pi >= warn_threshold (default 0.70, tunable via .claude/review-config.json → sycophancy_index.warn_threshold). This is why --decision-origin is a required enum captured on every lesson.

Why it works

If the agent's suggestions are almost always accepted verbatim, the human review that MTK depends on has quietly stopped happening — a measurable failure mode. Computing π from provenance that's already captured on every lesson turns that risk into an observable metric instead of a vibe.

#scripts/learnings.sh

script

The deterministic engine behind the two-tier lessons store — appends structured entries, rebuilds the markdown, and answers retrieval queries.

Trigger Bash script with subcommands: add, query, metrics, regen-markdown, migrate, list — called by the capture/promote skills and at spec/fix start

What it does

This is the single write/read path for structured learnings. add allocates a monotonic id (L-YYYY-MM-DD-NNN), enforces the required --decision-origin enum, and appends a JSONL entry; regen-markdown rebuilds tasks/lessons.md from the store; query runs the 5-layer retrieval filter; metrics computes the sycophancy index; migrate back-fills the store from existing markdown; list dumps id+title for diagnostics. It is pure-bash so it can append and grep without an external JSON parser (per S3.3).

Example

At the start of a fix, a workflow skill runs bash scripts/learnings.sh query to pull the top 10 relevant lessons for the files about to change. When a lesson is captured, learnings.sh add … --decision-origin claude-recommended-rejected … appends one JSONL line to .mtk/learnings.jsonl, then learnings.sh regen-markdown re-emits tasks/lessons.md. Regenerating is protected by a shrink-guard (hooks/lib/shrink-guard.sh) that refuses a rewrite shrinking the file below 50% of its bytes or 80% of its lines unless MTK_SHRINK_GUARD_OVERRIDE=1 is set (which just emits a warning).

How it works

add validates --decision-origin against its enum — required for capture sources (correction/promotion/golden-path/review-finding), auto-set to system-inferred for manual/migrate/incident — accepts free-text --source as pass-through (so golden-path is valid without a parser change), and guards only the outer shape of the optional contract fields (--output-contract must be {...}, --prefinal-checklist must be [...]). query applies the 5-layer filter (proximity/recurrence/severity/validity/superseded/phase), returns top 10 (--max N), and takes --scope team|personal|all (default all). metrics aggregates decision_origin over a window (--window-days). migrate parses any prior tasks/lessons.md format into source: "manual", scope: "team" entries, and is idempotent — a re-run is a no-op once the auto-generated store is seeded. Replacing entries goes through mtk_guarded_write to avoid truncation.

Why it works

Skills alone can't guarantee a consistent, dedup-safe, non-truncating write to institutional memory; a single deterministic engine enforces the schema, allocates stable ids, keeps the committed markdown and machine store in sync, and makes retrieval reproducible — turning "lessons" from prose into queryable, ranked context.

#scripts/pr-review-mine.sh

script

Fetches merged PR review threads via gh and clusters repeated reviewer-feedback phrases, fail-soft and suggest-only.

Trigger Bash script: bash scripts/pr-review-mine.sh --prs 10 (also called by repo-health and setup-audit --mine-prs)

What it does

The deterministic miner under the pr-review-mining skill. It reads PR-level review summaries and per-line review comments from merged PRs (not issue comments, which are status chatter), finds phrases starting with an imperative-ish verb, and reports those whose 4-word prefix recurs across ≥2 distinct merged PRs. It emits markdown by default or JSON with --json, and is fail-soft: when gh (or jq) is missing, or gh is unauthenticated, it emits status: skipped with phrases: [] instead of blocking or guessing.

Example

bash scripts/pr-review-mine.sh --prs 10 --json returns { "status": "ok", "scanned_prs": 10, "phrases": [ { "phrase": "add a regression test", "count": 4, "prs": [21, 22, 23] } ] }. Boilerplate like lgtm, nit, looks good, +1 is filtered by the ## Denylist in the patterns reference, which the script reads at runtime.

How it works

A phrase becomes a candidate when it starts with a verb from IMPERATIVE_HINTS (add, remove, use, rename, prefer, avoid, …), its 4-word prefix occurs in ≥2 distinct merged PRs, and it is not on the denylist. The denylist under the ## Denylist heading of .claude/references/pr-mining-patterns.md is read at runtime (until the next ## heading), so teams suppress noise by editing that file. Output is always tagged [MINED:feedback] and never auto-edits architecture-principles.md; exit is 0 unless argument validation fails.

Why it works

Reviewer feedback is a rich, underused signal for what should become a written convention, but naively scraping it would surface boilerplate and single-reviewer opinions; requiring an imperative prefix plus ≥2 distinct PRs, a runtime denylist, and fail-soft behavior keeps the output actionable and never fabricated.

#5-Layer Retrieval Filter

concept

Ranks and filters stored lessons by proximity, recurrence, severity, validity/supersession, and phase so only relevant ones load.

Trigger Run by learnings.sh query at the start of spec-driven-development and fix; returns ranked entries (default top 10, --max N)

What it does

The scoring-and-filtering pass that decides which of potentially many stored lessons actually surface at the start of a spec or fix. Rather than dumping every lesson into context, it scores entries by how close they are to the current change and how important they are, and hard-drops entries that are expired, superseded, or out-of-phase. Default is the top 10, overridable with --max N.

Example

Starting a fix that touches scripts/learnings.sh, learnings.sh query scores a lesson whose files include that path +30 (proximity), adds +20 because its recurrence.count >= 2, adds +25 because it's block severity, keeps it because it isn't expired or superseded and its phase matches — so it ranks near the top of the returned set, while a stale expired == true lesson is dropped entirely.

How it works

Five layers: (1) Proximity — any files/directories overlap with the current change manifest → +30; (2) Recurrence — recurrence.count >= 2 → +20; (3) Severity — incident=40, block=25, warn=10, info=5 added to score; (4) Validity — expired == false required else drop, plus 4b Superseded — an entry whose id appears in a newer entry's supersedes is dropped; (5) Phase — must match the current phase or be phase=any. A separate --scope flag (team|personal|all, default all) filters entries by their stored scope.

Why it works

Loading every lesson eagerly wastes the context budget and buries the relevant few; deterministic scoring by proximity and severity plus hard drops for stale/superseded/out-of-phase entries means the model sees the handful of lessons that actually apply to the work in front of it, not a wall of history.

#scripts/validate-toolkit.sh

script

The structural gate — version sync, skill anatomy, frontmatter, budgets, and wiring, checked before every commit.

Trigger Bash script run before every commit: bash scripts/validate-toolkit.sh; also --task-scoped <comma-separated file list> for a fast wiring check

What it does

The canonical structural gate (rule S3.9). It verifies the toolkit is internally consistent: required files present, the three version numbers in sync, every skill follows its anatomy and line budget, references carry frontmatter, generated indexes are fresh, and the reviewer agents exist. Rule C0.8 requires seeing its "Toolkit validation passed." line before reporting any change complete.

Example

Before committing a new skill, the engineer runs the gate:

$ bash scripts/validate-toolkit.sh
Skill descriptions: 5820 chars (~1455 tokens) / 7000-char budget.
Toolkit validation passed.

Had the skill's frontmatter name not matched its directory, or manifest/plugin/marketplace versions differed, or CLAUDE.md exceeded 120 lines, it would instead print ERROR: ... and exit 1 on the first violation.

How it works

Pure bash on the coreutils/grep/sed/awk/find/git + python3 baseline (S3.3). A full run checks: required files (README, CONTRIBUTING, AGENTS, manifest, plugin.json, the generic references, and the three reviewer agents compliance-reviewer/test-reviewer/architecture-reviewer), settings.json carries permissions.deny and hooks blocks, every hooks/ and scripts/ shell script is chmod +x with set -euo pipefail, the tier-2 queue files exist (hooks/lib/skill-queue.sh, hooks/userprompt-dispatch.sh), triggers.index is in sync, the toolset registry resolves, the version triple matches (manifest==plugin==marketplace), every manifest source path exists, skill anatomy by type (entry-point/tech-stack/phase-based/workflow), line budgets (CLAUDE.md 120-line hard cap, skills 500/1000, rules 120), the skill-description budget (200-char cap per skill, 7000-char aggregate), reference frontmatter (description/globs/alwaysApply), references.index and rule-index freshness, a skill bash lint, and read-only enforcement of mcp/src/tools. A --task-scoped <csv> mode instead verifies only that touched skills/hooks/agents/references are fully wired (name matches dir, registered in manifest/plugin/settings/index). It fails fast with ERROR: or prints "Toolkit validation passed.".

Why it works

A plugin distributed to many repos breaks silently when a skill is not registered, versions drift across the three manifests, or a generated index goes stale — failures that surface downstream as missing routes or a bad install. Making one deterministic script the gate, and requiring its green line before "done," converts those into a fast local failure instead of a shipped bug.

#scripts/repo-health-score.sh

script

The 12-asset scorecard engine — grades a repo pass/partial/fail across 3 buckets and awards a medal.

Trigger Bash script: bash scripts/repo-health-score.sh (markdown) or --json; also invoked by the repo-health skill

What it does

The deterministic engine behind repo-health. It checks 12 named assets across three buckets — AI Context, Dev Workflow, Onboarding — returning pass/partial/fail/na for each. It computes a medal from the pass count and renders either a markdown scorecard or JSON.

Example

bash scripts/repo-health-score.sh prints a header like Medal: 🥈 silver — 6 pass / 3 partial / 3 fail (of 12 scoring assets; 0 n/a) followed by each asset with a 🟩/🟨/⬜ icon, grouped by bucket:

### AI Context
- 🟩 **1. CLAUDE.md present** — 92 lines
- 🟨 **2. Architecture principles tagged** — only 3 tagged principles
- ⬜ **4. Lessons captured** — tasks/lessons.md missing

bash scripts/repo-health-score.sh --json | jq .medal returns the medal string for dashboards.

How it works

Each asset records name|bucket|status|note. AI Context (4): CLAUDE.md non-empty; architecture-principles.md with ≥5 lines tagged [EXTRACTED|INFERRED:N.N|AMBIGUOUS|MINED:feedback]; .claude/tech-stack matching a tech-stack-<stack> skill; tasks/lessons.md with ≥1 '## ' entry. Dev Workflow (4): docs/specs and docs/plans each with ≥1 *.md modified in the last 90 days; manifest.json==plugin.json version (via python3); validate-toolkit.sh exits 0. Onboarding (4): README non-empty; build/test commands grep-found in CLAUDE.md/README/tech-stack skill; .claude/rules with ≥2 files; .gitignore excludes settings.local.json (plus analytics.json if present). Medal from pass count: 🏆 platinum ≥10, 🥇 gold ≥8, 🥈 silver ≥6, 🥉 bronze ≥4, else (no medal); the denominator is 12−na. JSON is rendered via an embedded python3 heredoc.

Why it works

"Is this repo ready for AI?" is otherwise a vibe. Encoding it as 12 deterministic, individually-cited checks with a bounded medal makes the answer reproducible, diffable over time, and safe to run in CI — and keeps the standard honest: partial never counts toward the medal, and na assets drop out of the denominator.

#.claude/references/repo-health-assets.md

reference

Canonical 12-asset checklist, pass/partial/fail rubric, and medal thresholds behind the repo-health scorecard.

Trigger Loaded on-demand (frontmatter globs it to scripts/repo-health-score.sh and .claude/skills/repo-health/**); read by engineers as the canonical rubric

What it does

The source-of-truth reference defining what "AI-ready" means for a repo. It lists the 12 assets and their buckets, the exact pass/partial/fail/na rubric for each, the medal thresholds, and when to re-run. The scorecard script and skill must not add assets without updating this file.

Example

A reviewer wants to know why asset #2 is only "partial." They open repo-health-assets.md and read the rubric: architecture-principles.md needs ≥5 tagged principles for pass, 1–4 for partial, and missing-or-untagged for fail — with the tag pattern [EXTRACTED|INFERRED:N.N|AMBIGUOUS|MINED:feedback]. The medal table confirms ≥8 pass = 🥇 gold.

How it works

Frontmatter (alwaysApply:false) globs it to scripts/repo-health-score.sh and .claude/skills/repo-health/**, so it loads only when relevant. It contains the bucket table (AI Context / Dev Workflow / Onboarding), a per-asset rubric with concrete thresholds, the medal-thresholds table (partial excluded from the medal, na excluded from the 12−na denominator), and 'when to re-run' guidance. The pattern is borrowed from github.com/johnpapa/ai-ready (bounded scorecard + medal). The repo-health skill's Red Flags treat the 12 assets as canonical — extending them requires editing this reference.

Why it works

If the checklist lived only in the script, the definition of "AI-ready" would drift each time someone tweaked a grep. Pinning the 12 assets, thresholds, and medal cutoffs in one human-readable reference keeps the standard stable, auditable, and shared across the script, the skill, and the engineers reading their score.

#hooks/security-gate.sh

hook

PreToolUse Bash gate that blocks nuanced destructive commands the settings.json deny-list misses (exit 2 = blocked).

Trigger Automatic PreToolUse hook on the Bash tool (wired in .claude/settings.json with matcher "Bash", timeout 5). Receives the tool payload as JSON on stdin.

What it does

Before any Bash command runs, this gate inspects the command string and hard-blocks a small set of catastrophic operations: destructive database statements, force-pushes to protected branches, and recursive force-deletes on broad paths. It is the safety net that catches destructive intent expressed in ways the exact-match deny-list in settings.json cannot. Exit 0 allows the command; exit 2 blocks it and the reason is printed to the agent.

Example

The agent tries to run git push --force origin main. The gate's regex git\s+push\s+.*--(force|force-with-lease)\s+.*\b(main|master)\b matches, the hook exits 2, and the agent sees:

BLOCKED: Force push to main/master is not allowed.

Similarly psql -c "DROP TABLE users" returns BLOCKED: Destructive database operation detected. Use a migration instead.

How it works

Sources hooks/lib/hook-io.sh, then extracts tool_name and the command via mtk_extract_tool_name / mtk_extract_command. Non-Bash payloads exit 0; a Bash payload it cannot parse fails closed (exit 2). It runs three grep -qiE / grep -qE checks: (1) DROP TABLE|DATABASE|SCHEMA, TRUNCATE TABLE, or DELETE FROM ...; (2) git push --force / --force-with-lease targeting main|master; (3) rm -rf on ., /, ~, or $HOME. Any match prints a BLOCKED message to stderr and exits 2. Everything else exits 0. The header explicitly notes the settings.json deny list handles exact-pattern matches; this script catches the alternate-syntax and disguised variants.

Why it works

Deny-lists match literal strings, so an agent (or a paraphrased command) can slip a destructive operation past them via flags, pipes, or alternate syntax like --force-with-lease. This deterministic gate stops the highest-blast-radius mistakes — dropped tables, rewritten protected-branch history, wiped working trees — before they execute, with no reliance on the model's judgment.

#hooks/read-guard.sh

hook

PreToolUse read gate that blocks pulling secret-bearing files into context (exit 2) and warns on generated/vendored noise dirs.

Trigger Automatic PreToolUse hook on Read|Grep|Glob (wired in .claude/settings.json, timeout 5). Env knob: MTK_READ_GUARD=advisory downgrades blocks to warnings.

What it does

Before the agent reads, greps, or globs a file, this gate classifies the target. Secret-bearing files (.env, private keys, credential files) are blocked so credentials never enter the model's context without a human in the loop. Generated or vendored directories (bin, obj, node_modules, dist, .venv) get an advisory warning but are allowed. Everything else passes silently.

Example

The agent tries to Read deploy.pem. The basename matches the *.pem secret class — a private-key vector the settings.json deny-list does NOT cover — the hook exits 2, and prints:

READ-GUARD: blocked read of secret-bearing file 'deploy.pem'. Reading credentials into context requires explicit human approval — do NOT attempt to work around this. STOP and ask the engineer...

Reading node_modules/react/index.js instead only warns: READ-GUARD (advisory): 'node_modules/react/index.js' is under a generated/vendored path, then allows it. (This guard fired for real during fact-checking when a Read hit core/secrets.txt, whose basename matches the secrets.* class.)

How it works

Sources lib/hook-io.sh and lib/mtkignore.sh. Classifies by basename: allowlists *.example|*.template|*.sample|*.dist; treats .env/.env.* (note: only a leading-dot .env or .env.<suffix> — a name like prod.env does NOT match), *.pem/*.key/*.pfx/*.p12/*.kdbx/*.keystore/*.jks, id_rsa*/id_dsa*/id_ecdsa*/id_ed25519*, secrets.*/*.secret/*.secrets, and .npmrc/.netrc/.pgpass/credentials as secrets. On a secret: if MTK_READ_GUARD=advisory it warns and exits 0; otherwise it checks a per-day approval list at $TMPDIR/mtk-readguard-approved-<repo-cksum>-<date> and exits 2 if not listed. The block message deliberately omits any self-approval recipe — the human grants access out-of-band. For noise dirs it loads ignore patterns via mtk_load_ignore_patterns, normalizes to a repo-relative path, and emits an advisory (exit 0).

Why it works

An agent that can silently read .env or a private key can leak credentials into its context window and downstream transcripts. Making the secret read a hard block with no agent-runnable bypass keeps a human in the loop for credential access. The noise-dir advisory separately stops the agent from wasting context grounding itself on machine-generated output instead of source.

#hooks/scope-guard.sh

hook

Advisory PreToolUse Edit|Write hook that flags edits to files not declared in the active spec's manifest (never blocks).

Trigger Automatic PreToolUse hook on Edit|Write (wired in .claude/settings.json, timeout 5). No-op when there is no active spec JSON sidecar.

What it does

When the agent edits or writes a file, this hook checks whether that file is listed in the active spec's change_manifest or test_manifest. If the file is undeclared, it prints a scope-creep warning naming the spec. It is advisory only (exit 0) — it never blocks the edit, it surfaces drift so the engineer can update the spec or stop the agent.

Example

During an approved spec whose manifest lists only src/PaymentService.cs, the agent edits src/EmailSender.cs. The hook prints:

SCOPE GUARD: src/EmailSender.cs is not in the approved spec (2026-07-09-refund-flow). If this change is necessary, update the spec's change_manifest first. Undeclared file modifications are the #1 source of spec drift.
How it works

Sources lib/hook-io.sh, resolves the repo-relative path, then finds the active spec JSON sidecar under docs/specs (most recently modified within 7 days via find -mtime -7). It extracts every "path" entry inside the change_manifest and test_manifest sections (awk with a grep/sed fallback for older awk), always appends docs/specs/, docs/plans/, tasks/, and .claude/ as allowed, and caches the allow-list keyed by spec path + mtime. If the edited path matches no allowed prefix it calls mtk_record_scope_guard_warning and prints the SCOPE GUARD line. No active spec sidecar means a silent no-op.

Why it works

Undeclared file modifications are the number-one source of spec drift — the implementation quietly grows beyond what was approved. By echoing the manifest boundary at the moment of an out-of-scope edit, the guard keeps the actual change honest to the approved plan without ever getting in the way of legitimate work.

#hooks/workflow-continuation.sh

hook

Stop-hook nudge that surfaces one line when a session ends with an active workflow that still has unfinished batches.

Trigger Automatic Stop hook (wired in .claude/settings.json). Tier-2 (skill-invoking); respects MTK_HOOKS_TIER2 (default 1, set 0 to silence).

What it does

When a session stops, this hook checks whether the newest workflow is still active with batches left to do. If so, it prints a single advisory line telling the engineer their options: resume the next batch, hand off, or abandon the workflow. It never blocks and never auto-continues work — the human decides.

Example

A session ends mid-implementation. The hook reads the newest .mtk/workflows/*.json, sees status active with 4 batches total and 2 done, and prints:

WORKFLOW IN PROGRESS: 2026-07-09-refund-flow has 2 of 4 batches unfinished (2 done). Resume it (continue the next batch per tasks/todo.md), hand off (`handoff` skill), or close it: bash scripts/workflow-artifact.sh abandon 2026-07-09-refund-flow --reason "<why>".
How it works

Exits early if MTK_HOOKS_TIER2=0. Sources lib/hook-io.sh, finds the newest .mtk/workflows/*.json by portable stat mtime, and extracts fields with jq-free grep/sed helpers (json_num/json_str per S3.3). It proceeds only if status is active and batches_completed < batches_total. It debounces on a marker at $TMPDIR/mtk-wf-continuation-<uuid-cksum> keyed on the done count, so it fires at most once per (workflow, batches_completed) — no spam within a Stop burst, but it re-notifies after a batch actually completes.

Why it works

Long multi-batch implementations routinely get interrupted by context limits, crashes, or the engineer walking away. Without a nudge, an in-progress workflow silently strands — the next session has no idea work was mid-flight. This surfaces the exact state (X of N batches) and the three concrete exits, so nothing is lost and the engineer, not the agent, chooses what happens next.

#hooks/verify-completion

hook

Stop hook that flags a strong completion claim ('task complete', 'all done') when it lacks fresh cited verification evidence.

Trigger Automatic Stop hook (wired in .claude/settings.json and, for plugin installs, hooks/hooks.json; no arguments — reads the Stop payload from stdin). On a gap it blocks the stop once with a reason; never when stop_hook_active is set.

What it does

When the agent's final message makes a strong completion claim, this hook checks the session state for evidence that verification actually ran after the last edit. If there's no fresh verification, if an edit landed after the last verification (criteria re-armed), or if the message doesn't cite command output, it blocks the stop once with a one-line VERIFICATION GAP reason telling the agent to run the tech-stack verification command and cite exit code plus pass/fail counts.

Example

The agent ends with 'Implementation complete, ready for review.' but never ran the test suite this session. The hook matches the strong-claim regex, finds last_verification_epoch = 0, and blocks the stop with:

VERIFICATION GAP: completion claim without a fresh verification command. Run the verification command from your active tech stack (.claude/skills/tech-stack-{stack}/SKILL.md → ## Build & Test Commands) and cite exit code + pass/fail counts. For toolkit work: 'bash scripts/validate-toolkit.sh' and cite 'Toolkit validation passed.'
How it works

Sources lib/hook-io.sh, reads the Stop payload from stdin and derives the claim from the transcript's last assistant message (transcript_path), and matches a deliberately narrow STRONG_CLAIM regex (all done|task complete|work complete|implementation complete|ready for review|fully verified|✅ done|🟢 done, plus top-line 'done/completed/fixed/verified/finished' with terminal punctuation) — tuned to skip casual mentions. On a match it loads session state via mtk_session_file / mtk_load_session_state and checks three conditions in order: no verification this session (last_verification_epoch=0); an edit after the last verification (last_edit_seq > last_verification_seq → 'criteria re-armed'); or a message missing execution evidence (an EVIDENCE regex covering exit codes, test-runner output, and 'Toolkit validation passed'). Each prints its own VERIFICATION GAP line. It always exits 0 (advisory).

Why it works

The classic AI failure mode is declaring victory without running anything — 'done' with no test output behind it. This hook makes the completion claim itself the trigger for demanding fresh, cited evidence, and the 're-armed' check specifically catches the case where the agent verified, then edited again, then claimed done on stale results. It blocks at most once per stop — never when stop_hook_active is set — so it can't wedge a session.

#hooks/pre-commit-linters.sh

script

Deterministic lint engine that scans added diff lines against active guard packs and emits JSON findings (confidence 100).

Trigger Bash script: hooks/pre-commit-linters.sh [--cached | --head] [--stack <name>] [--human]. Run over staged changes as the deterministic first pass before AI review.

What it does

This is the engine that runs MTK's guard packs against a diff. It scans only added lines in staged (or HEAD) changes, matches each against the active packs' regex rules, and emits findings in the review-finding schema with source 'linter' and confidence 100. It computes a verdict — PASS, or NEEDS_CHANGES if any critical rule matched — and outputs JSON (default) or a human-readable summary. It feeds the AI review pass rather than replacing it.

Example
$ hooks/pre-commit-linters.sh --human
Linter verdict: NEEDS_CHANGES  (critical=1 warning=0 suggestion=0)
{"id":"L001","severity":"critical","confidence":100,"source":"linter","rule":"RAW-SQL-INTERPOLATED","file":"src/Repo.cs","line":42,...}

A staged line using FromSqlRaw($"...{id}") trips the .NET pack's critical SQL-injection rule and the commit is flagged NEEDS_CHANGES.

How it works

Detects the stack from .claude/tech-stack (or --stack), then builds the pattern list in discovery order: core/ (always) → stack-<name>/ → domain-<name>/ (from .claude/domains, one per line) → project/ (with flat-file fallbacks for pre-6.2 layouts). It runs git diff --cached -U0 --no-color (or git diff HEAD -U0 --no-color with --head), an awk pass extracts file/line/content for added lines only, and each pack rule (tab-separated RULE_ID|SEVERITY|regex|rationale|fix) is matched with grep -qEi (case-insensitive ERE). Matches become JSON findings; the verdict is NEEDS_CHANGES when critical>0.

Why it works

Some AI failure modes are mechanical and falsifiable — a hardcoded secret, interpolated SQL, a float used for money — and don't need a reasoning model to catch. Running them deterministically over the diff gives a cheap, 100%-confidence floor that never rationalizes or misses on a bad day, freeing the reviewer agents to spend judgment on the things regex can't decide.

#hooks/linter-patterns/ (guard packs)

reference

Composable, shippable regex rule packs — core, per-stack, per-domain, and project-local — that are MTK's deterministic review units.

Trigger Loaded on-demand by hooks/pre-commit-linters.sh; packs auto-activate by directory (core always; stack-/domain- by .claude/tech-stack and .claude/domains). Authoring guide in .claude/references/guard-packs.md.

What it does

A guard pack is a tab-separated file of regex rules (RULE_ID, SEVERITY, ERE_REGEX, RATIONALE, SUGGESTED_FIX) that the linter engine runs against staged changes. Packs are organized so quality gates ship as reusable units instead of prose checklists: core packs always apply, stack and domain packs turn on to match the repo, and a gitignored project/ pack holds local overrides. Only 'critical' severity blocks a commit; 'warning' and 'suggestion' are advisory.

Example

The finance domain pack (domain-finance/patterns.txt) ships FLOAT-MONEY-CS (critical) — (float|double)[[:space:]]+(amount|balance|price|fee|cost|total|sum|payment|rate) — and FIN-AUDITED-DELETE (warning), which flags Transactions.Remove(...) as breaking the audit trail. Core packs ship SECRET-HARDCODED and AWS-ACCESS-KEY (secrets.txt) plus SLOP-SKIP-TEST (slopwatch.txt — a disabled test that may be masking a failure); the .NET pack ships RAW-SQL-INTERPOLATED and WEAK-HASH (MD5/SHA1).

How it works

Directory layout under hooks/linter-patterns/: core/ (docdrift.txt, secrets.txt, slopwatch.txt — always active), stack-<name>/ (stack-dotnet, stack-python, stack-typescript — active when .claude/tech-stack matches), domain-<name>/ (domain-finance — active when .claude/domains lists it), and project/ (gitignored, .gitkeep placeholder). pre-commit-linters.sh concatenates active packs in that order. Rules use POSIX ERE matched case-insensitively via grep -Ei, so PCRE features like lookahead don't work. Authoring: keep rules conservative, reserve 'critical' for almost-always-wrong smells, add a test under tests/hooks/, and run scripts/validate-toolkit.sh. Packs are the deterministic floor; reviewer agents (compliance-reviewer, silent-failure-hunter) are the judgment ceiling.

Why it works

Prose checklists rot and depend on someone remembering to apply them. Packaging quality gates as composable, testable, auto-activating regex units means a repo automatically gets exactly the checks its stack and domain warrant — finance repos catch float money and audited deletes, .NET repos catch interpolated SQL — with a stable RULE_ID for every finding and a conservative-severity discipline that keeps the packs from crying wolf.

#hooks/format-on-edit.sh

hook

PostToolUse hook that auto-formats the file the agent just edited, dispatching by extension; never blocks the edit.

Trigger PostToolUse hook on Edit|Write. Command string bash $CLAUDE_PLUGIN_ROOT/hooks/format-on-edit.sh (matcher Edit|Write), added to a target repo's .claude/settings.json during setup via the tech-stack skill's 'Settings Additions' block. NOTE: it is not present in the toolkit's own .claude/settings.json — that file wires only the guard hooks. Reads the edited file_path from stdin JSON.

What it does

After the agent edits or writes a file, this hook runs the appropriate formatter for that file's language so the committed diff stays clean and consistent. Formatter selection is by file extension. Any formatter failure is reported to stderr (so it shows in the hook log) but never blocks the edit.

Example

The agent writes src/handlers/refund.py. The hook sees the .py extension and, if ruff is installed, runs ruff format then ruff check --fix on that file; the saved file comes back formatted with no separate 'please run the formatter' step. If ruff is absent it falls back to black --quiet; if neither is installed, the hook silently does nothing for that language.

How it works

Sources lib/hook-io.sh, extracts file_path via mtk_extract_file_path, and dispatches on the extension: ts/tsx/js/jsx/mjs/cjs → npx --no-install biome format --write then prettier as fallback; py → ruff format + ruff check --fix, else black --quiet; cs → dotnet format --include. Each formatter is probed with command -v / npx --no-install so a missing tool is a no-op. run_formatter captures the exit code and logs 'mtk format-on-edit: <label> failed ...' on failure. The hook always exits 0.

Why it works

Formatting drift produces noisy diffs and review friction, and an agent that hand-formats wastes tokens and gets it inconsistently. Doing it deterministically the moment a file is written keeps every edit canonical without adding a manual step, and failing soft means a missing or broken formatter never wedges the agent's edit.

#hooks/check-prerequisites.sh

script

Reports which recommended dev tools are missing (shellcheck, jq, stack-specific) as warnings — never blocks.

Trigger Bash script: bash hooks/check-prerequisites.sh [stack] (stack: dotnet|python|typescript, else reads .claude/tech-stack). Called by setup-bootstrap after tech-stack detection.

What it does

During setup, this script probes for the development tools that make MTK's hooks and linters work best and reports any that are missing. It checks core tools for every stack plus stack-specific tools, and prints install hints for anything absent. It only warns — the tools are optional and MTK works without them.

Example
$ bash hooks/check-prerequisites.sh python

Prerequisites: 2 recommended tool(s) not found:
  ⚠  shfmt — formats shell scripts consistently (brew install shfmt)
  ⚠  mypy — static type checking (pip install mypy)

These are optional but improve hook and linter quality.
Install them when convenient — MTK works without them.
How it works

Resolves the stack from the argument or .claude/tech-stack. check_tool uses command -v to probe each tool and, on a miss, records a ⚠ <name> — <purpose> (<install hint>) line. Core tools (all stacks): shellcheck, shfmt, jq. Stack-specific: dotnet → dotnet, dotnet-format; python → python3, ruff, mypy, pytest; typescript → node plus the active package manager from .claude/tech-stack-pm. It prints either 'Prerequisites: all recommended tools found.' or, when any are missing, a header with the count followed by the ⚠-bulleted list (the individual lines are bulleted, not numbered). It never exits non-zero.

Why it works

MTK's deterministic layer leans on external tools (shellcheck for hook quality, jq for JSON, ruff/dotnet-format for formatting). Silently degrading when they're absent would leave engineers wondering why a linter or formatter did nothing. Surfacing the gaps with exact install hints at setup time — as warnings, not gates — lets the engineer close them on their own schedule without ever blocking onboarding.

#hooks/session-start

hook

Injects MTK's active banner plus session-recovery, version-drift, and personal-lesson notices at session start.

Trigger Automatic SessionStart hook (matcher "startup|clear|compact"), wired in hooks/hooks.json

What it does

Runs at the start of every new/cleared/compacted session. It announces that the toolkit is active and names the entry points, surfaces any in-progress spec/plan/todo so the agent resumes instead of restarting, warns when the installed MTK version has drifted from the source it was installed from, lists personal lessons in play, and silently rebuilds the optional MCP server if its bundle is stale. It also adapts its output format to the host CLI.

Example

Open Claude Code in a repo where you wrote a spec yesterday. The hook emits a single JSON context line:

{"context": "Moberg toolkit is active. Available entry points: /mtk and /mtk-setup. Skills load on demand via progressive disclosure. SESSION RECOVERY: Found recent spec at docs/specs/2026-07-08-payments.md. You may have an in-progress feature. Read the spec to resume where you left off."}
How it works

Platform detection reads CLAUDE_PLUGIN_ROOT / CURSOR_PLUGIN_ROOT / COPILOT_CLI / GEMINI_EXTENSION_ROOT and emits either {"context":"..."} or the SDK-standard {"role":"system","content":"..."}. Recovery scans docs/specs for *.md modified in the last day, then docs/plans, then tasks/todo.md for '[ ]' items (first match wins). Version drift compares .claude/mtk-version.json against ${CLAUDE_PLUGIN_ROOT}/.claude/manifest.json and prints 'MTK update available: X → Y'. Personal lessons come from counting '## ' headers in .claude/lessons/personal.md. If an mcp/ directory exists and node is on PATH, and dist/mtk-mcp-server.cjs is missing or any file under mcp/src is newer, it runs scripts/build-mcp.sh --quiet (non-blocking). JSON escaping is done with bash parameter substitution only.

Why it works

A fresh session has no memory of in-flight work, so without recovery the agent silently re-starts or duplicates a half-finished feature. The advisory version check keeps installs from quietly running an outdated toolkit, and the MCP self-build keeps the optional deterministic layer current without any manual npm step.

#hooks/context-budget.sh

hook

Counts reads/edits/ops per session and nudges a checkpoint or handoff before the context window degrades.

Trigger Automatic PostToolUse hook (fires after every tool), wired in .claude/settings.json; timeout 3s

What it does

After every tool call it updates per-session counters and, when work crosses a threshold, prints a one-line advisory. It tracks unique files read, modifications, total operations, and an estimate of context tokens consumed, so it can tell you to narrow focus, commit a checkpoint, or reset before quality drops. It never blocks — it only nudges, once per threshold.

Example

After the 30th distinct file read in a session, the hook prints to the transcript:

CONTEXT BUDGET: 30 unique files read this session. Consider narrowing focus to the files that matter for your current task. Use context-engineering to load only relevant references.

At ~60% of the window it instead suggests capturing state with the handoff skill and starting fresh.

How it works

State lives in a per-project/per-day temp file (mtk_session_file). Each Read accumulates bytes_read for new unique files (capped at 100k each); Edit/Write bump mods; Bash that matches mtk_command_is_verification records last_verification_*. Thresholds: 30 unique files, 40 modifications, 120 operations, and a read-bytes floor estimate (bytes_read/4) reaching MTK_CONTEXT_BUDGET_PCT (default 60) of MTK_CONTEXT_WINDOW_TOKENS (default 200000). Each threshold fires once via warned_files/warned_mods/warned_ops/warned_ctxpct flags flipped with sed. Concurrency-safe via an mkdir advisory lock around the load-modify-save cycle (hooks/lib/hook-io.sh).

Why it works

Context degrades silently as the window fills, and the agent has no reliable internal sense of how full it is. Deterministic byte/operation counting gives an objective, cheap signal to checkpoint or hand off before answers get worse — the 'reset before degradation' idea, made measurable.

#hooks/session-analytics.sh

hook

Persists per-session stats into .claude/analytics.json so teams can track MTK adoption and effectiveness.

Trigger Automatic Stop hook (fires when the agent stops), wired in .claude/settings.json

What it does

When a session ends it rolls that session's counters into a cumulative, gitignored analytics file. It accumulates sessions, operations, modifications, specs created, lessons captured, scope-guard warnings, benchmark runs, queue activity, and an estimated-context-tokens figure. Trivial sessions (fewer than 5 operations) are skipped so noise doesn't inflate the numbers.

Example

After a real working session, .claude/analytics.json grows:

{
  "sessions": 43,
  "total_operations": 1291,
  "total_modifications": 575,
  "specs_created": 5,
  "lessons_captured": 12,
  "bytes_read": 4120000,
  "estimated_context_tokens": 1030000
}

The toolkit-health skill later reads this file to report adoption.

How it works

Reads the session state file written by context-budget.sh (via mtk_load_session_state). Exits early if session_ops < 5. Initializes .claude/analytics.json if absent, then reads current values with jq-free grep/sed helpers, increments them, computes estimated_context_tokens = bytes_read/4, counts new specs in docs/specs (*.json newer than the analytics file) and lessons via '## ' headers in tasks/lessons.md, and writes atomically (temp file + mv) to avoid truncation races. Queue counters (queue_writes/queue_drains/queue_expired) are carried through from the tier-2 queue.

Why it works

Adoption is organic on this team (no mandates), so a lightweight, gitignored, no-dependency ledger is the only honest way to see whether MTK is actually being used as intended — and it gives toolkit-health real numbers instead of guesses.

#hooks/post-compact.sh

hook

Re-injects on-disk state (stack, specs, plans, tasks, handoffs, active workflow) after auto-compaction.

Trigger Automatic SessionStart hook with matcher "compact" (fires after auto-compaction), wired in .claude/settings.json and hooks/hooks.json; timeout 5s

What it does

Auto-compaction throws away conversation history, so this hook reconstructs the essentials from disk and feeds them back as a single recovery context. It reminds the agent of the active tech stack, any recent spec/plan, incomplete tasks, the latest handoff, and the active durable workflow — so the post-compaction session keeps working instead of losing the thread.

Example

Mid-implementation the session auto-compacts. Immediately after, the hook emits:

{"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": "POST-COMPACTION RECOVERY: Active tech stack: dotnet. Package manager: nuget. Active spec: docs/specs/2026-07-08-payments.json — read before resuming implementation or review. Incomplete tasks in tasks/todo.md — check before starting new work. Active workflow: 2026-07-08-payments (type=implement, phase=implementation, last_event=batch_verified) — read .mtk/workflows/... before advancing; do not re-init."}}
How it works

Reads .claude/tech-stack (+ .claude/tech-stack-pm), docs/specs/*.json and docs/plans/*.md modified in the last day, '[ ]' items in tasks/todo.md, docs/handoffs/*.md, and the active workflow artifact under .mtk/workflows/ (grep for status active, then workflow_type and phase_cursor, plus the last event_type from the paired .events.jsonl). It concatenates these into one string, escapes it with bash-only substitution, and prints a SessionStart hookSpecificOutput.additionalContext envelope carrying "POST-COMPACTION RECOVERY: ...". Per rule S3.11 it does no blocking and has no side effects.

Why it works

Compaction is the single biggest silent context-loss event: the agent can forget it was mid-feature and start fresh or contradict prior decisions. Re-hydrating from durable on-disk artifacts (rather than chat memory) is what lets orchestration survive compaction at all.

#hooks/pre-compact-snapshot.sh

hook

Stashes uncommitted work right before auto-compaction so context loss never costs in-flight changes.

Trigger Automatic PreCompact hook (fires before auto-compaction), wired in .claude/settings.json and hooks/hooks.json; opt-in for plugin installs via MTK_COMPACT_SNAPSHOT=1; timeout 8s

What it does

Just before an auto-compaction, this hook takes a safety snapshot of the working tree using git stash and immediately re-applies it, so the tree looks unchanged but a recoverable checkpoint exists. It only acts on automatic compaction, never touches a clean tree, and stays out of the way during rebases/merges. It logs every action for auditability.

Example

You have uncommitted edits when an auto-compaction triggers. The hook prints to stderr and appends to a log:

[mtk] precompact snapshot saved → mtk-precompact-2026-07-09T14:03:11Z (branch=feat/payments)

If anything went wrong you recover with git stash apply (or scripts/mtk-recover.sh).

How it works

Reads the hook JSON payload; it exits only when the payload declares a non-auto trigger (manual /compact is respected). When the trigger is "auto" or the payload has no trigger field, it proceeds. Requires a git work tree; skips when a rebase-merge/rebase-apply/MERGE_HEAD/CHERRY_PICK_HEAD/REVERT_HEAD marker exists; skips clean trees (no staged/unstaged/untracked changes). Otherwise runs git stash push --include-untracked -m mtk-precompact-<UTC-timestamp> then git stash apply --index (falling back to plain apply) so the tree — including the staged/unstaged split — is left unchanged. Every outcome (saved/skipped/failed) is appended to .claude/observability/precompact-snapshots.log. A trap 'exit 0' ERR guarantees it never blocks compaction.

Why it works

Auto-compaction can be followed by the agent doing something destructive or losing awareness of edits; a pre-compaction stash means in-flight work is always recoverable even in the worst case. Skipping manual compaction and in-progress git operations keeps the safety net from interfering with deliberate user actions.

#hooks/compress-monitor.sh

hook

Flags large Bash outputs that landed in context without being piped through mtk-compress.sh.

Trigger Automatic PostToolUse hook (matcher Bash), wired in .claude/settings.json; timeout 3s

What it does

After a Bash tool call, if the command dumped a large amount of raw output straight into context and did not already compress it, this hook suggests re-running it through the compression helper. It is advisory — it does not alter the result — and it picks a sensible compression mode based on what the command looked like.

Example

You run dotnet test and it prints 12,000 chars of output. The hook returns:

{"continue": true, "additionalContext": "💡 mtk-compress: Bash output was 12,000 chars (warn ≥ 5,000). Pipe long output through `bash scripts/mtk-compress.sh tests` to reclaim ~70-95% of context tokens. Set MTK_COMPRESS_MONITOR_DISABLED=1 in .claude/settings.local.json env to silence."}
How it works

A small python3 program parses the PostToolUse payload: it exits unless tool is Bash, the command does not already contain 'mtk-compress' (or 'MTK_COMPRESS_DISABLED'), and the result length is >= MTK_COMPRESS_WARN_CHARS (default 5000). It infers a mode from the command shape — 'tests' for test runners, 'logs' for builds/tsc/biome, 'html' for curl of HTML, 'json' for jq/cat of .json, else 'auto' — and emits {"continue": true, "additionalContext": "..."}. The whole hook is disabled per-machine with MTK_COMPRESS_MONITOR_DISABLED=1.

Why it works

Verbose command output is one of the fastest ways to burn the context window on low-value text; nudging the engineer to pipe it through the compressor reclaims most of those tokens without losing the signal the agent actually needs.

#hooks/userprompt-dispatch.sh

hook

Single UserPromptSubmit entry point that merges tier-1 nudges, trigger hints, and the tier-2 queue drain.

Trigger Automatic UserPromptSubmit hook (runs on every user prompt), wired in .claude/settings.json; timeout 5s

What it does

On every user prompt this one hook does three jobs and merges them into a single injected-context payload: it emits inline tier-1 nudges (e.g. correction detection), surfaces keyword-triggered skill hints, and drains the tier-2 skill queue that other hooks wrote since the last prompt. Consolidating everything into one script avoids the unguaranteed ordering of multiple UserPromptSubmit hooks.

Example

You type a new prompt after a hook queued a follow-up skill. The dispatcher returns:

{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"MTK-QUEUED (1 pending):\n- planning-and-task-breakdown — spec approved. Consider: invoke the planning-and-task-breakdown skill now."}}
How it works

Sources hooks/lib/skill-queue.sh (and, if present, lib/prompt-nudges.sh and lib/trigger-hints.sh). Short-circuits via mtk_queue_enabled (MTK_HOOKS_TIER2). Extracts the prompt with the escape-aware mtk_extract_json_string. Collects tier-1 nudges (mtk_collect_prompt_nudges) and trigger hints (mtk_collect_trigger_hints, S2.22). Drains .claude/queue/*.json newest-first: deletes entries older than their ttl_hours, dedups by skill|source_hook, caps at 3 surfaced per turn, and records queue_drains/queue_expired counters. Output is grouped under MTK-NUDGE / MTK-TRIGGERS / MTK-QUEUED and returned as escaped additionalContext.

Why it works

Some suggestions can't be delivered at the moment they arise — they must wait for the next prompt or survive a session boundary. A single deterministic dispatcher gives one predictable place for all prompt-time context injection, so nudges never race each other or get silently dropped by hook ordering.

#.claude/queue/ (tier-2 skill queue)

concept

Deferred, advisory skill-invocation queue: hooks drop JSON entries that the prompt dispatcher surfaces next turn.

Trigger Named mechanism: writers call queue_skill() in hooks/lib/skill-queue.sh; drained by hooks/userprompt-dispatch.sh; kill-switch env MTK_HOOKS_TIER2

What it does

This is the mechanism behind 'tier-2' hooks. When a hook wants to suggest a skill but the moment isn't a user prompt — or the suggestion should persist across a session boundary — it writes a small JSON entry under .claude/queue/ instead of nudging inline. The next user prompt drains it. Entries are advisory ('propose, never act'): the agent decides whether to invoke.

Example

A hook fires: queue_skill planning-and-task-breakdown "spec approved" spec-approval-trigger.sh. That writes .claude/queue/1751...-planning-and-task-breakdown-<hash>.json:

{ "queued_at": "2026-07-09T14:03:11Z", "queued_epoch": 1751000000, "skill": "planning-and-task-breakdown", "reason": "spec approved", "context": { "excerpt": "..." }, "ttl_hours": 24, "source_hook": "spec-approval-trigger.sh" }

On your next prompt the dispatcher surfaces it as an MTK-QUEUED line.

How it works

queue_skill <skill> <reason> <source_hook> [excerpt] writes <epoch>-<skill>-<hash>.json atomically (tmp-then-rename) with fields queued_at, queued_epoch, skill, reason, context.excerpt (truncated 500 chars), ttl_hours (default MTK_QUEUE_TTL_HOURS=24), source_hook. The dispatcher's drain sorts newest-first, expires entries past their per-entry TTL, dedups by skill|source_hook, caps 3 per turn, and bumps queue_writes/queue_drains/queue_expired in .claude/analytics.json. Everything is gated by mtk_queue_enabled (MTK_HOOKS_TIER2, default 1); when 0, writers and dispatcher no-op. Per S3.13 tier-2 must never modify source code. The queue is workspace-local and gitignored.

Why it works

Inline nudges can't defer or survive compaction/session end, and multiple UserPromptSubmit hooks have no guaranteed order. A durable, deduped, TTL-bounded queue lets any hook safely propose a skill for later while keeping the agent in control and giving engineers a per-machine kill switch.

#scripts/build-rule-index.sh

script

Regenerates .claude/rules/INDEX.md from rule frontmatter; --check fails CI on a stale index.

Trigger bash script — bash scripts/build-rule-index.sh after editing any rule file; CI runs bash scripts/build-rule-index.sh --check

What it does

The deterministic generator behind the wake-up layer. It reads each rule file's frontmatter and content and rebuilds the INDEX.md table so the always-on layer never drifts from the actual rules. A --check mode lets CI fail the build when someone edits a rule but forgets to regenerate the index.

Example
$ bash scripts/build-rule-index.sh
Wrote .claude/rules/INDEX.md (23 lines, budget 60)

If an engineer later edits a rule's frontmatter and commits without regenerating, CI runs bash scripts/build-rule-index.sh --check and fails: STALE: .claude/rules/INDEX.md does not match current rules — run scripts/build-rule-index.sh (exit 1).

How it works

For each .claude/rules/*.md (skipping INDEX.md), awk extracts the axes: block (decision / topic / scope) and the paths: glob list from frontmatter, greps the first H1 for the title and the 'Cite rules as <prefix>' line for the rule-id prefix, counts matching - **<prefix>. rule lines, and runs wc -l for the line count — then emits the markdown table. BUDGET_LINES=60 triggers a non-fatal WARN if the generated index is longer. --check regenerates to a temp file and diffs against the committed INDEX.md, exiting 1 if they differ or the file is missing.

Why it works

A hand-maintained index rots the moment a rule changes, and a stale wake-up layer silently misroutes rule loading. Generating it deterministically and gating on --check in CI keeps the cheap always-on layer trustworthy.

#scripts/mtk-compress.sh

script

stdin→stdout filter that compresses noisy build/test/JSON/HTML output before it lands in context.

Trigger bash pipe — <command> 2>&1 | bash scripts/mtk-compress.sh <mode>; bash scripts/mtk-compress.sh stats for savings

What it does

A pipe-through filter that shrinks long, repetitive command output — build logs, test runs, JSON dumps, fetched HTML — before it reaches the model's context, keeping the diagnostic signal (failures, summaries, timings) while dropping the repetition. It has explicit safety carve-outs for content that must never be summarized away.

Example
dotnet test 2>&1 | bash scripts/mtk-compress.sh tests

A 48,000-char test log collapses to ~1,800 chars — every FAIL/Error line and the summary survive while runs of identical PASS rows are elided. Each run appends one JSON line to .claude/observability/compression.jsonl, and bash scripts/mtk-compress.sh stats later reports all-time and current-session savings.

How it works

Five modes: auto (detect by content shape, default), json (truncate arrays past N items to head+elision+tail, clip long strings), logs (keep first/last N lines, insert an elision marker), tests (preserve FAIL/Error/summary, collapse PASS noise), html (strip script/style/comments, collapse whitespace, truncate). Tunable via .claude/settings.local.json env block: MTK_COMPRESS_MIN_CHARS (1000), MTK_COMPRESS_MAX_LOG_LINES (30), MTK_COMPRESS_MAX_ARRAY_ITEMS (5), MTK_COMPRESS_DISABLED (0 → pass through), MTK_COMPRESS_WARN_CHARS (5000), MTK_COMPRESS_MONITOR_DISABLED (0). Safety carve-outs — never route secrets/tokens/keys, security/auth findings, migration/destructive-operation output, or audit/compliance content through it (compressing a leak does not contain it); compress only the surrounding noise.

Why it works

Build and test commands routinely emit 20-60k chars where 95%+ is identical PASS rows or progress lines, burning context budget without proportional information. Head+elision+tail reclaims thousands of tokens per command while failures, summary counts, and timing markers stay intact.

#hooks/compress-monitor.sh

hook

PostToolUse hook that warns when large Bash output skipped the compressor.

Trigger automatic PostToolUse hook

What it does

A monitoring hook that runs after a tool call and flags large Bash command outputs that did not go through mtk-compress.sh. It makes compression opt-in but visible — nudging the engineer when a big, uncompressed output just burned context budget.

Example

An engineer runs a Bash command that dumps a ~60,000-char log straight into context without piping it through the compressor. After the tool call, the PostToolUse hook emits a {"continue": true, "additionalContext": "💡 mtk-compress: Bash output was 60,000 chars (warn ≥ 5,000). Pipe long output through bash scripts/mtk-compress.sh logs …"} nudge, picking the suggested mode (tests/logs/html/json/auto) from the command shape.

How it works

Fires as a PostToolUse hook, reads the JSON payload on stdin, and warns only when the tool was Bash, the result exceeds MTK_COMPRESS_WARN_CHARS (default 5000), and the command did not already contain mtk-compress (or MTK_COMPRESS_DISABLED). It can be silenced per-machine with MTK_COMPRESS_MONITOR_DISABLED=1. It only warns — no blocking, no mutation of the tool result. Requires python3 to parse the payload.

Why it works

Compression only helps if it's actually used; a silent miss means budget is wasted with no signal. The monitor turns 'forgot to compress' into a visible, correctable nudge instead of an invisible cost.

#hooks/context-budget.sh

hook

PostToolUse hook that tracks per-session read/edit/op counters and nudges a deliberate reset/handoff before context degrades.

Trigger automatic PostToolUse hook (registered in .claude/settings.json)

What it does

Maintains per-session, per-project-per-day counters (unique files read, modifications, total operations, bytes read) in a temp file and prints advisory CONTEXT BUDGET nudges as thresholds are crossed. It is the deterministic backstop behind the context-engineering budget guidance — purely advisory, it never blocks.

Example

The script fires four warnings, once each per session: 30+ unique files read → 'Consider narrowing focus…'; 40+ modifications → 'Consider committing a checkpoint…'; 120+ total operations → 'If switching tasks, capture state with the handoff skill first'; and when the read-bytes token estimate (bytes_read/4) crosses MTK_CONTEXT_BUDGET_PCT% (default 60) of MTK_CONTEXT_WINDOW_TOKENS (default 200000) it prints 'estimated ~N context tokens consumed … past 60% of the 200000-token window. Reset deliberately before quality degrades: capture state with the handoff skill and start a fresh session.'

How it works

Runs as a PostToolUse hook, sources hooks/lib/hook-io.sh, and holds a session lock across load→modify→save so concurrent firings don't lose counter updates. It counts by tool type: Read accumulates unique file paths and bytes read (capped at 100k per file), Edit/Write increment mods, Bash/Glob/Grep increment ops. The token figure is a read-bytes FLOOR (bytes_read/4) that under-counts because it cannot see assistant output or non-read tool results. Tunable via MTK_CONTEXT_WINDOW_TOKENS and MTK_CONTEXT_BUDGET_PCT; advisory only, never blocks.

Why it works

A session can ride the budget up to a forced mid-edit compaction with no warning. The 60% nudge is the hard floor that fires before that, and pairing it with context-engineering's ~40% proactive reset gives a two-stage guardrail; keeping it advisory avoids interrupting legitimate long sessions.

#hooks/post-compact.sh

hook

Post-compaction hook (SessionStart matcher "compact") that re-injects on-disk state (tech stack, active spec/plan, incomplete tasks, handoff, active workflow) after auto-compaction.

Trigger automatic SessionStart hook with matcher "compact" (registered in .claude/settings.json and hooks/hooks.json, per rule S3.11)

What it does

After Claude Code auto-compacts a session, this hook scans the repo for durable state the compacted conversation may have lost and emits it back as a recovery context block. It is the mechanism that makes handoff artifacts and workflow artifacts actually survive compaction — the write-side skills persist state, this hook points the fresh context back at it.

Example

After compaction the hook reads .claude/tech-stack (+ tech-stack-pm), the most recent docs/specs/*.json and docs/plans/*.md touched in the last day, any unchecked [ ] items in tasks/todo.md, the newest docs/handoffs/*.md, and any .mtk/workflows/*.json with "status":"active" (surfacing workflow type, phase_cursor, and last event). It prints one line: {"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": "POST-COMPACTION RECOVERY: … Active workflow: wf-… (type=BUILD, phase=phase-3, last_event=…) — read .mtk/workflows/wf-….json before advancing; do not re-init."}}.

How it works

Runs as a SessionStart hook with matcher "compact" (there is no PostCompact event). No external dependencies — pure bash file scanning plus parameter-substitution JSON escaping (no jq). It emits a SessionStart hookSpecificOutput.additionalContext envelope only; no blocking, no side effects on the session. For an active workflow it explicitly instructs the orchestrator to read the artifact before advancing and not to re-init.

Why it works

Auto-compaction silently destroys chat-resident orchestration and session state; the durable artifacts written by handoff and workflow-artifacts are only useful if a post-compaction session is pointed back at them. This hook closes that loop automatically instead of relying on the model to remember to re-read.

08 Under the hood 38 entries

The machinery the workflows stand on: context budgeting, the rule wake-up layer, durable workflow artifacts, and the extensibility surface.

#/mtk

skill

Classifies a plain-English request and dispatches it to the right MTK workflow skill; first match wins.

Trigger Slash command: /mtk <description> (natural language). Invoked with no argument, it prints help.

What it does

The single natural-language entry point for everything except setup. You describe what you want in plain English and it classifies the intent, then loads the matching workflow skill and follows it end to end. It is explicitly a router: it does NOT do the work itself and it does NOT summarize your request — it delegates by reading the target skill and following every step.

Example

/mtk fix the auth feature matches both the fix row (verb) and the feature row (feature-sized noun) with similar specificity, so the router takes the ambig branch and asks one clarifying question: "Is this a small fix (1-3 files) or a larger feature?" Answer "small" routes to .claude/skills/fix/SKILL.md; "larger" routes to .claude/skills/implement/SKILL.md. A clean input like /mtk review before commit matches exactly one row and routes silently to .claude/skills/pre-commit-review/SKILL.md.

How it works

The Routing Decision Graph is walked top to bottom, first match wins; the Route Table is the keyword/intent lookup that maps patterns to target skill files (fix, implement, pre-commit-review, repo-health, pr-review-mining, mtk-doctor, toolkit-health, research-context, context-report, claude-md-audit, claude-md-capture, promote-lesson, lesson-mining, handoff). Routing Rules: (1) strip and pass through flags --terse/--verbose/--staged-only/--preview/--merge/--non-interactive; (2) unambiguous -> route silently; (3) literal escalated from fix -> implement; (4) genuinely ambiguous (two rows, similar specificity) -> ask ONE question; (5) no input -> help; (6) pass the user's description through verbatim; (7) setup asks -> redirect to /mtk-setup, never absorb. Route Disambiguation (negative boundaries) encodes contested pairs (fix vs implement, research-context vs implement/fix, pre-commit-review vs code-review-and-quality, context-report vs toolkit-health, claude-md-capture vs handoff, promote-lesson vs lesson-mining, /mtk-setup vs implement/fix) to cut misroutes. A Red flags while routing table pre-empts common mis-route rationalizations (e.g. "user said 'fix' so just route to fix"). Before dispatch it applies Toolset Expansion (S2.19). Execution: read the target SKILL.md and follow every step including verification — the target skill owns its acceptance criteria.

Why it works

One dispatch point means engineers never memorize the ~15 workflow skill names or their exact triggers. The decision graph plus negative boundaries measurably cut misroutes on adjacent intents (e.g. 'fix' vs 'implement'), and the 'pass description through verbatim / don't do the work myself' discipline keeps the router thin and leaves the workflow skills authoritative — the router can't silently drop steps.

#escalated from fix

concept

Self-escalation path: when a small fix outgrows its scope, fix hands off to implement automatically.

Trigger Automatic internal marker: produced only by the fix Scope Guard and consumed by the /mtk router (Routing Rule 3). Never typed by a user.

What it does

A change that looked like a small fix sometimes turns out to be feature-sized once you're in it. Instead of doing multi-file work under the lightweight fix skill, the fix skill's Scope Guard emits the literal marker escalated from fix. The router detects the marker and routes straight to the full implement workflow.

Example

/mtk fix the timezone handling routes to .claude/skills/fix/SKILL.md. Mid-fix the Scope Guard finds the change spans several files and adds a new public contract, so it emits escalated from fix. The router sees the literal marker (Rule 3), and — without asking any clarifying question — routes straight to .claude/skills/implement/SKILL.md.

How it works

In the Routing Decision Graph the first diamond after 'empty/help?' is the esc node ('contains escalated from fix?'), which on yes goes directly to implement — ahead of every keyword row. The Route Table lists escalated from fix as its top row, flagged 'internal self-escalation only'. Routing Rule 3 hardcodes: input literally containing escalated from fix routes straight to implement, and it is 'produced only by the fix Scope Guard'.

Why it works

Prevents the failure mode where a change that deserved a spec, plan, and review gets done under the fix skill's minimal ceremony. Because the marker is literal and checked before the keyword rows, scope creep automatically upgrades the workflow instead of quietly bypassing the heavier gates.

#Toolset Expansion (S2.19)

concept

Before dispatch, the router resolves a skill's declared toolsets into an advisory tool scope.

Trigger Automatic: the router runs it before dispatching a skill that declares toolsets, resolving them via bash scripts/resolve-toolsets.sh <name> ....

What it does

Workflow skills can declare which tool groups they need or must never touch. Before loading a target skill, the router reads its frontmatter and expands those declarations into a concrete tool scope, so (for example) a read-only review skill can't quietly start editing source code.

Example

Routing to a review skill whose frontmatter says required-toolsets: [read-only]: the router runs bash scripts/resolve-toolsets.sh read-only (resolved against .claude/toolsets/read-only.yaml) and treats the result as the advisory tool scope for that dispatch. A skill that also declares forbidden-toolsets: [code-edit] (.claude/toolsets/code-edit.yaml) has those tools off-limits for the whole dispatch, even if required-toolsets or allowed-tools would otherwise permit them.

How it works

Read the target's frontmatter: required-toolsets: [<name>, ...] resolves via scripts/resolve-toolsets.sh into an advisory scope (don't invoke tools outside it without a written escalation note). forbidden-toolsets: [<name>, ...] makes those tools off-limits for the dispatch and wins on overlap. An explicit allowed-tools: in the skill takes precedence (no surprise widening) — toolset expansion is only for skills that leave tool scope to the dispatcher. When a skill declares toolsets, the resolved scope is logged to analytics via the existing hooks/session-analytics.sh pipeline so adoption is visible.

Why it works

Enforces least-privilege at dispatch time. A router that silently granted full tools to every skill would let review/audit skills mutate the very code they're supposed to judge; explicit toolset resolution keeps that boundary and surfaces usage in analytics.

#.claude/references/model-routing.md

reference

Per-phase model-tier policy (haiku/sonnet/opus) that reserves the expensive tier for code and adversarial review.

Trigger Loaded on-demand as policy. Enforced for review agents via model: in .claude/agents/<name>.md frontmatter; advisory for workflow skills (they run on the session model).

What it does

The source of truth for which model tier each workflow phase and review agent should run on. The expensive tier is spent only where capability changes the outcome — code that writes real logic and the adversarial reviews that protect serious software — while everything mechanical (discovery, structured comparison, bounded judgment) runs cheaper. This keeps a typical feature in the low-single-digit-dollars range without losing capability where it counts.

Example

A feature run: setup scan/discovery on haiku; spec authoring, planning, standard implementation, spec-drift detection, and pre-commit review on sonnet; the compliance-reviewer agent and the security-and-hardening phase on opus; a batch flagged novel/tricky bumped to opus. The compliance agent's tier isn't a suggestion — it's pinned by model: opus in .claude/agents/compliance-reviewer.md frontmatter (verified), while the other review agents (architecture-reviewer, test-reviewer, silent-failure-hunter, plan-gap-reviewer, context-miner) pin model: sonnet.

How it works

Two enforcement surfaces: review agents pin model: in frontmatter (enforced); workflow skills run on the user's session model (advisory recommendation only — skills can't self-select a model). The policy table maps each phase/role to a tier (e.g. setup discovery -> haiku; spec-driven-development/planning-and-task-breakdown/incremental-implementation/research-context/pre-commit review -> sonnet; compliance review/security-and-hardening/brainstorming/novel batch -> opus). Provider tier slots fast/default/strong abstract over the concrete Claude bindings (haiku/sonnet/opus) so a model rename is a one-line re-bind. subagent-implementation defaults per-batch implementers to sonnet, choosing opus only for a batch the plan flags novel/tricky. Overrides are allowed — the policy exists to stop the reflex of running everything on the top tier.

Why it works

Without a written policy the reflex is to run every phase on the most capable (most expensive) model. This pins spend to the phases where capability actually changes the result and keeps per-feature cost predictable, while still guaranteeing the highest-stakes reviews get the strongest tier.

#.claude/references/audit-grounding.md

reference

Grounding ruleset for generated docs: rule tags, transient-state ban, no partial lists, terminology denylist, SHA stamp.

Trigger Loaded on-demand by setup-audit STEP 3.7 and setup-bootstrap STEP 3.5a

What it does

The rulebook every generator follows when emitting CLAUDE.md, architecture-principles.md, and conventions.md so the output is factually defensible. Each rule maps to a real failure mode from a client run that produced ~20 factual errors a single grep would have caught.

Example

A proposed rule "Never write ../../" can only be tagged [ENFORCED] if it holds in every sampled file (zero exceptions); when the codebase does ../../ everywhere, committing it as enforced is exactly the failure this ruleset blocks — the line is dropped (or downgraded to [ASPIRATIONAL] if it's genuinely the team's stated intent), never [ENFORCED]. A generated line Active branch: fix/ltv-slider-bounds-and-typo is dropped by the transient-state lint; a line using "path alias" for a TS paths config is flagged terminology-needs-review in weak-claims.json (never auto-rewritten).

How it works

Eight numbered rule areas. (1) Rule tags [ENFORCED] (every sampled file), [CONVENTION] (≥80%), [ASPIRATIONAL] (stated intent), each requiring an evidence anchor; untagged rules quarantined to ## Untagged (review). (2) Transient-state ban with regex for branches/PRs/dates/emails/sprints. (3) No partial lists — enumerate fully or see <path>. (4) Terminology + LLM-prose-tic denylist (flag, don't rewrite). (5) Weak-claims surfacing ranked zero-hit anchor > terminology > partial list > no anchor. (6) audited-against: <sha> stamp read by audit-drift-check.sh. (7) Eat-our-own-dogfood (verify-claims.sh must run). (8) Live references over restated facts.

Why it works

LLM-generated setup docs are exactly the ones that fail benchmarks. Codifying the grounding rules — and enforcing them mechanically — is what makes MTK's generated docs trustworthy rather than confidently wrong.

#.claude/references/regen-diff-contract.md

reference

The single non-destructive contract for re-running a generator over an existing file; bans union merge.

Trigger Loaded on-demand by setup-refresh, setup-audit (re-run merge), and setup-bootstrap (CLAUDE.md merge mode)

What it does

Defines exactly what happens when a generator re-runs over an existing file. It classifies each file against a cached ancestor and either overwrites (only when byte-identical to the last stock template), proposes the MTK delta hunk-by-hunk for engineer-edited files, or defers unresolvable hunks to Needs review. It guarantees the engineer's on-disk content is never destroyed.

Example

On a re-audit, architecture-principles.md differs from its cached ancestor (.claude/.mtk-cache/v<prev>/) because you edited it, so instead of overwriting, MTK computes diff ancestor fresh (the toolkit's delta, not your edits) and asks via AskUserQuestion "MTK wants to make 4 change(s) to <file>..." with options Apply all / Let me pick which / Skip all. You approve 3 and skip 1 → the run summary shows PROPOSED (4 hunks: 3 applied, 1 skipped). A separate hunk that no longer applies cleanly (you rewrote the region it targets) is never force-applied — it lands under Needs review with the conflicting region quoted, reported as NEEDS REVIEW (N hunks).

How it works

Terms: ancestor (cached stock template at .claude/.mtk-cache/v<prev>/<file>) / current (on-disk) / fresh (this run's output). §2 classification: protected→SKIP; no current→write fresh (Created); current==ancestor→OVERWRITTEN (stock); current!=ancestor→engineer-edited (§3); ancestor missing→no-ancestor (§3a, additive-only, migration question). §3 proposes diff ancestor fresh hunks each with a reason; unclean hunks → Needs review (never force-applied, no conflict markers). §4 cache-update rule (only fully-resolved files re-seed the cache). §6 invariants: git merge-file --union banned, the AskUserQuestion gate is not skippable, non-interactive defers engineer-edited files, deletion is never an outcome.

Why it works

A naive re-run either clobbers hand edits or --union-merges and silently duplicates prose lines. One shared, auditable contract makes every generator's re-run safe, reviewable, and impossible to turn into silent data loss.

#File Preservation Policy

concept

Bootstrap/refresh are additive, merge-only — they never delete a file they didn't generate; retirements are loud.

Trigger Enforced by setup-bootstrap on every write; inherited wholesale by setup-refresh

What it does

The non-destructive contract governing all setup writes. Only MTK-owned files (provenance-stamped or on a known-generated-paths list) may be overwritten in place; everything else — nested CLAUDE.md, custom rules/references, lockfiles, source — is preserved untouched. There is no "replace mode," regardless of how the skill was invoked.

Example

Running /mtk-setup on a repo that has a hand-authored packages/api/CLAUDE.md and a custom .claude/rules/team-conventions.md leaves both untouched and lists them under "Preserved hand-authored files (untouched)" in the STEP 5 report. Staging uses explicit git add <paths>, never git add -A, so scratch files can't leak into the commit.

How it works

MTK-owned = carries the <!-- mtk-setup provenance stamp OR is a known generated path (root CLAUDE.md, standard .claude/rules/*, .claude/references/*, .claude/tech-stack, .claude/settings.json, .claude/detected-tools.json, .claude/mtk-version.json, CODE_INDEX.md, pre-commit-review-list.md). product.md/decisions.md and architecture-principles.md are never overwritten once present (changes route through regen-diff-contract.md). Superseding an MTK file must be explicit and listed under "Retired prior MTK files." Nested/per-package CLAUDE.md is never overwritten or deleted, monorepo or not. setup-refresh inherits this policy and must not weaken it.

Why it works

A real rollout hit a destructive-deletion bug; a setup tool that can delete a team's hand-authored files is unusable. Making non-destruction a hard, loudly-reported contract is what lets engineers run setup on a live repo without fear.

#Repomap ranked symbol graph + Provenance

concept

A ranked symbol graph is the primary audit input; each principle must cite a symbol, disclosed in a Provenance section.

Trigger setup-audit STEP 0.5 (build graph) and STEP 3.5 (mandatory Provenance section); also used by bootstrap's inline principles generation

What it does

Before any prose, the audit builds a deterministic ranked symbol graph so principles are grounded in the codebase's structural backbone rather than vibes. Every principle must cite at least one symbol from the ranked list, and a mandatory Provenance section discloses the graph's quality tier — or admits it fell back.

Example

bash scripts/repomap.sh dotnet --budget=4000 --out=.claude/.mtk-cache/repomap.json yields fit: "ranked"; the audit then cites symbols like InvoiceCommandHandler, IMediator for a "CQRS via MediatR" principle and appends a ## Provenance table (CQRS via MediatR | InvoiceCommandHandler, IMediator, OrderQueryHandler (3 handlers, 47 refs)). If tree-sitter/LSP is unavailable (fit: "fallback"), Provenance states the audit degraded to scan-recipes-only.

How it works

STEP 0.5 runs scripts/repomap.sh <stack>; the JSON fit field (full/ranked/defer-to-mcp/fallback) sets audit behavior. When fit != fallback the prompt flips to "cite at least one ranked symbol per principle; don't claim what you can't evidence." .NET defer-to-mcp attempts csharp-lsp enrichment per file. STEP 3.5 requires the ## Provenance section (symbol-evidence table, or the fallback disclaimer when no graph is available). Tree walks honor .mtkignore via scripts/repomap-tree-sitter.py.

Why it works

An audit that reads files and free-associates produces plausible-but-wrong principles. Anchoring to a deterministic, ranked symbol graph — and forcing disclosure when that graph isn't available — keeps the architecture doc evidence-backed.

#Resumable scan ledger (workflow-artifact.sh)

concept

Long bootstraps/audits persist per-step progress to a durable artifact so a crash or compaction resumes cleanly.

Trigger setup-bootstrap STEP 1.5 and setup-audit STEP 0.9 (durable workflow artifact)

What it does

Bootstrap and audit can run long enough to hit a session compaction or crash. Both record progress to a durable workflow artifact after each numbered STEP, so a re-run resumes from where it stopped and reloads recorded outputs instead of re-scanning from zero. On large repos the ledger, not the conversation, is the working memory.

Example

A bootstrap on a 3000-file monorepo crashes after STEP 2. Re-running /mtk-setup calls scripts/workflow-artifact.sh list, finds the incomplete artifact (<24h old), and offers via AskUserQuestion "A previous setup-bootstrap run is in progress (last step: STEP 2)... Resume from STEP 2 / Start fresh / Cancel". Resume skips completed STEPs and reloads their step_completed summaries (e.g. "scanned 214 files, 3 inconsistencies").

How it works

bash scripts/workflow-artifact.sh init <type> --goal ... returns a UUID; after each STEP an event <uuid> step_completed --data '{"step":...,"outputs":...,"summary":...}' plus set <uuid> current_step=... record progress; completion sets status=completed. A resume check runs before init: an incomplete <24h artifact prompts; ≥24h auto-abandons (--reason stale-24h). Context economy: repos >1000 tracked source files process scan categories one at a time, writing each to the ledger and keeping only a 1–2 sentence summary in context. Summaries must be concrete, never vague.

Why it works

A setup that loses all progress to a compaction is unusable on the large repos that most need it. Durable, concrete per-step state makes long scans crash-safe and keeps working context small.

#.claude/references/delta-spec-model.md

reference

Each feature spec is a delta against a living per-area baseline, synced back on clean-drift archive.

Trigger Loaded on-demand by spec-driven-development and spec-drift-detection; auto-attaches on files matching docs/specs/**

What it does

Defines three artifacts: the per-feature delta spec, a canonical per-area baseline at docs/specs/baseline/<area>.{json,md}, and an audit trail at docs/specs/baseline/<area>.audit.jsonl. Each spec declares a baseline_area. On archive — only after a clean drift PASS — the delta is merged into the baseline and one audit line is appended, producing a durable specified-vs-built trail.

Example

After the webhook-retry feature ships and spec-drift-detection returns a clean PASS, bash scripts/spec-archive.sh docs/specs/2026-07-09-webhook-retry-queue.json merges its public_contracts into docs/specs/baseline/webhook.json, appends a line to webhook.audit.jsonl, and regenerates webhook.md. Re-running the same slug is a no-op (idempotent, detected via the audit trail).

How it works

The sidecar's baseline_area makes a spec archivable; optional delta.adds/modifies/removes declare changes relative to the current baseline (removals must be explicit — archiving never infers deletion). spec-archive.sh merges the change_manifest/public_contracts (or the explicit delta) into the baseline JSON, applies delta.removes, appends the audit record, and regenerates the human-readable .md. The baseline is derived, never hand-edited, and is never archived on drift NEEDS_CHANGES.

Why it works

Over many changes, "what is the system supposed to be, and does the code match?" gets lost across dozens of dated spec files. A living per-area baseline plus an audit trail keeps that answer durable — which matters in regulated/finance contexts.

#JSON sidecar / typed handoff manifest (docs/specs/<date>-<slug>.json)

concept

The machine-parseable JSON sidecar that carries spec→plan→implement and drives drift detection.

Trigger Written by spec-driven-development, extended by planning-and-task-breakdown (and later implement); validated against .claude/schemas/handoff.schema.json

What it does

Every spec ships a structured JSON sidecar carrying change_manifest, public_contracts, success_criteria (each with evidence_channel + observable), test_manifest, out_of_scope, security_impact, baseline_area, delta, assumptions, and risks. It is the source of truth for drift detection and the typed handoff that downstream skills append their plan and implement sections to — one artifact in two shapes (markdown for humans, JSON for machines).

Example

spec-driven writes docs/specs/2026-07-09-webhook-retry-queue.json with a success criterion whose observable is "exit 0 with 12/12 tests passed" and evidence_channel is "test-run". Planning appends "plan": { "batches": [...] }. After Phase 2.5 approval those success_criteria are frozen — their id/observable/evidence_channel become read-only, and verification-before-completion runs a tamper check before accepting any pass.

How it works

Validated against .claude/schemas/handoff.schema.json (required keys: slug, date, scope, change_manifest, success_criteria, security_impact). Every change_manifest entry must be intended (no speculative files); security_impact must NOT be none if the diff touches auth/payments/audit/secrets/PII/IAM (spec-drift-detection blocks understated impact). assumptions/risks carry provenance tags [VERIFIED:path] / [ASSUMED] / [CITED:url] — an [ASSUMED] claim counts as an open decision that blocks MTK_AUTO_PROCEED. Every plan files entry must already exist in the top-level change_manifest, or planning must re-plan rather than widen scope.

Why it works

Keeping the same declared contracts in a machine-readable shape lets the loop stay honest end-to-end: what the spec declares is exactly what drift detection verifies at ship time, and a frozen success_criteria set prevents quietly moving a goalpost to manufacture a pass.

#docs/specs/<date>-<slug>.json (typed handoff sidecar)

concept

The machine-readable spec sidecar that threads scope, contracts, criteria, and batch results through the whole loop.

Trigger Written by spec-driven-development at Phase 1, appended to by the implementation skills, read by drift/verification; schema at .claude/schemas/handoff.schema.json.

What it does

The structured backbone the entire implementation loop reads and writes. It carries the fields that let each phase be deterministic instead of judgment-based — scope, change_manifest, public_contracts, success_criteria, out_of_scope, security_impact, and an implement block appended during the build. It's what makes drift detection and criterion-by-criterion verification possible.

Example

spec-driven-development writes the sidecar at Phase 1; the Rigor Score is computed from it; incremental/subagent implementation append implement.actual_files, completed_batches, deviations, and behavioral_diff; scripts/validate-handoff.sh docs/specs/<date>-<slug>.json diffs the manifest to surface drift; spec-drift-detection reads it at Phase 3.5; verification-before-completion reads success_criteria[] from it (and diffs it for the tamper check); Phase 7.5 archives its delta via scripts/spec-archive.sh.

How it works

Structure is governed by .claude/schemas/handoff.schema.json. The implement section is required to include actual_files (from git diff --name-only <base>...HEAD), completed_batches, honest deviations, and behavioral_diff. success_criteria carry id/description/evidence_channel/observable, frozen at Phase 2.5. On the subagent path each batch result is appended to sidecar.implement.completed_batches[]; scripts/validate-handoff.sh validates the shape and computes file/security drift before review.

Why it works

A single structured source of truth is what lets downstream gates compare two lists instead of trusting a summary — drift findings are high-confidence 'by construction', verification checks a frozen criteria list rather than memory, and the tamper check can prove the goalposts didn't move because the sidecar was committed at approval.

#review-finding-schema.md

reference

Canonical output contract for all reviews — markdown table + JSON, confidence rubric, thresholds, verdict mapping.

Trigger Loaded on-demand by every review skill and reviewer agent; defines the shared output contract they all emit.

What it does

The single format every review emits: a human-readable markdown findings table plus a trailing fenced JSON block, where the JSON is the source of truth. It defines the finding fields (severity, confidence, rule, source, failure_mode, decision_origin), the scores object, the confidence rubric, the default 80 threshold, the empty-findings anti-sandbagging rule, the False-Positive Exclusion List, and the verdict mapping.

Example

The confidence rubric bands run 95-100 'Deterministic' (regex-detected secret, missing [Authorize]) down to '< 50 Below report floor — do not report.' The anti-inflation rule forbids promoting an honest 65 to 80 just to clear the ≥2-findings bar; if findings[] has fewer than 2 entries, below_threshold_rationale is mandatory.

How it works

JSON envelope carries findings[], scores{}, summary{}, and below_threshold_rationale. The source field distinguishes ai / linter / analyzer / context (linter and analyzer always confidence 100). Threshold comes from .claude/review-config.json thresholds.default (per-engineer override in .claude/review-config.local.json); only confidence >= threshold surfaces, and below-threshold findings count in summary.filtered_below_threshold. A 7-category FP Exclusion List drops non-findings before scoring. Verdict mapping: any critical at threshold, any gate: "mandatory", any scores.<dim>.value < 7, or uniform scores → NEEDS_CHANGES; otherwise PASS. Also houses the scores block and decision-origin tagging specs.

Why it works

Without one contract, deterministic, AI, and context findings could not be merged, deduped, or aggregated, and 'zero findings' could hide a lazy review. A fixed schema makes every source comparable and forces a written rationale for near-empty reviews.

#.claude/references/learnings-schema.md

reference

Defines the structured learning entry — fields, enums, provenance, retrieval layers, and the optional executable contract.

Trigger Loaded on-demand by the capture/promote skills and learnings.sh; auto-attaches on .mtk/learnings.jsonl and tasks/lessons.md

What it does

The canonical schema for a learning entry in .mtk/learnings.jsonl. It specifies every field and its meaning: id, scope, source (correction/promotion/manual/review-finding/incident/golden-path), decision_origin, severity, memory_type (episodic/semantic/procedural), supersedes, validity, recurrence, wrong_turns, time_cost, evolution_actions, and the optional v7.25 contract fields. It also documents the 5-layer retrieval filter, migration rules, and the sycophancy index formula.

Example

A skill author unsure what decision_origin values are allowed reads this file and sees the enum (user-directed, claude-recommended-approved, claude-recommended-modified, claude-recommended-rejected, system-inferred) plus the note that it is required at emit time for capture sources — entries missing it are rejected by learnings.sh add, while deterministic sources (manual/migrate/incident) default it to system-inferred — and that it feeds the sycophancy index π = approved / (approved + modified + rejected).

How it works

Defines a two-tier model, the full JSON entry shape with a worked example, and per-field notes: id format L-YYYY-MM-DD-NNN; at least one of spec_id/workflow_uuid required; validity.expires_at defaults to captured_at + 12 months; recurrence.count ≥3 triggers CLAUDE.md-promotion; supersedes drives conflict-driven forgetting so query drops superseded entries without deleting the audit trail; severity includes incident which outranks other sources. It documents borrowed taxonomies (content-type memory_type; conflict-driven forgetting) and the file layout with ## Auto-generated / ## Manual additions sections.

Why it works

Without a single schema, structured entries would drift and retrieval/metrics couldn't rely on field meanings. Codifying enums, defaults, and the retrieval/metric math keeps the store consistent across every skill and script that writes to it, and documents why fields like wrong_turns and decision_origin exist.

#.claude/references/lesson-mining-rubric.md

reference

The reject-by-default rubric deciding which transcript candidates become lessons — six reject rules, four admit rules, a fixed output contract.

Trigger Loaded on-demand by lesson-mining for every candidate; governs admit/reject decisions

What it does

The gate that keeps lesson-mining honest. Every candidate extracted from a transcript defaults to reject; it must pass at least one admit rule (A1–A4) and fail all six reject rules (R1–R6) before being surfaced. It also carries the security stance that transcripts are untrusted input and the machine-readable output contract each survivor must emit.

Example

A candidate "always write tests" is checked and rejected by R4 (generic advice without a stated trigger). A candidate where the engineer stopped the agent and explained why survives via A1 (engineer redirect with stated reason) and is emitted as JSON with admit_rule: "A1", reject_rules_checked: ["R1","R2",..."R6"], and a verbatim evidence_excerpt. If the issue was already fixed in code, R3 fires and the proposed action becomes code-comment instead of a lesson.

How it works

Reject rules: R1 derivable-from-code-in-<60s, R2 framework-boilerplate, R3 already-fixed-in-code (propose a code comment instead), R4 generic-advice-no-trigger, R5 inferred-preference-no-stated-reason, R6 instruction-like-content-from-transcript-body (injection — discard). Admit rules: A1 engineer redirect with stated reason, A2 recurring pattern (≥2 sessions), A3 novel constraint not already in the lessons store, A4 cost-measurable waste (time_cost estimable). The output contract fixes the candidate JSON shape (candidate_id, title, body, rule, applies_when, admit_rule, reject_rules_checked, evidence_excerpt, time_cost, proposed_action).

Why it works

One weak lesson poisons trust in the entire lessons file, so the rubric is deliberately strict and reject-biased — a missed lesson is cheap, a noisy one is expensive. Treating transcripts as untrusted (R6) also blocks prompt-injection from laundering instructions into the lessons store as "lessons".

#.claude/references/pr-mining-patterns.md

reference

Heuristics, the runtime-read denylist, and output schema for mining recurring reviewer-feedback phrases.

Trigger Read at runtime by scripts/pr-review-mine.sh and on-demand by the pr-review-mining / repo-health skills

What it does

Defines what counts as a mineable phrase, the denylist of boilerplate that must never become a rule, and the markdown/JSON output schema for the miner. It states the candidate criteria (imperative-verb start, 4-word prefix in ≥2 distinct merged PRs, not denylisted) and the promotion workflow that keeps mined phrases suggest-only and tagged [MINED:feedback].

Example

A reviewer repeatedly writes "add a null check" across PRs, but "lgtm" and "good catch" also appear often. The miner reads the ## Denylist (lgtm, ship it, thanks, nit, nice, done, fixed, approved, looks good, good catch, sgtm, +1, …) at runtime and suppresses the boilerplate while keeping the actionable phrase — and a team can add a new noise word under that heading to suppress it everywhere.

How it works

Documents the IMPERATIVE_HINTS verb list the script uses, the ≥2-distinct-PR threshold, and that only PR review summaries and per-line comments are mined (not issue comments). The denylist section is machine-read: lines under ## Denylist are consumed by the script until the next ## heading. Provides the markdown output table and the --json schema (status ok|skipped, scanned_prs, phrases[]), noting skipped with empty phrases when gh is unavailable. The promotion workflow requires human review, the [MINED:feedback] tag, and cited PR numbers.

Why it works

Auto-promoting mined phrases would amplify reviewer biases and one-time disagreements into team-wide rules, so human review is the mandated filter. A runtime-editable denylist and an explicit schema keep the miner's output stable, noise-suppressible, and never silently authoritative.

#scripts/mtk-savings.sh

script

Reports MTK's context-token footprint — always-on floor vs. deferred, review offload, and output compression.

Trigger Bash script: bash scripts/mtk-savings.sh; linked from context-report's Reference Footprint section

What it does

Quantifies MTK's own context cost and the savings its architecture delivers, with every number derived from the installed files or the on-disk compression log — nothing from telemetry MTK does not collect. It separates the always-on floor (loaded every session) from everything progressive disclosure keeps out, plus review offload to subagents and on-demand skill bodies. It also reports real output-compression savings when a log exists.

Example

bash scripts/mtk-savings.sh prints an 'Always-on — loaded into EVERY session' block (skill descriptions + CLAUDE.md + rules/INDEX.md + agent descriptions, summed into an 'always-on floor'), a 'Kept OUT of always-on by progressive disclosure' block with a 'deferred total ← would be always-on if inlined into CLAUDE.md', and a summary line: "MTK keeps ~N tok out of your always-on context (deferred + review offload) … and holds the always-on floor to ~M tok/session."

How it works

It approximates ~4 chars/token. Always-on = the sum of every skill description + agent descriptions + CLAUDE.md + .claude/rules/INDEX.md bytes. Deferred (kept out by progressive disclosure) = .claude/references/*.md (glob-gated) + rule bodies excluding INDEX.md (path-gated) + manifest.json (MCP-gated, never inlined). Review offload = total agent-body bytes (run in isolated subagent context, never entering the main thread). On-demand = all SKILL.md bodies (one loads per skill run). The output-compression section reads .claude/observability/compression.jsonl (written by scripts/mtk-compress.sh) via python3, reporting all-time and current-session (CLAUDE_CODE_SESSION_ID) tokens saved and ratio, or a hint if the log is absent.

Why it works

The most common objection to a large toolkit is "it eats my context window." This script makes MTK's context economics auditable and honest — showing that references, rule bodies, the manifest, and review all stay out of the always-on floor via progressive disclosure and subagent offload — so teams can see the real per-session cost instead of assuming the worst.

#mtk_session_file (shared per-session state)

concept

Per-project, per-day temp file that accumulates read/edit/op counters shared across the budget and analytics hooks.

Trigger Internal helper in hooks/lib/hook-io.sh; read/written by context-budget.sh and session-analytics.sh (capture-learnings.sh reads a same-named file but recomputes the path independently from pwd)

What it does

The deterministic hooks share one state file so counting is consistent across them. It is keyed by repo root and today's date, so counters reset each day and never mix projects. context-budget.sh writes it on every tool call; session-analytics.sh reads it at session end to summarize the session. capture-learnings.sh also reads a same-named file at session end, but it computes the path from pwd rather than through this helper, so the two agree only when the working directory is the repo root.

Example

After running dotnet test and editing a few files, the state file (e.g. $TMPDIR/mtk-context-budget-1837465012-20260709) contains:

ops=57
mods=8
bytes_read=412000
last_verification_command='dotnet test'
warned_files=1
How it works

mtk_session_file builds the path as ${TMPDIR:-/tmp}/mtk-context-budget-<repo-root-cksum>-<YYYYMMDD> (repo root via git rev-parse --show-toplevel, falling back to pwd). mtk_load_session_state / mtk_save_session_state round-trip the fields (reads, files, mods, ops, bytes_read, warned_* flags, event_seq, last_edit_*, last_verification_*, scope_guard_warnings, benchmarks_run, benchmark_last_score) using escaped single-quoted values plus atomic temp-file rename. mtk_session_lock_acquire/release use an mkdir-based advisory lock (no flock dependency) so concurrent PreToolUse/PostToolUse firings don't lose updates to last-writer-wins races. mtk_command_is_verification classifies build/test commands as verification evidence.

Why it works

Without a single shared, atomic, race-safe state file, counters would be lost to concurrent hook firings and the Stop-time analytics/learning hooks would have no reliable record of a session they didn't observe themselves. Daily/per-project keying keeps stats scoped and self-cleaning.

#context-engineering

skill

Loads the minimum relevant context per phase and resets deliberately before quality degrades.

Trigger loaded on-demand (frontmatter user-invocable: false) — activates at session start, phase switch, entering unfamiliar code, or when output drifts from project norms

What it does

Treats context — the full information payload the model sees at generation time — as something to engineer, not accumulate. It loads only what the current step needs, tracks how much has been loaded, watches for degradation, and forces a clean reset before compaction fires mid-edit. It gives MTK a shared vocabulary (Write / Select / Compress / Isolate) for the moves the toolkit already makes.

Example

At the end of Phase 0 of an implement run the skill emits a one-block footprint so the engineer sees the cost of what loaded:

Context footprint (Phase 0):
  security-checklist.md                         78 lines  (~2k tokens)
  testing-patterns.md                          112 lines  (~2k tokens)
  dotnet/coding-guidelines.md                  195 lines  (~3k tokens)
  Total: 3 files, 385 lines (~5k tokens)

Later, after batch 2 finishes green and usage crosses ~40% of the window, it resets at that clean boundary — re-anchoring on the goal and the files now in scope — instead of riding the budget up to a mid-batch forced compaction.

How it works

Four operations map to concrete MTK mechanisms: Write (auto-memory + tasks/lessons.md, workflow-artifacts, handoff), Select (rules INDEX.md wake-up layer + path-scoped applyTo reference loading), Compress (output-compression.md / mtk-compress.sh), Isolate (subagent-implementation one implementer per batch, review agents via context: fork). Loading workflow: CLAUDE.md first, then only task-relevant references, then read the target file plus 2-3 neighbors. It tracks four weighted context-fatigue signals — token utilization (40%), scope scatter (25%), re-read ratio (20%), error density (15%) — and when 2+ are elevated it prunes, re-summarizes, or escalates to handoff. Budget guidance: CLAUDE.md 60-80 lines (cap 120), each skill 60-120 lines, no more than 3 skills loaded at once. The context-budget hook nudges when estimated consumption passes MTK_CONTEXT_BUDGET_PCT% (default 60) of MTK_CONTEXT_WINDOW_TOKENS (default 200000); that 60% is the hard floor, the ~40% proactive reset is the target. A rot-symptom override (re-reading, re-asking, contradicting a prior decision, reintroducing rejected code) forces a reset regardless of the token count. Model routing reserves opus for real logic and adversarial reviews, running discovery/planning/comparison on sonnet/haiku (full policy in model-routing.md). Reference reads are issued as parallel Read calls in a single message.

Why it works

Good output depends on good context, and more context is not better — irrelevant context crowds out the rules that actually matter. Quality degrades long before the window fills, and tool-forced compaction tends to fire at the worst possible moment (mid-phase, mid-edit); resetting deliberately at a clean boundary keeps later work sharp.

#applyTo path-scoped reference loading

concept

References auto-load only when a touched file matches their applyTo glob — no speculative loading.

Trigger automatic inside context-engineering — touched files (from the spec's change_manifest or git diff --name-only HEAD) are matched against manifest applyTo globs; MCP-first via mtk_resolve_references, bash fallback otherwise

What it does

Reference entries in .claude/manifest.json can declare an applyTo glob array. When the current task has a known set of files in scope, this mechanism loads only the references whose globs actually match one of those files and skips the rest. References without an applyTo (like coding-guidelines) stay always-on, loaded on demand per phase.

Example

A spec's change_manifest lists src/Features/Users/CreateUserHandler.cs. context-engineering calls the mtk_resolve_references MCP tool with that file list; it returns deterministic matches — the .NET mediatr-slice-patterns.md reference activates because its applyTo globs (/Features/, /Handlers/, /*Handler.cs) match, while every Python reference (globs like /models.py, **/views.py) matches nothing and is never loaded. If the scope grows mid-session, the match is re-run against the new file set.

How it works

When file scope is known, the skill prefers the mtk_resolve_references MCP tool, which returns deterministic matches against the manifest's applyTo arrays. If the MCP tool is unavailable it falls back to testing each touched file against the globs with bash case / fnmatch semantics. git diff --name-only HEAD is the authoritative touched-file list when in doubt. A reference whose globs match nothing is explicitly NOT loaded 'just in case' — that would defeat the budget.

Why it works

Front-loading every reference dilutes the instructions that matter for the current task and burns context budget. Tying reference activation to the files actually in scope keeps the loaded set minimal and relevant, deterministically.

#.claude/rules/INDEX.md (rule wake-up layer)

concept

A token-budgeted rule index read first; full rule files load only when their axes or paths match the task.

Trigger always-on layer — read INDEX.md first every session; pull a full rule file only when its axes or paths match the active task

What it does

Instead of loading every rule file eagerly, MTK keeps a compact index — one line per rule file — that is cheap enough to always read. Each line carries three axes plus a path-glob list, so the agent can decide which full rule files are worth their tokens before spending them.

Example

About to edit a file under hooks/, the agent reads INDEX.md and sees hooks-and-scripts.md tagged decision: process, topic: hooks, paths: hooks/; scripts/. Because the touched path matches hooks/**, it pulls that one 44-line rule file and leaves the git, skills, and manifest rule files unread. During a security pass it can instead pull every rule tagged decision: security even when no path matched.

How it works

INDEX.md lists each rule file with axes — decision (structure|process|authoring|security), topic (manifest|skills|hooks|git|…), scope (global|project) — plus a paths glob list, rule count, and line count, targeting under 60 lines total. A rule whose paths glob matches a touched file is always relevant (machine-scoped auto-attach); the axes let you additionally pull rules by intent. The file is generated by scripts/build-rule-index.sh and must never be hand-edited; CI runs --check and fails on a stale index.

Why it works

Loading all rule files 'just in case' defeats the context budget — the whole point is to decide relevance cheaply first. The index makes the rule layer scale as rules are added without every session paying for all of them.

#handoff

skill

Captures session state into a <100-line recovery artifact so a fresh session resumes without re-discovery.

Trigger loaded on-demand (frontmatter user-invocable: false) — when context nears its limit, before ending a long session with in-progress work, on teammate handoff, or on a mid-feature branch switch; also on 'save state' / 'snapshot' / 'hand off'

What it does

Writes a small markdown artifact that records the branch, in-progress work, decisions, blockers, and key files so the next session — yours after compaction, a new conversation tomorrow, or a teammate — can pick up cleanly. The artifact is a pointer, not a transcript: it says where to look, not what every step was.

Example

Context is nearing its limit mid-feature. The skill gathers git branch, git log --oneline -10, git status --short, the active spec path, and today's lessons, then writes docs/handoffs/2026-07-09-user-profile-page.md (under 100 lines) with sections Branch / Active Spec / Goal / Completed / In Progress / Decisions Made / Blocked or Needs Attention / Key Files / Resume Instructions, adds docs/handoffs/ to .gitignore, and tells the engineer: 'Start a new session, read docs/handoffs/2026-07-09-user-profile-page.md, then continue.'

How it works

Gathers state (current branch, recent commits, uncommitted changes, tasks/todo.md, the active spec/plan from docs/specs/ and docs/plans/ recorded as the full path incl. version suffix into spec_path, today's tasks/lessons.md entries), then reads the four context-engineering fatigue signals to decide whether the handoff is a 'clean snapshot' or a 'salvage' — leading with elevated signals so the next session knows what to mistrust. Writes docs/handoffs/YYYY-MM-DD-<slug>.md (today's real date via date +%Y-%m-%d) with fixed sections and an optional Fatigue Signals block, keeps it under 100 lines with no code blocks over 5 lines, and ensures docs/handoffs/ is gitignored. The post-compact hook (SessionStart matcher "compact") re-injects handoff artifacts after auto-compaction (per rule S3.11). After writing the handoff it records results.handoff_path and publishes/updates the workflow Artifact in place so a teammate opening the one URL sees current state (standalone publish if no workflow is active); disk-first, capability-gated, silent no-op when unavailable or MTK_ARTIFACT_PUBLISH=0.

Why it works

Orchestration and reasoning state that lives only in chat is destroyed by compaction. A git log shows what was committed, not the in-progress work or the why behind decisions — a 100-line pointer beats a 5,000-token transcript because the next session has tools to re-load the detail.

#workflow-artifacts

skill

Persists orchestration state to .mtk/workflows/ so a workflow survives compaction, crash, and handoff.

Trigger loaded on-demand — at the start of every BUILD/DEBUG/REVIEW/PLAN/FIX workflow, at each phase transition, and at every named gate; all state driven through scripts/workflow-artifact.sh

What it does

Stores the state of a running workflow — phase cursor, gate decisions, spec/plan/todo paths, per-criterion verification status — on disk instead of in chat. Other workflow skills (implement, fix, planning-and-task-breakdown, spec-drift-detection) call it at phase boundaries and gate decisions so the orchestration can resume after a crash or compaction.

Example
MTK_WF_UUID=$(scripts/workflow-artifact.sh init BUILD --goal "Add user-profile page")
scripts/workflow-artifact.sh gate "$MTK_WF_UUID" phase_exit_gate pass --reason "all batch tests green"

After a crash, a fresh session runs scripts/workflow-artifact.sh list, finds the single active BUILD workflow sitting at phase-3, and offers to resume it — reconstructing the run's state from disk with no chat history.

How it works

Each workflow is two files under .mtk/workflows/: {uuid}.json (current state, overwritten in place) and {uuid}.events.jsonl (append-only event log), with workflow_uuid in the form wf-YYYYMMDDTHHMMSSZ-{6 hex}. ALL reads/writes go through scripts/workflow-artifact.sh (init / set / event / gate / criteria / abandon / list / read) — never Edit/Write the JSON directly, or the event log desyncs. Five named gates are persisted (plan_trust_gate, phase_exit_gate, failure_stop_gate, memory_sync_gate, skill_precedence_gate); a failure_stop_gate: fail terminates the workflow. criteria_status tracks each success criterion (SC1, SC2, …) as pending | verified | re-armed, and the re-arm rule flips every verified criterion back to re-armed after any Edit/Write, so verification-before-completion rejects a completion claim unless all criteria are verified. Event types cover the full lifecycle (workflow_started, phase_started/completed, gate_decided, agent_dispatched/returned, remediation_*, workflow_completed/failed). .mtk/ is gitignored by setup-bootstrap; only the orchestrator writes artifacts — subagents return structured output and the orchestrator persists it via an agent_returned event.

Why it works

Auto-compaction silently destroys conversation-resident state; init costs one line but its value is realized exactly when something fails. On-disk artifacts let /mtk implement resume after crash/handoff and give review and post-mortem an objective trace independent of the transcript, testable via JSON fixtures.

#.claude/references/artifact-publishing.md

reference

Additive, capability-gated procedure for publishing a workflow's outputs (spec/plan/handoff/health) as one in-place-updated Claude Artifact.

Trigger Followed by spec-driven-development / planning-and-task-breakdown / handoff / repo-health after each writes its output to disk

What it does

The single source of the artifact-publishing procedure so each skill stays a thin navigation layer. Workflow skills persist their human-facing outputs to disk (the machine pipeline — drift, approval seal, EARS lint, plan-gap review, recovery, baseline — reads local files). This reference tells them how to ALSO publish those outputs as one browsable Claude Artifact per workflow run, keyed to workflow_uuid and updated in place across phases, so the engineer gets one stable URL rendered for review outside the terminal.

Example

spec-driven-development creates the artifact from the spec; planning-and-task-breakdown adds a Plan section by re-publishing the same file with the recorded results.artifact_url; handoff and repo-health add their sections. One link fills in as the workflow progresses — Spec -> Plan -> Handoff -> Health.

How it works

Gate (both required, else silent no-op): the Artifact tool is present in the harness (Claude Code / claude.ai; cursor/codex are disk-only) AND MTK_ARTIFACT_PUBLISH != 0. Procedure: resolve the workflow uuid ($MTK_WF_UUID, else the single active workflow, else publish standalone); record the output path on the workflow artifact; assemble the rollup via scripts/workflow-artifact-md.sh; publish the rollup, passing the recorded results.artifact_url as url to update in place when one exists; persist the returned URL with workflow-artifact.sh set. Disk is written first and unconditionally — publishing never gates or replaces it. Nothing beyond the named on-disk workflow outputs is ever published (no secrets, no settings.local.json, no transcript).

Why it works

A hosted artifact can't back the machine pipeline (it can't be hashed for the approval seal or diffed for drift), so disk must stay the source of truth — publishing is purely an additive review surface. Capability-gating keeps cursor/codex working unchanged, and the MTK_ARTIFACT_PUBLISH opt-out lets regulated repos refuse the data egress that publishing to claude.ai entails.

#scripts/workflow-artifact-md.sh

script

Assembles a single markdown rollup of a workflow's on-disk outputs (spec/plan/handoff/health) for publishing as a Claude Artifact.

Trigger Bash: scripts/workflow-artifact-md.sh <uuid> -> writes .mtk/workflows/<uuid>.artifact.md, prints its path

What it does

The deterministic assembler behind artifact publishing. It reads a workflow's JSON, then concatenates — with section headers and a title from intent.goal — whichever recorded source docs currently exist on disk (results.spec_path / plan_path / todo_path / handoff_path / health_report_path). Missing paths are skipped, not errors, so the rollup always reflects exactly what exists at assembly time. Output overwrites .mtk/workflows/<uuid>.artifact.md in place.

Example

With only a spec recorded, scripts/workflow-artifact-md.sh <uuid> emits a rollup containing just the Spec section (exit 0); once a plan is written and recorded, a re-run adds the Plan section — no code change, just the current on-disk state.

How it works

bash + python3 (within the S3.3 baseline), set -euo pipefail, executable (S3.2). It resolves paths relative to the repo root, skips any recorded path that isn't a file on disk, and emits a placeholder section when nothing exists yet. It never writes anything except the derived rollup under gitignored .mtk/, and never mutates the workflow JSON or the source docs.

Why it works

Keeping assembly in one deterministic script (rather than model-driven concatenation) makes the published artifact reproducible and keeps the publishing skills thin — they record a path and call one helper. Tolerating missing paths is what lets the same rollup be re-published in place at every phase without special-casing which sections exist yet.

#research-context

skill

Runs grounded web research and returns a small cited brief with per-claim provenance tags — never edits code.

Trigger loaded on-demand (frontmatter user-invocable: false) — on version-sensitive / current-best-practice / migration decisions; keyword triggers best-practice, latest-version, upgrade-guide, migration-guide; or engineer says 'research X'

What it does

Answers the forward-looking question 'given what this repo already does, what is the current best way to do X?' by pulling fresh external information (release notes, current best-practices, migration paths, security advisories) and grounding it against named files in this repo. It is MTK's research-mode primitive: research is a first-class workflow step, not an ad-hoc web search. It produces a brief and stops — another skill acts on it.

Example

A spec hinges on whether an EF Core API is current for the installed version. The skill first reads Directory.Packages.props for the exact version and names an existing repo file that uses that area, does ~3 WebFetches, then emits a brief with lines like [CITED:https://…] the fluent API replaces attributes in v8, [VERIFIED:Directory.Packages.props:12] EF Core 8.0.x installed, and one [ASSUMED] the old pattern still compiles. That [ASSUMED] claim, once it reaches the spec, blocks MTK_AUTO_PROCEED at the implement Phase 2.5 gate until a human confirms it.

How it works

Step 1 frames the question against the repo — naming the active tech stack (.claude/tech-stack), the installed library version read from the lockfile/project file, and 1-3 grounding files (an ungrounded brief is marked as such). Step 2 chooses depth from a table: the light path uses WebSearch + WebFetch directly (2-4 fetches) or the context7 MCP for library docs; the deep path delegates to the built-in /deep-research workflow (via the Skill tool) for multi-angle/high-stakes questions rather than hand-rolling a fan-out. Step 3 classifies each finding (verified-from-source / applies-to-this-repo / conflicts-with-repo / still-uncertain). Step 4 emits a compact Markdown brief where every claim carries exactly one mandatory provenance tag — [CITED:<url>], [VERIFIED:<file:line>], or [ASSUMED] — the [ASSUMED] tag being load-bearing because it blocks MTK_AUTO_PROCEED downstream. Step 5 persists a research_brief event when $MTK_WF_UUID is set. A written phase-locked tool-discipline note pins it to Read/Grep/Glob + web tools — never Edit/Write of source or test code, the only artifact it may write is its own brief (it is deliberately NOT locked to the read-only toolset because it needs WebSearch/WebFetch) — and it runs on sonnet. Its brief is consumed by spec-driven-development (version-sensitive ambiguity gate, step 6) and implement (Phase 3).

Why it works

Model memory has a training cutoff while libraries ship versions constantly — a recommendation for the wrong major/minor can be an anti-pattern in the installed one, so a version-matched, cited answer beats a confident guess. Surfacing repo-vs-best-practice conflicts (rather than silently rewriting) keeps the decision with the spec/plan and a human.

#using-git-worktrees

skill

Creates an isolated git worktree safely — gitignore-checked, deps installed, baseline tests green first.

Trigger loaded on-demand (frontmatter user-invocable: false) — for feature isolation, parallel development, experiments, or a clean rollback path

What it does

Sets up an isolated workspace for feature work or experiments without the overhead of a full clone, and does it safely: it verifies the worktree location is gitignored, installs the right dependencies for the stack, and proves the baseline tests pass before any new code is written.

Example
git worktree add .worktrees/user-auth -b feat/user-auth

Before running that, the skill confirms .worktrees/ is in .gitignore (adding and committing it first if not). After creating the worktree it auto-detects the stack, runs dotnet restore, runs the project's test command, and reports '42 passing (baseline clean), worktree at .worktrees/user-auth on branch feat/user-auth' — so any later failure can't be blamed on pre-existing breakage.

How it works

Determines the location by checking for .worktrees/, worktrees/, or .claude/worktrees/ (Claude Code's native location) and CLAUDE.md preferences, defaulting to .worktrees/. Verifies gitignore safety and commits the .gitignore change before creating anything. Creates via git worktree add <path> -b <branch-name> with feat/<slug> or fix/<slug> naming (S4.1). Auto-detects and installs dependencies by project file (*.csproj/*.sln → dotnet restore, package.json → npm install, requirements.txt/pyproject.toml → pip/poetry install, go.mod → go mod download, Cargo.toml → cargo fetch), then runs baseline tests. Finishing offers four options — merge locally, push and create PR, keep as-is, or discard (requires typed 'discard') — cleaning up only options 1 and 4 via git worktree remove <path> and git branch -d. Resolves the lessons path (tasks/lessons.md) against the main worktree using git rev-parse --git-common-dir.

Why it works

Isolation keeps feature work and experiments from contaminating the main branch and gives a clean rollback path. A fresh worktree has no node_modules/bin/obj/venv, so installing deps and proving the baseline green up front prevents new work being blamed for pre-existing failures.

#writing-skills

skill

Meta-skill for authoring an MTK skill: observe baseline failures, write the minimum, pressure-test, then register.

Trigger Loaded on-demand when creating or substantially rewriting a skill (frontmatter is user-invocable: false); reached by describing the task to /mtk

What it does

A skill is a reusable behavior contract that shapes how the agent approaches a category of work. writing-skills is the process you follow to add or rewrite one: watch the agent fail at the task without the skill first, write only enough documentation to close those specific gaps, then prove the skill resists pressure before it ships. It also enforces the shared skill anatomy and the CSO (Condition-based Skill Descriptions) principle that every MTK skill must follow.

Example

You want a skill that stops the agent from skipping migration review. writing-skills makes you run 2-3 real scenarios WITHOUT the skill first and record what got skipped and which rationalizations were used (the 'RED' baseline). You then write a <500-line SKILL.md, save adversarial scenarios to tests/pressure-tests/migration-review-pressure.md, add the skill to .claude/manifest.json with action sync, and run bash scripts/validate-toolkit.sh until it reports the skill passes.

How it works

Five phases: Phase 1 Observe Baseline Failures (document skipped steps + rationalizations = the RED baseline); Phase 2 Write The Minimum Skill (anatomy from docs/skill-anatomy.md — frontmatter + ## Overview/## When To Use/## Workflow/## Verification; apply the CSO principle so the description triggers on conditions, not a workflow summary; reference the shared rationalization table in context-engineering rather than duplicating it; keep it under 500 lines using progressive disclosure — S2.26 'navigation layer, not a payload'); Phase 3 Test Under Pressure (3-5 adversarial scenarios — time/simplicity/authority/precedent — saved to tests/pressure-tests/<skill-name>-pressure.md); Phase 3b Add Evals if the skill gates shipping (evals/<skill-name>/ with positive/negative/adversarial scenarios + grader.md, run via bash scripts/run-evals.sh); Phase 4 Register (add to .claude/manifest.json, update AGENTS.md routing, rebuild the triggers index with bash scripts/build-triggers-index.sh if it declares triggers:, run bash scripts/validate-toolkit.sh). A dedicated 'Cache-Stable Prefixes' section tells reviewer/entry-point authors to put invariants first and inject volatile state (branch, diff) last so prompt caching hits.

Why it works

Skills written speculatively (without observing failures) either get ignored or are gamed by rationalizations. This process forces evidence-first authoring, condition-based triggering so the agent reads the whole skill instead of following a summary, and pressure/eval coverage so a discipline skill actually holds when the agent is under pressure to skip it.

#tech-stack-{name} (selected via .claude/tech-stack)

concept

The pluggable per-stack layer: workflow skills stay language-agnostic, one tech-stack skill injects stack-specific context.

Trigger Loaded automatically by workflow skills and review agents when .claude/tech-stack names the stack; the file is written during setup-bootstrap; not invoked directly

What it does

MTK's workflow skills (spec-driven-development, incremental-implementation, test-driven-development) and review agents are language-agnostic. All the stack-specific knowledge — build/test commands, ORM rules, framework patterns, coding-style path, detection markers, and pre-commit items — lives in one tech-stack skill loaded on demand. Which one loads is decided by the .claude/tech-stack file. This is the seam where you add support for a new language.

Example

A repo with .claude/tech-stack containing dotnet gets tech-stack-dotnet loaded whenever a workflow skill runs, so verification uses dotnet build / dotnet test. Switch the file to python and the same workflow skills now verify with pytest / mypy . and pull SQLAlchemy rules — no change to the workflow skills themselves.

How it works

Tech-stack skills carry type: tech-stack frontmatter and follow the declarative S2.13 anatomy (Build & Test Commands, File Extensions & Markers, ORM & Data Layer Guidance, Framework Patterns, Test Level Guidance, Coding Style Reference, Reference Files, Settings Additions, Format Command, Scan Recipes) — the ## Workflow section is omitted because they are declarative, not procedural. Each declares its detection markers (used by setup-bootstrap), its .claude/references/{stack}/ reference list, allowedTools/deny merges for .claude/settings.json, and a PostToolUse formatting hook (hooks/format-on-edit.sh). To add a stack you author a new tech-stack-{name}/SKILL.md and its .claude/references/{name}/ files; setup-bootstrap detects it and writes the marker into .claude/tech-stack.

Why it works

Without this seam every workflow skill would need per-language branches, and adding a stack would mean editing dozens of skills. Isolating stack knowledge behind a single loadable contract keeps the workflow layer stable and makes new-language support a matter of writing one skill plus its references.

#tech-stack-dotnet

skill

.NET/C# context: build/test commands, EF Core + MediatR rules, Roslyn analyzers, and the dotnet-claude-kit companion.

Trigger Loaded automatically when .claude/tech-stack contains dotnet; not invoked directly

What it does

Provides .NET-specific build and test commands, EF Core and MediatR/CQRS guidance, coding-style conventions, detection markers, and reference paths to the generic workflow skills and review agents. It is the first-class stack in MTK and includes analyzer-capture wiring and an optional Roslyn MCP companion plugin.

Example

During implementation the agent verifies with dotnet build and dotnet test. On a .slnx solution it knows a bare dotnet test <slnx> --list-tests fails and instead runs dotnet test --solution <slnx> --list-tests. Each edited .cs file is auto-formatted by the PostToolUse hook, which runs dotnet format --include <file> --verbosity quiet.

How it works

Declares markers (*.sln/*.slnx/*.csproj High, global.json/Directory.Build.props Medium) for setup-bootstrap detection; ships EF Core rules (AsNoTracking() on reads, .Select() over Include(), async query methods, one SaveChanges per handler) and MediatR slice conventions; points to references under .claude/references/dotnet/ (coding-guidelines, ef-core-checklist, mediatr-slice-patterns, testing-supplement, performance-supplement, analyzer-config). Formatting is wired via hooks/format-on-edit.sh (matcher Edit|Write, dispatch by tool_input.file_path from stdin JSON — NOT $CLAUDE_FILE). Analyzer output is captured with dotnet build 2>&1 | tee /dev/tty | hooks/parse-build-diagnostics.sh > .mtk/analyzer-output.json. Before installing any un-referenced NuGet package it runs dotnet package search <id> --exact-match --source https://api.nuget.org/v3/index.json (criterion 0 of the dependency-intake-checklist); planning-and-task-breakdown tags unverified AI-recommended packages [ASSUMED] and inserts a checkpoint:human-verify step. Scan Recipes supply the grep/find commands setup-audit runs on a .NET repo.

Why it works

Generic workflow skills cannot know that .NET verification means dotnet build/dotnet test, that in-memory EF providers hide relational bugs, or that MediatR handlers should keep one SaveChanges. Encoding this once per stack means every workflow and review runs with correct, stack-idiomatic gates instead of guessing.

#tech-stack-python

skill

Python context: type-check/test as verification gates, SQLAlchemy/Django ORM rules, FastAPI patterns, ruff/black.

Trigger Loaded automatically when .claude/tech-stack contains python; not invoked directly

What it does

Provides Python-specific verification commands (there is no compile step, so type checking and tests are the gates), SQLAlchemy and Django ORM guidance, FastAPI/Django framework patterns, and reference paths. It adapts to the project's toolchain (tox, Poetry, Makefile/justfile targets) rather than assuming one.

Example

On a FastAPI + SQLAlchemy repo the agent verifies with mypy . (or pyright) and pytest, and is steered to eager-load with joinedload/selectinload to avoid N+1s and to use select() (SQLAlchemy 2.0 style) over the legacy Query API. Edited .py files auto-format via hooks/format-on-edit.sh, which picks ruff then black based on what's installed.

How it works

Detection markers: pyproject.toml/setup.py(or setup.cfg)/Pipfile/poetry.lock High, requirements.txt Medium, .python-version supplemental (Low). Verification: mypy ./pyright as the compile-equivalent, pytest (or tox, poetry run pytest, make test) for tests, ruff check for lint. Analyzer capture: ruff check --output-format json . | hooks/parse-build-diagnostics.sh --format ruff > .mtk/analyzer-output.json. References live under .claude/references/python/ (coding-guidelines placeholder, sqlalchemy-checklist, fastapi-patterns, testing-supplement, performance-supplement, analyzer-config). Dependency intake verifies un-listed PyPI packages with pip index versions <pkg>. Scan Recipes drive setup-audit on a Python repo. The coding-guidelines file is an explicit placeholder to be authored when the team starts its first Python project.

Why it works

Applying .NET-style 'compile then test' to Python wastes effort — Python is interpreted, so type checking is the real compile gate. Encoding the correct gates, the Postgres-vs-SQLite test-provider caveat, and framework-idiomatic patterns per stack keeps verification meaningful instead of ritual.

#tech-stack-typescript

skill

TS/JS context across React, RN/Expo, Next.js, Tauri, Node — with package-manager auto-detection and Prisma/Drizzle rules.

Trigger Loaded automatically when .claude/tech-stack contains typescript; not invoked directly

What it does

Provides TypeScript/JavaScript build, type-check, and test commands, ORM guidance (Prisma, Drizzle, TanStack Query), and framework patterns for React SPAs, React Native/Expo, Next.js, Tauri desktop apps (with Rust sidecar commands), and Node backends. JavaScript-only projects use the same skill with the TS-specific rules relaxed to recommendations.

Example

On a pnpm Next.js repo the agent detects the package manager from pnpm-lock.yaml, writes pnpm to .claude/tech-stack-pm, and verifies with pnpm run typecheck (falling back to npx tsc --noEmit) and pnpm test. It flags 'use client' only at the boundary and Server Actions that aren't Zod-validated. If src-tauri/Cargo.toml exists it adds cd src-tauri && cargo check / cargo clippy -- -D warnings and treats every #[tauri::command] as the trust boundary.

How it works

Package-manager auto-detection by lockfile priority (bun.lock/bun.lockb → bun, pnpm-lock.yaml → pnpm, yarn.lock → yarn, package-lock.json → npm, else npm) writes the choice to .claude/tech-stack-pm, and all commands substitute <pm>. Markers include package.json/tsconfig.json (High), src-tauri/Cargo.toml (Tauri sidecar), next.config.*, react-native/expo deps, metro.config.*. Formatting via hooks/format-on-edit.sh dispatches biome → prettier by extension. Analyzer capture: npx tsc --noEmit 2>&1 | hooks/parse-build-diagnostics.sh --format tsc > .mtk/analyzer-output.json. References under .claude/references/typescript/ (coding-guidelines placeholder, data-layer-checklist, framework-patterns, testing-supplement, performance-supplement, analyzer-config). Dependency intake verifies un-listed npm packages with npm view <pkg> name version time.modified. Extensive Scan Recipes cover monorepo, framework, ORM, and RN/Expo config detection for setup-audit.

Why it works

The TS ecosystem has no single dominant ORM or package manager, and React/RN/Next/Tauri/Node each have distinct correctness traps (SSR-unsafe browser access, RN's no-DOM rule, Tauri allowlist discipline). Detecting the concrete toolchain and injecting the matching rules prevents cross-paradigm mistakes the generic workflow layer cannot catch.

#.claude/toolsets/<name>.yaml (required-toolsets / forbidden-toolsets)

concept

Named tool bundles a skill can require or forbid, so a run's tool surface is scoped to the task class, not the whole repo.

Trigger Declared in a skill's frontmatter; resolved by the /mtk router (or any orchestrator) at dispatch via scripts/resolve-toolsets.sh <name>

What it does

A toolset is a named bundle of Claude Code tools that a skill can require or explicitly forbid. Instead of every skill getting the full tool surface, a skill declares which bundle it needs; the router expands that into allowed-tools when it dispatches the skill. This narrows a review or audit run to read-only tools and keeps destructive tools (push, run) out of scope.

Example

A drift-detection skill declares required-toolsets: [read-only] and forbidden-toolsets: [code-edit] in frontmatter. When /mtk dispatches it, scripts/resolve-toolsets.sh merges the resolved tool list into allowed-tools, so the run can Read and diff but cannot Edit files or git push — even a rationalizing agent has no tool to do so.

How it works

Each toolset is a .claude/toolsets/<name>.yaml file (name must match the filename) with description, optional extends: <parent> (inheritance forms a DAG; the validator rejects cycles), and a tools: list of Claude Code tool specs (e.g. Read, Edit, Bash(git diff:*)). Three built-ins ship: read-only (review/audit/drift), git-safe (stage/unstage/read stash, extends read-only), code-edit (edit + build/test, extends git-safe). Skills reference them with required-toolsets/forbidden-toolsets (S2.19-S2.21); forbidden-toolsets wins on overlap, and an explicit allowed-tools already in the skill takes precedence over expansion (no surprise widening). Static agents under .claude/agents/*.md declare allowed-tools directly — toolsets exist for dynamic dispatch where scope varies by invocation.

Why it works

In a regulated context a compliance-review run must not have git push or dotnet run in scope. Making the tool surface a declared contract, resolved at dispatch, turns 'please don't touch source in a review' from a hope into an enforced boundary — and forbidden wins so requiring git-safe while forbidding code-edit can't be widened by inheritance.

#References system (.claude/references/)

concept

On-demand detail docs — shared plus per-stack — that skills point to instead of inlining, keeping SKILL.md a navigation layer.

Trigger Reference files are loaded on-demand by workflow skills and review agents — shared references for any stack, per-stack references keyed on the active tech stack

What it does

References are the detail docs that skills link to rather than inline. They split into shared references (any stack) and per-stack references under .claude/references/{stack}/. A skill's frontmatter and body cite the reference for a given phase; the reference is read only when that phase runs, keeping the always-loaded skill body small (the S2.26 'navigation layer, not a payload' rule).

Example

During a .NET review the agent is pointed at .claude/references/dotnet/ef-core-checklist.md only when it reaches data-layer review — it doesn't front-load every reference. Each tech-stack skill's ## Reference Files section is the canonical list of what exists for that stack.

How it works

Reference files carry YAML frontmatter with description, globs, and alwaysApply. The set of per-stack references is declared canonically in each tech-stack skill's ## Reference Files section (S2.14). Shared references include security-checklist, testing-patterns, performance-checklist, domain-finance, and dependency-intake-checklist. Two generators consume references frontmatter to fan out into other tools (AGENTS.md and native tool configs), and scripts/build-references-index.sh compiles all frontmatter into a machine-readable .claude/references.index. Progressive disclosure (S2.10/S2.11) means orchestrating skills specify which references each phase needs.

Why it works

Inlining 800-line coding guides or full checklists into every SKILL.md would blow the context budget and get loaded even when irrelevant. A frontmatter-tagged, on-demand reference layer lets skills stay small and route to authoritative detail exactly when a phase needs it, and lets the same detail power cross-tool config generation.

#scripts/build-references-index.sh

script

Compiles reference-file frontmatter into the machine-readable .claude/references.index; --check fails CI if the index is stale.

Trigger Run manually (bash scripts/build-references-index.sh); --check variant runs in the validator/CI to fail on drift

What it does

Scans every reference doc under .claude/references/ for its frontmatter and emits a tab-separated .claude/references.index listing each file's path, alwaysApply flag, description, and globs. This index is the fast lookup layer for which reference applies to which files, without reading every reference body.

Example

Running bash scripts/build-references-index.sh reports wrote .claude/references.index (<N> lines) — a header row plus one row per reference doc. The validator later runs the --check form; if you forgot to rebuild after editing a reference, CI fails with references.index out of sync. Run: bash scripts/build-references-index.sh.

How it works

Finds .claude/references/**/*.md (null-delimited, LC_ALL=C sorted for stable ordering), pulls description, globs, and alwaysApply from each file's frontmatter block via awk, normalizes empties (globs → ["*"], alwaysApply → false, description → (no description)), strips brackets/quotes from the globs array into a comma-separated form, and writes a header row plus one row per file. It operates on the caller's CWD (the project repo), not the script's own directory, so a plugin-root invocation still indexes the project's references. The real write goes through mtk_guarded_write from hooks/lib/shrink-guard.sh (S3.16), which refuses a write that shrinks the index by >50% bytes or >20% lines unless MTK_SHRINK_GUARD_OVERRIDE=1 is set. --check rebuilds in memory and exits 1 on any difference from the on-disk index.

Why it works

Reference scoping needs a cheap, always-current lookup rather than re-parsing every reference body each session, and a stale index would silently mis-scope rules. The --check gate makes staleness a hard CI failure, and the shrink-guard prevents a bug or truncated run from wiping the index.

#scripts/generate-agents-md.sh

script

Composes a portable, budget-capped AGENTS.md from references so non-Claude tools (Cursor, Codex, Copilot) read the same conventions.

Trigger Run after setup-bootstrap or setup-audit (bash scripts/generate-agents-md.sh [--force] [output-file])

What it does

Generates an AGENTS.md that summarizes the project's reference and rules files into one file other AI coding tools read. Every source is summarized (its own headings, a 1-2 line distillation, and a See <path> pointer) rather than concatenated in full, so the output stays inside a 60-120 line budget. It refuses to clobber a hand-curated AGENTS.md.

Example

After bootstrap you run bash scripts/generate-agents-md.sh and get Generated AGENTS.md (<N> lines). If AGENTS.md already exists and lacks the Auto-generated by MTK marker in its first 10 lines, the script instead writes AGENTS.generated.md and warns rather than overwriting your hand-written file; --force overrides that.

How it works

Composition strategy DF-6: the only verbatim copy is CLAUDE.md's ## Critical Rules body (extracted with a prefix match so ## Critical Rules (Always Apply) also matches); every reference (architecture-principles, per-stack coding-guidelines, security/testing/performance checklists, stack supplements, domain-finance) is run through build_summary_section (H1/H2 headings + frontmatter description or first paragraph + See <path>). A SOFT_BUDGET of 120 lines drops the lowest-priority sections first (stack supplements at priority 10, then shared checklists 20-23), and a HARD_CEILING of 140 truncates as a last resort, leaving a <!-- sections summarized to fit... --> note. Detects the active stack from .claude/tech-stack, preserves any ## Custom: sections across regeneration, and gates overwrites on the Auto-generated by MTK marker in the first 10 lines.

Why it works

Teams rarely use one AI tool; Cursor, Copilot, Codex, and Gemini need the project's conventions too, but they have no hook enforcement and a tight context budget. A summarized, budget-capped, pointer-based AGENTS.md gives them the rules without dumping 800-line guides, and the marker/--force/## Custom: guards keep it from destroying hand-written routing docs.

#scripts/generate-tool-configs.sh

script

Emits native config files for Cursor, Copilot, Windsurf, Gemini, and Cline from the same references that power AGENTS.md.

Trigger Run manually (bash scripts/generate-tool-configs.sh --all or --format <name>, plus optional --force)

What it does

Generates project-guideline files in each non-Claude tool's native format from the shared reference set, so Cursor rules, Copilot instructions, Windsurf rules, GEMINI.md, and Cline rules all carry the same conventions. Output is read-only — you regenerate rather than hand-edit — and hand-curated files are protected.

Example

bash scripts/generate-tool-configs.sh --all writes glob-scoped .cursor/rules/mtk-*.mdc files, .github/copilot-instructions.md, .windsurfrules, GEMINI.md, and .clinerules, printing e.g. Generated <N> Cursor rule files in .cursor/rules/. Cursor's testing rule gets globs pulled from the manifest's applyTo (falling back to "/tests/", "/*.test.*", "/*.spec.*", "**/*Test*") with alwaysApply: false, while critical-rules and security get alwaysApply: true.

How it works

Formats: cursor-rules (one .cursor/rules/mtk-<topic>.mdc per reference, with frontmatter description/globs/alwaysApply; globs resolved from the manifest via extract_apply_to), copilot, windsurf, gemini, cline (single-file generators). Content is assembled by assemble_all_content: CLAUDE.md ## Critical Rules verbatim first, then architecture-principles, a coding-guidelines pointer (not the full body — it can be 800+ lines), security/testing/performance checklists, stack supplements (skipping coding-guidelines and analyzer-config), and domain-*.md files. Every write carries the Auto-generated by MTK marker and check_overwrite_allowed refuses to clobber a file without it (skip-and-continue, never abort) unless --force. --all honors an optional .claude/tool-configs.conf allowlist of formats, else generates all five. Stale mtk-*.mdc files are removed only if they still carry the marker (or under --force).

Why it works

Each external tool reads a different native config format, and manually maintaining five copies of the same conventions guarantees drift. Generating them all from one reference source keeps them consistent, the pointer-not-body approach avoids wasting each tool's per-request context, and the marker/--force guard prevents overwriting configs a human curated by hand.

#Companion Plugin: dotnet-claude-kit (Roslyn MCP tools)

concept

Optional Roslyn-MCP companion giving semantic, deterministic .NET analysis that file-based review cannot match.

Trigger Optional — if codewithmukesh/dotnet-claude-kit is installed, its Roslyn MCP tools become available to .NET MTK workflows; MTK degrades gracefully if absent

What it does

tech-stack-dotnet can lean on an optional external plugin, dotnet-claude-kit, whose 15 Roslyn MCP tools load the actual MSBuild workspace and provide semantic code intelligence. MTK skills call specific tools during pre-commit, batch review, and architecture review for deterministic findings, and fall back cleanly to build-output parsing when the plugin isn't installed.

Example

During pre-commit review on a .NET repo, if dotnet-claude-kit is present the agent calls DetectAntiPatterns, which returns deterministic findings (AsyncVoid, BroadCatch, EfCoreNoTracking, HttpClientInstantiation, MissingCancellationToken, SyncOverAsync) that MTK treats as source: "analyzer", confidence: 100. On large solutions it calls GetProjectGraph to scope builds to only the affected projects and feeds that to incremental-implementation.

How it works

Key tools MTK invokes: DetectAntiPatterns (pre-commit/batch review, treated as analyzer-sourced, confidence 100); GetProjectGraph/GetDependencyGraph (scope builds/tests to affected projects on big solutions); FindDeadCode/DetectCircularDependencies (architecture review); GetDiagnostics (targeted compiler/analyzer warnings per file); GetTestCoverageMap (verify coverage claims during test review). Graceful degradation: if the plugin is not installed, all MTK skills fall back to build-output parsing via hooks/parse-build-diagnostics.sh plus AI-based review — the workflow is unchanged, only the deterministic layer is thinner. Per S3.12, the MCP layer is optional and dependency-carrying, and every skill must have a bash fallback path.

Why it works

AI-based file review can't match a real MSBuild/Roslyn workspace for detecting anti-patterns, dead code, circular dependencies, or accurate coverage — and treating those as confidence-100 analyzer findings removes false positives. Making the companion optional with a bash fallback means teams get deterministic depth when it's installed without MTK ever hard-depending on it.

That's the whole toolkit.

One command bootstraps it, one command routes everything else. The discipline above runs on every feature you ship — so "it works" finally means it works.

Install MTK View on GitHub