Sysop a monograph Preface Audience Day one The arc I · Lifecycle II · Parallel III · Quality IV · Convention V · Hooks
Sysop · a self-improving workflow for Claude Code

The workflow
gets quieter
over time.

Twenty-two skills that carry a project from a brain-dump to a merged PR — and beyond it, to the release notes — turning every code review into a permanent convention along the way. Extracted from a real production codebase, dogfooded one phase at a time, and refusing to forget what it has already learned.

22
Skills
96
Shipped phases
78
Promoted conventions
2
Install paths

Why this page
exists.

Sysop is a self-improving development workflow for Claude Code. Its core idea is simple: when a code review keeps catching the same mistake, that finding stops being one-off feedback and becomes a written convention the workflow consults automatically on every future task. To be precise about the term: promoting a finding to a convention is a human judgment call — what's self-improving is the enforcement, which from that point on is automatic and permanent. Reviews get cheaper over time because the workflow stops re-finding what it already knows. So far that loop has distilled nearly 3,300 review findings into 78 enforceable conventions across two codebases — and § IV shows what the archive measures, and, just as carefully, what it can't.

I built it to solve a problem. I was shipping a production app solo, generating far more code than I could review line by line. So instead of inspecting every line, I built a system where the review is structural: deterministic gates plus adversarial plan review. Agents, like staff, make mistakes — so you need a process that catches them when the code is written and again before it deploys. Adversarial plan review is handing a colleague your spec and asking them to tear apart your assumptions — a review rigorous enough to come back with the line numbers that need to change. Recurring defects get rarer by design, not because I'm eyeballing every line.

The instinct comes from running a team that shipped real systems: analysts and economists building SQL and front-end tools, plus contract developers. You can't read every line your people write, so you put a process in place that lets you ship anyway.

There's a quieter design constraint underneath all of it: the context window. It turned out to be the most precious resource in this whole system. Even when you stay comfortably inside the limits, an instruction given early in a session has to compete with everything generated since. The model predicts one token at a time, and the more context that has piled up between that instruction and the work happening now, the less it weighs on what comes next. The rule is still in the window; it's just too far from the work to carry much signal. Piling every rule into one standing document makes this worse, not better.

So the workflow is built to nurture the window rather than fill it. Conventions live in glob-keyed maps, and only the entries matching the files a task actually touches get pulled in, at the two moments they matter: during planning and after implementation. Skills load when invoked, not as permanent preamble. Hooks inject their guidance at the instant of the event they handle, not a thousand turns earlier. The principle throughout: don't tell the model everything up front and hope it remembers. Hand it the applicable instruction when and where it applies, so it's actually applied.

What it is,
and why it matters.

Most teams treat code review as a checkpoint — gate-keep the PR, ship it, move on. Sysop treats every review as a data point. When the same finding recurs across PRs, it gets promoted from "feedback on this change" to a written convention that future planning and implementation steps consult automatically.

The shape

A planning front door — /intake (Phase 60) — turns a brain-dump into a populated, validated tasks/index.yml. From there, four lifecycle skills — /next-task, /claim-task, /document-work, /review-close — carry a single task from queued to merged. Two quality skills (/codebase-review, /security-audit) generate work back into the queue; a third (/test-audit) assesses the test suite's standing health. One orchestrator (/auto-build) fans the same lifecycle across N tasks in parallel. A read-side pair answers a project's two standing questions — /roadmap (what's left, in what order) and /daily-summary (what happened) — and /release bundles the merges since the last tag into a changelog entry, a semver bump, and an annotated tag. Two hooks (PermissionDenied, SubagentStop) handle the boring mechanics so the skills can stay short.

The differentiator

Per-pack convention_map.md files are the persistent memory of the workflow. Each entry traces back to the review round where the pattern was first observed. /claim-task consults them before planning and after implementation; /codebase-review uses them as the rubric. Promotion into the map is a human decision; everything after it is enforcement. And the loop reaches past one repo: /contribute-convention takes a locally-promoted convention, strips your project's fingerprints down to placeholder vocabulary, and files it upstream as a pack proposal — the give-back half of the same loop.

Three readers,
and one who should close the tab.

Sysop isn't a productivity skin you sprinkle on any repo. It pays for itself in three specific situations, and asks more than it's worth in a fourth. Name yours before going further.

You, shipping a real product alone. The case the workflow was built for. You're a competent engineer producing more code than you can review line by line, and you can't add reviewers. The review has to become structural — deterministic gates, written conventions, adversarial plan critique — instead of eyeball-by-eyeball. The compounding payoff (§ IV) is the reason a solo builder can hold the line on quality at team velocity.

A team shipping via agentic tools. Agents generate diffs faster than reviewers can read them. The workflow puts gates between generation and merge — convention checks, plan critique, batched cleanup — so autonomous code doesn't reach main unread. The value here is risk posture as much as throughput: same speed, fewer of the predictable failure modes (schema drift, naive auth, silent data loss, half-applied fixes) sneaking through. One boundary to name up front: the coordination Sysop provides is between parallel sessions — locks and worktrees keep concurrent agent work from colliding — under a single human reviewer. Two people sharing one queue (assignment, review handoff, who runs /review-close) is out of scope today: a deliberate non-goal, not an oversight.

A builder growing into the judgment the workflow assumes. Sysop's gates ask for calls — approve this plan, promote this convention, accept this risk — that presume review experience. Guided mode (Phase 76; opt-in via one section in your project's CLAUDE.md) re-shapes those gates for someone still building that judgment: the skill states the decision plainly, stress-tests its own recommendation, and hands you only the calls that are genuinely yours. An honest tradeoff gets pros and cons; a false choice — a security hole dressed as an option — gets collapsed, not offered as a peer; a call you couldn't fairly weigh gets a conservative default and a note explaining why. The review bar doesn't move — the deterministic gates are unchanged. What changes is how much of the reasoning you get to see.

Not a fit: anything you can hold in your head. Side projects, quick scripts, prototypes, weekend tooling — anything where reviewing your own diff costs less than running the process. Sysop assumes the codebase is big enough that structural review pays for itself. If it's small enough to keep in your head, you don't need this; the overhead won't earn its keep.

Before any of this
runs, you need a queue.

Everything in this document — the lifecycle, the parallel orchestrator, the convention loop — assumes a populated tasks/index.yml. For most of Sysop's life you filed that by hand. Phase 60 added /intake: a planning front door that runs the brain-dump-to-queue loop with you, writes the tasks, and validates them — while leaving every judgment call that matters to you.

Ask it more than you think. The strongest move most people miss with these tools is using them upstream of code: as a sounding board for scope, business model, tech stack, schema, pricing — the whole shape of the thing before any of it gets built. The trick is how you ask. Propose an idea and tell the LLM to tear it apart. Read the critique, keep what's real, drop what's reflexive, decide. The LLM can pressure-test the structure of an idea; it can't tell you whether the idea is worth building. That judgment is yours, and the workflow doesn't pretend otherwise — /intake is built around exactly this boundary: it argues its own decomposition calls, but it will not adjudicate whether the venture is worth pursuing. The same adversarial primitive the workflow applies to plans (§ I) you apply to the premise, one layer up.

What /intake does. A few rich turns, not a wizard. You brain-dump; it plays the idea back so you can catch where it misheard you; it acts as a sounding board, arguing the weak spots; then it proposes phases, sorts them by priority, and emits a phase-one slice into tasks/index.yml with body files alongside. Decomposition follows a calibrated rubric (_shared/decomposition-rubric.md, Phase 60b) — one-sentence done-conditions, effort kept independent of blast_radius, depends_on reserved for physical impossibility. The skill is re-enterable: later phases stay coarse until you come back to sharpen them.

Two legs of done. The floor is mechanical — validate_tasks.py passes in pre-commit, so a malformed queue can't land. The ceiling is human — you read the slice and confirm it's the work you meant. Intent that should outlive a single session lives in two optional, consumer-owned files at the tasks/ root: vision.md (what this is) and decisions.md (what you've ratified and why), so the next /intake pass re-checks itself against them instead of relitigating settled calls.

And when the project predates Sysop entirely — code shipped, a ROADMAP.md or an issue tracker full of backlog, no intent layer — /onboard (Phase 88) runs the adoption: with consent it reads the repo's own evidence and drafts vision.md + decisions.md for you to confirm (observations recorded as fact; inferred rationales explicitly marked, never asserted), and/or imports the existing backlog — a roadmap file or open GitHub issues — into the queue, each task provenance-marked surfaced_by: [imported] so downstream batching treats its effort numbers as the archaeological guesses they are — then hands off to /intake for everything after.

Between planning sessions, single tasks arrive constantly — a bug spotted in a screenshot, an idea mid-review. /add-task (Phase 89) is the thirty-second capture for that moment: one thought in, a validated, deduplicated queue entry out, uncommitted for your sign-off. It never creates phases or touches the intent layer — a phase-shaped thought gets routed back to /intake.

Three phases of a
project's life.

You don't use Sysop once. You use it across the arc of a project — plan, execute, maintain — and the value compounds across all three.

PLAN what gets built — /intake · brain-dump → queue — /roadmap · order of attack — a populated, validated queue tasks/index.yml tasks/open/<ID>.md EXECUTE how it gets built — single: /next-task → close — parallel: /auto-build batch — convention checks gate both slower than reckless — faster than reckless + fallout MAINTAIN what stays clean — /codebase-review · /security-audit — /test-audit · /pr-dependabot — /auto-fix · /auto-judge review_tasks.md parallel batch · --merge sequential new findings feed back into the queue — the cycle continues
Fig. 1 The arc of a project. The maintenance loop returns new work to execution; the workflow repeats with sharper conventions each time.
Planyou · spec'd intent
The judgment Sysop can't replace. You carry the product intent, the scope, the priorities; the queue is the artifact those choices produce. See Day one for the procedure.
Executeopus · sonnet · haiku
The lifecycle (§ I) runs single tasks; the orchestrator (§ II) runs batches. Both consult the convention_map.md (§ IV) before and after every implementation, so the gates aren't friction — they're how the workflow ships clean without a human reading every line.
Maintainopus + parallel agents
Periodic /codebase-review and /security-audit generate batches in review_tasks.md. /auto-fix handles mechanical fixes in parallel (default 4 agents); /auto-judge handles flagged batches with Opus judgment. Both accept a --merge flag that runs overlapping batches sequentially — same-file work waits its turn so the second batch sees the first batch's fixes already in place. /pr-dependabot (§ III) handles dependency upkeep on the same cadence, and /test-audit assesses the standing health of the test suite itself — coverage the diff-scoped gates never look at, and existing tests worth retiring.
Report & releaseread-side · person-gated tag
The arc's backward glance. /daily-summary reads the git log and the queue and answers what happened — yesterday in detail, the week in shape. /roadmap answers what's left and in what order, computing 1–3 orderings from the queue's own signals. When enough has merged, /release bundles the commits since the last tag into a proposed semver bump, a changelog entry, and an annotated tag — dry-run by default, person-gated like every merge.

One task, four skills,
one merge.

The happy path for a developer working on a single feature or fix. Each skill hands off to the next via the filesystem (tasks/index.yml, .locks/, .pending-docs/) so any step can be re-run independently.

tasks/index.yml — source of truth for the queue — /next-task role · quick (haiku) pick claimable /claim-task role · reasoning (opus) lock + plan + execute /document-work model · inherits commit + pending-doc /review-close role · reasoning (opus) verify + merge + archive reviewer-executor sub-agent · phase 29 .pending-docs/ handoff to senior review .locks/ prevents double-claim
Fig. 2 The single-task lifecycle. Solid arrows: skill handoff. Dashed: filesystem coordination. Pinned skills declare roles — reasoning, mechanical, quick — not models (Phase 69); the parenthesized model is the default mapping, remappable per project (§ VI). Un-pinned skills (like /document-work) inherit the session model.
/next-taskhaiku · selection
Single claimable task with effort + blockers. Anchored on current_focus: true, sorted by effort low→high.
/claim-taskopus · planning + exec
Creates lock + worktree, builds a plan, runs adversarial review, then a sub-agent owns implementation, verification, and the commit.
/document-workinherits · narration
Boy-scout simplify pass, follow-up-stub HARD-FAIL gate, then writes .pending-docs/<branch>.md for senior review.
/review-closeopus · gate
Worktree-state classification; project pre-merge gates — including a crown-jewel coverage gate (Phase 61) that fails the merge when a critical path loses test coverage, with no baseline escape hatch; a verify-the-record check (Phase 59) that reads back the task's ## Test decision and confirms it against the actual diff; the manual-smoke gate; merge to main; archive the task; clean up branches.

Adjacent skills

Two skills that sit next to the lifecycle rather than inside it — useful for ad-hoc planning and for surveying state when multiple branches are in flight (typically after an /auto-build batch — § II).

/plan-reviewopus · adversarial pass
Adversarial pass on an ad-hoc plan from conversation or a file. Uses the same prompt template as /claim-task Step 7 (_shared/adversarial-review.md); surfaces findings, presents a revised plan for approval before any execution. Phase 58a added a source-verification dimension: external SDK or framework calls with no in-repo precedent get flagged unverified rather than trusted on faith.
/sitrepinherits · orientation
Read-only survey across worktrees, locks, branches, the index, and pending review batches. Reports lifecycle position per task — queued, claimed, in-progress, documented, awaiting close — and recommends a single next move. Phase 40 introduced the Doc-Work: git trailer that lets it classify documented vs undocumented branches without grepping prose. Phase 54 made the read-only guarantee structural — a disallowed-tools guard (Claude Code 2.1.152) on both /sitrep and /next-task so neither can mutate state even when asked.

One orchestrator,
N tasks, three phases each.

When the queue holds several independent tasks, /auto-build runs the same lifecycle in batched parallelism. Architectural tasks solo, migrations solo, cross-module capped, complexity-sum ceiling, and a heavy-solo invariant (any single task with weight > K = 6 runs alone, never alongside siblings — Phase 42a). Default N = 4.

/auto-build orchestrator · phase 29 Step 5 · sequential pre-claim on main · yaml round-trip + claim_task.sh --lock Task A Task B Task C 6a · plan-only opus 6a · plan-only opus 6a · plan-only opus 6b · adversarial review 6b · adversarial review 6b · adversarial review 6c · orchestrator classifies — halt on blocker, or absorb fixable findings 6e · execute + commit 6e · execute + commit 6e · execute + commit — human runs /review-close per branch; the merge remains person-gated —
Fig. 3 Batched fan-out. Steel-blue phases are sub-agent work; orange phases execute and commit. The orchestrator never recurses; it owns the classification seam.

Reviews and audits
write tasks,
they don't open PRs.

/codebase-review and /security-audit are the workflow's task generators. Their output isn't a comment thread or a checklist — it's structured entries in review_tasks.md. /triage classifies each batch as auto or flag; /auto-fix and /auto-judge drain the two pools in parallel. Findings have to survive the same gates as feature work.

Neither generator opens with the model. A review's first pass is deterministic — the shared check registry (checks.yml): grep rules for the mechanical convention violations, a language-server pass (pyright/tsc diagnostics), Semgrep AST rules for the patterns grep can't express, lint and dependency-audit stages riding the same plumbing. One finding shape, one baseline, one blocking gate — those findings file themselves, no model in the loop. The model reviews on top of that floor, and the language server arms it a second time on the way out: each novel finding is amplified across the codebase, symbol-anchored ones through find-references — which resolves imports, aliases, and renames — so a promoted convention lands as a task at every call site, not just the one place an agent happened to look. Judgment gets spent where tools can't see; enumeration goes to the tools.

/codebase-review reuse · quality · drift /security-audit OWASP-aligned threat scan review_tasks.md batched, threat-categorized /triage auto vs flag · Phase 44 /auto-fix parallel · mechanical fixes /auto-judge parallel · opus judgment findings become work — work goes through the same gates as feature PRs — or /claim-task for one-off manual review —
Fig. 4 Quality skills funnel into review_tasks.md; /triage splits the pile into auto and flag; /auto-fix and /auto-judge drain the two pools in parallel (or sequentially via --merge when batches share files).
/codebase-reviewopus · rubric pass
Runs the deterministic pre-scan, then walks the codebase against convention_map.md, files findings as batched tasks (Batch 1 / Batch 2 / …) with effort + blast_radius pre-populated. Drained by /auto-fix.
/security-auditopus · OWASP pass
Threat-categorized OWASP pass against security_map.md. Pack-aware (python / postgres / nextjs-react / llm) so findings cite the right CWE and remediation pattern.
/test-auditopus · suite health
Phase 80 — the standing counterpart to the diff-scoped coverage gate. Crown-jewel-first assessment of the whole test suite: modules that never show up in a diff but carry no tests, covered-but-hollow tests a coverage number can't see, and existing tests worth retiring (provably dead, redundant, brittle). Read-only — it ranks recommendations and offers to route the accepted ones to /intake; it never writes a test or claims a task itself.
/triageinherits · classifier
Phase 44 split this out of /auto-fix: read each pending batch, classify as auto (fully mechanical) or flag (needs judgment), persist the verdict back into review_tasks.md. Idempotent, so it's safe to re-run. Prerequisite for the two drainers below.
/auto-fixparallel · mechanical
Drains the auto pool. Claims each batch via an isolated parallel sub-agent (default 4), fixes, verifies, pushes. --merge handles file-overlapping batches sequentially so the second batch sees the first's fixes.
/auto-judgeopus · judgment
Drains the flag pool. Same parallel + --merge shape, but each sub-agent is Opus and can FIX, DROP, or FAIL a task after adversarial re-reading. The two drainers run concurrently against disjoint pools.

The one sweep that does touch PRs

Phase 53 added the workflow's first GitHub-touching skill — the exception to this section's title, scoped to bot-authored dependency PRs only.

/pr-dependabotopus · github sweep
Sweeps open Dependabot PRs: classifies each by ecosystem + semver, merges patch/minor updates on green CI, holds majors (even from trusted publishers), closes pip and open-set-superseded PRs. Dry-run by default; --execute to act. A client-side replacement for native auto-merge, which Free-plan private repos don't get. Classifier validated against ~50 real PRs.

The loop that makes it
self-improving.

This is the thing. Every other section is plumbing for this one. When a finding from /review-close recurs across two review rounds, it gets promoted into the relevant pack's convention_map.md. From that moment on, /claim-task consults it during planning and after implementation, and /codebase-review uses it as a rubric on future passes. The workflow forgets nothing.

convention_map.md per-pack: python · postgres · nextjs-react · llm core: bash · skill markdown /review-close finds finding round N fix in this PR noted in review_tasks.md same pattern again round N+1 · N+2 promoted to map attributed to round N /claim-task step 2 · pre-impl check step 7 · post-impl re-check /codebase-review uses map as rubric apply repeat? promote consult enforce next round
Fig. 5 — centerpiece The convention loop. Findings travel clockwise; the map ages forward, the patterns get cheaper to enforce.

What's in a map entry

Each entry is a glob pattern, a one-line rule, and an attribution: the review round where the pattern was first surfaced. The round attribution is load-bearing. When an entry later turns out to be wrong, you can find the review that promoted it and learn what changed.

**/*.py
  · Use yaml.safe_load, never yaml.load without a Loader  (Round 12)
  · Subprocess calls go through shared_cli.run(), not raw subprocess.run  (Round 23)
  · No bare except: — catch a specific exception or re-raise  (Round 7)

tests/**/*.py
  · Integration tests use a real database, never mocks  (Round 4 — burned by mock/prod divergence)

The promotion history is the workflow's audit trail.

The flip side of forgetting nothing: entries can become wrong. A convention that was right at round 12 can be stale by round 60 — the codebase moved, the library fixed the footgun, the rule outlived its reason. The round attribution is the first concession to that: a bad entry traces back to the review that promoted it, so it can be retired deliberately rather than worked around forever. The second is a backward sweep that runs every review round, alongside the forward coverage audit: it flags the statically detectable staleness — a map section whose glob no longer matches any file (a removed category), and a bullet citing a helper that has since been renamed or deleted (a dead reference) — at the moment the code moves out from under the rule. What stays manual is the decision: both halves surface candidates, a human adjudicates them. And the harder, judgment-driven demotion — a blocking rule that keeps firing false positives long after it stopped catching real ones — is now built too: each false positive a reviewer triages away is recorded as a stale-verdict in a per-rule ledger, and once a rule accrues them across two or more rounds, the review session prompts to retire it, demote it to advisory, tighten it, or keep it. Anyone who has maintained a lint config for five years knows how that debt accrues; the map differs in keeping the receipts and, now, in both noticing out loud when an entry's referent disappears and retiring the rules that have quietly gone wrong.

Does it actually get quieter?

Measured, not asserted — 71 review rounds and 3,298 findings from the upstream project's review archive, February through July 2026. The honest version first: raw findings per round go up, not down. The rubric grows with every promoted convention, and later rounds file one task per call site where early rounds bundled. Many findings are process-induced — total findings track the rubric's size, not the code's health.

What falls is severity — and then it stays down. The cleanest metrics need no denominator at all: the critical share of findings fell from 21% in February to 5% in April — the last full month before Round 70's retro-application sweep — and then, that sweep aside, held in the low single digits through partial July (17% in May during the sweep, 0% in June, 5% in July) while the rubric kept growing. It is not a monotonic decline; the point of the claim is that the severe share dropped from roughly one-in-five to the low single digits and has stayed there. The share of low-severity findings mirrors it, rising from 22% in February to a 75% peak in June, 65% in July. Activity-normalized rates agree on direction: criticals per 10,000 effective lines changed fell roughly 3× from the March peak to May, lower still through June and partial July. Reviews don't get smaller; they stop finding things that matter.

Two confounds were checked rather than assumed. Commit size shrank ~5× across the interior of the period (median 65 → 13 lines per commit by May), which inflates any per-commit rate — hence the headline rests on denominator-free shares, corroborated per-line. And severity labels are the reviewer's own; the rubric is impact-categorical (security, data integrity, crash potential), and the late rounds labeled map-enforcement findings critical rather than discounting them as known — but the direction of any labeling drift can't be fully pinned, so treat the shares as the primary evidence and the rates as the check.

And the limits, plainly — stated because they bound how much the chart can carry. This is one project, and the loop's author ran it, ran the reviews, and adjudicated every severity — n = 1 and self-graded, which no re-analysis of the same archive can fix. The labels have moved at least once: June's lone critical was reclassified out during a later archive consolidation, so June's 0% is one bookkeeping decision away from ~1.5%. The tail is thin — 68 findings in June, 37 in partial July — so a single bad round would move the late shares by double digits. And the strongest rival explanation is ordinary maturation: young codebases surface their worst findings early and harden over time with or without a convention loop, so some of the 21% → 5% fall was coming anyway. Nothing in Fig. 7 separates the two hypotheses.

What would separate them is specified in the method notes (docs/analysis/REPORT.md) and has not been run: partition findings into classes covered by an already-promoted convention at the time of the round versus classes nothing covered, separate new-code findings from retro-application backfill (Round 70 files covered-class findings by design), and track both per line of diff. Maturation depresses both classes together; the loop selectively suppresses the covered ones. That analysis needs the upstream per-finding archive, which is private — the round-level dataset that ships here cannot answer it. Until it runs, the claim this section makes is the narrower, defensible one: the severe share fell early and stayed low while the rubric grew, and where a convention is mechanized, its machine check makes silent recurrence of that finding class structurally harder — a prose-only convention's gate is the reviewer re-armed with the map. What would falsify it: covered classes recurring in new code at the uncovered classes' rate, or a fresh project that starts with the map pre-loaded and still produces February's one-in-five critical share.

critical share of findings (%) share of low-severity findings 21% 10% 5% 17% * 0% 5% 22% 29% 59% 43% 75% 65% Feb 2026 Mar Apr May Jun Jul (partial) * May's bump is Round 70, the first broad sweep after /codebase-review became language-server-driven (LSP): ~25 of its 45 criticals are promoted conventions amplified one task per call site — a sharper instrument, not a Feb-level defect rate.
Fig. 7 Severity fell, then held low, while the rubric grew — a correlation on one self-graded project; limits in the text. Bars are denominator-free shares; activity-normalized rates (per-commit, per-line) agree on direction — see docs/analysis/REPORT.md. Mined from the upstream project's review_tasks archive (71 rounds, 3,298 findings) and its convention history (78 entries, all dated). The mined dataset ships alongside (docs/analysis/gdp_review_metrics.json), and the miner that produced it is included for provenance — re-mining needs the upstream repo's private archive, so verify against the dataset.

Boring mechanics,
handled outside the skills.

Claude Code 2.1.x added two hook points that Sysop leans on heavily. The hooks let skill prose stay short — instead of writing escape instructions inline for every possible permission denial, the hook injects them automatically. Neither hook can authorize anything. The real gate is the human invoking the skill and, for protected operations, typing the escape — the hooks only remove the re-explaining.

PERMISSIONDENIED · 2.1.89 skill emits Bash call e.g. git push origin main classifier blocks protected-branch policy permission_denied_hook.py matches pattern · emits ! escape via additionalContext model relays the escape to the human ask user to type: ! git push origin main human types it — push succeeds SUBAGENTSTOP · 2.1.47 reviewer-executor sub-agent finishes emits envelope at top of message SubagentStop fires last_assistant_message in stdin parse_subagent_envelope.py finds last yaml block w/ task+status writes .subagent-envelopes/<id>.json parent skill reads JSON fall back to regex if race lost structured handoff · no regex on prose
Fig. 6 Two hook points. The accent flow is the hook itself; the rest is the surrounding mechanism. Note the final actor on the left: the push lands only when the human types the escape — the !-prefix is a user-side control the model cannot emit.
PermissionDeniedclaude code · 2.1.89
Hook can't bypass the classifier — it injects additionalContext showing the exact !-shell-escape for the human to type at the next prompt. It grants nothing; the person typing the escape remains the actor — and is expected to read the command before typing it; the relay is a request, not an instruction. Scoped to three matchers (protected-branch push, --delete push, protected-branch commit), each one a command already in the settings.json allow-list that the classifier overrides anyway; unmatched denials pass through untouched. Collapsed four paragraphs of /review-close escape prose into pointers.
SubagentStopclaude code · 2.1.47
Fires when a sub-agent finishes. Parses the structured envelope (TASK: / STATUS: / WORKTREE: / …) into JSON so the parent skill doesn't have to grep the return text. Phase 54 added an agent_transcript_path fallback and message_source provenance, retiring the earlier timing caveat — the parser no longer depends on winning a race against the sub-agent's final message.

Two install paths,
one source of truth.

Claude Code plugin

/plugin marketplace add getsysop/sysop then /plugin install sysop@sysop plus any packs. Delivers namespaced slash commands like /sysop:claim-task.

core python postgres nextjs-react llm beancount streamlit pandas mcp-server kotlin swift-ios flutter

Bash installer

bash install.sh <target> --packs python,postgres works with any agent (or none) — or --packs auto to detect the stack from the target's own markers (Phase 75). Writes scripts, git hooks, permission settings, and per-pack convention maps. Updates via bash scripts/sysop-update.sh from the consumer side once $SYSOP_SRC is set.

Model roles, not model pins

Since Phase 69, skills that pin a model declare a rolereasoning, mechanical, or quick — rather than a model id (skills without a pin simply inherit the session model). Install resolves roles through .claude/served_models.yml (default mapping: opus · sonnet · haiku); a consumer remaps any role in served_models.local.yml, which is never overwritten and layered on top — so an upstream model sunset and a local preference both survive the next update. The default served: allowlist also pre-lists fable — nothing maps to it by default, but opting the deep-reasoning tier into Claude Fable 5 is a one-key local override (reasoning: fable).

Where things live in a consumer repo

consumer-repo/
├── tasks/                          # queue lives here
│   ├── index.yml                  # source of truth · validated on every commit
│   ├── vision.md                  # optional intent layer · /intake-authored, consumer-owned (Phase 60a)
│   ├── decisions.md               # ratified product decisions · re-checked on re-intake (Phase 60a)
│   ├── open/                      # task body files for status: open
│   └── archive/                   # completed task bodies (git mv from open/)
├── .claude/
│   ├── settings.json              # permission allow-list + hooks
│   ├── convention_map.md          # core + per-pack content concatenated
│   ├── security_map.md            # OWASP map · pack-extended
│   ├── checks.yml                 # concat of pack check fragments
│   ├── served_models.yml          # role → model mapping · override via served_models.local.yml (Phase 69)
│   └── *.project.{md,yml}          # consumer-authored extensions (Phase 24a) · promotion writes land here (Phase 78)
├── scripts/
│   ├── claim_task.sh              # lock + worktree + branch creation
│   ├── close_batch.sh             # review_tasks.md batch closure
│   ├── validate_tasks.py          # schema enforcement · runs in pre-commit
│   ├── permission_denied_hook.py  # Phase 36 · classifier escape injection
│   ├── parse_subagent_envelope.py # Phase 37 · structured envelope parsing
│   ├── run_checks/                # Phase 49 split · Phase 61 coverage.py crown-jewel gate
│   └── sysop-update.sh              # the consumer-side update entry point
├── SYSOP_ISSUES.md                  # friction log · seeded once, project-owned · /report-issues files upstream
├── .locks/                        # lives in main repo · git-common-dir resolved (Phase 32)
├── .pending-docs/                 # /document-work writes here · /review-close consumes
└── .subagent-envelopes/           # Phase 37 · SubagentStop hook writes JSON here