arcane-cli 1.11.0 → 1.11.2
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/governance/development-methodology.md +1 -1
- package/dist/assets/.arcane/governance/git-conventions.md +34 -3
- package/dist/assets/.arcane/spells/spell-bug.md +6 -0
- package/dist/assets/.arcane/spells/spell-close-session.md +5 -4
- package/dist/assets/.arcane/spells/spell-commit-work.md +10 -3
- package/dist/assets/.arcane/spells/spell-create-pull-request.md +3 -0
- package/dist/assets/.arcane/spells/spell-eas-store-deploy.md +33 -0
- package/dist/assets/.arcane/spells/spell-feedback.md +12 -4
- package/dist/assets/.arcane/spells/spell-open-session.md +1 -0
- package/dist/assets/.arcane/spells/spell-plan.md +2 -0
- package/dist/assets/.arcane/spells/spell-scope.md +2 -0
- package/package.json +1 -1
|
@@ -154,7 +154,7 @@ GitHub Issues has no configurable work-item-type hierarchy the way Azure DevOps
|
|
|
154
154
|
- **Creation:** `gh issue create --title "{title}" --body "{body}" [--label <name>]` — prints the issue URL on success; the trailing path segment is the issue number.
|
|
155
155
|
- **Fetching:** `gh issue view {id} --json title,body,labels`.
|
|
156
156
|
- **Closing:** `gh issue close {id} --reason completed [--comment "{note}"]` (`--reason` also accepts `not planned` or `duplicate`).
|
|
157
|
-
- **Commit/PR linkage:** `Fixes #{id}` / `Closes #{id}` in a commit message or PR body auto-closes the issue on merge (GitHub-native behavior, no extra command needed) — prefer this over a manual `gh issue close` call when the fix lands via a PR.
|
|
157
|
+
- **Commit/PR linkage:** `Fixes #{id}` / `Closes #{id}` in a commit message or PR body auto-closes the issue on merge (GitHub-native behavior, no extra command needed) — prefer this over a manual `gh issue close` call when the fix lands via a PR. Write one keyword per issue (git-conventions.md, Optional Footer), and confirm each issue's state after the merge (`spell-create-pull-request` Step 6).
|
|
158
158
|
|
|
159
159
|
### External Provider TODOs
|
|
160
160
|
|
|
@@ -126,7 +126,7 @@ Changes:
|
|
|
126
126
|
Use for:
|
|
127
127
|
|
|
128
128
|
- **Breaking changes:** `BREAKING CHANGE: removed support for plaintext token auth`
|
|
129
|
-
- **Issue/ticket references:** `Closes #535` or `Fixes #127`
|
|
129
|
+
- **Issue/ticket references:** `Closes #535` or `Fixes #127` — one keyword per issue, each on its own footer line (`Closes #535`, then `Closes #536`), never `Closes #535 and #536`, because GitHub closes only the issue directly after a keyword
|
|
130
130
|
|
|
131
131
|
Most commits in this repo won't need footers.
|
|
132
132
|
|
|
@@ -399,6 +399,35 @@ refusing to delete a worktree-attached one, are the enforcement ARC-028 R3/R7 re
|
|
|
399
399
|
|
|
400
400
|
This applies to all actors — humans, interactive tools (Copilot, Claude), and autonomous agents. The `spell-commit-work` and `spell-close-session` prompts enforce this check.
|
|
401
401
|
|
|
402
|
+
**Links inside the worktree — check before `git worktree remove --force`.** A worktree can hold a
|
|
403
|
+
directory link into another tree, most often a `node_modules` linked to the primary checkout's copy to
|
|
404
|
+
skip a reinstall. Before removing a worktree with `--force`, list its links and remove each link (not
|
|
405
|
+
its target) first:
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
# POSIX: find does not follow links, so this lists them without walking into their targets
|
|
409
|
+
find <path> -type l
|
|
410
|
+
rm "<link>" # removes the link only
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
```powershell
|
|
414
|
+
# Windows: a recursive listing that never descends into a junction or symlink
|
|
415
|
+
# (Windows PowerShell 5.1's Get-ChildItem -Recurse can follow them into the real tree)
|
|
416
|
+
function Find-Links($p) { Get-ChildItem -Force -LiteralPath $p | ForEach-Object {
|
|
417
|
+
if ($_.Attributes -band [IO.FileAttributes]::ReparsePoint) { $_.FullName }
|
|
418
|
+
elseif ($_.PSIsContainer) { Find-Links $_.FullName } } }
|
|
419
|
+
Find-Links "<path>"
|
|
420
|
+
cmd /c rmdir "<link>" # a directory junction or symlink only; a file symlink: cmd /c del "<link>"
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
What the evidence covers: on Windows, `--force` was observed once (a consumer session, 2026-10-01) to
|
|
424
|
+
follow a `node_modules` junction and empty the real directory, 194 entries to 0; that has not been
|
|
425
|
+
re-tested, and the PowerShell commands above have not been run. On Linux with git 2.43.0, a symlink to a
|
|
426
|
+
194-entry directory was tested on 2026-10-04 and removed as a link, with its target intact. Unlink first
|
|
427
|
+
on every platform anyway: the check is one command, and the failure deletes real files.
|
|
428
|
+
**Enforcement: explicitly advisory prose (ARC-023) — `spell-close-session` step 10 points here; no check
|
|
429
|
+
lists or unlinks links before a worktree removal, and `spell-commit-work` does not remove worktrees.**
|
|
430
|
+
|
|
402
431
|
### Content-Verified Branch Deletion (TODO.md merged-branch-cleanup finding)
|
|
403
432
|
|
|
404
433
|
Ancestry (`git branch --merged`, plain `git branch -d`) is not a reliable "is this landed?" test on this repo. Two of this repo's own sanctioned states defeat it identically: **Rebase-and-fast-forward** (ARC-009 §7) and any **pre-ARC-009 squash merge** both rewrite commit SHAs, so a fully-landed branch stays permanently invisible to `--merged` and refused by `-d` — not a rare edge case; every branch merged the sanctioned way hits this. **Enforcement: structured spell gate (ARC-023) — `spell-commit-work` Step 10 and `spell-close-session`'s branch-deletion step both require running this content-verification procedure (steps 1-6 below) before a branch deletion; `spell-close-session` runs it as an idempotent sweep every session, and `spell-open-session`'s stale-branch check applies the same procedure to its read-only candidate list.** Verify by content instead, for any local branch except `<trunk>` and any branch currently attached to another worktree (`git worktree list`):
|
|
@@ -675,8 +704,8 @@ Agent-authored commits MUST include trailers in the commit message footer. Human
|
|
|
675
704
|
| `Agent` | Yes | `claude`, `copilot`, `codex` | The runtime that executed — always resolvable from the tool itself. |
|
|
676
705
|
| `Persona` | Conditional | `kellar`, `merlin` | The roster identity operated as. Present only when a roster exists and one was assigned; omit entirely otherwise — never guess. |
|
|
677
706
|
| `Role` | Conditional | `CTO / Architecture Lead` | Derived **only** from `Persona`'s roster entry (`AgentDefinition.role`, reached via the roster entry's `definition` pointer — see `agent-loader.ts`), never typed by hand. Present only when `Persona` is present; a `Role` value not sourced from the actual roster file is a defect, not a stylistic choice — see the grading-probe example below. |
|
|
678
|
-
| `Model` | Yes | `claude-opus-4-20250918`, `gpt-4o`
|
|
679
|
-
| `Model-Source` | Yes | `self-reported`
|
|
707
|
+
| `Model` | Yes | `claude-opus-4-20250918`, `gpt-4o`, `withheld` | Self-reported by the runtime. `withheld` only when the runtime forbids disclosing it (see below). |
|
|
708
|
+
| `Model-Source` | Yes | `self-reported`, `withheld-by-runtime` | `self-reported` marks that `Model` (and `Agent`) is self-reported, not independently verified. `withheld-by-runtime` marks that the runtime forbids disclosing the model, so `Model` is `withheld` on purpose (`Agent` and `Provider` remain self-reported in that case). The `Model-Source` field exists because a fabricated `Model`/author trailer once survived eight PRs and human review undetected; the field makes that limitation visible instead of silently trusted. |
|
|
680
709
|
| `Provider` | Yes | `anthropic`, `openai` | |
|
|
681
710
|
| `Task-Type` | Optional | `docs`, `code`, `review`, `marketing` | |
|
|
682
711
|
| `Approval` | Optional | `interactive`, `batch`, `post-review` | |
|
|
@@ -686,6 +715,8 @@ Agent-authored commits MUST include trailers in the commit message footer. Human
|
|
|
686
715
|
| `Session` | Optional | `session-01` | |
|
|
687
716
|
| `Rollback-Ref` | Optional | path or commit to the pre-change state | |
|
|
688
717
|
|
|
718
|
+
**When the runtime withholds the model.** Some agent runtimes forbid writing a model identifier into commits. In that case write `Model: withheld` and `Model-Source: withheld-by-runtime`, and nothing else in their place. `Agent` and `Provider` stay required, because the tool and the company are still known; only the model name is held back. Never guess or infer a model name to fill the field. Use these two values only when the runtime's own instructions explicitly forbid disclosing the model. Not knowing the model is not the same as being forbidden to say it: an agent that cannot determine its model asks the operator before committing, and never writes `withheld` as a fallback. A runtime that can name its model writes it, with `Model-Source: self-reported`.
|
|
719
|
+
|
|
689
720
|
**Grading probe for whether a session actually inherited this rule** (not a hypothetical — this exact question surfaced a real gap before this split existed): *"What does `Role: developer` in a commit trailer resolve from?"* The wrong answer is "the agent roster" stated with confidence. The correct behavior is to check — read `.arcane/agents.yaml` if it exists, resolve the acting persona's `AgentDefinition.role` — and if no roster exists or the value can't be traced to a real entry, omit the `Role` trailer rather than typing a plausible-sounding guess.
|
|
690
721
|
|
|
691
722
|
### Vendored Framework Trailers
|
|
@@ -70,6 +70,12 @@ Workflow:
|
|
|
70
70
|
- Identify the exact line(s) where the bug manifests.
|
|
71
71
|
- Determine *why* the bug exists (data mismatch, missing case, race condition, etc.).
|
|
72
72
|
- Document the root cause concisely.
|
|
73
|
+
- **"It worked before" and the code is unchanged: diff the environment before the code.** When the report is a regression and the code the symptom points at has not changed since the last build that worked, the runtime around it moved. Before patching that code:
|
|
74
|
+
1. Compare the dependency set and any platform or SDK version of the last good build against the current one, for example `git show <last-good-commit>:package.json` against the current file, or the two lockfiles.
|
|
75
|
+
2. If a dependency in that diff plausibly owns the behavior, read its actual implementation (for a native module, the native source under `node_modules`, not its README), and check its issue tracker and release notes for the versions in the diff.
|
|
76
|
+
3. Only then design the fix. Prefer the upstream fix plus a stopgap over a workaround in the calling code.
|
|
77
|
+
|
|
78
|
+
A run of fixes to the calling code that each address a real but narrower cause, none of which resolves the report, is the signal that this check was skipped.
|
|
73
79
|
|
|
74
80
|
4. **Implement the fix** — write the minimal code change:
|
|
75
81
|
- Fix only the bug — no refactoring, no feature additions.
|
|
@@ -57,7 +57,7 @@ Workflow:
|
|
|
57
57
|
- `### Lessons Learned` — one heading per lesson with enough context to prevent the mistake again. Use narrative, not bullet fragments.
|
|
58
58
|
- `### Open Items Carried Forward` — items that remain undone from this session, including every `pending`/`dispatched`/`failed`/`unverifiable` item from step 1b.
|
|
59
59
|
|
|
60
|
-
2b. **Lesson routing: send each lesson where it can be acted on.** A lesson left only in the journal never reaches the people who could fix its cause. Skip this step when step 2 wrote no `### Lessons Learned`, or when the invocation includes `--no-route`. With `--no-route`, the lessons stay in the journal only; say `Lesson routing skipped (--no-route)` in the closure report.
|
|
60
|
+
2b. **Lesson routing: send each lesson where it can be acted on.** A lesson left only in the journal never reaches the people who could fix its cause. An assistant's private memory is a convenience for the current assistant only and is never a destination for a lesson. Another assistant, or the same one in another environment, never sees it. Saving a lesson there and reporting it as saved is not routing it. Skip this step when step 2 wrote no `### Lessons Learned`, or when the invocation includes `--no-route`. With `--no-route`, the lessons stay in the journal only; say `Lesson routing skipped (--no-route)` in the closure report.
|
|
61
61
|
- **Classify each lesson** written in step 2 into exactly one class:
|
|
62
62
|
- **Framework-shaped:** the fix belongs in Arcane itself, meaning a spell prompt, template, governance doc, playbook, or CLI behavior this repository installed rather than authored. This is `spell-feedback` Step 3's definition. Destination: `FEEDBACK.md`, queued for upstream.
|
|
63
63
|
- **Product idea:** the lesson suggests something to build in this repository's own product. Destination: `spell-save-idea`.
|
|
@@ -118,10 +118,10 @@ In `ai-context/system-prompt-context.md`, replace the existing `## Next Session
|
|
|
118
118
|
- **Next concrete action:** The exact first thing to do at next session start. Be specific: file, command, or decision. Never write "continue work." Name where step 4b registered it (e.g. `path ("the item's own quoted title")` — a stable locator naming the actual file and its bold title, never a bare line number that drifts the moment that file is next edited).
|
|
119
119
|
- **Active files:** Files with uncommitted changes or the focus of the last edit (comma-separated).
|
|
120
120
|
- **Branch:** Output of `git branch --show-current`.
|
|
121
|
-
- **Blockers:** Known unresolved blockers or dependencies. Write "None" if clear.
|
|
121
|
+
- **Blockers:** Known unresolved blockers or dependencies. Write "None" if clear. Write any claim about the local environment (a failing hook, a broken tool, a flaky test) with the date it was observed and a one-line, read-only command that re-verifies it (no install, network fetch, write or secret access): `<claim> — observed YYYY-MM-DD — re-verify: <command>`. An undated environment claim reads as a standing fact, and the next session repeats it after it stops being true.
|
|
122
122
|
- **Pending Verification:** One line per non-`succeeded` item from step 1b: `<what> — <state> — <verification action>`. States: `dispatched`, `pending`, `failed`, `unverifiable` (never list a `succeeded` item here — it belongs in `Last completed step` / TODO.md / the journal instead, worded per the gating above). The verification action is required for `dispatched`, `pending`, and `unverifiable` — "N/A" is only valid for `failed`, where the state is already resolved and there is nothing left to check. Write "None" only if step 1b found every dispatched item resolved to `succeeded` or `failed` before this handoff was written.
|
|
123
123
|
- **Needs you:** One line per action only the operator can take that this session leaves open: `<what to do> — <why the agent cannot do it>`. This block is committed before steps 9–10 run, so write it with them in view and name now the actions they will leave open: the pull request step 9c opens needs a human merge, and when `push_policy` (read it now, per step 9c's push-policy check) is `blocked`, or `guarded` in an autonomous run, the push itself waits on the operator. The closure report's `## ⚠ Needs you` block (step 11) lists the same items, plus any that arose after this handoff was committed. Write "None" when nothing needs the operator. Never leave such an action only in `Blockers`, `Notes` or `Carry Forward`; it may be repeated there, but it always appears here.
|
|
124
|
-
- **Notes:** Anything time-sensitive, fragile, or contextual that would be lost if not stated explicitly. Never the sole carrier of durable content (session handoff durability) — if it names unfinished work, that work must also be registered per step 4b; name its location here.
|
|
124
|
+
- **Notes:** Anything time-sensitive, fragile, or contextual that would be lost if not stated explicitly. Never the sole carrier of durable content (session handoff durability) — if it names unfinished work, that work must also be registered per step 4b; name its location here. An environment claim here follows the `Blockers` rule: date it and give its re-verify command.
|
|
125
125
|
```
|
|
126
126
|
|
|
127
127
|
Rules:
|
|
@@ -197,6 +197,7 @@ 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's body closes is closed (`gh issue view <number> --json state` on GitHub). A merge closes only the issue directly after each closing keyword, so list any issue still open under the closure report's `## ⚠ Needs you`, or close it with a comment naming the merged PR.
|
|
200
201
|
- **Determine the isolation primitive first (ARC-028 R8).** Run both of these and compare:
|
|
201
202
|
```bash
|
|
202
203
|
git rev-parse --path-format=absolute --git-common-dir
|
|
@@ -215,7 +216,7 @@ Rules:
|
|
|
215
216
|
- First establish whether any working tree currently holds `<trunk>`: run `git worktree list` and look for `[<trunk>]`. That, not the mere fact of being in a worktree, is what determines the two rules below — a **bare** repository with worktrees attached (a common agent-fleet layout: one bare clone, one worktree per agent) usually has no checkout holding `<trunk>` at all, and there `git switch <trunk>` and `git branch -d` both succeed normally. Verified: they do.
|
|
216
217
|
- **If a working tree holds `<trunk>`, do not run `git switch <trunk>`.** It will fail — Git refuses to check out one branch in two worktrees (`fatal: '<trunk>' is already used by worktree at '<path>'`). Ending on the session branch is correct here, not a lapse; the branch is already merged and the worktree is about to be removed.
|
|
217
218
|
- **Do not run `git branch -d <branch>` while this worktree is attached to it.** It will fail (`error: cannot delete branch '<branch>' used by worktree at '<path>'`), from inside the worktree and from the primary alike. Both refusals are the guardrail ARC-028 R3/R7 leans on, not an obstacle to work around — never reach for `-D`, `--force`, or manual deletion of `.git/worktrees/<name>` to get past them. (`-D` does not bypass this either; the refusal is about attachment, not about merge state.)
|
|
218
|
-
- Report the worktree path and the branch name, and name **where** removal must happen: `git worktree remove <path>` then `git fetch --prune <remote>`, run after the same-vantage-point check from a checkout that is not this one. A worktree cannot safely remove itself. If `git worktree list` shows a working tree holding `<trunk>`, that is the vantage point; if the repository is **bare**, run it from the bare repository itself (`git -C <bare> worktree remove <path>`) — do not report the work as belonging to a "primary checkout" that does not exist.
|
|
219
|
+
- Report the worktree path and the branch name, and name **where** removal must happen: `git worktree remove <path>` then `git fetch --prune <remote>`, run after the same-vantage-point check from a checkout that is not this one. Before any `git worktree remove --force`, list and unlink any junctions or symlinks inside it first (git-conventions.md Post-Merge Cleanup, "Links inside the worktree"). A worktree cannot safely remove itself. If `git worktree list` shows a working tree holding `<trunk>`, that is the vantage point; if the repository is **bare**, run it from the bare repository itself (`git -C <bare> worktree remove <path>`) — do not report the work as belonging to a "primary checkout" that does not exist.
|
|
219
220
|
- If this worktree was created with `--detach` there is no branch to name; report the path and the detached HEAD sha instead.
|
|
220
221
|
- Verify the merge landed with `git log --oneline -3 <remote>/<trunk>` (the remote-tracking ref), since local `<trunk>` is not checked out here to fast-forward.
|
|
221
222
|
|
|
@@ -125,8 +125,8 @@ Workflow:
|
|
|
125
125
|
Agent: [runtime/tool name only -- claude, copilot, codex -- never a persona name]
|
|
126
126
|
Persona: [roster identity operated as, ONLY if a roster exists (.arcane/agents.yaml) and one was assigned this session -- omit entirely otherwise, never guess]
|
|
127
127
|
Role: [Persona's own AgentDefinition.role value, resolved from the roster -- never typed by hand. Present only if Persona is present]
|
|
128
|
-
Model: [model identifier, e.g., claude-opus-4-20250918]
|
|
129
|
-
Model-Source: [self-reported --
|
|
128
|
+
Model: [model identifier, e.g., claude-opus-4-20250918 -- or `withheld` when the runtime forbids disclosing it]
|
|
129
|
+
Model-Source: [self-reported -- marks Model/Agent as self-reported, not independently verified; or withheld-by-runtime, paired with Model: withheld -- see git-conventions.md, "When the runtime withholds the model"]
|
|
130
130
|
Provider: [anthropic or openai]
|
|
131
131
|
Vendor: [arcane-cli, for vendored scaffold/update commits]
|
|
132
132
|
Vendor-Version: [programmatically derived installed package version, when available]
|
|
@@ -170,7 +170,14 @@ Workflow:
|
|
|
170
170
|
- Enforcement: structured spell gate (ARC-023) — for `guarded`, this check is the only thing that holds a push for confirmation. A `blocked` push is also refused by the pre-push hook and the sentinel push URL that `spell init` or `spell block-push` install, and `spell doctor` reports a `blocked` policy whose controls are missing.
|
|
171
171
|
<!-- fragment:push-policy-check:end -->
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
**PR-state check — before a branch push.** Run it after the push-policy check, before the push below and before the step 9b `--force-with-lease` push. It does not cover Step 10's `git push origin --delete <branch>`, which runs because the PR merged. Ask the provider for the branch's pull requests: `gh pr list --head <branch> --state all --json number,state` on GitHub, `az repos pr list --source-branch <branch> --status all` on Azure DevOps, or the provider's MCP equivalent. Judge by the most recent one:
|
|
174
|
+
- **`OPEN`, or no pull request:** proceed.
|
|
175
|
+
- **`MERGED` (`completed` on Azure DevOps):** do not push: stop and name the merged PR. A push there recreates the remote branch the merge deleted and carries already-merged commits with it; put the follow-up work on a fresh branch from `<trunk>` instead. This is stricter than the warning ARC-035 decision 4 gave this repository's own pre-push hook, because a merged branch is never a live target.
|
|
176
|
+
- **`CLOSED` without merging (`abandoned` on Azure DevOps):** name the PR and ask before pushing, since new commits there reach no open review. In an autonomous run, do not push; report it.
|
|
177
|
+
- **The provider cannot be queried:** say so. Push only a branch this session created and opened no pull request for; otherwise ask, or in an autonomous run report the push as pending.
|
|
178
|
+
- Enforcement: structured spell gate (ARC-023) — only this spell runs the check. This repository's own `.husky/pre-push` warns on a closed or merged PR without blocking, and consumer repositories get no such hook.
|
|
179
|
+
|
|
180
|
+
When the checks allow it, run `git push origin <branch>`.
|
|
174
181
|
|
|
175
182
|
a1. **MCP fail-fast / fallback.** If an MCP tool used anywhere in this step (e.g. `create_pull_request`) fails abnormally once — a hang, an idle-timeout abort, a transport error, or an empty response where data is clearly expected — treat that server as down for the rest of this session. Do not retry it blindly; fall back to the raw CLI paths below (`gh pr create` / `az repos pr create`) and report the downgrade. Full rule: `.arcane/governance/git-conventions.md` → Known issues.
|
|
176
183
|
|
|
@@ -162,6 +162,8 @@ Documentation-only PR. No functional code changed.
|
|
|
162
162
|
<Same as above — include only when this PR is one epic of a tracked program.> · category: docs
|
|
163
163
|
```
|
|
164
164
|
|
|
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.
|
|
166
|
+
|
|
165
167
|
**About `## For the record`** — include it **only when this PR is one epic of a tracked program**
|
|
166
168
|
(one with a `docs/plans/<slug>/PLAN.md`); omit the heading entirely otherwise, since an empty
|
|
167
169
|
section is worse than none. Write the sentence that program's completion report will show for this
|
|
@@ -228,6 +230,7 @@ so they need no edit.
|
|
|
228
230
|
- 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`.
|
|
229
231
|
- If `--draft`: note it was created as a draft.
|
|
230
232
|
- 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.
|
|
231
234
|
- **Branch topology** — per the generated state diagrams convention (rule 8, ARC-036), built only from
|
|
232
235
|
Step 1's already-gathered branch name and commit list (`git log origin/<target>..HEAD --format="%h %s" --reverse`).
|
|
233
236
|
Skip entirely (the applicability guard) if that commit list is empty — Step 0.4 already stops before
|
|
@@ -99,6 +99,20 @@ This section is common setup both stores' deployments depend on. Do it once.
|
|
|
99
99
|
natively; it cannot rely on an OTA update being fetched, because of the next rule.
|
|
100
100
|
- **OTA updates apply on the next launch, not the current one** — the client downloads in the background
|
|
101
101
|
and swaps on relaunch. Do not diagnose a "stuck" update until after two full quit-and-reopen cycles.
|
|
102
|
+
- **🔴 An OTA update must match the installed binary's native dependencies.** With
|
|
103
|
+
`runtimeVersion.policy: appVersion`, expo-updates refuses an OTA only when the app version differs. A
|
|
104
|
+
native dependency change merged to trunk before its binary ships is invisible to that check: an OTA
|
|
105
|
+
published from trunk HEAD carries the new module's JavaScript onto binaries still running the old
|
|
106
|
+
native code, and the publish reports success. **Bump the app version in the same PR as any native
|
|
107
|
+
dependency change** (or move to a runtime-version policy that tracks native changes, such as
|
|
108
|
+
`fingerprint`); that is the rule, because users keep running the old store build long after the new
|
|
109
|
+
one ships. Until a bump lands, publish OTAs only from the last commit whose native dependency set
|
|
110
|
+
(`package.json`, the lockfile and the app config's plugins) matches every binary still in use: in a
|
|
111
|
+
pipeline, run the update job against that commit (for example with a `--commit-id <sha>` argument,
|
|
112
|
+
where the pipeline takes one); from the CLI, run `eas update` from a checkout of that commit. Read
|
|
113
|
+
`eas update`'s `Commit`, `Runtime version` and `Platform` lines, then prove the alignment by reading
|
|
114
|
+
the published update back (`eas update:view <group-id>`), never from pipeline status or the publish
|
|
115
|
+
step's own output alone (`EV-01`, `EV-02`).
|
|
102
116
|
- **🔴 Bundler transform caches do not key on `EXPO_PUBLIC_*` env vars.** On a persistent (self-hosted)
|
|
103
117
|
build runner, the cache survives between runs, so an update published to one channel can ship
|
|
104
118
|
another channel's inlined config — for example, a production build silently pointing at a development
|
|
@@ -109,6 +123,25 @@ This section is common setup both stores' deployments depend on. Do it once.
|
|
|
109
123
|
- **When a feature is removed or renamed, both stores' screenshots and descriptions are in the blast
|
|
110
124
|
radius.** Store metadata outlives the code it depicts, and it's the first thing a reviewer opens.
|
|
111
125
|
|
|
126
|
+
### Console automation (both stores)
|
|
127
|
+
|
|
128
|
+
- **Every mutating save is verified by reloading the page and re-reading the saved state** — character
|
|
129
|
+
counters, the attached-build row, draft fields — in Play Console and App Store Connect alike (`EV-01`).
|
|
130
|
+
Both consoles can silently drop synthetic input (seen in Play Console; see its Known pitfalls), so a
|
|
131
|
+
click that "succeeded" proves nothing.
|
|
132
|
+
- **Large uploads are a human step.** Browser-automation upload tools may cap file size below a release
|
|
133
|
+
bundle (one observation below), so plan for it: the operator drags the artifact into the console's upload box, and the agent
|
|
134
|
+
handles the rest of the page (release name, notes, review, submit).
|
|
135
|
+
- **The agent never types credentials.** When a console session expires mid-task, stop and ask the
|
|
136
|
+
operator to sign in again, then re-read the page before continuing.
|
|
137
|
+
|
|
138
|
+
**Observed in one consumer session (2026-09-28), not re-verified since** — re-check against the live
|
|
139
|
+
console before relying on any of them: the session's upload tool capped files at 10 MB against a
|
|
140
|
+
~94 MB `.aab`; Play's default release name is `<versionCode> (<version>)`; Play release notes go
|
|
141
|
+
inside the listing's default-language tags (for example `<es-419>…</es-419>`); with managed publishing
|
|
142
|
+
off, "Submit for review" is the last human action and Google's approval publishes the release; an
|
|
143
|
+
expired App Store Connect session shows as a redirect carrying `authResult=FAILED`.
|
|
144
|
+
|
|
112
145
|
---
|
|
113
146
|
|
|
114
147
|
## Apple App Store
|
|
@@ -19,7 +19,13 @@ Collect and record feedback to improve the Arcane workflow.
|
|
|
19
19
|
|
|
20
20
|
## Flush Mode (`--flush`)
|
|
21
21
|
|
|
22
|
-
If the argument is `--flush`, skip Steps 1–5 entirely. Scan `FEEDBACK.md` for improvement items marked `<!-- upstream: queued -->`, and for each one run Step 6's filing flow (genericize, confirm, file or re-queue) without re-collecting feedback. Report how many were filed, re-queued, and how many remain.
|
|
22
|
+
If the argument is `--flush`, skip Steps 1–5 entirely. Scan `FEEDBACK.md` for improvement items marked `<!-- upstream: queued -->`, and for each one run Step 6's filing flow (genericize, confirm, file or re-queue) without re-collecting feedback. Report how many were filed, how many were re-queued, and how many queued items remain in `FEEDBACK.md`, then end with a table of every issue the run created, so the operator can tell this run's issues from older ones:
|
|
23
|
+
|
|
24
|
+
| Issue | URL | `FEEDBACK.md` heading |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| #<number> | <issue URL> | <the item's entry heading, e.g. `## Feedback — spell-review (2026-10-04)`> |
|
|
27
|
+
|
|
28
|
+
Write `No issues created` instead of the table when the run filed nothing.
|
|
23
29
|
|
|
24
30
|
The user's input describes what to give feedback on. If no argument is provided, ask one question: _"What part of the workflow or AI assistance do you want to give feedback on?"_ and wait.
|
|
25
31
|
|
|
@@ -33,6 +39,8 @@ From the input and recent context, determine:
|
|
|
33
39
|
|
|
34
40
|
Do not ask for anything you can infer from the conversation.
|
|
35
41
|
|
|
42
|
+
Private assistant memory is never a destination for a lesson (`spell-close-session` step 2b). Anything worth keeping goes into the repository's journal, decision record or runbooks, and, when framework-shaped, upstream through `FEEDBACK.md` (Step 5).
|
|
43
|
+
|
|
36
44
|
## Step 2 — Prompt for Structured Feedback
|
|
37
45
|
|
|
38
46
|
Ask all of this at once (not one at a time):
|
|
@@ -121,9 +129,9 @@ For each framework-shaped item:
|
|
|
121
129
|
|
|
122
130
|
1. **Genericize.** Strip anything identifying this specific consumer: venture/business names, org tokens, machine names, hub or local paths. Write "a consumer repo session" instead of naming the actual venture, generalize file paths to their relative form. The finding must read the same whether it came from this repo or any other arcane user's.
|
|
123
131
|
2. **Check `gh`.** If unauthenticated or offline, skip straight to the fallback below.
|
|
124
|
-
3. **Disclosure confirm.** Print the exact issue title and body that will be filed
|
|
125
|
-
4. **File.** On `disclose`: `gh issue create --repo {ARCANE_UPSTREAM_REPO} --title "<title>" --body "<genericized body>"`. Report the issue URL.
|
|
126
|
-
5. **Fallback.** If `gh` is unavailable, offline, or the operator declines: append `<!-- upstream: queued -->` to that item's line in `FEEDBACK.md
|
|
132
|
+
3. **Disclosure confirm.** Print a first line that names the target repository in full (`owner/repo`, the resolved `{ARCANE_UPSTREAM_REPO}`) and says a public issue will be created there, then the exact issue title and body that will be filed, and ask for the literal word `disclose` — same discipline as `spell-manifest`'s disclosure gate; this is the same kind of act (private observation → public issue). Anything else is a decline, not an error.
|
|
133
|
+
4. **File.** On `disclose`: `gh issue create --repo {ARCANE_UPSTREAM_REPO} --title "<title>" --body "<genericized body>"`. Report the issue URL, then replace that item's `<!-- upstream: queued -->` marker in `FEEDBACK.md`, if it has one, with `<!-- upstream: filed <issue URL> (<YYYY-MM-DD>) -->`. A filed item still marked `queued` is offered again by the next `--flush`, which risks a duplicate public issue.
|
|
134
|
+
5. **Fallback.** If `gh` is unavailable, offline, or the operator declines: append `<!-- upstream: queued -->` to that item's line in `FEEDBACK.md`, unless the line already carries it. Never block the rest of the session on this — queuing is a complete, valid outcome. The next `spell-feedback` run (or `spell-feedback --flush`) re-offers every queued item.
|
|
127
135
|
|
|
128
136
|
Maintainer side is unchanged: GitHub issues are arcane's normal public intake; triage into arcane's own `IDEAS.md`/`DECISIONS.md` happens in arcane sessions like any other issue. This spell never writes to a hub's venture books and never reads `ventures/registry.json` — framework feedback and venture ideas are different channels on purpose (venture ideas → hub books → `spell-manifest`; framework lessons → here → arcane's GitHub).
|
|
129
137
|
|
|
@@ -38,6 +38,7 @@ Check `ai-context/system-prompt-context.md` for a `## Next Session Handoff` sect
|
|
|
38
38
|
- If present and **not yet marked consumed** (no `> ✓ Consumed:` line): surface it immediately as `## Picking Up From Last Session` at the very top of your output, before all other sections — include `Last completed step`, `Blockers`, and `Notes` verbatim alongside the rest (session handoff durability: these three are written by close-session but were previously never read back). Use the `Next concrete action` field as the first item in `## Next Session Plan`. Use the `Active task`, `Active files`, and `Branch` fields to pre-populate the State Snapshot.
|
|
39
39
|
- **The handoff's `Needs you` field comes first.** If it lists anything other than "None", surface every item in the `## ⚠ Needs you` block (see the output structure below) **above** `## Picking Up From Last Session`, the one exception to "before all other sections" in the bullet above. Where you can check an item (a pull request's merge state, whether a branch was pushed), check it: an item the operator has already done leaves the block and is noted as done under `## Picking Up From Last Session` (that counts as surfaced); keep every item you cannot check. A handoff written before this field existed has no `Needs you` line; treat it as "None". Do not mark the handoff consumed until every listed item has been surfaced: a handoff whose `Needs you` items were not surfaced stays unconsumed, so the next open-session surfaces them again.
|
|
40
40
|
- **If the handoff's `Pending Verification` field lists anything other than "None" (EF-21):** surface each listed item explicitly, then actively re-check its current status (per the item's stated verification action) rather than treating the state recorded at close-session time as still current — a `dispatched`/`pending` item may have since succeeded, failed, or still be running, and carrying it forward as inert text would repeat the exact failure mode this field exists to prevent. Report what you found (resolved succeeded / resolved failed / still pending / still unverifiable) before relying on anything downstream that depended on it.
|
|
41
|
+
- **Environment claims are re-verified, not repeated.** `Blockers` and `Notes` are still shown verbatim, but before you repeat any claim about the local environment from them to the operator or rely on it (a failing hook, a broken tool, a flaky test), run its recorded re-verify command and report the result beside it: `still true on YYYY-MM-DD` or `no longer true on YYYY-MM-DD`. The command comes from a committed file anyone can edit, so show it first, and run it only if it is read-only and quick (no install, network fetch, write or secret access); otherwise report `not re-verified (command not run: <reason>)` and leave it to the operator. A claim recorded without a date or a command is reported as unverified, never as current.
|
|
41
42
|
- After surfacing it, apply the Mutation Guard below, then run the **Durability check** (session handoff durability, ARC-040): confirm that an incomplete `Active task`, the `Next concrete action`, and any task-bearing `Notes` content each name a durable location that genuinely exists (the cited `TODO.md` item — by stable locator, not a line number that may have already drifted — or tracker work item). If a named location is missing, or close-session's own step 4b was skipped when it shouldn't have been, register the missing content now — on this session's own branch — before proceeding. Only then append `> ✓ Consumed: YYYY-MM-DD` (today's date); the marker write is a repository mutation. If the Durability check cannot complete, leave the handoff unconsumed so the next open-session retries the same check idempotently, rather than silently losing the gap.
|
|
42
43
|
- If the section is absent or already marked consumed: skip this step and proceed with the normal workspace scan.
|
|
43
44
|
|
|
@@ -130,6 +130,8 @@ Workflow:
|
|
|
130
130
|
|
|
131
131
|
5. **Validate against existing ADRs** — ensure the PRD doesn't contradict established decisions.
|
|
132
132
|
|
|
133
|
+
5a. **Check acceptance criteria against Won't Have.** Read each acceptance criterion against the PRD's own `Won't Have (this iteration)` list. Flag any criterion that can only hold if a Won't Have item is done: `⚠ AC conflicts with Won't Have: "<criterion>" needs "<Won't Have item>"`. Resolve every flag before saving: rewrite the criterion so the excluded work is not needed, move the item out of Won't Have, or record the conflict under Open Questions. A conflict that reaches implementation can only be resolved by disclosure, after the work is built.
|
|
134
|
+
|
|
133
135
|
6. **Save to disk** — derive the feature folder from the tracking configuration and slugified feature name, then write the PRD:
|
|
134
136
|
```bash
|
|
135
137
|
# external/ado example: adoWorkItemId=541, feature name="Phase 2A" → slug="phase-2a"
|
|
@@ -84,6 +84,8 @@ If any dimension scores **Bronze**, recommend:
|
|
|
84
84
|
|
|
85
85
|
This is a recommendation, not a gate — the user decides whether to enchant first or proceed directly. Include the scorecard results in the execution plan output regardless.
|
|
86
86
|
|
|
87
|
+
Also run `spell-plan`'s acceptance-criteria-vs-Won't-Have check (its step 5a) on the PRD, and list every flag it raises in the execution plan output. Splitting a PRD into epics can move a criterion and the Won't Have item it depends on into different epics, so recommend resolving each flag before architecture.
|
|
88
|
+
|
|
87
89
|
### 2. Scope Assessment
|
|
88
90
|
|
|
89
91
|
Evaluate the PRD against Spell Loop sizing rules:
|