arcane-cli 1.11.2 → 1.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/.arcane/agents/agent-policies-template.md +1 -2
- package/dist/assets/.arcane/governance/agent-policies.md +2 -3
- package/dist/assets/.arcane/governance/git-conventions.md +20 -35
- package/dist/assets/.arcane/governance/spell-authoring-standards.md +4 -1
- package/dist/assets/.arcane/governance/universal-agent-rules.md +1 -1
- package/dist/assets/.arcane/spells/_fragments/branch-naming.md +9 -0
- package/dist/assets/.arcane/spells/_fragments/branch-rename-gate.md +7 -0
- package/dist/assets/.arcane/spells/spell-architect.md +1 -1
- package/dist/assets/.arcane/spells/spell-close-session.md +8 -1
- package/dist/assets/.arcane/spells/spell-commit-work.md +1 -1
- package/dist/assets/.arcane/spells/spell-create-pull-request.md +23 -2
- package/dist/assets/.arcane/spells/spell-full-cycle.md +1 -1
- package/dist/assets/.arcane/spells/spell-open-session.md +29 -7
- package/dist/assets/.github/instructions/agent-output.instructions.md +10 -13
- package/dist/index.js +788 -68
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
|
@@ -281,9 +281,8 @@ If an agent encounters a situation outside its defined parameters, it should:
|
|
|
281
281
|
|
|
282
282
|
6. **Delete the branch** (local and remote) after merge.
|
|
283
283
|
|
|
284
|
-
**Branch naming format:** `
|
|
284
|
+
**Branch naming format:** the autonomous roster-agent form of the branch-naming rule in `git-conventions.md` → Branch Naming, stated once there and not restated here. The agent-slug prefix makes ownership obvious and prevents naming collisions.
|
|
285
285
|
|
|
286
|
-
- The agent-slug prefix makes ownership obvious and prevents naming collisions.
|
|
287
286
|
- Use standard commit types: `feat`, `fix`, `docs`, `refactor`, `chore`, etc.
|
|
288
287
|
|
|
289
288
|
**What happens if ff-only fails:** Another actor merged to main first. This should not happen if you followed step 3. If it does, rebase and retry:
|
|
@@ -332,10 +332,9 @@ If an agent encounters a situation outside its defined parameters, it should:
|
|
|
332
332
|
|
|
333
333
|
6. **Delete the branch** (local and remote) after merge. **Enforcement: structured spell gate (ARC-023) — `spell-commit-work` Step 10 and `spell-close-session`'s branch-deletion step both require running the Same-Vantage-Point Check before a worktree-adjacent deletion; the vantage-point determination itself remains a manual judgment call, which `git-conventions.md` discloses as "not a code-level gate."**
|
|
334
334
|
|
|
335
|
-
**Branch naming format:** `
|
|
335
|
+
**Branch naming format:** the autonomous roster-agent form of the branch-naming rule in `git-conventions.md` → Branch Naming, stated once there and not restated here. The agent-slug prefix makes ownership obvious and prevents naming collisions.
|
|
336
336
|
|
|
337
|
-
-
|
|
338
|
-
- Use standard commit types: `feat`, `fix`, `docs`, `refactor`, `chore`, etc. **Enforcement: explicitly advisory prose (ARC-023) — no check in this repo validates an agent branch name against this format (unlike session-branch naming, which `spell-open-session` does check).**
|
|
337
|
+
- Use standard commit types: `feat`, `fix`, `docs`, `refactor`, `chore`, etc. **Enforcement: explicitly advisory prose (ARC-023) — no check in this repo validates an autonomous agent's branch name against that rule; the rename gate in `git-conventions.md` → Branch Naming runs only in interactive sessions.**
|
|
339
338
|
|
|
340
339
|
**What happens if ff-only fails:** Another actor merged to main first. This should not happen if you followed step 3. If it does, rebase and retry — **from the primary checkout**, since this block checks out `main`:
|
|
341
340
|
|
|
@@ -198,34 +198,34 @@ Every unit of work runs in exactly one **session workspace**: one instance of on
|
|
|
198
198
|
|
|
199
199
|
### Branch Naming
|
|
200
200
|
|
|
201
|
-
|
|
201
|
+
The rule below is the one source for every branch format Arcane names. The spells and the generated client instruction files carry copies that `npm run fix:self-host-parity` keeps identical to it (ARC-039 fragment `branch-naming`); nothing restates a format by hand.
|
|
202
202
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
```
|
|
203
|
+
<!-- fragment:branch-naming:start -->
|
|
204
|
+
**Branch naming (every actor).** `main` is integration-only. All work happens on a topic branch, named by who creates it:
|
|
206
205
|
|
|
207
|
-
|
|
206
|
+
| Who creates the branch | Format | Example |
|
|
207
|
+
| --- | --- | --- |
|
|
208
|
+
| A human | `type/short-description` | `fix/auth-token-regression` |
|
|
209
|
+
| An interactive session (Claude Code, Copilot, Codex chat), including every worktree it opens and every parallel subagent it spawns | `sessions/YYYY-MM-DD-<topic-slug>`; a parallel subagent appends `-<agent>` | `sessions/2026-10-05-branch-naming`, `sessions/2026-10-05-branch-naming-merlin` |
|
|
210
|
+
| An autonomous roster agent on a dispatched job (`spell-full-cycle`, from `stories.json`) | `{agent-slug}/type/short-description` | `lafayette/feat/api-endpoint` |
|
|
208
211
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
```
|
|
212
|
+
The slug comes from the work — the focus, the handoff's active task, the top next action, the story, the pull request title — never from a generator. A tool-generated name (`claude/<adjective>-<surname>-<hash>`, any random adjective-noun name) is noncompliant wherever it appears and is renamed on sight. When a client offers to create a worktree or a parallel agent, supply the branch name yourself: Claude Code's `EnterWorktree` takes a `name`, and `git worktree add <path> -b <branch>` pre-creates the branch for any client; never accept a generated name. **Enforcement: structured spell gate (ARC-023) — `spell-open-session`'s Mutation Guard and `spell-create-pull-request`'s Step 0 run the branch rename gate on the branch they find; `spell agents sync` renders this rule into every client instruction file; no CI check reads a pull request's head-branch name.**
|
|
213
|
+
<!-- fragment:branch-naming:end -->
|
|
212
214
|
|
|
213
215
|
Create the session branch as the **first action** of any session, before any file edits or commits. All session commits land on this branch. At close, push and open a PR. After merge, return to `main` — **in the primary checkout only** (ARC-028 R8). A session running in a linked worktree ends with push → PR → worktree removal instead; it must not check out `main`, because the primary checkout already holds it and Git will refuse the second checkout.
|
|
214
216
|
|
|
215
|
-
**
|
|
217
|
+
**Session branch policy (required):**
|
|
216
218
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
+
<!-- fragment:branch-rename-gate:start -->
|
|
220
|
+
**Branch rename gate.** Applied to the current branch before a session's first mutation (`spell-open-session`) and before a pull request's first push (`spell-create-pull-request`):
|
|
219
221
|
|
|
220
|
-
|
|
222
|
+
1. Read `git branch --show-current` and `git worktree list` (a read only: across a bridged or mounted filesystem it can misreport a live worktree, EF-33 — never act on that output alone). Act only on the branch checked out in this worktree; never rename, switch or delete a branch attached to another worktree.
|
|
223
|
+
2. Judge the name against the branch-naming rule above. A compliant name stays. A noncompliant one is renamed only when no open pull request depends on it — check with the provider (`gh pr list --head <branch> --state open` or `az repos pr list --source-branch <branch> --status active`); if one exists, keep the name, report it, and rename after that pull request merges.
|
|
224
|
+
3. Derive the deterministic name from the work, in the session form of the rule: today's date, a kebab-case topic from the focus, the handoff's active task, the top next action or the pull request title, and the `-<agent>` suffix only for a parallel subagent.
|
|
225
|
+
4. `git branch -m <old> <new>`. If `<old>` was already pushed: `git push -u origin <new>`, then `git push origin --delete <old>`, both under the push-policy check (ARC-049). Give each command a per-call timeout: on some Windows filesystems a rename or delete blocks on a file lock (the EF-20 hazard in git-conventions.md).
|
|
226
|
+
5. Report `Renamed <old> → <new>` in the run's output. If the worktree, the pull-request state or the remote cannot be determined, stop without renaming and ask the operator.
|
|
227
|
+
<!-- fragment:branch-rename-gate:end -->
|
|
221
228
|
|
|
222
|
-
- Session branches must be deterministic, human-readable, and derived from the active task title. **Enforcement: structured spell gate (ARC-023) — `spell-open-session`'s Mutation Guard renames a noncompliant, unpushed branch to the deterministic `sessions/YYYY-MM-DD-<topic-slug>` format before the first mutation, and its "Session branch naming compliance" check records the required rename when a PR doesn't already depend on the old name.**
|
|
223
|
-
- Default format for new interactive sessions: `sessions/YYYY-MM-DD-<topic-slug>`.
|
|
224
|
-
- Random adjective-noun branches (for example, `ideal-disco`) are non-compliant and must be renamed.
|
|
225
|
-
- If a non-compliant branch was already pushed, migrate it safely:
|
|
226
|
-
1. `git branch -m <old> <new>`
|
|
227
|
-
2. `git push -u origin <new>`
|
|
228
|
-
3. `git push origin --delete <old>` (skip this if an active PR still depends on `<old>`).
|
|
229
229
|
- **EF-20 hazard:** on some Windows/Git-for-Windows filesystems, a rename or delete can trigger
|
|
230
230
|
an interactive retry prompt (e.g. a file lock from an editor or antivirus scan) that blocks
|
|
231
231
|
indefinitely on a terminal with no non-interactive fallback. When running these commands
|
|
@@ -234,22 +234,7 @@ Create the session branch as the **first action** of any session, before any fil
|
|
|
234
234
|
(`src/modules/git.ts`) applies this as a standing contract — closed stdin plus a
|
|
235
235
|
command-class timeout — for every `git` invocation the CLI itself makes. **Enforcement: explicitly advisory prose (ARC-023) — the recommendation to set a timeout applies to an agent's own separate shell tool invocations of raw `git`, which nothing here checks. The cited precedent is independently verified: `runGit` (`src/modules/git.ts`) closes stdin immediately and applies a `DEFAULT_TIMEOUTS_MS`-scoped timeout for every git invocation Arcane's own CLI makes internally — a real executable check, but scoped to Arcane's own code path, not to a human's or agent's independent terminal command.**
|
|
236
236
|
|
|
237
|
-
**
|
|
238
|
-
|
|
239
|
-
```
|
|
240
|
-
{agent-slug}/type/short-description
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
The agent slug prefix makes branch ownership obvious in `git branch -r` output and prevents naming collisions between agents. **Enforcement: explicitly advisory prose (ARC-023) — no check in this repo validates an agent branch name against this format, unlike session-branch naming above (which `spell-open-session` does check and, when noncompliant, rename).**
|
|
244
|
-
|
|
245
|
-
**Examples:**
|
|
246
|
-
|
|
247
|
-
- `docs/readme-update` (human)
|
|
248
|
-
- `feat/spell-commit-work` (human)
|
|
249
|
-
- `fix/auth-token-regression` (human)
|
|
250
|
-
- `lafayette/feat/api-endpoint` (agent — Lafayette)
|
|
251
|
-
- `merlin/docs/architecture-update` (agent — Merlin)
|
|
252
|
-
- `lince/fix/test-regression` (agent — Lince)
|
|
237
|
+
**Autonomous agents:** the agent-slug prefix in the rule above makes branch ownership obvious in `git branch -r` output and prevents naming collisions between agents working in parallel. **Enforcement: explicitly advisory prose (ARC-023) — no check in this repo validates an autonomous agent's branch name against the rule; the rename gate above runs only in interactive sessions (`spell-open-session`, `spell-create-pull-request`).**
|
|
253
238
|
|
|
254
239
|
### Human Workflow
|
|
255
240
|
|
|
@@ -234,7 +234,10 @@ source under `.arcane/spells/_fragments/` and never shipped on its own. A spell
|
|
|
234
234
|
`<!-- fragment:needs-you:start -->` / `<!-- fragment:needs-you:end -->` marker pair placed where the
|
|
235
235
|
spell describes its final output or report, and `npm run fix:self-host-parity` expands the fragment
|
|
236
236
|
between the markers. Edit the fragment, never an expanded copy, and never restate its rules by hand
|
|
237
|
-
in a spell body.
|
|
237
|
+
in a spell body. Any shipped Markdown file can host a fragment the same way, not only a spell: the
|
|
238
|
+
`branch-naming` fragment is expanded into `git-conventions.md` and the Copilot instruction file as
|
|
239
|
+
well as into spells, and `spell agents sync` renders it into every client instruction file, so the
|
|
240
|
+
branch formats are written in exactly one place. Required actions also survive the end of a session: `spell-close-session` writes
|
|
238
241
|
the open items into the handoff's `Needs you` field, and `spell-open-session` surfaces that field
|
|
239
242
|
first, above the rest of the handoff, and does not mark the handoff consumed until it has.
|
|
240
243
|
|
|
@@ -49,7 +49,7 @@ See also: [[README]], [[DECISIONS]], [[governance/git-conventions|Git Convention
|
|
|
49
49
|
|
|
50
50
|
13. **For runtime config changes. Enforcement: structured spell gate (ARC-023), hedged — `spell-commit-work`'s Step 8 operator-approval gate covers this commit like any other before it can be made, though it verifies diff/message approval rather than a dedicated confirmation that testing occurred.** (anything touching the agent runtime's config files or service restarts): do NOT commit supporting docs until the user has confirmed the change is working. Stage and present — but wait for explicit approval after testing.
|
|
51
51
|
|
|
52
|
-
14. **Never commit directly to main. Enforcement: explicitly advisory prose (ARC-023) — `spell doctor`'s `checkPlatformBranchPolicy` verifies only the platform's merge-method policy (squash disallowed), not that direct pushes/commits to main are blocked.** All work — human or agent — happens on topic branches
|
|
52
|
+
14. **Never commit directly to main. Enforcement: explicitly advisory prose (ARC-023) — `spell doctor`'s `checkPlatformBranchPolicy` verifies only the platform's merge-method policy (squash disallowed), not that direct pushes/commits to main are blocked.** All work — human or agent — happens on topic branches, each named in the form the Branch Naming rule in git-conventions.md gives its actor (stated once there). Main receives changes only through PR completion, not local direct merges. See [[governance/git-conventions#branch-discipline|Branch Discipline]].
|
|
53
53
|
|
|
54
54
|
---
|
|
55
55
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
**Branch naming (every actor).** `main` is integration-only. All work happens on a topic branch, named by who creates it:
|
|
2
|
+
|
|
3
|
+
| Who creates the branch | Format | Example |
|
|
4
|
+
| --- | --- | --- |
|
|
5
|
+
| A human | `type/short-description` | `fix/auth-token-regression` |
|
|
6
|
+
| An interactive session (Claude Code, Copilot, Codex chat), including every worktree it opens and every parallel subagent it spawns | `sessions/YYYY-MM-DD-<topic-slug>`; a parallel subagent appends `-<agent>` | `sessions/2026-10-05-branch-naming`, `sessions/2026-10-05-branch-naming-merlin` |
|
|
7
|
+
| An autonomous roster agent on a dispatched job (`spell-full-cycle`, from `stories.json`) | `{agent-slug}/type/short-description` | `lafayette/feat/api-endpoint` |
|
|
8
|
+
|
|
9
|
+
The slug comes from the work — the focus, the handoff's active task, the top next action, the story, the pull request title — never from a generator. A tool-generated name (`claude/<adjective>-<surname>-<hash>`, any random adjective-noun name) is noncompliant wherever it appears and is renamed on sight. When a client offers to create a worktree or a parallel agent, supply the branch name yourself: Claude Code's `EnterWorktree` takes a `name`, and `git worktree add <path> -b <branch>` pre-creates the branch for any client; never accept a generated name. **Enforcement: structured spell gate (ARC-023) — `spell-open-session`'s Mutation Guard and `spell-create-pull-request`'s Step 0 run the branch rename gate on the branch they find; `spell agents sync` renders this rule into every client instruction file; no CI check reads a pull request's head-branch name.**
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
**Branch rename gate.** Applied to the current branch before a session's first mutation (`spell-open-session`) and before a pull request's first push (`spell-create-pull-request`):
|
|
2
|
+
|
|
3
|
+
1. Read `git branch --show-current` and `git worktree list` (a read only: across a bridged or mounted filesystem it can misreport a live worktree, EF-33 — never act on that output alone). Act only on the branch checked out in this worktree; never rename, switch or delete a branch attached to another worktree.
|
|
4
|
+
2. Judge the name against the branch-naming rule above. A compliant name stays. A noncompliant one is renamed only when no open pull request depends on it — check with the provider (`gh pr list --head <branch> --state open` or `az repos pr list --source-branch <branch> --status active`); if one exists, keep the name, report it, and rename after that pull request merges.
|
|
5
|
+
3. Derive the deterministic name from the work, in the session form of the rule: today's date, a kebab-case topic from the focus, the handoff's active task, the top next action or the pull request title, and the `-<agent>` suffix only for a parallel subagent.
|
|
6
|
+
4. `git branch -m <old> <new>`. If `<old>` was already pushed: `git push -u origin <new>`, then `git push origin --delete <old>`, both under the push-policy check (ARC-049). Give each command a per-call timeout: on some Windows filesystems a rename or delete blocks on a file lock (the EF-20 hazard in git-conventions.md).
|
|
7
|
+
5. Report `Renamed <old> → <new>` in the run's output. If the worktree, the pull-request state or the remote cannot be determined, stop without renaming and ask the operator.
|
|
@@ -77,7 +77,7 @@ Workflow:
|
|
|
77
77
|
|
|
78
78
|
5a. **Validate stories.json schema** — before outputting, self-check every field. Missing fields break `spell-implement`. Required top-level fields:
|
|
79
79
|
- `feature` (string)
|
|
80
|
-
- `branchName` (string,
|
|
80
|
+
- `branchName` (string, in the autonomous roster-agent form of the branch-naming rule — `git-conventions.md` → Branch Naming)
|
|
81
81
|
- `assignedAgent` (string, agent slug)
|
|
82
82
|
- `trackingMode` (`internal` or `external`)
|
|
83
83
|
- `externalProvider` (required when `trackingMode=external`)
|
|
@@ -197,7 +197,14 @@ Rules:
|
|
|
197
197
|
- Skip this entire step for local-only and read-only sessions.
|
|
198
198
|
- Resolve `<remote>` and `<trunk>` from observed Git/provider state: use the usable authenticated remote selected in step 9 and the merged PR's target branch (falling back to that remote's default branch). Never assume `origin` or `main`.
|
|
199
199
|
- Verify through the detected provider that the PR is merged before changing branches.
|
|
200
|
-
- Then confirm each issue the PR
|
|
200
|
+
- Then confirm each issue the PR closed is closed. That is every issue its body names after a closing keyword, every issue named after a closing keyword in its commit messages (`git log <base>..<merge-sha> --format=%B`), and every issue GitHub links to it (`gh pr view <number> --json closingIssuesReferences`). Check each one's state with `gh issue view <number> --json state,stateReason`. If GraphQL is unavailable, use REST: `gh api repos/<owner>/<repo>/issues/<number> --jq '.state,.state_reason'` for the state. Then treat as linked every issue that follows a closing keyword in the PR body (ignore any negation) and any issue the PR references whose `closed` event (`gh api repos/<owner>/<repo>/issues/<number>/events`) falls within a minute of the PR's `merged_at`. A merge closes every issue that directly follows a closing keyword, even inside a negation: "does not close #N" closes #N. List any issue still open under the closure report's `## ⚠ Needs you`, or, once the diff check below passes, close it with a comment naming the merged PR.
|
|
201
|
+
- **A closed issue is not evidence its change shipped.** For each issue above, confirm the merged diff contains the change that issue asked for:
|
|
202
|
+
- Read the issue (`gh issue view <number> --json title,body`, or REST), then the diff (`gh pr diff <number>`).
|
|
203
|
+
- Name the file that carries the requested change. If you cannot name one, treat the change as missing.
|
|
204
|
+
- A diff that touches only tracking files (`FEEDBACK.md`, the journal, the handoff, `CHANGELOG.md`, `.arcane/delegations.json`) never counts as the change.
|
|
205
|
+
- Reopen the issue only when all of these hold: its `closed` event falls within a minute of this PR's merge; its state reason is `completed`; and the change is not already on `<trunk>` from an earlier PR (`git log <trunk> --grep '#<number>'`). When they hold, reopen the issue with a comment naming the merged PR and listing what is missing, or which part, if only part shipped.
|
|
206
|
+
- When you cannot reopen it (permissions), list it under the closure report's `## ⚠ Needs you` instead.
|
|
207
|
+
- Never report such an issue as done.
|
|
201
208
|
- **Determine the isolation primitive first (ARC-028 R8).** Run both of these and compare:
|
|
202
209
|
```bash
|
|
203
210
|
git rev-parse --path-format=absolute --git-common-dir
|
|
@@ -28,7 +28,7 @@ Workflow:
|
|
|
28
28
|
1. **Check git status and guard the branch** — run `git status` to see what changed, and confirm the current branch.
|
|
29
29
|
- Classify the Git/remote state before enforcing the protected-branch guard. A usable merge path requires a configured remote on a supported provider (`github.com`, `dev.azure.com`, or `visualstudio.com`) and authenticated provider tooling. A remote URL alone is insufficient.
|
|
30
30
|
- Apply exactly one path:
|
|
31
|
-
- **Supported, authenticated GitHub/ADO remote + `main` or `master` checked out:** **STOP — do not stage or commit directly.** Create and switch the current worktree to a compliant topic branch (
|
|
31
|
+
- **Supported, authenticated GitHub/ADO remote + `main` or `master` checked out:** **STOP — do not stage or commit directly.** Create and switch the current worktree to a compliant topic branch (a session branch, named per `git-conventions.md` → Branch Naming), then continue.
|
|
32
32
|
- **Supported, authenticated GitHub/ADO remote + valid topic/PR branch checked out:** stay on that branch and continue.
|
|
33
33
|
- **No remote, unsupported remote, or provider authentication unavailable:** remain on the current trunk. Print `Local-only checkpoint: no usable remote merge path; commit remains on <trunk> and no remote push/PR will run.` Continue through the local commit gate, then skip Steps 9 and 10 entirely.
|
|
34
34
|
- If the current worktree, branch, provider, or authentication state cannot be determined, fail closed before staging and ask the operator. Never strand a local-only commit on a topic branch with no usable merge path.
|
|
@@ -58,6 +58,27 @@ Before enabling auto-complete/auto-merge or completing an existing PR, resolve `
|
|
|
58
58
|
> This rule is on the **agent**, not on the spell. Calling `az repos pr create` / `gh pr create` directly is **not** an escape hatch — it is a governance violation. See `.arcane/governance/git-conventions.md` → **🛑 Agent-mandatory pre-PR guard**.
|
|
59
59
|
|
|
60
60
|
1. Run `git branch --show-current`. **STOP** if on `main`/`master` — you cannot PR the integration branch into itself. Also **STOP** if detached HEAD (empty output) — check out a named branch first.
|
|
61
|
+
- **Branch rename gate (before any push in this Step 0):** judge the branch you found against the branch-naming rule and rename it when the gate says so — a tool-generated name must not reach a pull request. A branch a worktree or a parallel subagent created is judged the same way.
|
|
62
|
+
<!-- fragment:branch-naming:start -->
|
|
63
|
+
**Branch naming (every actor).** `main` is integration-only. All work happens on a topic branch, named by who creates it:
|
|
64
|
+
|
|
65
|
+
| Who creates the branch | Format | Example |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| A human | `type/short-description` | `fix/auth-token-regression` |
|
|
68
|
+
| An interactive session (Claude Code, Copilot, Codex chat), including every worktree it opens and every parallel subagent it spawns | `sessions/YYYY-MM-DD-<topic-slug>`; a parallel subagent appends `-<agent>` | `sessions/2026-10-05-branch-naming`, `sessions/2026-10-05-branch-naming-merlin` |
|
|
69
|
+
| An autonomous roster agent on a dispatched job (`spell-full-cycle`, from `stories.json`) | `{agent-slug}/type/short-description` | `lafayette/feat/api-endpoint` |
|
|
70
|
+
|
|
71
|
+
The slug comes from the work — the focus, the handoff's active task, the top next action, the story, the pull request title — never from a generator. A tool-generated name (`claude/<adjective>-<surname>-<hash>`, any random adjective-noun name) is noncompliant wherever it appears and is renamed on sight. When a client offers to create a worktree or a parallel agent, supply the branch name yourself: Claude Code's `EnterWorktree` takes a `name`, and `git worktree add <path> -b <branch>` pre-creates the branch for any client; never accept a generated name. **Enforcement: structured spell gate (ARC-023) — `spell-open-session`'s Mutation Guard and `spell-create-pull-request`'s Step 0 run the branch rename gate on the branch they find; `spell agents sync` renders this rule into every client instruction file; no CI check reads a pull request's head-branch name.**
|
|
72
|
+
<!-- fragment:branch-naming:end -->
|
|
73
|
+
<!-- fragment:branch-rename-gate:start -->
|
|
74
|
+
**Branch rename gate.** Applied to the current branch before a session's first mutation (`spell-open-session`) and before a pull request's first push (`spell-create-pull-request`):
|
|
75
|
+
|
|
76
|
+
1. Read `git branch --show-current` and `git worktree list` (a read only: across a bridged or mounted filesystem it can misreport a live worktree, EF-33 — never act on that output alone). Act only on the branch checked out in this worktree; never rename, switch or delete a branch attached to another worktree.
|
|
77
|
+
2. Judge the name against the branch-naming rule above. A compliant name stays. A noncompliant one is renamed only when no open pull request depends on it — check with the provider (`gh pr list --head <branch> --state open` or `az repos pr list --source-branch <branch> --status active`); if one exists, keep the name, report it, and rename after that pull request merges.
|
|
78
|
+
3. Derive the deterministic name from the work, in the session form of the rule: today's date, a kebab-case topic from the focus, the handoff's active task, the top next action or the pull request title, and the `-<agent>` suffix only for a parallel subagent.
|
|
79
|
+
4. `git branch -m <old> <new>`. If `<old>` was already pushed: `git push -u origin <new>`, then `git push origin --delete <old>`, both under the push-policy check (ARC-049). Give each command a per-call timeout: on some Windows filesystems a rename or delete blocks on a file lock (the EF-20 hazard in git-conventions.md).
|
|
80
|
+
5. Report `Renamed <old> → <new>` in the run's output. If the worktree, the pull-request state or the remote cannot be determined, stop without renaming and ask the operator.
|
|
81
|
+
<!-- fragment:branch-rename-gate:end -->
|
|
61
82
|
2. **Verify the target exists:** `git rev-parse --verify origin/<target>`. If it fails, `git fetch origin <target>`; if still missing, **STOP** and ask the user for the correct `--target`.
|
|
62
83
|
3. **Ensure the branch is on origin (upstream):** run the push-policy check below before any push in this Step 0 — this item's `git push -u` and item 6's `--force-with-lease` push. If it stops a push, stop the spell: a PR cannot be opened for a branch the remote does not have.
|
|
63
84
|
|
|
@@ -162,7 +183,7 @@ Documentation-only PR. No functional code changed.
|
|
|
162
183
|
<Same as above — include only when this PR is one epic of a tracked program.> · category: docs
|
|
163
184
|
```
|
|
164
185
|
|
|
165
|
-
**Closing issues: one keyword per issue.** GitHub reads a closing keyword only for the issue reference directly after it, so `Closes #A and #B` closes #A and leaves #B open. In either template, write one keyword per issue, each in its own sentence: `Closes #A. Closes #B.` (the same for `Fixes` and `Resolves`), never `Closes #A and #B`. Write `Part of #N` for an issue this PR does not finish.
|
|
186
|
+
**Closing issues: one keyword per issue.** GitHub reads a closing keyword only for the issue reference directly after it, so `Closes #A and #B` closes #A and leaves #B open. In either template, write one keyword per issue, each in its own sentence: `Closes #A. Closes #B.` (the same for `Fixes` and `Resolves`), never `Closes #A and #B`. Write `Part of #N` for an issue this PR does not finish. Never put a closing keyword (close, closes, closed, fix, fixes, fixed, resolve, resolves, resolved) directly before an issue you do not mean to close, including in a negation: "does not close #N" closes #N. Write `Refs #N` or `Part of #N` instead.
|
|
166
187
|
|
|
167
188
|
**About `## For the record`** — include it **only when this PR is one epic of a tracked program**
|
|
168
189
|
(one with a `docs/plans/<slug>/PLAN.md`); omit the heading entirely otherwise, since an empty
|
|
@@ -230,7 +251,7 @@ so they need no edit.
|
|
|
230
251
|
- If no reviewers were resolved (none passed, or names the provider could not match): `⚠ No reviewers assigned — add them in the PR or re-run with --reviewers`.
|
|
231
252
|
- If `--draft`: note it was created as a draft.
|
|
232
253
|
- Delete the body temp file (`<git-dir>/arcane-pr-body.md`) once the PR is confirmed created and verified.
|
|
233
|
-
- **Issues the body closes:** list each one by number. The PR has not merged yet, so add one line to the `Needs you` block: `After merging <PR URL>, confirm #A and #B are closed (gh issue view <number> --json state)`. `spell-close-session` step 10 runs the same check once it confirms the merge. Never assume the merge closed them.
|
|
254
|
+
- **Issues the body closes:** list each one by number, and any issue linked to the PR in GitHub's sidebar. The PR has not merged yet, so add one line to the `Needs you` block: `After merging <PR URL>, confirm #A and #B are closed (gh issue view <number> --json state)`. `spell-close-session` step 10 runs the same check once it confirms the merge, and that the merged diff contains each issue's requested change. Never assume the merge closed them, or that a closed issue's change shipped.
|
|
234
255
|
- **Branch topology** — per the generated state diagrams convention (rule 8, ARC-036), built only from
|
|
235
256
|
Step 1's already-gathered branch name and commit list (`git log origin/<target>..HEAD --format="%h %s" --reverse`).
|
|
236
257
|
Skip entirely (the applicability guard) if that commit list is empty — Step 0.4 already stops before
|
|
@@ -119,7 +119,7 @@ Execute the `spell-implement` workflow:
|
|
|
119
119
|
git fetch --prune origin # origin/main is now current
|
|
120
120
|
```
|
|
121
121
|
and branch from `origin/main` in step 1. Check which primitive you are in with `git rev-parse --path-format=absolute --git-common-dir` vs `--git-dir` — equal means primary checkout. `--path-format=absolute` is required, or every primary checkout looks like a worktree from any subdirectory.
|
|
122
|
-
1. Create the topic branch from `stories.json.branchName` (
|
|
122
|
+
1. Create the topic branch from `stories.json.branchName` (the autonomous roster-agent form of the branch-naming rule in `git-conventions.md` → Branch Naming).
|
|
123
123
|
2. Loop:
|
|
124
124
|
a. Pick the next story where `passes: false`, ordered by priority.
|
|
125
125
|
b. Implement the story — minimum code to satisfy acceptance criteria.
|
|
@@ -48,8 +48,8 @@ Do not create a branch during a read-only session. Immediately before the first
|
|
|
48
48
|
|
|
49
49
|
| Observed state | Required action before mutation |
|
|
50
50
|
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
-
| Supported, authenticated GitHub/ADO remote + trunk checked out | Create and switch the **current worktree only** to
|
|
52
|
-
| Already on a compliant
|
|
51
|
+
| Supported, authenticated GitHub/ADO remote + trunk checked out | Create and switch the **current worktree only** to a session branch named per the branch-naming rule below, derived deterministically from the focus, handoff active task, or top next action. |
|
|
52
|
+
| Already on a compliant session branch | Stay on it; do not create or switch branches. |
|
|
53
53
|
| Noncompliant unpushed branch with no active PR | Rename it to the deterministic session name before mutation. |
|
|
54
54
|
| Branch has an active PR | Stay on the PR branch and report it; never rename a branch backing an active PR. |
|
|
55
55
|
| Current path is a linked worktree | Mutate/switch only the current worktree. Never switch or delete a branch attached to another worktree. |
|
|
@@ -59,6 +59,30 @@ A usable merge path requires a configured remote on a supported provider (`githu
|
|
|
59
59
|
|
|
60
60
|
If branch creation/rename is required, complete it before writing the handoff consumed marker or any other file. If the guard cannot determine the current worktree, active-PR state, or merge capability, fail closed without mutation and ask for operator direction.
|
|
61
61
|
|
|
62
|
+
The branch-naming rule the table applies, and the rename gate it runs on a noncompliant branch:
|
|
63
|
+
|
|
64
|
+
<!-- fragment:branch-naming:start -->
|
|
65
|
+
**Branch naming (every actor).** `main` is integration-only. All work happens on a topic branch, named by who creates it:
|
|
66
|
+
|
|
67
|
+
| Who creates the branch | Format | Example |
|
|
68
|
+
| --- | --- | --- |
|
|
69
|
+
| A human | `type/short-description` | `fix/auth-token-regression` |
|
|
70
|
+
| An interactive session (Claude Code, Copilot, Codex chat), including every worktree it opens and every parallel subagent it spawns | `sessions/YYYY-MM-DD-<topic-slug>`; a parallel subagent appends `-<agent>` | `sessions/2026-10-05-branch-naming`, `sessions/2026-10-05-branch-naming-merlin` |
|
|
71
|
+
| An autonomous roster agent on a dispatched job (`spell-full-cycle`, from `stories.json`) | `{agent-slug}/type/short-description` | `lafayette/feat/api-endpoint` |
|
|
72
|
+
|
|
73
|
+
The slug comes from the work — the focus, the handoff's active task, the top next action, the story, the pull request title — never from a generator. A tool-generated name (`claude/<adjective>-<surname>-<hash>`, any random adjective-noun name) is noncompliant wherever it appears and is renamed on sight. When a client offers to create a worktree or a parallel agent, supply the branch name yourself: Claude Code's `EnterWorktree` takes a `name`, and `git worktree add <path> -b <branch>` pre-creates the branch for any client; never accept a generated name. **Enforcement: structured spell gate (ARC-023) — `spell-open-session`'s Mutation Guard and `spell-create-pull-request`'s Step 0 run the branch rename gate on the branch they find; `spell agents sync` renders this rule into every client instruction file; no CI check reads a pull request's head-branch name.**
|
|
74
|
+
<!-- fragment:branch-naming:end -->
|
|
75
|
+
|
|
76
|
+
<!-- fragment:branch-rename-gate:start -->
|
|
77
|
+
**Branch rename gate.** Applied to the current branch before a session's first mutation (`spell-open-session`) and before a pull request's first push (`spell-create-pull-request`):
|
|
78
|
+
|
|
79
|
+
1. Read `git branch --show-current` and `git worktree list` (a read only: across a bridged or mounted filesystem it can misreport a live worktree, EF-33 — never act on that output alone). Act only on the branch checked out in this worktree; never rename, switch or delete a branch attached to another worktree.
|
|
80
|
+
2. Judge the name against the branch-naming rule above. A compliant name stays. A noncompliant one is renamed only when no open pull request depends on it — check with the provider (`gh pr list --head <branch> --state open` or `az repos pr list --source-branch <branch> --status active`); if one exists, keep the name, report it, and rename after that pull request merges.
|
|
81
|
+
3. Derive the deterministic name from the work, in the session form of the rule: today's date, a kebab-case topic from the focus, the handoff's active task, the top next action or the pull request title, and the `-<agent>` suffix only for a parallel subagent.
|
|
82
|
+
4. `git branch -m <old> <new>`. If `<old>` was already pushed: `git push -u origin <new>`, then `git push origin --delete <old>`, both under the push-policy check (ARC-049). Give each command a per-call timeout: on some Windows filesystems a rename or delete blocks on a file lock (the EF-20 hazard in git-conventions.md).
|
|
83
|
+
5. Report `Renamed <old> → <new>` in the run's output. If the worktree, the pull-request state or the remote cannot be determined, stop without renaming and ask the operator.
|
|
84
|
+
<!-- fragment:branch-rename-gate:end -->
|
|
85
|
+
|
|
62
86
|
**Drift Check (run before deep context gathering):**
|
|
63
87
|
|
|
64
88
|
Run the `spell-check-drift` spell first, before building the state snapshot or gathering deep context. Drift compounds the longer a session runs, so catch it early.
|
|
@@ -98,10 +122,8 @@ Before anything else, check:
|
|
|
98
122
|
- **Otherwise** → **primary checkout on a session branch** (R2). This is the default and it is deliberately the do-nothing path: one actor, one checkout, no new tooling.
|
|
99
123
|
- **Footprint overlap overrides the choice (R4).** If this session's work touches the same files or the same shared sequence (migration numbers, generated indexes, lockfiles) as work already running in another primitive, do **not** isolate it — serialize instead: wait, or re-scope to a disjoint footprint first. Isolation does not prevent those collisions, it hides them until merge review.
|
|
100
124
|
- Working in a linked worktree changes two things later in the session and nothing else: `spell-close-session` must not check out `main` (see git-conventions.md's Post-Merge Cleanup section), and the worktree contains no untracked tooling state — budget for `npm install` or equivalent in a code repo. Branch naming, attribution, and the PR gate are identical from every primitive (R8).
|
|
101
|
-
- **Session branch naming compliance:**
|
|
102
|
-
-
|
|
103
|
-
- Disallow random adjective-noun generator names (example: `ideal-disco`) as session defaults.
|
|
104
|
-
- If current branch is non-compliant and no active PR depends on it, record the required rename for the Mutation Guard. Do not rename during a read-only session.
|
|
125
|
+
- **Session branch naming compliance:** judge the current branch against the branch-naming rule (Mutation Guard section above), including a branch a worktree or a parallel subagent created:
|
|
126
|
+
- If it is noncompliant and no active PR depends on it, record the required rename for the Mutation Guard. Do not rename during a read-only session.
|
|
105
127
|
- If an active PR uses the old branch name, do not force-delete the old remote branch; flag it and continue with a follow-up rename plan.
|
|
106
128
|
- **Stale local branches:** Run `git branch --merged main` to list branches already merged that should be deleted — this session's own read-only candidate list (`--merged` also misses every branch landed via this repo's sanctioned rebase-and-fast-forward, so treat a *clean* result as "no ancestry-visible candidates," not "nothing to check"). Open-session only lists; it does not delete. Apply the [Content-Verified Branch Deletion](../../.arcane/governance/git-conventions.md#content-verified-branch-deletion-todomd-merged-branch-cleanup-finding) procedure (git-conventions.md) to any candidate before flagging it as safe, and leave the actual idempotent prune to `spell-close-session`'s sweep.
|
|
107
129
|
- **Tracker configuration check (early):** resolve active tracking settings before planning, in this order: root `.arcane.json` (if present) -> the committed self-hosted source manifest (`src/assets/.arcane.json`, read only when it declares `selfHosted: true` -- this is EF-14's recorded self-hosting resolution tier, distinct from treating the mere presence of a self-host marker as license to infer other, unrelated config) -> the current feature PRD frontmatter -> ask. Persisted once at `spell init`/`spell update` (EF-14); do not re-ask when any of the first three sources already sets it.
|
|
@@ -159,7 +181,7 @@ Before anything else, check:
|
|
|
159
181
|
- Provide a 1–4 word, sentence-case, human-meaningful session name.
|
|
160
182
|
- If a focus/task argument is provided, generate the name deterministically from that focus (never use the generic name `Open session`).
|
|
161
183
|
- If no focus argument is provided, generate the name from the top recommended action in `## Next Session Plan`.
|
|
162
|
-
- Optional helper line: `Branch helper:
|
|
184
|
+
- Optional helper line: `Branch helper: <session branch name>`, in the session form of the branch-naming rule.
|
|
163
185
|
|
|
164
186
|
## Risks And Gaps
|
|
165
187
|
|
|
@@ -133,19 +133,16 @@ expected) marks that server down for the rest of the session — fall back to th
|
|
|
133
133
|
repos pr` / `gh` / REST), no blind retries, and report the downgrade in output. Full rule and origin
|
|
134
134
|
incident: `.arcane/governance/git-conventions.md` → Known issues.
|
|
135
135
|
|
|
136
|
-
## Branch Naming
|
|
136
|
+
## Branch Naming
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
sessions/YYYY-MM-DD-topic-slug
|
|
141
|
-
```
|
|
138
|
+
<!-- fragment:branch-naming:start -->
|
|
139
|
+
**Branch naming (every actor).** `main` is integration-only. All work happens on a topic branch, named by who creates it:
|
|
142
140
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
141
|
+
| Who creates the branch | Format | Example |
|
|
142
|
+
| --- | --- | --- |
|
|
143
|
+
| A human | `type/short-description` | `fix/auth-token-regression` |
|
|
144
|
+
| An interactive session (Claude Code, Copilot, Codex chat), including every worktree it opens and every parallel subagent it spawns | `sessions/YYYY-MM-DD-<topic-slug>`; a parallel subagent appends `-<agent>` | `sessions/2026-10-05-branch-naming`, `sessions/2026-10-05-branch-naming-merlin` |
|
|
145
|
+
| An autonomous roster agent on a dispatched job (`spell-full-cycle`, from `stories.json`) | `{agent-slug}/type/short-description` | `lafayette/feat/api-endpoint` |
|
|
147
146
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
type/short-description
|
|
151
|
-
```
|
|
147
|
+
The slug comes from the work — the focus, the handoff's active task, the top next action, the story, the pull request title — never from a generator. A tool-generated name (`claude/<adjective>-<surname>-<hash>`, any random adjective-noun name) is noncompliant wherever it appears and is renamed on sight. When a client offers to create a worktree or a parallel agent, supply the branch name yourself: Claude Code's `EnterWorktree` takes a `name`, and `git worktree add <path> -b <branch>` pre-creates the branch for any client; never accept a generated name. **Enforcement: structured spell gate (ARC-023) — `spell-open-session`'s Mutation Guard and `spell-create-pull-request`'s Step 0 run the branch rename gate on the branch they find; `spell agents sync` renders this rule into every client instruction file; no CI check reads a pull request's head-branch name.**
|
|
148
|
+
<!-- fragment:branch-naming:end -->
|