arcane-cli 0.23.0 → 0.24.1

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.
@@ -58,6 +58,48 @@ Assign a default power level per agent × per repo. Human approval and commit go
58
58
 
59
59
  ---
60
60
 
61
+ ## Solo-Operator Delegation Records (No Roster)
62
+
63
+ The Per-Repo Power Level Matrix above assumes an installed agent roster (`.arcane/agents.yaml`) — each
64
+ agent has a slug and a per-repo power level, and that's where standing authority lives. A repo with **no
65
+ roster installed at all** has nowhere in that model to record a delegation, so one tends to get granted
66
+ ad hoc — free prose in whatever document is at hand — and then forgotten, since nothing lists it.
67
+
68
+ For exactly this case, record the grant explicitly instead, in `.arcane/delegations.json`:
69
+
70
+ ```json
71
+ {
72
+ "delegations": [
73
+ {
74
+ "id": "short-identifier",
75
+ "grantedBy": "operator (name)",
76
+ "grantedAt": "YYYY-MM-DD",
77
+ "scope": "What this delegation covers — be specific, not 'everything'",
78
+ "permittedActions": ["create-session-branches", "commit", "push", "..."],
79
+ "excludedActions": ["platform-settings-mutations", "..."],
80
+ "status": "active",
81
+ "revocation": "Edit or remove this entry and commit."
82
+ }
83
+ ]
84
+ }
85
+ ```
86
+
87
+ - **Explicit:** a structured, git-tracked file — not prose buried in an unrelated document.
88
+ - **Listable:** `spell doctor` reads this file (when present) and reports every active delegation's
89
+ scope and exclusion list. A missing file is a silent pass — a repo with no ad hoc grants has nothing
90
+ to report.
91
+ - **Revocable per repo:** revocation is the git-native act of editing or removing the entry and
92
+ committing — set `status` to `"revoked"` or delete the entry outright.
93
+ - **Grants no new authority.** This mechanism does not permit anything `agent-policies.md` didn't
94
+ already allow in principle (human execution required absent an explicit grant) — it only makes an
95
+ existing kind of ad hoc grant explicit and discoverable instead of buried.
96
+ - **No scaffold is shipped.** A delegation record is inherently repo-specific and starts empty for a
97
+ fresh install — there is nothing generic to seed.
98
+ - **Does not replace the roster-based matrix.** A repo with an installed roster still uses per-agent
99
+ power levels for standing authority; this is the parallel, lighter-weight path for repos without one.
100
+
101
+ ---
102
+
61
103
  ## Agent-to-Agent Topology
62
104
 
63
105
  Spawn rights are configured per agent. Use a small number of orchestrators; keep most agents as leaf nodes that cannot spawn subagents.
@@ -10,7 +10,7 @@ agent: agent
10
10
  - This prompt chains all Spell Loop phases into a single autonomous pipeline.
11
11
  - Includes optional PRD enchantment (spell-enchant) between Plan and Architect for quality enhancement.
12
12
  - Only one human touchpoint: approving the PR at ship time.
13
- - Delegates each phase to the appropriate specialized agent role (research, architecture, build, QA, marketing review). Resolve concrete personas from [agent-policies](../../.arcane/governance/agent-policies.md) / [naming-conventions](../../.arcane/governance/naming-conventions.md); if neither is present, proceed with role names directly.
13
+ - Delegates each phase to the appropriate specialized agent role (research, architecture, build, QA, marketing review) — **in practice, a single agent switches hats per phase** unless a real per-tool subagent registry is configured (e.g. `.claude/agents/` for Claude Code); this framework ships no such registry by default, so "delegates" names the intended role model, not a literal handoff between separately running agents (2026-07-22 dogfooding finding). Resolve concrete personas from [agent-policies](../../.arcane/governance/agent-policies.md) / [naming-conventions](../../.arcane/governance/naming-conventions.md); if neither is present, proceed with role names directly.
14
14
  - If any phase fails, the pipeline halts and reports the blocker — no silent failures.
15
15
 
16
16
  ---
@@ -58,8 +58,9 @@ Execute the `spell-plan` workflow:
58
58
 
59
59
  1. Gather requirements from the feature description.
60
60
  2. Check existing ADRs and business docs for constraints.
61
- 3. Produce `PRD.md` with requirements, acceptance criteria, constraints, and tracking configuration (`tracking_mode`, optional `external_provider`, optional `adoWorkItemId`/`githubIssueId`).
62
- 4. **Gate:** Validate every requirement has at least one testable acceptance criterion. If scope is too large for one sprint, halt and recommend splitting.
61
+ 3. **If the feature description hands in a pre-diagnosed root cause** (a specific file/line/mechanism already named as the cause of a bug), re-verify it against current source before accepting it — a handed-in diagnosis can be stale (2026-07-22 dogfooding finding: a filed root-cause lead was already fixed and the real causes were elsewhere). Do not carry a stale diagnosis into the PRD or stories unexamined.
62
+ 4. Produce `PRD.md` with requirements, acceptance criteria, constraints, and tracking configuration (`tracking_mode`, optional `external_provider`, optional `adoWorkItemId`/`githubIssueId`).
63
+ 5. **Gate:** Validate every requirement has at least one testable acceptance criterion. If scope is too large for one sprint, halt and recommend splitting.
63
64
 
64
65
  Store the PRD in the working directory. Proceed automatically to Phase 1.5.
65
66
 
@@ -118,6 +119,7 @@ Execute the `spell-implement` workflow:
118
119
  2. Loop:
119
120
  a. Pick the next story where `passes: false`, ordered by priority.
120
121
  b. Implement the story — minimum code to satisfy acceptance criteria.
122
+ b1. **DB migration guard.** If `architecture.md`/the PRD did not name a database migration as part of this feature's scope, but implementing this story is about to produce one anyway, **halt and flag it** — do not write the migration file silently (2026-07-22 dogfooding finding: this is a scoped hard guard, not a blanket ban). If a migration genuinely is in scope, **re-derive its sequence number from a fresh `git pull --ff-only` of the target branch immediately before writing the file** — not once at branch-creation time in step 0 — so a concurrent epic that landed a migration in the meantime is reflected. This is the concrete fix for a real incident: two epics run in parallel worktrees each independently claimed the same migration sequence number, invisible until human merge review.
121
123
  c. Write tests per `governance/testing-standards.md`.
122
124
  d. Run tests. If passing:
123
125
  - In `external/ado` mode, commit with `#{adoWorkItemId} type(scope): description` and agent attribution trailers (`Agent`, `Model`, `Provider`).
@@ -136,8 +138,9 @@ Execute the `spell-test` workflow:
136
138
  1. Run the full test suite (not just per-story — full regression).
137
139
  2. Validate coverage: 80% line minimum, 95% critical path.
138
140
  3. Map each acceptance criterion from the PRD to a specific passing test.
139
- 4. Update `stories.json` with consolidated test evidence.
140
- 5. **Gate:** If coverage is below threshold or any acceptance criterion lacks a test, loop back to Phase 3 to add missing tests. Maximum 2 coverage-fix loops before halting.
141
+ 4. **If this run produced a database migration:** a fresh/empty test database is not sufficient evidence by itself — this is a real incident's root cause (a unique index conflicted with legitimate duplicate rows the target app allows by design; every test passed against an empty DB, then broke the consumer's actual deploy). If the execution sandbox has access to a snapshot or copy of the target environment's real data, replay the migration against it and report the result. **If it does not** (the common case for an isolated sandbox), do not silently proceed as if this were validated — name the gap explicitly in the ship report's disclosure (see below) so a human reviews the migration against real data before it ships.
142
+ 5. Update `stories.json` with consolidated test evidence.
143
+ 6. **Gate:** If coverage is below threshold or any acceptance criterion lacks a test, loop back to Phase 3 to add missing tests. Maximum 2 coverage-fix loops before halting.
141
144
 
142
145
  Proceed automatically to Phase 5.
143
146
 
@@ -159,12 +162,24 @@ Proceed automatically to Phase 6.
159
162
  Execute the `spell-ship` workflow:
160
163
 
161
164
  1. Verify all prior phases passed (PRD, architecture, all stories, tests, review).
165
+ 1a. **PR strategy: one branch, one PR, per invocation.** This is the default and it is not optional to
166
+ restate per run — a 2026-07-22 dogfooding finding found this pattern had to be imposed by the
167
+ operator each time because the spell never stated it. If a later invocation would touch files an
168
+ earlier, still-open PR from a prior invocation already changed, do not open a second, conflicting PR
169
+ — rebase this branch onto the earlier PR's branch (or wait for it to merge, per this spell's own
170
+ multi-epic serialization default in the Rules section) before opening this one.
162
171
  2. Run pre-deploy checklist:
163
172
  - No merge conflicts with target branch.
164
173
  - No secrets or credentials in the diff.
165
174
  - DECISIONS.md updated if new ADRs were created.
166
175
  - Documentation updated for user-facing changes.
167
176
  3. Generate the ship report (phase status table, test evidence, review findings, recommendation).
177
+ **Mandatory disclosure section:** explicitly list anything this run could not verify in its own
178
+ execution sandbox — a real-data migration replay Phase 4 couldn't attempt, an external integration
179
+ it couldn't reach, a manual step it couldn't perform — never omit this section just because nothing
180
+ applies (state "Nothing withheld" in that case). Fold it into the consumer repo's own QA checklist
181
+ so a human reviewer sees exactly what still needs a human look, not just what passed (2026-07-22
182
+ dogfooding finding: honest disclosure of sandbox limits, not just of failures).
168
183
  4. Create the PR:
169
184
  - `external/ado` mode: `az repos pr create --repository {repo} --source-branch {branch} --target-branch main --title "{feature}" --work-items {adoWorkItemId}`
170
185
  - `internal` mode (or provider TODO mode): create PR without `--work-items`.
package/dist/index.js CHANGED
@@ -4065,6 +4065,38 @@ async function checkPushPolicy(targetDir) {
4065
4065
  message: `declared "blocked" but not enforced \u2014 ${missing.join("; ")}. The manifest claims a protection this repository does not have.`
4066
4066
  };
4067
4067
  }
4068
+ async function checkDelegations(targetDir) {
4069
+ const name = "Standing delegations (T13)";
4070
+ const filePath = join11(targetDir, ".arcane", "delegations.json");
4071
+ let raw;
4072
+ try {
4073
+ raw = await readFile6(filePath, "utf8");
4074
+ } catch {
4075
+ return { name, passed: true, blocking: false, message: "no delegations recorded" };
4076
+ }
4077
+ let parsed;
4078
+ try {
4079
+ parsed = JSON.parse(raw);
4080
+ } catch {
4081
+ return {
4082
+ name,
4083
+ passed: false,
4084
+ blocking: false,
4085
+ message: `.arcane/delegations.json exists but contains invalid JSON`
4086
+ };
4087
+ }
4088
+ const active = (parsed.delegations ?? []).filter((d) => d.status === "active");
4089
+ if (active.length === 0) {
4090
+ return { name, passed: true, blocking: false, message: "no active delegations" };
4091
+ }
4092
+ const summary = active.map((d) => `${d.id} (scope: ${d.scope}; excludes: ${d.excludedActions.length} action(s))`).join("; ");
4093
+ return {
4094
+ name,
4095
+ passed: true,
4096
+ blocking: false,
4097
+ message: `${active.length} active: ${summary}`
4098
+ };
4099
+ }
4068
4100
  async function checkPlatformBranchPolicy(targetDir) {
4069
4101
  const name = "Platform branch/merge policy (T11)";
4070
4102
  const targets = await pushTargets(targetDir);
@@ -4117,7 +4149,8 @@ async function runDoctor(targetDir, options = {}, assetsDir) {
4117
4149
  Promise.resolve(checkIncidentReleaseGate()),
4118
4150
  checkPullRebase(targetDir),
4119
4151
  checkPushPolicy(targetDir),
4120
- checkPlatformBranchPolicy(targetDir)
4152
+ checkPlatformBranchPolicy(targetDir),
4153
+ checkDelegations(targetDir)
4121
4154
  ]);
4122
4155
  const sessionResults = await checkSessionContinuity(targetDir);
4123
4156
  results.push(...sessionResults);