Worktree lanes, path claims, and scoping

The rules file every session loads carries the short normative form of each rule. This document is the deep home the rules file points at: the same rules with the reasoning, the worked failure modes, the flag matrices, and the edge cases that decide close calls. Read the section you need before the action it governs — nothing here is optional background, it is simply longer than a startup channel can carry.

Execution levels and worktree lanes

An execution level is an ordered capability band whose options are exact launchable selections; a registering session is labeled with the level whose option matches its harness, model, and effort (universe levels, or a project's session-routing override — see execution levels). Its name and glyph identify the group on the board and dashboard; it carries no skill permissions. Workflow bindings select stage skills, and explicit staffing assigns work.

A worktree lane is a registered code checkout protected by the item work claim. Its branch, path claims, verification, and merge contracts isolate implementation work. An execution-level label never grants checkout authority.

Worktree discipline

  • NEVER use --no-worktree unless the user explicitly asks. NEVER write implementation code on main.
  • When the pinned definition selects implement with worktrees=single_implementation_lane, the same session that ran preflight and acquired the claim continues through that bound implementation/review segment. When it selects conduct with generated tasks and worker/integration lanes, each dispatched lane acquires its own work claim. Authority over every lane is the work claim (validated per call by lint_session_cwd); launcher cd behavior is only convenience.
  • Do not jump across a binding or invent a destination stage. The active skill advances only inside its pinned half-open segment; the next skill starts at through_stage_id as a fresh command entrypoint. Delivery stages and no-run shortcuts apply only when the pinned delivery policy and skill binding declare them.
  • Pre-implementation authoring activities run on main unless their pinned policy explicitly provisions a lane. Every development-entry skill — Dash, Issue implementation entry, Epic task activation, Blitz — creates or reuses the item's registered worktree immediately after the claim, read, and minimal survey its own preparation requires, and before any deeper investigation or edit; Issue implementation entry and Epic task activation already provision that worktree atomically before any code is read, so Dash and Blitz carry the explicit ordering in their own skill steps. Preparation starts from verified-current upstream, whatever the workflow. One shared step in worktree preparation (repo_upstream_freshness, reached through worktree_preflight.run_preflight and create_worktree) fetches the branch the item's project declares as its default from the remote git records as tracking it — neither name assumed, and neither the checked-out branch nor the checkout the caller happens to stand in is a substitute for one: an item whose project declares no default branch (the folder-project floor) is gated on nothing rather than on an unrelated repository. Freshness is established or preparation refuses: a remote-backed project whose remote cannot be read — failed fetch, no remote recorded as tracking the branch, unreadable branch or comparison — reports verified=False, names no revision to start from, and blocks as upstream-unverified. There is no implicit offline fall back to the local branch, because that is precisely the silent stale start the step exists to prevent; a project with no remote at all is the different, verified answer no_remote (local-only, silent). On an established reading, a local default branch with no commits the remote lacks is fast-forwarded and the new lane cut from that verified revision; an uncommitted change blocking the fast-forward, or a default branch checked out elsewhere, leaves everything untouched and still hands the lane the fetched upstream revision, which is current. A diverged branch refuses instead (upstream-stale, pending reconciliation): its local commits are preserved and reported, never replayed or discarded, but the only base a lane could take there is local — missing the commits just fetched — so the lane is not created rather than started stale. Ahead-only local is not divergence: it already holds every upstream commit, so it still starts a lane. Nothing is ever reset, rebased, or force-updated — a resumed lane is reported on only. Laneless work (worktrees=none, --no-worktree) commits onto that branch itself, so it blocks as upstream-stale whenever the branch is behind its remote at all, diverged included. Deduplication is scoped to one preparation, never to the process: preparation_scope() shares one reading across the reads of a single preparation, so a long-running API process reads the remote again for the next one; outside a scope every call fetches, and use_cache=False forces a fresh reading inside one. Session start and post-landing checkout sync run the same primitive. The outcome is in actions_taken as upstream:<state> and anything actionable is an envelope note beginning upstream freshness:. Preparation envelopes identify the item by public_ref; a refused item-detail read names item_detail_unavailable and stops before claiming or creating a lane.

Path claims

  • File Budget and path claims are independent policy axes — never fuse them. Before authoring or gating either surface, consume result.effective_policies.file_budget and result.effective_policies.path_claims from registered workflows.item.get; never reconstruct them from raw pinned policies or posture. The universal 350-line authored-file limit always applies, even when File Budget policy is off. File Budget/path-claim parity applies only when both effective axes are enabled. With File Budget off and path claims on, derive claim paths from the execution document, instruction, or conflict survey. With File Budget on and path claims off, use the budget for sizing and conflict evidence without registering a claim. With both off, neither artifact is required.
  • Claimed paths do not narrow work item scope. A work item's scope must never be narrowed, descoped, or rewritten solely because a required path is already claimed. If the right fix touches a file, the file stays in the work item and its execution artifact; when enabled, it also stays in ## File Budget and the path-claim attempt regardless of who holds an overlapping claim.
  • Active path claims are coordination/dependency/blocking facts, not scope facts — a live claim signals who coordinates a path; it never authorizes omitting a required file from a work item. item_dependencies rows are directional: the dependent waits, the blocker does not (a blocks edge PREFIX-A→PREFIX-B gates B's activation, not A's). Storage is dependent_item_id / blocking_item_id (integer FKs to items.id); public PREFIX-N is the list/API token. Use yoke items dependency list PREFIX-N for routine reads. The hard-block gate and overlap classifier agree on direction — the blocker classifies NONE and activates immediately; the dependent classifies HAS_SERIAL and serializes. The repair sweep flips legacy upstream-blocker rows stuck at state='blocked' → planned. Accepted overlap remediations: classify the overlap (independent same-file edits → coordination_only edges, no gate; order-dependent → --gate-point activation with directional evidence; ambiguous → escalate), leave the candidate state="blocked" only when it's the dependent side of real upstream coordination, wait for the holder to release, coordinate with the holder, ask the holder to narrow or cancel, or operator override (path-claim-override) as last resort (never for the structural-upstream case the classifier already passes). Removing the required file is never an option.
  • "Avoid the overlap" never means "omit the file." It only authorizes coordinating with the holder or recording a dependency/blocker — never a smaller scope artifact. /yoke idea and /yoke refine keep the file in every enabled File Budget or claim surface; the workflow resolves the conflict downstream.
  • Coordination-only edges are agent-attested. When two items touch a shared path with semantically independent edits, a coordination_only item_dependencies row attests the overlap is compatible without gating lifecycle/activation (same-hunk collisions resolve at merge). Only authoring-phase agents write them — Architect at /yoke shepherd plan, or Idea Phase 3 / Refine readiness-repair — via yoke claims path coordination-decision-build, each with an authored rationale (HC path-claim-coordination-rationale flags missing/stale ones). Engineer/Tester/Boss/Conduct/Polish/Advance/Usher do NOT author them — runtime collisions there route back to /yoke refine. Un-attested overlap stays strict INCOMPATIBLE; auto-serialization without an authored row is rejected.
  • Claims coordinate on physical files, not path strings. For an in-repo symlink (e.g. CLAUDE.md → AGENTS.md), the symlink resolver pairs the symlink target_id with its canonical target_id so the claim covers both — a claim on only CLAUDE.md and one on only AGENTS.md overlap at registration (one coordination unit). External-target/dangling symlinks emit PathTargetSymlinkSkipped and stay symlink-name-only. HC-path-claim-symlink-coverage flags any non-terminal claim covering a symlink-source without its canonical target. /yoke idea/refine advise listing the canonical name; the claim covers both either way.
  • Typed owner separates authority from provenance. Each path_claims row carries owner_kind ∈ (item,session,process) + the matching owner_item_id/owner_session_id/owner_work_claim_id. Registration provenance lives on registered_by_actor_id/registered_by_session_id; readers use typed owner fields for authority, so an item-owned claim survives the registering session ending. HC-path-claim-owner-kind validates only the typed owner shape. The board shows item-owned claims as item file counts and only true session/process claims in the orphan form.

Project scoping

  • One work item = one project. The project field = where code deploys (project=external-webapp → External webapp repo, project=yoke → Yoke repo); scope is defined by where changes land, not where the idea came from. Never mix deploy targets in one item or its generated task graph. All work items live in the Yoke backlog regardless of target.
  • Cross-project work → linked companion items, one per project. Ambiguous target → ask; never silently default to yoke for work targeting an external system. Work that mandates changes in a second project gets a companion item filed in THAT project, linked to the first by an item_dependencies edge; one session may claim both items and execute both lanes together, because work claims and lane authority are already project-agnostic. There is no second-repo lane on a single item: worktree preparation resolves each item's own project checkout from the machine mapping (yoke project register <checkout> --project-id <id> adds a missing one) and refuses rather than borrowing the session's repo, so a lane always lives in its item's project; repair a wrong-repo lane with yoke item-worktrees path-record. Path claims stay single-project — path_targets rows are project-scoped, so one claim never spans repos. A change to a contract another project consumes is cross-project by definition. The companion item is mandatory in the consuming project, and an instruction that excludes redesigning that consumer never waives adapting it to the new contract. Delivery evidence must show the real consumer built against the exact candidate revision; a producer-only green run proves the producer and nothing else.

Lifecycle and routing

  • Canonical human guide: .yoke/docs/reference/lifecycle.md. Each item pins immutable workflow_id / workflow_version_id; that definition owns ordered stages, transitions, target-stage gates, policies, entry surfaces, and registered skill bindings.
  • Never route by a remembered workflow name or copied progression. Read yoke workflows item get PREFIX-N, then yoke workflows version get WORKFLOW VERSION; the binding whose half-open interval contains the live stage selects /yoke <skill_id>.
  • A binding's through_stage_id is a fresh command/claim handoff. Worktree and generated-task shape come from policies.worktrees and policies.generated_children, not from a workflow-id branch.
  • Frontier computation is documented in .yoke/docs/reference/charge-frontier.md; item routing follows the pinned workflow binding. Yoke core derives harness capabilities server-side from the harness manifest — harnesses do not self-report YOKE_SUPPORTED_PATHS. That manifest is where capability truth lives: read runtime/harness/<harness-dir>/manifest.json (directory claude, codex, or cursor; executor claude-code resolves to claude; contract: runtime/harness/manifest-schema.md) before stating what a harness can do, and never restate one of its facts in prose — agent_wake carries the wake answers, session_control the messaging ones.

Destructive operation discipline

  • Never run git reset --hard, git checkout --, git checkout -f <branch>, git restore --worktree, git clean -f/-fd/-fdx, git stash drop, git stash clear, or rm on files unless confirmed Yoke-managed or user-authorized — these silently discard tracked-but-uncommitted changes, untracked files matching the pattern, or saved stashes.
  • The PreToolUse destructive-git lint enforces this: it refuses only when it identifies BOTH a dangerous verb AND threatened local state (modified files, untracked files in the target subtree, or live stashes). Mode from project-local .yoke/lint-config guard key lint_destructive_git (protected; default deny). The token # lint:no-uncommitted-wipe-check cannot suppress the check — it is audit-only (outcome=suppression_attempted) and does NOT unblock. For worktree wipes, stash or commit first, or use a non-destructive verb (git reset --soft, git restore --staged, git stash). For git stash drop / git stash clear, preserve the patch (git stash show -p / git stash apply) and have the operator run the drop.
  • git stash push arg-order: -m/--message MUST come BEFORE -- — everything after -- is a pathspec, so a message flag there is silently consumed as a filename (stash gets git's auto label, no rationale). The stash-arg-order lint enforces this (suppression # lint:no-stash-arg-order-check, audit-only). Safe shape: git stash push -u -m "reason" -- <paths>.
  • User messages mid-sequence are checkpoints — stop and answer before proceeding.

Yoke authority over harness defaults

  • Skill says auto-X → do auto-X, no asking. Harness "confirm before issues/push/shared-state" defaults don't apply when a Yoke skill/rule directs autonomous execution. Skills with autonomous mandates — /yoke conduct, /yoke shepherd, /yoke usher, /yoke charge, /yoke steer — carry directive lines like "run sync automatically" / "don't wait for input" / "immediately continue"; follow them. Built for kick-off-and-walk-away — an unrequested pause is a regression. Invoking the skill authorizes it: conduct→GitHub sync (issues/labels/comments), usher→merge+deploy, shepherd→worktrees+subagents. Harness=substrate, Yoke=program. Yoke wins.
  • Security always holds. Never overrides <critical_security_rules>/prohibited actions (no banking/credential entry, no permanent deletes outside sanctioned paths, no auth bypass, no command injection) — collaboration cautions only.
  • Test: "Did the skill say ask?" not "would the harness ask?" Silent → harness default; unsure → reread the skill.

Worktree lanes, path claims, and scoping

Worktree lanes, path claims, and scoping · Yoke