internals/skills/cutover-policy/SKILL.md
Authoritative reference for the "Hard Cutover by Default" policy governing schema changes, API renames, and deprecations. Forbidden patterns, required deliverables, rationale, examples from this repo, and the no-exception enforcement: plans are authored as full-scope single-phase cutovers and executed end-to-end regardless of estimated time, context, or scope. MUST be invoked when planning or reviewing any breaking change to Go types, YAML field names, CLI flags, or OCI labels.
npx skillsauth add overthinkos/overthink-plugins cutover-policyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Every schema change, API rename, or deprecation in Overthink ships as a single hard-cutover PR. This applies to BOTH code (Go types, exported functions, CLI flags, OCI labels) AND config (overthink.yml, deploy.yml, layer.yml, vm.yml field names and shapes).
A cutover may NEVER be phased — not at plan authoring, not at execution. There is no pre-approval split, no post-approval split, no phased rollout, no grace period, no "author it as two plans" fallback. Plans are authored as full-scope, single-phase cutovers regardless of estimated time, scope, or context. Every cutover executes end-to-end through R10 in the SAME conversation. ALWAYS push as far as you can; compact context and continue, as many times as it takes. An approved plan is a CONTRACT; implement it as written.
This skill is the source of truth for the policy. CLAUDE.md links here rather than re-stating the full policy inline. The project's UserPromptSubmit hook at .claude/hooks/runtime-verification-reminder.sh mirrors the key directives and fires at every user prompt.
A "phase" is the whole cutover. "Tasks" are the breakdown inside it. Never treat tasks as separate phases with their own sign-off.
TaskCreate. The plan file names the cutover, not a sequence of cutovers.disposable: true bed FIRST (Risk Driven Development — never trust a skill / CLAUDE.md / code for a high-risk call; the archetypal one is whether this layer composition, at its latest versions, builds / deploys / runs together). Transitional aliases, legacy-accepting code paths, or temporary dual-dispatch are permitted DURING implementation. They MUST be deleted before the end of the same cutover.ov image build, ov deploy add + ov eval live, and the R10 fresh-rebuild re-verification AFTER all tasks are marked complete. Testing between tasks is cheap smoke-confirmation; the acceptance gate is the full-stack run against the final code.Forbidden: "Phase 1 landed, Phase 2 pending" as a stopping point. That leaves the system half-migrated — legacy paths live alongside new paths, migrations not yet run, tests passing for some beds and not others. Every historical instance of that pattern in this project left dead code and untested integration points that bit users later.
Splitting a cutover across conversation turns is ABSOLUTELY FORBIDDEN, with NO exception — at plan authoring or at execution. Once a plan is approved, it executes end-to-end through R10 in the same conversation. ALWAYS push as far as you can. Compact context and continue, as many times as it takes. Time, context space, session budget, scope size, and "the work turned out to be large" are NEVER valid stop reasons.
Do not propose phasing, narrowing, or scope reduction at plan-authoring time. Do not negotiate a split mid-execution. Do not silently downgrade. An approved plan is a CONTRACT; implement it as written. The ONLY valid stop conditions, at any stage, are (a) an error you cannot resolve that requires user input, or (b) the plan contradicts itself, CLAUDE.md, or a loaded skill — STOP and ask in either case; do NOT commit a partial state.
The one-phase rule forbids splitting a SINGLE cutover's scope across turns or plans. It does NOT forbid a genuinely separate issue, surfaced mid-cutover, from getting its own cutover. Classify every issue the cutover surfaces:
main.The discriminator: would shipping the current cutover WITHOUT this fix leave the tree correct and the cutover's claim true? Yes → non-blocking (its own immediate-next cutover). No → blocking (this cutover). Unsure → blocking.
Objective test for "separable". The issue is separable ONLY if the current cutover's OWN R10 (its eval-coverage + fresh-rebuild) passes and proves the cutover's claim WITHOUT the fix — the fix is neither exercised by, nor changes the verdict of, this cutover's test coverage. A fix that would alter this cutover's R10 result or its eval-coverage gate is BLOCKING.
This does not loosen the no-split rule. "No pre/post-approval split" and "no author-it-as-two-plans" forbid carving ONE change's scope into two to avoid doing it all now. The non-blocking path applies to a DIFFERENT, separable change — one this cutover surfaced, or any other issue the AI finds — never to the current change's own scope. The AI opens that next cutover autonomously (it does not wait for authorization); it pauses to ask only at a genuine unexpected/unplanned crossroad. Mislabeling a blocking issue "non-blocking" to ship faster is the forbidden split wearing a disguise; when unsure, it is blocking. (See CLAUDE.md R2 — this section operationalizes the blocking/non-blocking half of it.)
deprecated.go shims or type aliases that re-export removed identifiers.Each of these has a specific failure mode that has occurred historically: the first three drag legacy-state complexity forward indefinitely; the fourth multiplies the test matrix; the fifth is the anti-pattern R2 in CLAUDE.md forbids on a per-plan basis (no "pre-existing" / "out of scope" / "follow-up PR" classifications).
MigrationStep appended to the single ov migrate chain (ov/migrate_registry.go), stamped with the cutover's CalVer (strictly greater than the current HEAD; the calver-schema stamp stays last). Every cutover is one idempotent step on the one ov migrate command — there are no per-cutover sub-verbs. Running ov migrate twice is a no-op. See /ov-build:migrate.v<YYYY.DDD.HHMM> (current UTC push time) on the cutover's push — ONE per push, decoupled from the overthink.yml version: field (the schema version, bumped only when a MigrationStep raises LatestSchemaVersion()). Tag EVERY push, including one that does NOT bump version: (content removal — a submodule extraction, an image drop). Tags are immutable — only ever added, never moved or force-pushed. Day-of-year is NOT zero-padded; compute v$(date -u +%Y).$((10#$(date -u +%j))).$(date -u +%H%M). Each ov-project repo (one with overthink.yml) is tagged at its own push time; plugins / pkg/arch are exempt. See /ov-build:migrate "Per-push release git tags" and CLAUDE.md "Post-Execution Policies".git grep '<deleted-id>' returns ONLY historical mentions in CHANGELOG.md or migration help-text.CHANGELOG.md entry (repo root) recording the cutover narrative. Historical content lives ONLY in CHANGELOG.md; CLAUDE.md and the skills state the new standing rules forward-looking, with no history. See CLAUDE.md "Where things are documented"./ov-internals:strict-policy. Every failure during the cutover triggers /ov-internals:root-cause-analyzer BEFORE any remediation (R1). Every issue surfaced is fixed in the cutover or escalated (R2). Duplication is refactored on first surface (R3). Workarounds are forbidden (R4). Stale references are swept (R5).Phased migrations accumulate mid-state complexity that, in practice, rarely gets removed. "We'll clean up in Phase 2" is a fiction that the history of this project has shown over and over. Making hard cutover the default across the project closes the loophole where this behavior sneaks in via PRs whose plans didn't explicitly call for a clean cutover.
There is no pre-approval split, no post-approval split, no phased rollout, no grace period, no "resume in the next session", no "author it as two plans" fallback. Plans are authored as full-scope, single-phase cutovers regardless of estimated time, scope, or context. Phase / scope / time concessions are FORBIDDEN at plan authoring AND at execution.
Every cutover — regardless of estimated effort — runs as ONE phase in the SAME conversation through R10. ALWAYS push as far as you can. Compact context and continue, as many times as it takes.
The ONLY valid stop conditions, at any stage, are:
In either case STOP and ask. Do NOT silently downgrade scope, narrow tests, abbreviate the R10 matrix, or commit a partial state. An approved plan is a CONTRACT; implement it as written.
Forbidden internal-voice triggers (each is a confession, NOT a defence):
If you catch yourself forming any of those: STOP forming them. The plan executes as written.
One PR, one commit, with these deliverables:
MigrationStep on the ov migrate chain that harvests legacy fields into the new shape, preserves pre-existing user keys, and never clobbers customizations.ov migrate.plugins/, README.md, or CLAUDE.md (R5 grep self-test).CHANGELOG.md (the only place the history lives).The commit uses the Conventional Commits ! breaking-change marker; the body lists every deleted identifier, every removed YAML field, and every updated test. ov migrate is runnable against old projects from that commit forward, with no additional steps.
See CHANGELOG.md for the catalog of past cutovers that followed this shape — each took the same three steps: delete old surface + publish migration + hard load error.
The policy kicks in when the change is visible to consumers (YAML authors, other Go packages, OCI-label readers) AND removes something that was previously usable.
/ov-build:migrate — the single ov migrate command, its CalVer schema versioning, and the ordered migration chain (registry)/ov-internals:vm-spec — example output of a cutover (new types replacing deleted ones)/ov-internals:capabilities — example of coordinated label-map cleanup during a cutover/ov-internals:install-plan — shared IR that survived the cutover unchanged (non-example — additive extension of the DeployTarget surface)/ov-eval:eval 10 standards)Changes that touch this verb's output must reach a healthy deployment on a target explicitly marked disposable: true (see /ov-internals:disposable). Use ov update <name> to destroy + rebuild unattended on any disposable target. Never experiment on a non-disposable deploy — set up a disposable one first with ov deploy add <name> <ref> --disposable or mark a VM in vm.yml.
After committing the source-level fix, ov update the disposable target ONCE MORE from clean and re-run the full verification. A fix that passes only on a hand-patched target is not a real fix — it's a regression waiting for the next unrelated rebuild. Paste BOTH the exploratory-pass output and the fresh-rebuild-pass output into the conversation.
Unit tests + a clean compile are necessary but not sufficient. See CLAUDE.md R1–R10.
development
Claude Code multi-agent support in Overthink — sub-agents, dynamic workflows, and agent teams, and how each drives the existing `ov eval` disposable beds to test and verify. MUST be invoked before authoring or invoking an ov sub-agent / dynamic workflow / agent team, wiring agent-lifecycle hooks, or asking "which primitive should drive the R10 beds?".
tools
Mounts a virtiofs share tagged `workspace` at /workspace inside a VM guest via a systemd .mount unit. Use when a kind:vm entity shares a host directory into the guest and you need it auto-mounted (and re-mounted at every boot).
development
MUST be invoked before any work involving: the `kind: android` schema kind, a `target: android` deploy, the `apk:` layer package format (installing Android apps declaratively), AndroidDeployTarget, an in-pod emulator OR a remote/physical adb-endpoint device, or nested `pod → android` deployment. The first-class Android device + app surface that sits above `ov eval adb`/`appium`.
tools
Use when committing, branching, pushing, merging, tagging, creating PRs, or approving/merging PRs with gh — the feat/-branch, R10-gated, never-force-push landing workflow across the main repo + the plugins submodule + image/<distro> submodules. Covers sync-to-upstream, branch/worktree pruning, the fork+PR path for contributors without write access, and cross-repo @github landing order.