Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .agents/skills/agent-conduct/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Read `GOVERNANCE.md` "Verification Discipline" before reporting success on anyth
- **Run the repo's whole lint gate before every push**, not the parts that look relevant, because the tool most likely to catch a change is often the one it seems least about.
- **A launched process is not a result.** Report the output the wait produced, and where it produced none, that absence is the report. Never name an external cause the record does not carry.
- **A local clone is not the branch it names.** Fetch immediately before reading, or read the live ref, and name the ref and commit in any finding a local read produced.
- **A "does not exist" claim names the branch it was checked against.** A worktree's default branch is not necessarily the one the content lives on: in-flight content on a `release`-model repo lands on `develop` before `main`, per `GOVERNANCE.md` "Branching Model," so check that branch before reporting anything absent repo-wide.
- **A test asserts the mechanism it names, and a gate has to be watched failing.** A case that passes for an incidental reason is worse than no case, because it is later cited as evidence.
- **Platform-specific code is verified only on the platform it runs on.** Reasoning about PowerShell, macOS, or WSL-specific behavior from a different host is not verification, however closely it matches an already-tested equivalent elsewhere. State an untested structural match as exactly that, never in the words used for a tested fact, and when no agent in the loop has access to the target platform, say so and defer or ship it labeled unverified.

Expand Down
18 changes: 14 additions & 4 deletions .agents/skills/git-commit-conventions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,27 @@ scope-widened commit, a rewritten shared history, a destructive reset).
developer unless the developer has explicitly authorized committing for the current ask ("commit
this", "open a PR"). Authorization is scope-bound: it covers the commits that specific task
needs, not a blanket license for the rest of the session.
- **Stage by explicit path, never `git add -A` or `git add .`.** A blanket add stages whatever
else happens to be in the tree, and what it sweeps in is another task's uncommitted work,
landing in a commit whose subject never mentions it, committed by a session that never saw it.
That sweep has happened, which is why task isolation exists (the `repo-worktree` skill), and
isolation makes a shared tree rare rather than impossible. Name the files this task changed,
and let anything else stay unstaged.
- **"Commit" means commit and push.** An authorization to commit carries the push to the feature
branch the work belongs on, because nothing reviews a local commit. The Copilot review loop, the
required status checks, and the maintainer all read the remote, so stopping at `git commit`
leaves the review unstarted and the branch's state private to one machine, which reads as
progress while none of the gates have run. Push to the feature branch, never to a protected
branch, and never with `--force`. Holding a commit locally is the narrower case: it happens when
the developer asks for it, not by default.
- **Check `git status` for the maintainer's own uncommitted edits before committing.** The
maintainer hand-edits files live, often `README.md`/`HISTORY.md`, sometimes with an editor's
LF -> CRLF flip on top. If there are changes not made this session, ask whether to include them
rather than bundling half-finished work or stranding it in an unrelated commit.
- **Check `git status` before committing, and treat any change this session did not make as a
stop.** The maintainer hand-edits files live, often `README.md`/`HISTORY.md`, sometimes with an
editor's LF -> CRLF flip on top, and a sibling agent session sharing the tree leaves its edits
the same way. Whoever the author is, a change this session did not make is never bundled: ask
whether to include it, or leave it unstaged and say so, rather than committing half-finished
work or stranding it in an unrelated commit. An unexpected change in the tree is also the
signal to re-check isolation per the `repo-worktree` skill, since it may mean another task is
live in this checkout.

## Signing, verified not configured

Expand Down
1 change: 1 addition & 0 deletions .agents/skills/operational-vs-release-workflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ rather than guessing from the repo's contents.

## Branching (release model)

- **GitHub's repository setting for "default branch" reads `main`, but `develop` is where work starts and where in-flight content lives.** A worktree or clone that defaults to "the default branch" lands on `main` and can silently miss content that has merged to `develop` but not yet been promoted. Before branching off a change, or asserting something absent from this repo, check `develop`, not just whichever branch a tool defaulted to. See GOVERNANCE.md "Verification Discipline" on naming the branch a "does not exist" claim was checked against, and the `repo-worktree` skill, which owns the worktree-creation moment this base-branch choice is made at.
- `develop` is the integration branch. Feature branches -> `develop` is **squash-only**, which
keeps `develop` linear.
- `develop -> main` is **merge-commit only** (no squash, no rebase). Merge commits preserve
Expand Down
109 changes: 109 additions & 0 deletions .agents/skills/repo-worktree/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
name: repo-worktree
description: >-
Mandates and mechanizes task isolation in ptr727/ProjectTemplate fleet repos: every task,
including a continuation of a prior session's task, creates its own git worktree on its own
feature branch before its first file edit, based on the branch work starts on (develop on both
fleet workflow models unless the task is explicitly about main-only content, never whichever
branch a tool defaulted to). Also wraps the mechanics:
creating a worktree with git worktree add, the fleet layout convention, listing what is in
flight, and removing a worktree and its branch after merge. Use this whenever about to create
or edit files in a fleet repo, whenever starting or resuming a task, whenever the task's
branch is already checked out in a shared checkout, and whenever creating, listing, or
removing a worktree. Triggers even when the session was launched in the primary checkout or
the change looks like a one-line fix, because the primary checkout is the maintainer's own
surface and the incident this guards against was two sessions sharing one checkout, each
session's blanket add committing the other's uncommitted files.
---

# Repo Worktree

## Why This Exists

Two agent sessions once ran concurrently in the same primary checkout, on the same feature
branch, neither knowing the other was in the tree. One session's commits swept in the other
session's uncommitted files, so two commits landed carrying work their subjects never mention,
committed by a task that never saw it. No rule fired at the moment it was violated, which is the
first file edit: the commit-time and review-time skills all run after a sweep has already
happened. This skill is that missing task-start surface. `GOVERNANCE.md` "Repository Boundaries
and Write Safety" keeps the isolation law and wins on any disagreement, and the mechanics below
are this skill's own content.

## The Mandate

- **Every task isolates into its own worktree before its first file edit.** All new work begins
by creating a unique worktree (or clone) on its own feature branch. The primary checkout is
the maintainer's own surface, so a session launched there isolates before writing rather than
after noticing contention.
- **A continuation re-isolates.** A session resuming prior work finds its branch already checked
out somewhere and naturally resumes there, and that instinct is the hazard: a branch sitting
checked out in a shared tree is exactly how two sessions end up in one checkout. Create a
fresh worktree for the continuation and check the branch out there.
- **The moment is the first file edit, not the commit.** By commit time another task's
uncommitted work can already be swept into the staging area, so isolating late protects
nothing. Reading anywhere is fine, and the worktree exists before the first write.
- **Someone else's tree stays theirs.** A branch that changes when nothing you did changed it,
or an edit of yours reverted with no conflict, means another task is live in that tree, and
the response is to stop rather than to re-apply the edit, per `GOVERNANCE.md` "Repository
Boundaries and Write Safety".

## The Base Branch

Base the worktree on the branch work starts on for the repository's model, not on whichever
branch a tool defaulted to. GitHub's own "default branch" setting reads `main`, but on both
fleet workflow models work starts on `develop`, so a worktree defaulted to "the default branch"
lands on `main` and silently misses everything merged to `develop` but not yet promoted. Branch
from `develop` unless the task is explicitly about `main`-only content, per `GOVERNANCE.md`
"Branching Model". Fetch immediately before creating and base on the remote ref, because a clone
is whatever it last fetched rather than the branch it names.

## Creating a Worktree

The fleet layout convention keeps every base clone and every in-flight task visible in one
place, with no owner segment since every repo here is under one owner:

```text
~/repos/<Repo> base clone, on its default/working branch
~/repos/worktrees/<Repo>-<task-slug> one worktree per in-flight task, own branch
```

```sh
git -C ~/repos/<Repo> fetch origin develop
git -C ~/repos/<Repo> worktree add ~/repos/worktrees/<Repo>-<task-slug> -b <task-branch> origin/develop
```

A continuation attaches the task's existing branch rather than forking a fresh one:

```sh
git -C ~/repos/<Repo> fetch origin <task-branch>
git -C ~/repos/<Repo> worktree add ~/repos/worktrees/<Repo>-<task-slug> <task-branch>
```

When the base clone holds only the remote-tracking ref, the same command creates the local
branch tracking `origin/<task-branch>` through git's ordinary checkout guessing, so a fresh
clone needs no separate branch setup. Git refuses to attach a branch that is already checked
out somewhere else, and that refusal is the mandate working, since the branch sitting checked
out in a shared tree is the hazard the continuation rule exists for. Return that checkout to
its own working branch first when its tree is clean, and stop when it is not, because a dirty
tree there may be another task's uncommitted work.

A machine not yet migrated to this layout still isolates exactly the same way, since the mandate
is the isolation rather than the path: create the worktree beside whatever layout the machine
has, and note that the base clone may live elsewhere than `~/repos/<Repo>`.

Claude Code's own `EnterWorktree` tool acts only on an explicit instruction from the user or the
project instructions, which is why the carried rules state this mandate in so many words. Given
a `name`, it creates the worktree under `.claude/worktrees/` inside the repo and bases it on the
GitHub default branch, which is the wrong path and the wrong base here. Create the worktree with
`git worktree add` as above, then attach with `EnterWorktree` `path:`, not `name:`.

## Listing and Cleanup

- `git worktree list`, run in any checkout of a repo, names that repo's base clone and every
worktree with its branch. On the convention layout, one `ls ~/repos/worktrees/` reads what is
in flight across the whole fleet.
- After the task's pull request merges, remove the worktree and its branch from the base clone:
`git worktree remove ~/repos/worktrees/<Repo>-<task-slug>`, then `git branch -d <task-branch>`.
- A worktree that refuses removal is dirty, and force is not the fix: look at what is
uncommitted in it first, since discarding uncommitted work runs only on explicit instruction,
per the `git-commit-conventions` skill.
1 change: 1 addition & 0 deletions .claude-plugin/fleet-skills/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"./skills/operational-vs-release-workflow",
"./skills/pr-review-conduct",
"./skills/python-codestyle",
"./skills/repo-worktree",
"./skills/resync-a-repo",
"./skills/shell-codestyle",
"./skills/skill-lifecycle",
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/fleet-skills/.source-digest
Original file line number Diff line number Diff line change
@@ -1 +1 @@
787c26fa10640eb0
e4a4146ddbea9745
1 change: 1 addition & 0 deletions .claude-plugin/fleet-skills/skills/agent-conduct/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Read `GOVERNANCE.md` "Verification Discipline" before reporting success on anyth
- **Run the repo's whole lint gate before every push**, not the parts that look relevant, because the tool most likely to catch a change is often the one it seems least about.
- **A launched process is not a result.** Report the output the wait produced, and where it produced none, that absence is the report. Never name an external cause the record does not carry.
- **A local clone is not the branch it names.** Fetch immediately before reading, or read the live ref, and name the ref and commit in any finding a local read produced.
- **A "does not exist" claim names the branch it was checked against.** A worktree's default branch is not necessarily the one the content lives on: in-flight content on a `release`-model repo lands on `develop` before `main`, per `GOVERNANCE.md` "Branching Model," so check that branch before reporting anything absent repo-wide.
- **A test asserts the mechanism it names, and a gate has to be watched failing.** A case that passes for an incidental reason is worse than no case, because it is later cited as evidence.
- **Platform-specific code is verified only on the platform it runs on.** Reasoning about PowerShell, macOS, or WSL-specific behavior from a different host is not verification, however closely it matches an already-tested equivalent elsewhere. State an untested structural match as exactly that, never in the words used for a tested fact, and when no agent in the loop has access to the target platform, say so and defer or ship it labeled unverified.

Expand Down
18 changes: 14 additions & 4 deletions .claude-plugin/fleet-skills/skills/git-commit-conventions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,27 @@ scope-widened commit, a rewritten shared history, a destructive reset).
developer unless the developer has explicitly authorized committing for the current ask ("commit
this", "open a PR"). Authorization is scope-bound: it covers the commits that specific task
needs, not a blanket license for the rest of the session.
- **Stage by explicit path, never `git add -A` or `git add .`.** A blanket add stages whatever
else happens to be in the tree, and what it sweeps in is another task's uncommitted work,
landing in a commit whose subject never mentions it, committed by a session that never saw it.
That sweep has happened, which is why task isolation exists (the `repo-worktree` skill), and
isolation makes a shared tree rare rather than impossible. Name the files this task changed,
and let anything else stay unstaged.
- **"Commit" means commit and push.** An authorization to commit carries the push to the feature
branch the work belongs on, because nothing reviews a local commit. The Copilot review loop, the
required status checks, and the maintainer all read the remote, so stopping at `git commit`
leaves the review unstarted and the branch's state private to one machine, which reads as
progress while none of the gates have run. Push to the feature branch, never to a protected
branch, and never with `--force`. Holding a commit locally is the narrower case: it happens when
the developer asks for it, not by default.
- **Check `git status` for the maintainer's own uncommitted edits before committing.** The
maintainer hand-edits files live, often `README.md`/`HISTORY.md`, sometimes with an editor's
LF -> CRLF flip on top. If there are changes not made this session, ask whether to include them
rather than bundling half-finished work or stranding it in an unrelated commit.
- **Check `git status` before committing, and treat any change this session did not make as a
stop.** The maintainer hand-edits files live, often `README.md`/`HISTORY.md`, sometimes with an
editor's LF -> CRLF flip on top, and a sibling agent session sharing the tree leaves its edits
the same way. Whoever the author is, a change this session did not make is never bundled: ask
whether to include it, or leave it unstaged and say so, rather than committing half-finished
work or stranding it in an unrelated commit. An unexpected change in the tree is also the
signal to re-check isolation per the `repo-worktree` skill, since it may mean another task is
live in this checkout.

## Signing, verified not configured

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ rather than guessing from the repo's contents.

## Branching (release model)

- **GitHub's repository setting for "default branch" reads `main`, but `develop` is where work starts and where in-flight content lives.** A worktree or clone that defaults to "the default branch" lands on `main` and can silently miss content that has merged to `develop` but not yet been promoted. Before branching off a change, or asserting something absent from this repo, check `develop`, not just whichever branch a tool defaulted to. See GOVERNANCE.md "Verification Discipline" on naming the branch a "does not exist" claim was checked against, and the `repo-worktree` skill, which owns the worktree-creation moment this base-branch choice is made at.
- `develop` is the integration branch. Feature branches -> `develop` is **squash-only**, which
keeps `develop` linear.
- `develop -> main` is **merge-commit only** (no squash, no rebase). Merge commits preserve
Expand Down
Loading