Skip to content

About

Opinionated skills for working in very large codebases — by decomposing big changes into parallel lanes and routing each to the best model across a heterogeneous agent fleet

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

⏩ thunderkit

Ship big changes in big repos — by splitting the work into parallel lanes and routing each to the best model across a heterogeneous agent fleet.

skills npm CI Pages License format

Portable skill files · Host-qualified native workflows · User-selected models

Install · The loop · Model classes · Dependencies · Docs site · North Star


Big work in big repos is won by decomposition + heterogeneity, not by one smart model.

thunderkit is an opinionated skill pack. It takes a large change in a large repo and: decomposes it into disjoint, dependency-layered lanes → routes each lane within your chosen model classes and supported harness mappings → runs independent work in parallel across the reachable fleet → reviews the result across the required model families → remembers the project's intent as a committed artifact.

It's deliberately opinionated — see NORTH_STAR.md:

  • No single-model plans. A plan that can't be split into parallel lanes isn't done.
  • Review is always cross-family. A model family reviewing its own output is not review.
  • Done is evidence, not intent. Every lane carries a verification command, or it's blocked.
  • You pick the model classes. One capable planner, a set of executors, everyone as reviewers.
  • Context is committed, not recalled — the opinion travels with the repo.

How it works — the phase loop, made parallel

The loop is discuss → plan → plan review → execute → verify → prepare delivery. Independent lanes run in parallel; dependency and approval gates stay ordered. A large repo is decomposed so it never has to fit in one context window. Cross-family plan review must cover the current plan before execution, and diff review must cover the actual changes afterward.

  intake        plan            execute (parallel)      review           ship
 ┌────────┐   ┌────────┐   ┌──────┬──────┬──────┐   ┌────────────┐   ┌────────┐
 │tk-grill│─▶ │tk-plan │─▶ │lane 0│lane 1│lane 2│─▶ │ tk-review  │─▶ │tk-ship │
 │tk-ask  │   │(1 best │   │Opus  │Opus5 │Fable │   │ every      │   │(no auto│
 │tk-spec │   │ brain) │   │  ▲ own worktree each│   │ family     │   │ merge) │
 │tk-map  │   └────────┘   └──────┴──────┴──────┘   │ +verify    │   └────────┘
 └────────┘    planner        executors (a set)      reviewers (all)

Each lane is file-disjoint, with its own worktree and a model from the selected executor class. The workflow records genuine resume IDs when available, explicitly marking missing IDs as unavailable. A merge conflict stops integration for a fresh ownership check; it is not an excuse to overwrite another lane.

Policy stays here; native implementation is optional

Thunderkit owns model choice, lifecycle routing, portable project context, cross-family review, and completion gates. Stage skills may reuse a separately installed, pinned native peer:

Active host Eligible native peer Without a qualified peer
OpenCode OMO (oh-my-openagent) Thunderkit-owned portable procedure
Codex OMO (oh-my-openagent) Thunderkit-owned portable procedure
Hermes OMH (oh-my-hermes) Thunderkit-owned portable procedure
Claude Code or another skill-compatible host None declared Thunderkit-owned portable procedure

This is the host filter from dependencies.json, not a claim that every operation or selected model works on each host. A native route also requires the exact version, loaded source fingerprints, required tools, enforceable model bindings, and safety controls. A missing peer produces a named fallback, not a native success. If the owned procedure cannot meet the same model, review, or safety requirements, the stage stays blocked.

A native planning or execution handoff has one workflow owner until it returns. Thunderkit does not start a second execution loop alongside it. Native artifacts stay in their native locations; Thunderkit references them and checks their identity. A timeout with uncertain in-flight work blocks a duplicate launch. Delivery still needs separate user approval.

See Dependencies for exact pins, licenses, installation boundaries and qualification details. Set delegation: "off" to use owned procedures without invoking peers.

The three model classes

thunderkit's core opinion: one model can't be planner, coder, and reviewer at once — a fleet can, if the work is decomposed to feed it. So tk-router asks you to choose three classes (once per project, then it remembers in .thunderkit/config.json):

Class Cardinality Does Example choice — requires confirmation
🧠 Planner exactly one — the most capable model spec, discuss, plan, root-cause Opus 4.8
🔨 Executors a set — lanes spread by weight map, research, implement, docs Opus 4.8 · Opus 5 · Fable 5.1
🔍 Reviewers + verifiers "all" or a nonempty unique model list plan-check, review, verify, UAT, audit "all"

Planning is a single point of failure → one best brain. Execution is a throughput problem → many hands matched to lane weight. Review is a blind-spot problem → every family looks, so no one family's blind spot survives. A model can be in more than one class — the strongest model plans, takes the heaviest lane, and reviews.

The three classes have no automatic defaults. reviewers: "all" considers every catalog model, not just the planner and executors; preflight forms the reviewer set from successful responses and reports unavailable optional candidates. Every explicitly selected model must respond, and at least review_families_min distinct families must answer independently. opus48, opus5 and fable51 are one Anthropic family; sol is the OpenAI family.

Canonical configuration uses schema_version: 2 and classes.planner, classes.executors, and classes.reviewers. Missing operational fields default in memory to review_families_min: 2, max_layers: 3, frozen_paths: [], ecosystems: ["omo", "omh", "gsd"], and delegation: "auto". Existing versionless classes files remain readable without rewriting. A supplied decided_at is preserved; readers never invent one. See the configuration contract and model roster for validation and approved legacy migration.

A backend never replaces a selected model or lowers the family minimum. Unsupported host/model mappings are reported explicitly; changing a choice requires the user, not an automatic fallback.

The skills (22)

Stage Skill What it owns
entry tk-router Sizes the work, gets the three model classes chosen, routes the lifecycle.
restore tk-restore Model-free context load and verified backup export to a new/empty destination; never resumes work.
handoff tk-handoff Save portable context; validate model, artifact, repository and original-harness identity before an approved resume.
preflight tk-test Pings every configured model through its real harness CLI — proves the fleet is reachable and ≥2 review families answer before work starts.
intake tk-ask Answer discipline — yes/no, one word, a number, a path, or unknown. No prose. Routes each unknown to map/learn/user.
intake tk-grill Interrogates you and the harness with closed questions until the brief has no unknowns (--learn mode frames what to learn).
pre-plan tk-spec Ambiguity-scored Socratic loop pinning what the change delivers.
pre-plan tk-map Big-repo recon — a durable code map so planning works from structure.
pre-plan tk-discuss Captures implementation decisions + rejected alternatives before planning.
pre-plan tk-research Parallel investigation lanes for the unknowns, consolidated.
pre-plan tk-learn Research a topic online → source-backed knowledge note → optionally draft a new validated skill.
plan tk-plan Decompose into disjoint, dependency-layered lanes, each with acceptance + a verify command.
execute tk-execute One execution owner: a qualified native handoff or portable lane dispatch, with worktree and genuine session evidence.
execute tk-fast Trivial inline edit: no model choice, plan, subagents or review; targeted test and one atomic commit. Uses GSD gsd-fast where installed.
execute tk-quick Small task on one chosen model (planner default or model=<key>), atomic commits and tests, one different-family review before each commit.
verify tk-review Cross-family review + evidence gate (also --plan for pre-execution plan-check).
verify tk-verify-work Conversational UAT — walk each acceptance criterion through the real user surface.
verify tk-debug Scientific-method debug loop with persisted, resumable state.
deliver tk-ship Gate on review+UAT and prepare a PR body — no push, PR creation, publish, or merge.
deliver tk-docs Parallel doc write, then verify every claim against the live code with a second family.
deliver tk-audit Milestone done-ness vs original intent — orphaned/unverified requirements fail closed.
memory tk-memory Project north star, decision log, and the router's per-project config.json.

Shared sources: models.json defines model IDs, families and supported harness mappings; model-roster.md is its human reference. dependencies.json defines per-operation native targets and fallbacks; delegation.md defines their gates. Standalone skills include local copies of these Thunderkit-owned references and helpers.

Install

Thunderkit distributes Agent Skills (skills/<name>/SKILL.md) with small local Python helpers. The npm command is a pointer to the pinned Vercel skills distribution CLI, skills@1.7.0. Installing a skill file is not proof that the host can execute its workflow.

Toolchains: Thunderkit help, version and dependency display require Node ≥18. Install and list delegate to skills@1.7.0 and require Node ≥22.20.0, even though listing does not install skills. The local resolver/model helpers require Python ≥3.11.

# whole pack through the npm pointer (Thunderkit only)
npx thunderkit install

# equivalent direct distribution command
npx -y skills@1.7.0 add thunderock/thunderkit --all

# whole pack, but only for named harnesses
npx -y skills@1.7.0 add thunderock/thunderkit -s '*' -g --agent claude-code codex opencode hermes-agent

# one skill
npx -y skills@1.7.0 add thunderock/thunderkit -s tk-router -g

# what's in the repo, without installing
npx thunderkit list

Choose the intended agents and scope through the distribution CLI. A single-skill installation contains its own support files but does not install sibling tk-* stages. The router names a missing stage and stops there rather than guessing commands or installing it automatically.

Native peers are separate and optional. Installing Thunderkit does not install or activate OMO or OMH, authenticate providers, or change model selections. To display the registry without running peer installers or doctors:

npx thunderkit deps --json

# from an existing checkout, without npx package retrieval
node bin/thunderkit.js deps --json

The output is information, not a live readiness test. Dependencies documents the separately approved native install, activation and doctor steps. Then invoke tk-router through your host's skill interface (e.g. tk-router: refactor the auth layer), choose the model classes, and run tk-test before model-bearing dispatch. A different host must recheck support; portable project context does not make native sessions or model mappings interchangeable.

Project memory — .thunderkit/

Every project keeps its own north star, decisions, and selections — committed to the repo, so any agent (or you in a later session, or a teammate) inherits the project's opinion without re-litigating settled choices:

.thunderkit/
  NORTH_STAR.md   # this project's goals, constraints, non-negotiables
  DECISIONS.md    # append-only: what was decided, why, what was rejected
  config.json     # the three model classes + review families + layers + frozen paths
  continuity.json # optional portable preservation policy, separate from model choices
  HANDOFF.md      # saved stage, artifacts, decisions and original-harness session identities
  BRIEF.md        # tk-grill intake (closed answers, no unknowns)
  SPEC.md         # what the change delivers (tk-spec)
  MAP.md          # code map (tk-map)
  PLAN.md/.json   # current decomposition into lanes (tk-plan)
  REVIEW.md       # latest cross-family review (tk-review)
  UAT.md · AUDIT.md · debug/*.md · runs/*.json

Recovery and preservation

tk-restore reads context without model configuration, a native peer or the original checkout. Load is inert: saved commands, approvals, model choices and next actions never authorize execution. tk-handoff restore still requires current repository/branch/HEAD, artifact hashes, model bindings, the original harness/session and permission before any continuation.

The bundled helpers require Python ≥3.11 and supported Linux/POSIX no-follow filesystem operations; history verification/export also needs local Git and bounded writable scratch. Set SKILL_ROOT to the directory of the actually loaded skill. All path variables below are explicitly selected literal absolute paths without symlink components, not shell text from recovered data. SCOPE_ID is the exact approved identity, not a name inferred from cwd.

Load context, even without the checkout

CONTEXT_PATH may name a snapshot directory, its manifest.json, or a bounded legacy Markdown file. GLOBAL_CONFIG names the selected continuity settings file; it may be absent if no saved settings are needed, but an existing invalid file is refused.

python3 "$SKILL_ROOT/scripts/restore_context.py" load \
  --scope project --identity "$SCOPE_ID" --context "$CONTEXT_PATH" \
  --global-config "$GLOBAL_CONFIG"

# Use the remembered locator for this exact project identity instead.
python3 "$SKILL_ROOT/scripts/restore_context.py" load \
  --scope project --identity "$SCOPE_ID" --global-config "$GLOBAL_CONFIG"

No checkout is required: omit --project-root as above. When the actual project exists, add --project-root "$PROJECT_ROOT" to use its bound private identity and local locator. Selection order is explicit context, project-local locator, exact-project global locator, then an already available matching pointer supplied with --memory-pointer. Invalid explicit paths and stale, corrupt or mismatched selected locators fail rather than silently falling through. No broad home, memory, transcript or session-history search occurs, and loading never remembers a path.

Without --global-config, continuity settings are selected from $THUNDERKIT_HOME/continuity.json, then $XDG_CONFIG_HOME/thunderkit/continuity.json, then $HOME/.config/thunderkit/continuity.json. These are settings locations, not default backup roots. Approved global context uses --scope global and its own exact identity and source authority; project approval never covers another project or profile.

Inspect JSON stdout's source, integrity status, metadata and warnings. Legacy Markdown is unverified historical text. Snapshot hashes prove byte consistency, not trustworthy origin, current approval, complete protection or resumability. See tk-restore for limits, exclusions and refusal behavior.

Preview and approve a recovery export

Export is separate from load. Select an explicit snapshot and a new or empty destination whose parent already exists, outside the snapshot and any live checkout. Legacy Markdown is load-only.

python3 "$SKILL_ROOT/scripts/restore_context.py" preview \
  --context "$SNAPSHOT_PATH" --identity "$SCOPE_ID" --scope project \
  --destination "$DESTINATION"

Review the exported paths, deletion records, omissions, history status and manifest_sha256. After fresh approval for that exact preview and destination, use its unchanged digest:

python3 "$SKILL_ROOT/scripts/restore_context.py" export \
  --context "$SNAPSHOT_PATH" --identity "$SCOPE_ID" --scope project \
  --destination "$DESTINATION" --approve \
  --expect-manifest-sha256 "$PREVIEW_MANIFEST_SHA256"

Changed inputs require a new preview and approval. Export reconstructs approved bytes and modes without overwriting files; deletion records never authorize deletion from a live tree. Verified bundled history may be imported into a new isolated repository, without inherited remotes, hooks, credentials or grants. It never fetches remote-only history, executes recovered files, installs dependencies, changes models or resumes a session.

Preview project setup and capture a checkpoint

Save current artifacts and context at regular intervals, before risky transitions and before pausing. Then use the approved preservation procedure. For project setup, select an existing PROJECT_ROOT and BACKUP_ROOT, the exact SCOPE_ID, and explicit source allowlists. APPROVED_SOURCE below is one source file/directory; repeat --source for others. The saved handoff is explicitly included even if ignored by Git. WORKING_REF is the approved attached working branch, such as refs/heads/feature-name, not a tag or default/protected branch. History capture is bounded and subject to secret exclusions.

Choose INTEGRATION_ROOT beneath the project's .thunderkit/. Setup's private settings and integration files must be untracked and Git-ignored; arrange this with normal project-write approval, not by silently changing ignore rules. Backup storage must be outside the project.

python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" preview \
  --project-root "$PROJECT_ROOT" --scope project --identity "$SCOPE_ID" \
  --backup-root "$BACKUP_ROOT" --source "$APPROVED_SOURCE" \
  --source "$PROJECT_ROOT/.thunderkit/HANDOFF.md" \
  --context-entrypoint .thunderkit/HANDOFF.md \
  --include-history --history-ref "$WORKING_REF" --integration-root "$INTEGRATION_ROOT"

Preview is read-only. Inspect its mutations, blockers and capability report before approving those exact inputs. Setup rechecks them, qualifies storage readback, creates a first verified snapshot and writes private configuration; it does not activate a hook or scheduler:

Retain the actual successful JSON stdout as SETUP_RESULT: a new file in a private, user-owned directory outside the project and every capture source. The subshell's umask 077 and no-clobber setting protect a newly created mode-0600 file without replacing an existing receipt. Check the command's exit status and result before using the file; a failed setup's output is not a successful receipt. Never fabricate, edit or reconstruct this JSON.

(
  umask 077
  set -C
  python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" setup \
    --project-root "$PROJECT_ROOT" --scope project --identity "$SCOPE_ID" \
    --backup-root "$BACKUP_ROOT" --source "$APPROVED_SOURCE" \
    --source "$PROJECT_ROOT/.thunderkit/HANDOFF.md" \
    --context-entrypoint .thunderkit/HANDOFF.md \
    --include-history --history-ref "$WORKING_REF" \
    --integration-root "$INTEGRATION_ROOT" --approve > "$SETUP_RESULT"
)

If setup was interrupted and retained a valid local claim, resume that exact failed claim with unchanged project, identity, store, sources, context entrypoints, policy, history ref and integration root. Do not delete locks, adopt foreign paths or treat retry as approval for a new scope. Missing or changed ownership evidence blocks resumption. After fresh approval, retain stdout in a different new private file, RESUMED_SETUP_RESULT:

(
  umask 077
  set -C
  python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" resume-setup \
    --project-root "$PROJECT_ROOT" --scope project --identity "$SCOPE_ID" \
    --backup-root "$BACKUP_ROOT" --source "$APPROVED_SOURCE" \
    --source "$PROJECT_ROOT/.thunderkit/HANDOFF.md" \
    --context-entrypoint .thunderkit/HANDOFF.md \
    --include-history --history-ref "$WORKING_REF" \
    --integration-root "$INTEGRATION_ROOT" --approve > "$RESUMED_SETUP_RESULT"
)

Only after exit zero and status: "configured", select this successful result's path as SETUP_RESULT for the host commands below. --setup-result accepts the exact successful SetupResult JSON, owned by the current user and mode 0600. It is an ownership/integrity receipt, not executable approval, a push grant or permission to activate anything.

For an explicitly approved one-shot capture, choose an existing user-owned mode-0700 STATE_DIR, disjoint from the entire project and backup store. Capture uses explicit sources and roots, not saved authority:

python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" capture \
  --project-root "$PROJECT_ROOT" --scope project --identity "$SCOPE_ID" \
  --backup-root "$BACKUP_ROOT" --source "$APPROVED_SOURCE" \
  --source "$PROJECT_ROOT/.thunderkit/HANDOFF.md" \
  --context-entrypoint .thunderkit/HANDOFF.md \
  --include-history --history-ref "$WORKING_REF" --state-dir "$STATE_DIR" --approve

These commands are model-free and do not push. --approve is fresh consent for that invocation, not permission restored from a file. Save portable policy in .thunderkit/continuity.json, separate from model config; private .thunderkit/continuity.local.json holds machine-local identity/locator information. Successful setup remembers its verified snapshot. Capture reports the snapshot/store locator; do not assume it updates every private or global remembered pointer.

Policy defaults are a 15-minute interval, retention of 20 verified snapshots, pruning off, and git.push_after_commit: true; none creates a backup root, schedule or push grant. Project policy can disable backup or set git.push_after_commit: false. Mandatory secret, runtime-home, transcript, cache and .git exclusions still apply, including to approved branch history. Do not capture an entire home or copy live runtime databases.

Require exit zero and inspect the result, not just a success label. Report files, omissions, history protection, saved-context age, last verified snapshot and push status independently. Partial or failed preservation retains prior valid recovery state and does not become “fully protected.” Local write/readback is not machine-loss durability or remote/FUSE crash qualification.

Capture an independently approved global scope

Global capture is implemented by the same model-free runtime, with a separate identity, backup namespace and allowlist. Select GLOBAL_CONFIG explicitly: global capture requires --global-config and does not use the load command's implicit settings lookup. Review and save a private settings document under separate settings-write approval. This schema-valid example uses illustrative paths only; replace them with the user's exact approved literal paths and identity, never shell expressions or values obtained from recovered instructions:

{
  "schema": 1,
  "kind": "global-settings",
  "policy": {
    "schema": 1,
    "backup": {"enabled": true},
    "git": {"push_after_commit": false}
  },
  "global": {
    "identity": {"kind": "global", "id": "personal-notes"},
    "backup_root": "/example/global-backups",
    "approved_sources": ["/example/notes/notes.md"]
  }
}

Set GLOBAL_SCOPE_ID and GLOBAL_BACKUP_ROOT to the matching settings values. GLOBAL_SOURCE here is the single allowlisted regular file notes.md; its stored context entrypoint is sources/0/notes.md. Repeated --source arguments select an exact subset of global.approved_sources, not additional paths inferred from directory proximity. Source order determines the sources/<index>/ prefix; inspect actual manifest paths before loading other selections. Choose an existing user-owned mode-0700 GLOBAL_STATE_DIR, disjoint from all selected sources and the store. The backup root must also be disjoint from those sources.

python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" capture \
  --scope global --identity "$GLOBAL_SCOPE_ID" --global-config "$GLOBAL_CONFIG" \
  --backup-root "$GLOBAL_BACKUP_ROOT" --source "$GLOBAL_SOURCE" \
  --context-entrypoint sources/0/notes.md --state-dir "$GLOBAL_STATE_DIR" --approve

Do not supply --project-root, --include-history or --history-ref for global capture. The result has no project Git history or push; it reports the snapshot and its scope-local locator. It does not rewrite GLOBAL_CONFIG to remember that result. Keep project and global permissions separate; secrets and live runtime homes remain excluded even if allowlisted. Global one-shot capture does not configure a periodic service. Use the separately approved global setup and schedule commands below; project hook commands never cover globals.

Configure an independent global schedule

Use the same explicit global identity, config, source order, root and context entrypoint. Keep GLOBAL_CONFIG, GLOBAL_STATE_DIR, helper scripts, interpreter and saved setup result outside selected source trees. Choose GLOBAL_SETUP_RESULT as a new private mode-0600 file outside those trees. After approving storage qualification and initial capture:

(
  umask 077
  set -C
  python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" global-setup \
    --scope global --identity "$GLOBAL_SCOPE_ID" --global-config "$GLOBAL_CONFIG" \
    --backup-root "$GLOBAL_BACKUP_ROOT" --source "$GLOBAL_SOURCE" \
    --context-entrypoint sources/0/notes.md --state-dir "$GLOBAL_STATE_DIR" --approve \
    > "$GLOBAL_SETUP_RESULT"
)

Require exit zero and status: "configured" before using that result. It records actual software readback and the original backing identity, not hardware crash durability or saved consent. Never fabricate or edit it. A replaced store requires separate setup review; installation will not requalify it. Setup alone installs no scheduler or hook.

Select literal absolute, no-symlink TRUSTED_PYTHON and TRUSTED_CRONTAB paths from the trusted host, never from recovered data. --service-qualified and --exclusive-writer assert actual operator evidence of a running scheduler and exclusive table-write authority; neither a binary nor these flags establishes that evidence. Preview without writing:

python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" global-preview \
  --scope global --identity "$GLOBAL_SCOPE_ID" --global-config "$GLOBAL_CONFIG" \
  --backup-root "$GLOBAL_BACKUP_ROOT" --source "$GLOBAL_SOURCE" \
  --context-entrypoint sources/0/notes.md --state-dir "$GLOBAL_STATE_DIR" \
  --setup-result "$GLOBAL_SETUP_RESULT" --python "$TRUSTED_PYTHON" \
  --crontab "$TRUSTED_CRONTAB" --service-qualified --exclusive-writer

Review the result and blockers. Only with fresh approval for that unchanged scope:

python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" global-install \
  --scope global --identity "$GLOBAL_SCOPE_ID" --global-config "$GLOBAL_CONFIG" \
  --backup-root "$GLOBAL_BACKUP_ROOT" --source "$GLOBAL_SOURCE" \
  --context-entrypoint sources/0/notes.md --state-dir "$GLOBAL_STATE_DIR" \
  --setup-result "$GLOBAL_SETUP_RESULT" --python "$TRUSTED_PYTHON" \
  --crontab "$TRUSTED_CRONTAB" --service-qualified --exclusive-writer --approve

The state-local global-tick --tick launcher is pinned to the approved interpreter and helper bytes. A once-per-minute scheduler check captures only when the configured interval is due; global ticks call no Git, subprocess, model or publication path. They report the oldest selected saved-context age when it can be proven, otherwise null rather than a fabricated age. Captures use the last saved notes, not new semantic context.

Each tick rereads the explicit global policy. policy.backup.enabled: false, invalid settings or a changed source/root/interval/limit policy refuses further captures before writes. Changing settings does not retarget an old installation: remove and review a new setup instead. A missing or changed runtime helper fails safely until reviewed reinstall.

Removal needs fresh approval, the exact original identity/state and continuing scheduler qualification; it can run from a fresh process even after interrupted installation/removal:

python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" global-remove \
  --scope global --identity "$GLOBAL_SCOPE_ID" --state-dir "$GLOBAL_STATE_DIR" \
  --crontab "$TRUSTED_CRONTAB" --service-qualified --exclusive-writer --approve

Removal confirms the owned scheduler block is gone before deleting unchanged launcher, binding and ownership files. It preserves unrelated jobs, snapshots and existing state.json. Blocked/uncertain readback retains recovery records; inspect blockers and retry with fresh consent, never delete receipts to force success. Verify actual scheduled execution separately: fixture success and software readback do not establish live host coverage or durable storage.

Host connections require separate qualification

checkpoint_cli.py host-preview and host-install expose the connection API for an approved Linux local-repository project scope with an existing verified snapshot and approved attached-branch history. They require the exact successful SETUP_RESULT described above. Repeat the unchanged setup identity, project, backup root, source list, context entrypoints, history flags/ref and policy. The connection uses the receipt's recorded storage identity: a missing or changed backing store is refused, never requalified or replaced merely because the pathname is the same. Changed setup scope or owned settings requires renewed setup review, not an edited receipt.

Select TRUSTED_PYTHON as an explicit literal absolute path to the trusted host's supported Python interpreter. Do not obtain executables from a snapshot or infer trust from presence. Use the existing private STATE_DIR outside the project, source trees and backup store. The supported hook directory is the repository's empty .git/hooks; foreign hooks or an external hook manager block installation rather than being overwritten or bypassed. The connection uses Git's reference-transaction hook and the bounded checkpoint runtime; it never rewrites global core.hooksPath.

python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" host-preview \
  --project-root "$PROJECT_ROOT" --scope project --identity "$SCOPE_ID" \
  --backup-root "$BACKUP_ROOT" --source "$APPROVED_SOURCE" \
  --source "$PROJECT_ROOT/.thunderkit/HANDOFF.md" \
  --context-entrypoint .thunderkit/HANDOFF.md \
  --include-history --history-ref "$WORKING_REF" --state-dir "$STATE_DIR" \
  --python "$TRUSTED_PYTHON" --setup-result "$SETUP_RESULT"

Review the hook path, periodic command and blockers. A preview is read-only, not approval. After separate fresh approval for this exact host connection:

python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" host-install \
  --project-root "$PROJECT_ROOT" --scope project --identity "$SCOPE_ID" \
  --backup-root "$BACKUP_ROOT" --source "$APPROVED_SOURCE" \
  --source "$PROJECT_ROOT/.thunderkit/HANDOFF.md" \
  --context-entrypoint .thunderkit/HANDOFF.md \
  --include-history --history-ref "$WORKING_REF" --state-dir "$STATE_DIR" \
  --python "$TRUSTED_PYTHON" --setup-result "$SETUP_RESULT" --approve

Hook installation writes private connection/ownership state and activates the repository hook; it does not install a periodic job. Loading a skill and running project setup establish neither authority nor daemon activation. Full host activation is separately approved, not an automatic consequence of these examples.

Preview, install and remove the project schedule

After a successful host install, select TRUSTED_CRONTAB as the explicit literal absolute path to the trusted host's crontab executable. --service-qualified and --exclusive-writer require actual operator evidence of a qualified running scheduler service and exclusive table-write authority. They are assertions of prerequisites, not probes or switches that create those conditions. An installed binary alone proves neither. The driver uses read/compare-and-swap replacement and preserves unrelated crontab bytes.

python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" schedule-preview \
  --project-root "$PROJECT_ROOT" --state-dir "$STATE_DIR" \
  --crontab "$TRUSTED_CRONTAB" --service-qualified --exclusive-writer

Inspect the proposed owned block and blockers. With fresh approval for this schedule:

python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" schedule-install \
  --project-root "$PROJECT_ROOT" --state-dir "$STATE_DIR" \
  --crontab "$TRUSTED_CRONTAB" --service-qualified --exclusive-writer --approve

The once-per-minute command checks whether the configured interval is due; it does not capture every minute unconditionally. A generated launcher or fixture test does not prove idle-time backups on the actual host. Verify the scheduler's real execution and returned capture result separately before claiming coverage.

To disable capture without granting new authority, set portable project policy backup.enabled to false in .thunderkit/continuity.json through an approved project edit. The installed host boundary checks that current policy and refuses capture when disabled; this does not remove the hook/job. Other installed settings (including interval, retention, sources and storage binding) stay frozen and require reviewed reinstall, not a live policy edit presented as reconfiguration. A push preference never supplies publication authority.

Remove the owned schedule before removing the hook, with fresh removal approval and continuing scheduler qualification/exclusive-writer evidence:

python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" schedule-remove \
  --project-root "$PROJECT_ROOT" --state-dir "$STATE_DIR" \
  --crontab "$TRUSTED_CRONTAB" --service-qualified --exclusive-writer --approve

python3 "$SKILL_ROOT/scripts/checkpoint_cli.py" host-remove \
  --project-root "$PROJECT_ROOT" --state-dir "$STATE_DIR" --approve

Removal reloads private ownership records in STATE_DIR, so it can run from a fresh process. If installation or removal reports a blocked/uncertain result, retain those records and inspect the exact blockers before retrying with fresh approval. Do not delete ownership files to force cleanup. Only unchanged owned integration is removed; unrelated jobs, changed files and snapshots are not deleted. Host removal refuses while its owned schedule remains. These commands remove connections, not the original setup's private configuration or backups.

Ref events and periodic reconciliation preserve the current authorized reachable branch graph; they cannot prove capture of transient or deleted unreachable commits. Idle ticks capture the last saved context and report its age, not invented semantic updates. Missing coverage remains a blocker to full periodic/every-commit protection, not an implicit choice of reduced coverage.

Native workers remain delivery-disabled and tk-ship remains preparation-only. Only a separately approved checkpoint controller can push an exact working-branch checkpoint through current in-process authority after its review/test and ownership gates. A fresh PushGrant binds the exact repository, remote and ref; it is never stored in configuration or restored from backup. There is no force, tag, PR, publish, merge or default/protected-branch write on this path. Machine-loss durability, connection activation and observed idle execution remain separate operator gates; installing a skill or running project setup establishes none of them.

Development

make run_tests   # frontmatter validator + roster/leakage checks + site drift gate
make lint        # py_compile + shellcheck (best-effort)
make site        # regenerate the static docs site → site/_site

The test/build helpers use stdlib-only Python; the npm pointer uses Node built-ins. make run_tests works offline on a fresh checkout. CI runs the tests, a secrets/leakage denylist grep, and the site build on every push; a separate workflow publishes the docs site to GitHub Pages.

Why it works

Most multi-agent setups fail at scale for three reasons, and thunderkit answers each:

The failure thunderkit's answer
One model does everything and its weaknesses show everywhere Three model classes — the right model for planning, execution, and review
"Parallel" agents collide on the same files Lanes are file-disjoint by construction, each in its own worktree
Nothing checks the work; "done" is a claim Every lane has a verify command; review is cross-family; ship fails closed

Releasing

The Release workflow automatically publishes stable releases on pushes and merged PRs to master in thunderock/thunderkit. Feature branches cannot publish. There is no release PR or version commit: canonical v<version> Git tags are the version authority, not the source package.json. Release history continues in GitHub Releases.

Automatic versions. The highest stable canonical tag is the base; prerelease tags are ignored. Conventional Commits in the non-merge range since that base determine the strongest bump:

Base version Breaking change feat fix, docs, tests, CI, chores and other commits
Before 1.0.0 minor patch patch
1.0.0 and later major minor patch

Every nonempty non-merge range produces at least a patch; an empty range does not release. The base must be an ancestor of the source. Without a stable tag, the source package's canonical stable version is the bootstrap candidate, not an increment. If that version is occupied, release stops rather than guessing: choose an unused exact version after resolving initial package setup. Legacy tags can establish history but do not prove that old npm artifacts match this workflow.

Manual exact versions. Dispatch the retained release-please.yml filename on master with an optional version and npm_tag. For example, a maintainer can request:

gh workflow run release-please.yml --ref master -f version=1.0.0

Any unused canonical exact stable or prerelease version is allowed, including arbitrary jumps; it need not be the next major. Do not include a v prefix, range, whitespace, leading numeric zeros or build metadata (+...). Empty version selects automatic stable versioning and cannot be combined with a nonempty npm_tag. A supplied version is never silently bumped on collision.

Channels must match ^[a-uwyz][a-z0-9-]{0,63}$: 1–64 lowercase characters, starting with a letter other than v or x, followed by lowercase letters, digits or hyphens. Examples include latest, next, beta and maintenance-0; 1.x, v1, x and Latest are invalid. Stable versions default to latest, prereleases to next; prerelease + latest is forbidden. A version below the highest stable Git tag, including an older prerelease, requires an explicit non-latest channel. New explicit manual releases may intentionally move such a channel backwards. latest must never regress: writes must exceed the observed stable npm latest and cannot trail the highest stable Git tag. Git history alone is not proof of the registry's current channel.

What gets published. Both jobs use Node 24, npm 11.19.1 and Python 3.12 on hosted runners. The gate checks the source SHA, validates inputs, stamps a clean copy of that exact tracked source, and creates a real tarball. It inspects safe archive members against the source inventory and exercises the packed CLI before running tests, lint and the site build on the stamped source. The repository's package version stays unchanged; the npm package and its CLI report the released version. No developer working directory is published.

After checking that the tarball bytes survived the gates unchanged, the workflow uploads only release-plan.json and package.tgz as release-<runId>-<attempt>. The publisher downloads the exact immutable artifact ID from the same run, verifies the record's SHA-256 against the gate output, and checks request/source bindings, tarball size and SHA-512 integrity. It then creates an immutable annotated tag binding source, version, channel and tarball integrity, publishes that same tarball through npm OIDC trusted publishing with provenance, and creates the GitHub Release only after registry identity checks succeed. After an accepted upload, it checks availability every 30 seconds for up to 30 minutes: npm scans new versions before making them available. It never repeats the upload while waiting. Missing visibility at the deadline fails with E_REGISTRY_PENDING, not a false integrity mismatch; conflicting bytes, tags, channels or failed reads still stop immediately. The publish job has a 50-minute total timeout, including setup, the bounded upload and this wait. A pending timeout is not proof the version was never uploaded; inspect its availability before retrying or advancing to another version. Tag creation uses command-scoped bot identity; no persistent Git credentials or npm token is used.

Retries and recovery. Rerun the original workflow run after an interruption, rather than dispatching today's source. A failed-publisher-only rerun reuses the successful gate artifact; its earlier attempt is accepted only within the same run. A full rerun must recreate bytes identical to the annotated reservation, preserving its original version and channel. Omitted npm_tag restores that channel; a different supplied channel fails. Each invocation stops at its first error, even when a write may have succeeded but its acknowledgement was lost. The next same-run retry reads actual state and performs only missing operations, never replacing a tag or republishing a version. npm presence alone is insufficient: SHA-512 must match the reservation.

A matching historical npm version may finish its GitHub Release after a newer channel has superseded it, without moving the channel back. Missing, invalid or rewound channels stop recovery; there is no automatic channel repair or token fallback. Foreign packages, conflicting tags and inconsistent GitHub releases also stop. A new automatic release may advance past a valid ancestor reservation only when current npm metadata and an exact GitHub Release lookup both confirm that version was never published. The old tag stays immutable and is not reused; version selection still advances above it. A partial publication (npm present but GitHub missing), inconsistent identity, or unavailable evidence still blocks with E_BASE_INCOMPLETE. A fresh stale automatic source skips; a fresh stale manual source fails. Reserved retries must still belong to master history and cannot publish a missing old version below a newer stable tag or over newer latest. The shared release concurrency group serializes these writers. Expired artifacts require a full same-run rerun with identical bytes. Unreproducible bytes or an expired GitHub rerun window require separately authorized operator recovery, not today's source.

Enablement and cutover. Keep the npm trusted-publisher binding on release-please.yml and authorize direct publish, not stage-only access. Initial npm package/trusted-publisher setup, public provenance eligibility and GitHub tag/ruleset permissions are maintainer prerequisites; a package that does not yet exist may require separately authorized initial publication before trusted publishing can be configured. This workflow cannot bootstrap authentication itself.

Before enabling this route, drain or cancel old release runs, stop historical reruns and other manual publishers, remove any obsolete publish.yml trusted-publisher binding, and close any obsolete release-please PR after merge. Deleting the old workflow/config files does not cancel queued runs or revoke their historical definitions. This must be the sole package writer; uncoordinated npm maintainers or other workflows can race a registry read and channel write. Do not rewrite master history or release tags. Live OIDC exchange, permissions and these cutover conditions must be verified separately; offline tests do not certify them.

The repository-wide npm-release concurrency group never cancels a running release. GitHub's default queue retains only one pending run and may replace it, including a manual dispatch; ordering is not a guarantee of commit order or one release per push/dispatch. The eligible source that actually runs covers the full non-merge range since the completed stable base.

Star History

Star History Chart

A fleet of models, decomposed to win. · MIT © Ashutosh Tiwari

About

Opinionated skills for working in very large codebases — by decomposing big changes into parallel lanes and routing each to the best model across a heterogeneous agent fleet

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages