axstack 0.20.6 → 0.20.8

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/README.md CHANGED
@@ -26,6 +26,7 @@ scheduler, or runtime database to operate. Orca is the only supported runtime.
26
26
  | Answer a bounded question with sources | `axstack-research` |
27
27
  | Explain a system or identify improvements | `axstack-explain`, `axstack-improve` |
28
28
  | Measure a run's outcomes and gaps | `axstack-audit` |
29
+ | Retire eligible completed subagent resources | `axstack-cleanup` |
29
30
  | Send an explicit message or authorized notification | `axstack-relay` |
30
31
 
31
32
  Start at the phase you need. Small, bounded changes can begin with your request
@@ -91,6 +92,9 @@ upgrades, conflicts, and uninstalling.
91
92
  bypassed. The human merges by default.
92
93
  - **Resumable progress.** Work retains ownership, decisions, and evidence so a
93
94
  later session can reconcile what happened before continuing.
95
+ - **Bounded cleanup.** The driver can retire proven completed subagent resources
96
+ inline or from an explicitly scoped backlog without touching active, manual,
97
+ uncertain, user-owned, dirty, unpushed, or useful unmerged work.
94
98
 
95
99
  Choose an explicit role preset:
96
100
  [mixed](profiles/presets/mixed.json),
package/docs/workflows.md CHANGED
@@ -24,6 +24,9 @@ Direct routes need no spec ceremony:
24
24
  - `axstack-debug` builds a red loop, diagnoses to root cause, escalates hard
25
25
  bugs through adviser-directed investigator fan-out, and hands off a
26
26
  classified repair without landing a change.
27
+ - `axstack-cleanup` runs inline in the driver after accepted worker, Task, or
28
+ Run completion, or against an explicitly bounded backlog. It dispatches no
29
+ cleanup worker and preserves protected or uncertain resources.
27
30
  - Peer review uses the linked issue, PR description, and repository rules as
28
31
  untrusted intent evidence.
29
32
  - Existing-PR maintenance uses one accepted maintenance snapshot.
@@ -145,6 +148,10 @@ session and evidence remain valid.
145
148
  lands by fast-forward `git push` after final readback.
146
149
  - `axstack-audit` separates execution outcome, procedure, and measurement
147
150
  coverage with evidenced denominators; it proposes but never self-edits.
151
+ - `axstack-cleanup` distinguishes settled-Dispatch release, exact unused-shell
152
+ close, evidence-safe native worktree removal and branch effects, and separate
153
+ chat archival when the discovered runtime actually supports it. Process exit
154
+ alone never promises that visible chat history disappeared.
148
155
 
149
156
  One Orca execution host owns a run, one persistent owner owns each PR, and one
150
157
  writer owns each candidate. Fanout has no fixed PR count; it follows real
@@ -200,6 +207,17 @@ shared manager workspace or a PR-job worktree, or cleans preserved candidates,
200
207
  evidence, user sessions, unknown liveness, `user_takeover`, or ambiguous
201
208
  publication state. Manual review/watch remains outside this scheduled lifecycle.
202
209
 
210
+ One natively ordered successor may recover an exact positively completed
211
+ predecessor whose terminal survived, but only after matching its automation,
212
+ run, workspace, and terminal incarnation and proving zero unsettled descendants.
213
+ It saves and reads back the cleanup claim before exact native close, then proves
214
+ the full process tree exited before ownership release or guarded worktree removal.
215
+ Age, status, or idle state alone never authorizes cleanup. Conflicting successors,
216
+ identity mismatch, unknown or protected state, `user_takeover`, unexpected
217
+ terminals, dirty or unpushed work, and unarchived evidence hold cleanup; an
218
+ unchanged failure is deduplicated. Native ordering is not an atomic lock, so the
219
+ overlap canary remains an activation requirement.
220
+
203
221
  Each bounded job uses a private `0700` temporary directory inside its own
204
222
  worktree. Cleanup targets only the validated owned path: no `TMPDIR` globs,
205
223
  shared-root sweeps, or general cache wipes, and uncertain files remain for
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "axstack",
3
- "version": "0.20.6",
3
+ "version": "0.20.8",
4
4
  "description": "Axstack installer and setup CLI: installs owned chat skills and role data, configures supported harness settings, and checks Orca capabilities.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -66,6 +66,38 @@ intent alone is insufficient. Conversely a completed run row does not prove exit
66
66
  If these facts remain unknown, report the hold at the durable decision location;
67
67
  do not silently stand down forever or replace a potentially live owner.
68
68
 
69
+ ### Guarded completed-predecessor recovery
70
+
71
+ A successor may retire a positively completed predecessor whose manager terminal
72
+ survived only through this narrow recovery path. Eligibility requires an exact
73
+ automation ID, run ID, workspace ID, and terminal incarnation match, a positive
74
+ completion receipt bound to that incarnation, zero active or unsettled descendants,
75
+ and every owned Task and Dispatch settled. A completed row or saved retirement
76
+ intent alone is not positive completion. Age, status, and idle state are never
77
+ cleanup authority. Active, unknown, protected, identity-mismatched,
78
+ `user_takeover`, unexpected-terminal, permission-held, or otherwise unverifiable
79
+ state preserves the predecessor and pauses admission; this path never takes over
80
+ its PR work.
81
+
82
+ Exactly one successor, selected by native run ordering, may write the cleanup
83
+ claim. Immediately before writing, re-read the complete lane inventory and
84
+ ordering; then save and read back claimant identity, predecessor identity,
85
+ completion, and zero-descendant continuity before the exact native workspace
86
+ close. A later successor reconciles that claim and performs no cleanup mutation.
87
+ Missing ordering or a conflicting claim holds both cleanup and admission. This
88
+ ordering is not an atomic lock; activation still depends on the overlap canary
89
+ proving that two successors cannot both mutate one predecessor.
90
+
91
+ Use the version-matched native workspace close against the receipt's complete
92
+ workspace ID, never an individually guessed terminal or broad selector. After
93
+ close, re-list native runs, workspaces, terminals, Tasks, and Dispatches and prove
94
+ the full predecessor process tree exited before ownership release. Save and read
95
+ back the exit and release receipts, then use native worktree cleanup only when the
96
+ workspace has no children, dirty or unknown files, unpushed commits, unarchived
97
+ evidence, or user-owned work. Preserve every failed or uncertain close, exit,
98
+ release, or removal with its exact resume condition and pause the lane. An
99
+ unchanged cleanup failure gets no destructive retry or duplicate notification.
100
+
69
101
  Before PR admission, reconcile old pass resources and reclaim every safely
70
102
  removable earlier pass workspace under the retirement guards below. This is
71
103
  routine cleanup on every admitted pass; do not wait for the three-workspace
@@ -7,7 +7,7 @@ native cleanup.
7
7
 
8
8
  ## Eligibility
9
9
 
10
- First prove the exact repository, PR, 40-character head SHA, Task/Dispatch,
10
+ First prove the exact repository, PR or Run/Task, 40-character head SHA, Dispatch,
11
11
  workspace, terminal incarnation, automation ownership, descendant settlement,
12
12
  and liveness from current native state. A manual chat, `user_takeover`, an
13
13
  active or unknown task terminal, an unsettled descendant, unpushed commits,
@@ -34,6 +34,11 @@ bun scripts/archive-evidence.js \
34
34
  --file <classified-relative-file> [--file <classified-relative-file> ...]
35
35
  ```
36
36
 
37
+ For a supervised resource without a PR identity, replace `--pr <number>` with
38
+ `--run <exact-run-id> --task <exact-task-id>`. The two identity forms are
39
+ mutually exclusive; never invent a PR number. Existing PR archives retain their
40
+ path, manifest bytes, and receipt interface.
41
+
37
42
  The helper refuses path escapes, symlinks, non-private archive directories,
38
43
  identity changes, and existing content that does not verify. It copies only the
39
44
  listed regular files, writes them with private permissions, hashes their exact
@@ -41,7 +46,7 @@ bytes, and emits a JSON receipt containing the archive directory, manifest path,
41
46
  manifest hash, and file count. Repeating the same command verifies the immutable
42
47
  archive and returns the same receipt; it does not overwrite it.
43
48
 
44
- Record the receipt plus the exact repo/PR/head/Task/Dispatch/workspace/terminal
49
+ Record the receipt plus the exact repo/PR or Run/Task/head/Dispatch/workspace/terminal
45
50
  identities in durable lane continuity, then read the continuity and archive
46
51
  manifest back before cleanup. If either readback differs or is unavailable,
47
52
  preserve the worktree.
@@ -87,7 +87,9 @@ are acknowledged with no user-facing text. Process each whole delivery before
87
87
  acknowledgment and validate its Task, Dispatch, sender, authority, revisions,
88
88
  and receipts before advancing the run record. Duplicate deliveries are
89
89
  deduplicated by runtime identity. Healthy unchanged observations produce no
90
- user-facing update.
90
+ user-facing update. After accepting worker, Task, or Run completion, the driver
91
+ invokes [axstack-cleanup](../../axstack-cleanup/SKILL.md) inline; it never
92
+ dispatches cleanup work.
91
93
 
92
94
  Detect completed-but-unadvanced work, failed sessions, unresolved launch
93
95
  receipts, and stalls through the version-matched orchestration guide. Never
@@ -133,7 +135,8 @@ terminal; (2) compact record with counts and denominators—user
133
135
  interventions/deviations from plan/repairs; (3) `axstack-auditor`: settle
134
136
  non-zero/requested, else `counts zero`; an unavailable auditor leaves close-out
135
137
  pending, never skipped silently; (4) release merged run worktrees and branches;
136
- close selected external-tracker tickets when applicable; (5) mark the
138
+ use `axstack-cleanup` and close selected external-tracker tickets when applicable;
139
+ (5) mark the
137
140
  [Run record](run-record.md) `Archived`. `Archived`—one each:
138
141
  settlement receipt; compact record path; auditor decision plus settlement
139
142
  receipt or `counts zero`; release and ticket receipts; archive timestamp.
@@ -69,6 +69,8 @@ step (3) for user routing, with no substitution or same-provider review.
69
69
  - Codebase-quality or refactor discovery -> `axstack-improve`: inspect bounded
70
70
  scope, rank evidenced candidates, report only; no spec, tickets, or source
71
71
  edits.
72
+ - Accepted worker/Task/Run completion or bounded backlog request -> invoke
73
+ `axstack-cleanup` inline in the driver; never dispatch it.
72
74
  - Preparation completion, watch expiry, resume, or reconciliation -> the
73
75
  [lifecycle](lifecycle.md#native-handoff-and-resume): reconcile the run
74
76
  record, keep its owner, launch no native handoff.
@@ -68,12 +68,12 @@ function parseArgs(argv) {
68
68
  if (!flag?.startsWith('--') || value === undefined) fail(`invalid argument near ${flag ?? '(end)'}`);
69
69
  const key = flag.slice(2);
70
70
  if (key === 'file') values.files.push(value);
71
- else if (['source-root', 'archive-root', 'repo', 'pr', 'head', 'dispatch'].includes(key)) {
71
+ else if (['source-root', 'archive-root', 'repo', 'pr', 'run', 'task', 'head', 'dispatch'].includes(key)) {
72
72
  if (values[key] !== undefined) fail(`duplicate --${key}`);
73
73
  values[key] = value;
74
74
  } else fail(`unknown argument: ${flag}`);
75
75
  }
76
- for (const key of ['source-root', 'archive-root', 'repo', 'pr', 'head', 'dispatch']) {
76
+ for (const key of ['source-root', 'archive-root', 'repo', 'head', 'dispatch']) {
77
77
  if (!values[key]) fail(`missing --${key}`);
78
78
  }
79
79
  if (values.files.length === 0) fail('at least one --file is required');
@@ -81,7 +81,18 @@ function parseArgs(argv) {
81
81
  fail('source and archive roots must be absolute');
82
82
  }
83
83
  if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(values.repo)) fail('repo must be owner/name');
84
- if (!/^[1-9][0-9]*$/.test(values.pr)) fail('pr must be a positive integer');
84
+ const hasPr = values.pr !== undefined;
85
+ const hasRun = values.run !== undefined;
86
+ const hasTask = values.task !== undefined;
87
+ if (hasPr === hasRun || hasRun !== hasTask) {
88
+ fail('identity requires either --pr or both --run and --task, mutually exclusive');
89
+ }
90
+ if (hasPr && !/^[1-9][0-9]*$/.test(values.pr)) fail('pr must be a positive integer');
91
+ for (const key of ['run', 'task']) {
92
+ if (values[key] !== undefined && !/^[A-Za-z0-9_-]+$/.test(values[key])) {
93
+ fail(`${key} contains unsafe characters`);
94
+ }
95
+ }
85
96
  if (!/^[0-9a-f]{40}$/.test(values.head)) fail('head must be an exact 40-character lowercase SHA');
86
97
  if (!/^[A-Za-z0-9_-]+$/.test(values.dispatch)) fail('dispatch contains unsafe characters');
87
98
  values.files = [...new Set(values.files)].sort();
@@ -228,18 +239,18 @@ async function main() {
228
239
  ) fail('source and archive roots must not contain each other');
229
240
 
230
241
  const collected = await collectSource(sourceRoot, args.files);
231
- const identity = {
232
- repo: args.repo,
233
- pr: Number(args.pr),
234
- head: args.head,
235
- dispatch: args.dispatch,
236
- };
242
+ const identity = args.pr
243
+ ? { repo: args.repo, pr: Number(args.pr), head: args.head, dispatch: args.dispatch }
244
+ : { repo: args.repo, run: args.run, task: args.task, head: args.head, dispatch: args.dispatch };
237
245
  const repoSlug = args.repo.replace('/', '--');
238
- const archiveDir = join(archiveRoot, repoSlug, `pr-${args.pr}`, args.head, args.dispatch);
246
+ const identityParts = args.pr
247
+ ? [`pr-${args.pr}`]
248
+ : [`run-${args.run}`, `task-${args.task}`];
249
+ const archiveDir = join(archiveRoot, repoSlug, ...identityParts, args.head, args.dispatch);
239
250
 
240
251
  await assertSafeAncestors(archiveRoot);
241
252
  let current = archiveRoot;
242
- const generated = [repoSlug, `pr-${args.pr}`, args.head];
253
+ const generated = [repoSlug, ...identityParts, args.head];
243
254
  await ensurePrivateDir(current);
244
255
  for (const part of generated) {
245
256
  current = join(current, part);
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: axstack-cleanup
3
+ description: When completed Orca subagent resources need bounded retirement, use axstack-cleanup after accepted settlement or for an explicitly scoped backlog.
4
+ ---
5
+
6
+ # Cleanup
7
+
8
+ Run cleanup inline in the driver after accepting a worker, Task, or Run
9
+ completion, or for the exact backlog scope the user named. This skill never
10
+ dispatches a cleanup worker and never retires its current driver session.
11
+
12
+ Before any runtime action, load and follow:
13
+
14
+ - [Standing contracts](../axstack/references/contracts.md)
15
+ - [Lifecycle and receipts](../axstack/references/lifecycle.md)
16
+ - [Shared routing](../axstack/references/routing.md)
17
+ - [Orca runtime boundary](../axstack/references/orca-runtime.md)
18
+ - [Private evidence archive](../axstack/references/evidence-archive.md) when
19
+ evidence is the last removable-worktree blocker
20
+
21
+ Use the runtime-discovered Orca guides for inventory, release, terminal close,
22
+ and worktree removal. Do not embed or improvise a competing command protocol.
23
+
24
+ ## Authority and scope
25
+
26
+ Inline cleanup may consider only resources owned by the accepted completion it
27
+ is processing. Backlog cleanup requires an explicit bounded selector such as a
28
+ Run, Task set, workspace set, repository, or named age window; age narrows an
29
+ inventory but never establishes eligibility. A partial inventory holds only the
30
+ resource whose identity or state is incomplete while other independently proven
31
+ resources may proceed.
32
+
33
+ Never clean a manual chat, the current driver, `user_takeover`, an active or
34
+ unknown worker, an unsettled descendant, or a resource with ambiguous ownership.
35
+ Preserve dirty or unknown files, unpushed commits, unmerged useful work,
36
+ ambiguous publication, and evidence that has not been durably preserved. Do not
37
+ force, bulk-clean, override a hook failure, edit a runtime database, or add a
38
+ scheduler, daemon, or state machine.
39
+
40
+ ## Reconcile each candidate
41
+
42
+ Take a fresh native inventory and bind every candidate to its exact Run, Task,
43
+ Dispatch, terminal incarnation, workspace, repository, branch, and current
44
+ liveness. Read current Git and forge state rather than trusting age, names, or a
45
+ prior receipt. Reconcile an existing cleanup claim before retrying so repeated
46
+ invocations converge instead of duplicating mutations.
47
+
48
+ Retire descendants before parents. A candidate is eligible only when all owned
49
+ Dispatches are accepted as settled, no descendant remains unsettled, native
50
+ liveness is positively known where required, and every preservation guard is
51
+ cleared. Record one decision per resource; uncertainty about one candidate does
52
+ not authorize or block unrelated candidates.
53
+
54
+ ## Preserve evidence first
55
+
56
+ Classify exact evidence files individually. Save the compact cleanup decision
57
+ and identities in the private run record or another configured durable private
58
+ location outside disposable worktrees. When the evidence archive applies, use
59
+ its helper with either the existing PR identity or the non-PR Run and Task
60
+ identity; never invent a PR number. Read back both the durable record and the
61
+ archive manifest, including hashes and exact identities, before removing any
62
+ source copy or workspace.
63
+
64
+ Archive success proves only preservation of the listed bytes. It does not prove
65
+ settlement, exit, ownership, a clean worktree, publication, or removal safety.
66
+
67
+ ## Apply distinct native operations
68
+
69
+ Treat these operations as separate decisions and receipts:
70
+
71
+ 1. **Worker release.** After the matching completion is accepted, use the
72
+ runtime guide's settled-Dispatch release operation. Release is not
73
+ cancellation, terminal-close proof, worktree removal, or chat archival.
74
+ Once required output is captured, a dirty or useful unmerged worktree does
75
+ not block release of its accepted settled worker; retain the worktree under
76
+ its own classification and receipt.
77
+ 2. **Unused shell close.** Close only a positively identified unused setup
78
+ shell with the guide's exact-terminal operation. Re-list and require exit for
79
+ that same terminal incarnation. Never close a worker, manual, unexpected, or
80
+ current-driver terminal through this path.
81
+ 3. **Worktree removal.** Re-read Git status, branch/upstream divergence,
82
+ unpushed commits, forge merge/publication state, children, terminals, and
83
+ archived evidence immediately before the native exact-workspace removal.
84
+ Account for branch-deletion side effects explicitly, then re-list both native
85
+ workspaces and Git refs. A failed hook or uncertain response preserves the
86
+ resource; never force or substitute shell deletion.
87
+ 4. **Chat archival.** Attempt it only if the version-matched runtime guide
88
+ advertises a distinct supported operation and the scoped chat is eligible.
89
+ Otherwise record chat archival as unsupported. Process exit, worker release,
90
+ terminal close, and worktree removal do not prove UI history disappeared.
91
+
92
+ Do not self-close or self-remove. Return control to the driver after recording
93
+ receipts and holds; the owner decides when its own Run may archive.
94
+
95
+ ## Receipt
96
+
97
+ Report the scope and inventory denominator, then for each candidate record its
98
+ exact identity, classification (`removed`, `retained`, `held`, or `unsupported`),
99
+ the fresh evidence used, native receipt and readback, and any resume condition.
100
+ Keep settlement, worker release, terminal exit, worktree/branch effects,
101
+ evidence preservation, and chat archival as separate fields. An idempotent retry
102
+ reconciles these receipts and performs only still-pending eligible operations.