axstack 0.34.2 → 0.34.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.
@@ -1,5 +1,7 @@
1
1
  # Getting started
2
2
 
3
+ Linux is the only supported host OS.
4
+
3
5
  Install Axstack, check your host, then ask for one bounded task in T3 Code.
4
6
  For the mental model, read [concepts](concepts.md); for other journeys, use
5
7
  [guides](guides.md).
@@ -24,8 +26,8 @@ axstack check --harness codex
24
26
  ```
25
27
 
26
28
  Installation writes owned skills and instructions, preserving unrelated content,
27
- and fetches the pinned archify tool for explicit viewer requests. Reload the harness's skills in T3 after
28
- installation. Use the [installation reference](installation.md) for other
29
+ and fetches the pinned archify tool for explicit viewer requests. Reload the
30
+ harness's skills in T3 after installation. Use the [installation reference](installation.md) for other
29
31
  harnesses, source installs or conflicts.
30
32
 
31
33
  A successful check exits 0. Its five capability rows mean:
@@ -134,10 +134,9 @@ messages under the accepted fleet risk.
134
134
 
135
135
  ## Chat-run watch activation
136
136
 
137
- A bound T3 schedule resumes the driver thread on a 30-minute fallback while
138
- native PR watches are armed and work and release are settled.
139
- Unsettled work or release, unavailable or unarmed native watches for any open
140
- member, or failed re-arming restore five minutes. Human-only waits use 60 minutes
137
+ A bound T3 schedule wakes the driver thread on a normal 30-minute cadence.
138
+ Unavailable or unarmed native watches for any open member, failed re-arming,
139
+ or a resumed question run restore five minutes. Human-only waits use 60 minutes
141
140
  under [Chat-run watch runtime](../skills/axstack-watch/references/watch-runtime.md#chat-run-watch).
142
141
  A T3 "stopped watching" notice triggers a PR re-check and native re-arm in that same turn.
143
142
  The run record holds the schedule ID and driver thread.
@@ -1,5 +1,7 @@
1
1
  # Installation
2
2
 
3
+ Linux is the only supported host OS.
4
+
3
5
  Axstack's Bun CLI installs owned chat skills plus one selected role snapshot and
4
6
  checks host capabilities. T3 Code is the only supported active runtime. The CLI
5
7
  does not dispatch agents, edit T3 settings, run a scheduler, or maintain a
package/docs/workflows.md CHANGED
@@ -134,6 +134,7 @@ Antigravity and Grok routes.
134
134
  The mixed `axstack-auditor` uses Haiku 5.5 xhigh, pinned to `claude-haiku-5-5`;
135
135
  missing that ID or xhigh effort holds without substitution. The mixed
136
136
  `axstack-explainer-review` uses Sol medium; `axstack-explainer` stays Sonnet high.
137
+ In `claude-only`, `axstack-auditor` uses the Claude Sonnet class at high effort.
137
138
  The new `-sol` auditor, research-code, and explore-execution seats use Sol high
138
139
  in `mixed` and `codex-only`; `claude-only` records each as intentionally absent.
139
140
  Dispatch the base and Sol seats independently on the same brief, then reconcile
@@ -150,8 +151,7 @@ later installation changes without re-resolution.
150
151
 
151
152
  Peer roles use the stable IDs `axstack-reviewer-primary` and
152
153
  `axstack-reviewer-peer`; their provider/class mappings come only from the
153
- selected preset. The mixed cross-provider peer pair is Sol high followed by
154
- Opus medium. The mixed peer coordinator and review-lane binding
154
+ selected preset and the ordered reviewer table above. The mixed peer coordinator and review-lane binding
155
155
  `axstack-owner` uses Sol high and never reviews. `axstack-reviewer-secondary`
156
156
  is used in the authored mapping and, alongside `axstack-reviewer-primary`,
157
157
  in codebase findings mode.
@@ -259,7 +259,12 @@ for explicit ownership transfer.
259
259
  archive the exact eligible thread, then remove its exact worktree without
260
260
  force and delete only eligible local branches. T3 metadata actions do not
261
261
  remove worktrees. Accepted writer or delegated completion settles thread
262
- metadata only. Author threads and worktrees remain retained and unarchived
262
+ metadata only after receipt acceptance, which is distinct from T3 sidebar
263
+ settlement. Every writer follow-up, including rebase, base update, conflict,
264
+ body and receipt changes, follows same-turn settlement and readback under
265
+ [Settlement](../skills/axstack/references/workspace-hygiene.md#settlement).
266
+ Fresh native run inventory gates zero-unsettled and cadence claims; incomplete
267
+ inventory is UNKNOWN. Author threads and worktrees remain retained and unarchived
263
268
  until their PR merges or closes; the current pass, unsettled descendants,
264
269
  and user-taken-over threads remain protected.
265
270
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "axstack",
3
- "version": "0.34.2",
3
+ "version": "0.34.4",
4
4
  "description": "Axstack installer and setup CLI: installs owned chat skills and role data, configures supported harness settings, and checks T3 Code capabilities.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -34,8 +34,8 @@
34
34
  "scripts": {
35
35
  "lint": "biome lint . --error-on-warnings",
36
36
  "typecheck": "bun node_modules/typescript/bin/tsc --noEmit --project jsconfig.json",
37
- "test": "bun test",
38
- "test:all": "bun test"
37
+ "test": "bun skills/axstack/scripts/test-repository.js",
38
+ "test:all": "bun skills/axstack/scripts/test-repository.js"
39
39
  },
40
40
  "license": "MIT",
41
41
  "devDependencies": {
@@ -71,7 +71,7 @@ Missing ordering or ownership evidence holds admission and never guesses a winne
71
71
 
72
72
  Once identified as a duplicate, the new pass does no PR work, makes no further
73
73
  shared-record write and touches no PR-job thread or descendant owned by the
74
- live manager. A duplicate's only mutation is pass-start settlement of eligible
74
+ live manager. Besides its private pass note and authorized notification, a duplicate's only shared mutation is pass-start settlement of eligible
75
75
  terminal lane pass threads under [Finite-session teardown](#finite-session-teardown).
76
76
  A duplicate pass admits nothing and runs read-only discovery
77
77
  into its own pass note in its private evidence folder, recording new eligible
@@ -330,13 +330,16 @@ decision interpreter; the driver maps card replies in its prose checks.
330
330
 
331
331
  ## Finite-session teardown
332
332
 
333
+ Decisions outliving a finite manager pass retain their receipt and hold in durable continuity, with a GitHub user action route or explicit ownership transfer.
334
+ Use the T3 action route for such decisions; a reply to a closed pass never clears a hold, and a successor reissues a card only after reconciling the retained receipt and current authority.
335
+
333
336
  After lane reconciliation at pass start, every pass, including a duplicate,
334
337
  uses `t3_thread_organize` to settle finished predecessor and duplicate lane pass
335
338
  threads as metadata only, before discovery or admission.
336
339
  Settle another lane pass thread only with terminal run evidence, no unsettled
337
- descendants, no recorded open hold naming it, no user takeover, and exclusion of
340
+ descendants, zero open holds naming it, recorded or directly observed, no user takeover, and exclusion of
338
341
  the live owner and current pass.
339
- Leave held or stuck pass threads unsettled.
342
+ Keep held passes, wedged passes and stalled owners unsettled and visible.
340
343
  Settlement is separate from owner-only worktree removal, branch deletion and
341
344
  continuity writes.
342
345
 
@@ -352,7 +355,7 @@ Then run the driver-start orphan sweep under Workspace hygiene; the sweep is
352
355
  silent when nothing was removed. Record sweep results and holds in continuity's
353
356
  Open holds table.
354
357
  The orphan sweep covers the run record's repositories plus registered repositories on this host.
355
- Retirement settles remaining duplicate threads with `t3_thread_organize`
358
+ Retirement settles remaining predecessor, duplicate and failed-successor pass threads with `t3_thread_organize`
356
359
  (settle/archive changes metadata only) before separate guarded Git worktree removal.
357
360
  Unknown, active or user-taken-over threads,
358
361
  ambiguous publication and failed salvage stay preserved.
@@ -89,7 +89,7 @@ Never require a manual `axstack-watch` invocation.
89
89
  The driver remains the single owner and sole run-record writer.
90
90
  Never create a per-PR session or an ownership hand-off.
91
91
  Read the [T3 runtime boundary](t3-runtime.md) and use its bound
92
- `schedule_task` wake (`everyMs:300000`), recording the scheduledTaskId.
92
+ `schedule_task` wake (`everyMs:1800000`), recording the scheduledTaskId.
93
93
  Follow [Native PR links and watches](t3-runtime.md#native-pr-links-and-watches)
94
94
  alongside that bound schedule.
95
95
  Apply [Chat-run watch runtime](../../axstack-watch/references/watch-runtime.md#chat-run-watch)
@@ -136,7 +136,8 @@ Record the reason for each driver-elected repair in `Decisions`.
136
136
 
137
137
  Record concise IDs, SHAs, URLs, status, timestamps, next actions, and evidence
138
138
  references. Private records include the checkout, evidence and worktree paths
139
- that the schema requires. Include no tokens, transcripts, full worker output,
139
+ that the schema requires. Include no other machine-specific paths in private records.
140
+ Include no tokens, transcripts, full worker output,
140
141
  credentials or private prompts. Publication of a sanitized summary needs separate
141
142
  authority.
142
143
 
@@ -238,10 +238,24 @@ the failed attempt's branch is kept until salvage. Unknown liveness holds
238
238
  replacement; reconcile the old writer before admitting another.
239
239
 
240
240
  With an unsettled launched thread, the driver turn must end only while a bound
241
- `schedule_task` with `bindToCurrentThread:true`, `everyMs:300000` is armed and
242
- its ID recorded. Each wake must reconcile all unsettled runs, including a
241
+ `schedule_task` with `bindToCurrentThread:true` is armed at the default
242
+ `everyMs:1800000` or for its 5-minute recovery cases in
243
+ [Chat-run watch runtime](../../axstack-watch/references/watch-runtime.md#chat-run-watch),
244
+ with its ID recorded. Each wake must reconcile all unsettled runs, including a
243
245
  writer that died without sending; failed runs hold incomplete work. The watch
244
246
  inherits the driver model/workspace and adds no runtime of Axstack's own.
247
+ T3 queues a scheduled wake even while the driver is busy, so copies pile up.
248
+ After a provider error, server restart or Stop, T3 holds the queue: held
249
+ messages do not start, and later queued messages are held too.
250
+ At the start of each run-watch wake, the driver lists every queued message with
251
+ `t3_queue_list` and treats it as stranded.
252
+ The driver must cancel every queued copy of its own run-watch prompt.
253
+ One wake reconciles all those copies.
254
+ For each stranded worker receipt or PR-watch update, the driver must read it
255
+ with `t3_queue_read`, act on it in that wake, then cancel it with `t3_queue_cancel`.
256
+ A queued user message is a pending local action, so the driver must keep every
257
+ queued user message and report it in its reply even on an otherwise unchanged
258
+ wake, because only the user resumes a held queue.
245
259
 
246
260
  Once nothing remains unsettled the driver must `delete_scheduled_task` for the
247
261
  run watch and use `list_scheduled_tasks` to read back its absence; an uncertain
@@ -286,10 +300,10 @@ If re-arming fails, restore the bound 5-minute schedule.
286
300
  When the run waits only on a user decision or a human-only step, including a
287
301
  user merge, with no unsettled worker and no PR needing watch events, change
288
302
  the run watch cadence to 60 minutes at once.
289
- When work restarts, restore the normal 5-minute cadence.
303
+ When work restarts, restore the normal 30-minute cadence.
290
304
  Keep native PR watches.
291
305
  For chat-run cadence changes, follow [Chat-run watch runtime](../../axstack-watch/references/watch-runtime.md#chat-run-watch)
292
- for 30-minute fallback eligibility, 5-minute recovery, and schedule update read-back.
306
+ for the normal 30-minute cadence, 5-minute recovery, and schedule update read-back.
293
307
  The schedule still reconciles author and review tasks, readiness, and release.
294
308
  When a PR merges or closes, or its watch is torn down, call `unwatch_pull_request`
295
309
  and keep its link.
@@ -1,6 +1,6 @@
1
1
  # UI verification
2
2
 
3
- Every Playwright, browser, or rendered-UI check outside the three named author
3
+ Every Playwright, browser, or rendered-UI check outside the three named browser
4
4
  exceptions, including a "confirm it in the browser" step, goes through async
5
5
  `delegate_task` to `axstack-ui-verifier` from the run's role snapshot. Give it the exact build, URL, or artifact and the private
6
6
  dispatch's evidence folder. The verifier is read-only: it never edits source.
@@ -8,21 +8,16 @@ The PR writer remains the sole writer.
8
8
 
9
9
  Before using a PR preview URL, the UI verifier must read [PR previews](preview.md).
10
10
 
11
- The first named author browser exception is archify `finalize` as a headless build gate.
11
+ The first named browser exception is archify `finalize` as a headless build gate.
12
12
  Keep finalize outputs only in the private evidence folder.
13
13
  Never replace the verifier's rendered pass with finalize.
14
14
 
15
- The second named author browser exception is the driver's two `html_preview`
15
+ The second named browser exception is the driver's two `html_preview`
16
16
  runs at 728px dark and 360px light.
17
- Scan the exact bytes of every page for remote src, srcset, and href on loaded
18
- resources, `@import`, `url()`, `fetch`, XHR, WebSocket, EventSource, and meta refresh.
19
- Reject scan matches except local url(#id) references such as SVG markers and gradients.
20
- Require correct layout, theme, zero console errors, empty `missingImages`,
21
- height, network, and word count.
22
17
  Never replace the verifier's interaction pass with `html_preview`.
23
18
  Follow [Inline pages](../../axstack-explain/references/inline-pages.md) for the page checks.
24
19
 
25
- The third named author browser exception permits dispatched roles to use
20
+ The third named browser exception permits dispatched roles to use
26
21
  `html_preview` as a non-publishing self-check.
27
22
  Dispatched-role self-checks never replace the driver's two previews or the `axstack-ui-verifier` pass.
28
23
  Dispatched roles never call `html_render`.
@@ -36,8 +31,8 @@ Candidate browser and visual checks must run in the delegated `axstack-ui-verifi
36
31
  For every interactive page, require a real-browser `axstack-ui-verifier` interaction pass.
37
32
  Give the evidence path and SHA-256 of the exact bytes in the verifier brief.
38
33
  The verifier serves the exact bytes on loopback.
39
- Open the page with `preview_open` using `open:false`.
40
- Check clicks, keyboard, focus order, screen-reader names, expanded states, and reduced motion.
34
+ The verifier opens the page with `preview_open` using `open:false`.
35
+ The verifier checks clicks, keyboard, focus order, screen-reader names, expanded states, and reduced motion.
41
36
  Require that `performance.getEntriesByType('resource')` is empty.
42
37
  The detached-checkout rule does not apply to a page without a candidate.
43
38
 
@@ -57,7 +52,8 @@ Keep browser recipes tool-neutral for the verifier.
57
52
 
58
53
  ## Proof standards
59
54
 
60
- Apply these standards to the verifier's candidate checks and inline-page interaction pass.
55
+ Apply these standards wherever referenced, including the verifier's candidate checks
56
+ and inline-page interaction pass.
61
57
 
62
58
  Drive the real user path.
63
59
  Capture the action, the resulting state and its side effects.
@@ -44,6 +44,15 @@ On forge-confirmed merge or closure, follow [Native PR links and watches](t3-run
44
44
  for same-turn author settlement and its required readback.
45
45
  A repair turn automatically un-settles the thread, making it visible while working.
46
46
  The driver settles the thread again after the next accepted repair completion.
47
+ Every writer follow-up, including rebase, base update, conflict, body and receipt changes, counts as repair for same-turn settlement and readback.
48
+ Immediately before settlement require native state proving the accepted run is current and the latest overall run.
49
+ Before settlement verify zero active, pending or queued work and zero unsettled descendants.
50
+ After settlement verify exact thread/run identity, current activity and protection state; a mismatch or missing readback holds only that dispatch's next advancement.
51
+ These checks are best effort, not atomic; never issue a compensating unsettle.
52
+ Successful receipt acceptance is distinct from T3 sidebar settlement metadata.
53
+ Cancellation disposition is separate from successful acceptance and requires native terminal cancellation, exact attempt identity, a driver reason and preservation pointers.
54
+ Cancellation disposition requires zero live or queued work, zero unsettled descendants, zero user takeover, zero unanswered questions, zero QUESTION or FAILED markers and zero serious-risk holds.
55
+ Never cancel solely to make a thread eligible for settlement.
47
56
  Threads with unaccepted completion, FAILED or QUESTION markers, held, failed or interrupted runs, or anything needing the user stay unsettled and visible.
48
57
  Retain a user-taken-over T3 thread and never send cleanup commands to it, including `t3_thread_organize` settle or archive.
49
58
  Never remove the current driver, current pass, an active or unknown thread, an unsettled descendant, or a resource with ambiguous ownership.
@@ -52,6 +61,8 @@ At final settlement include run-owned worktrees in other repositories of the
52
61
  same project and report each remaining resource as a hold with its reason. Recorded
53
62
  projectId, threadId/runId, delegated taskId/childThreadId/childRunId, attempt key,
54
63
  checkout path and revision decide ownership. Idle alone never proves exit.
64
+ Before any zero-unsettled claim, cadence relaxation, handback or Close-out, read a fresh fully paginated native inventory of run-owned writers, delegated children and known orphans, including already-settled threads with unconsumed results.
65
+ Incomplete inventory or unknown ownership records UNKNOWN and holds only the affected claim or transition.
55
66
 
56
67
  Read back the durable receipt and evidence manifest before removing source copies or the worktree; missing or differing readback holds removal.
57
68
  Evidence already outside the checkout needs no copy; read back its receipt and
@@ -12,14 +12,16 @@ Example: bun dispatch-plan.js --role axstack-author --roles roles.json --capabil
12
12
  --capabilities Saved orchestrator_capabilities JSON; reuse the run's saved snapshot
13
13
  --picker Saved picker JSON; uses chosenInstanceId
14
14
  --kind task emits target; launch emits modelSelection
15
- --verify Compare t3_thread_configuration JSON; print MATCH or each mismatch
16
- --out Write full JSON here and print input/path/omitted-count summary
15
+ --verify Compare t3_thread_configuration JSON; print evidence then MATCH or each mismatch
16
+ --out Save the payload as full JSON here; stdout is an input/path/omitted-count summary
17
17
  --help Show all flags and exit codes
18
18
 
19
19
  Read-only inputs: reads supplied JSON, spawns adjacent resolve-models.js, writes stdout/stderr.
20
20
  Writes only the caller's --out path when supplied; no retained state.
21
21
  Results name input paths. Plan output over 4096 bytes requires --out.
22
22
  Large verification retains its exit code, prints mismatches and advises --out on stderr.
23
+ An unwritable --out still prints verification mismatches and retains exit 2.
24
+ An omitted provider label is unknown; exact instance/model/options/mode checks still apply.
23
25
  Never dispatches or changes snapshots. Reuse recorded exact model pins as given.
24
26
  Exit codes: 0 ok, 1 error, 2 hold (including incomplete read-back).
25
27
  `;
@@ -76,7 +78,10 @@ function verify(configuration, instanceId, provider, model, options) {
76
78
  compare('options', options, selection?.options,
77
79
  normalizedOptions(options) === normalizedOptions(selection?.options));
78
80
  compare('runtimeMode', 'full-access', configuration?.runtimeMode);
79
- return mismatches;
81
+ return { mismatches, ...(selection?.provider == null ? {
82
+ providerEvidence: `provider: unknown; expected ${JSON.stringify(provider)}; observed missing; `
83
+ + 'provider binding checked via instanceId and saved capabilities',
84
+ } : {}) };
80
85
  }
81
86
 
82
87
  function plan(argv, io) {
@@ -89,17 +94,24 @@ function plan(argv, io) {
89
94
  const emit = (result) => {
90
95
  const full = { inputs, ...result };
91
96
  const inline = result.mismatches
92
- ? `${JSON.stringify({ inputs })}\n${result.mismatches.join('\n') || 'MATCH'}\n` : `${JSON.stringify(full)}\n`;
97
+ ? `${[JSON.stringify({ inputs }), result.providerEvidence, result.mismatches.join('\n') || 'MATCH'].filter(Boolean).join('\n')}\n`
98
+ : `${JSON.stringify(full)}\n`;
93
99
  if (!args['--out']) {
94
100
  if (new TextEncoder().encode(inline).length > 4096) {
95
101
  if (!result.mismatches) throw new Error('large output; pass --out <report.json> to save full JSON');
96
- io.stderr('Large verification output; pass --out <path>.json for the full list\n');
102
+ io.stderr('Large verification output; pass --out <path>.json for a saved JSON copy\n');
97
103
  }
98
104
  io.stdout(inline);
99
105
  return;
100
106
  }
101
107
  try { writeFileSync(args['--out'], `${JSON.stringify(full, null, 2)}\n`); }
102
- catch (error) { throw new Error(`${error.message}; pass --out <writable report.json path>`); }
108
+ catch (error) {
109
+ const message = `${error.message}; pass --out <writable report.json path>`;
110
+ if (!result.mismatches?.length) throw new Error(message);
111
+ io.stderr(`dispatch-plan hold: ${message}\n`);
112
+ io.stdout(inline);
113
+ return;
114
+ }
103
115
  io.stdout(`${JSON.stringify({ inputs, reportPath: args['--out'],
104
116
  omittedPayloadFields: Object.keys(result).length, omittedMismatches: result.mismatches?.length ?? 0 })}\n`);
105
117
  };
@@ -134,7 +146,10 @@ function plan(argv, io) {
134
146
  || (['grok', 'antigravity'].includes(role.provider) && picker.chosenInstanceId !== providerInstanceId)) {
135
147
  hold(`picker instance ${picker.chosenInstanceId} does not bind provider ${role.provider}`);
136
148
  }
137
- const selectedModel = chosen[0].models?.find((entry) => entry.id === model);
149
+ if (!Array.isArray(chosen[0].models)) {
150
+ hold(`malformed models on instance ${picker.chosenInstanceId}; expected an array; observed ${JSON.stringify(chosen[0].models) ?? 'missing'}`);
151
+ }
152
+ const selectedModel = chosen[0].models.find((entry) => entry.id === model);
138
153
  if (!selectedModel) hold(`model ${model} unavailable on instance ${picker.chosenInstanceId}`);
139
154
  if (effortOption && !selectedModel.options?.some((option) => option.id === effortOption.id
140
155
  && option.options?.some((value) => value.id === effortOption.value))) {
@@ -142,9 +157,9 @@ function plan(argv, io) {
142
157
  }
143
158
  const options = effortOption ? [effortOption] : [];
144
159
  if (args['--verify']) {
145
- const mismatches = verify(readJSON(args['--verify']), picker.chosenInstanceId, providerInstanceId, model, options);
146
- emit({ mismatches });
147
- return mismatches.length ? 2 : 0;
160
+ const verification = verify(readJSON(args['--verify']), picker.chosenInstanceId, providerInstanceId, model, options);
161
+ emit(verification);
162
+ return verification.mismatches.length ? 2 : 0;
148
163
  }
149
164
  const selection = { [args['--kind'] === 'launch' ? 'instanceId' : 'providerInstanceId']: picker.chosenInstanceId,
150
165
  model, options };
@@ -67,6 +67,12 @@ function snapshot(repo, revision) {
67
67
  paths: [...entries.keys()],
68
68
  has: (path) => entries.get(path)?.type === 'blob',
69
69
  mode: (path) => entries.get(path)?.mode,
70
+ readKeys(paths) {
71
+ try {
72
+ const blobs = readTextBlobs(repo, paths.map((path) => entries.get(path).oid));
73
+ return new Map(paths.filter((path) => /^100/.test(entries.get(path).mode)).map((path) => [path, blobs.get(entries.get(path).oid)]));
74
+ } catch { return new Map(); } // Unavailable blobs cannot prove key absence.
75
+ },
70
76
  read(path) {
71
77
  if (entries.get(path)?.type !== 'blob') return '';
72
78
  text ??= readTextBlobs(repo, ids);
@@ -81,6 +87,19 @@ function jsonc(text) {
81
87
  return JSON.parse(stripped.replace(/"(?:\\.|[^"\\])*"|,\s*(?=[}\]])/g, (token) => token.startsWith('"') ? token : ''));
82
88
  }
83
89
 
90
+ function pemStatus(text) {
91
+ if (typeof text !== 'string') return 'unknown';
92
+ if (/-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY-----/.test(text)) return 'fail';
93
+ let blocks = 0, valid = true;
94
+ const remainder = text.replace(/-----BEGIN (CERTIFICATE|PUBLIC KEY|RSA PUBLIC KEY)-----\r?\n([A-Za-z0-9+/=\r\n]+)-----END \1-----/g, (_, _label, body) => {
95
+ blocks++;
96
+ const encoded = body.replace(/[\r\n]/g, '');
97
+ if (!encoded || Buffer.from(encoded, 'base64').toString('base64') !== encoded) valid = false;
98
+ return '';
99
+ });
100
+ return blocks && valid && !remainder.trim() ? 'pass' : 'unknown';
101
+ }
102
+
84
103
  const definitions = [
85
104
  ['docs.agents', 'Docs'], ['docs.claude', 'Docs'],
86
105
  ['style.linter', 'Style'], ['style.formatter', 'Style'], ['style.strict', 'Style'], ['style.pre-commit', 'Style'],
@@ -183,8 +202,15 @@ function assess(files) {
183
202
  const workflowReason = prTests ? '' : invalid ? 'invalid workflow YAML' : prCandidate && manager.reason ? manager.reason : 'missing pull_request test step';
184
203
  set('security.pr-tests', prTests ? 'pass' : invalid || (prCandidate && manager.reason) ? 'unknown' : 'fail', workflows.length ? workflows.map((path) => `git show ${revision}:${path}`) : [`git ls-tree -r ${revision}: no workflows`], workflowReason);
185
204
  configured('security.updates', has(/(^|\/)(\.github\/(dependabot\.ya?ml|codeql\/[^/]+\.ya?ml)|(?:\.?)renovaterc(?:\.json5?)?|renovate\.json5?)$/).concat(codeql));
186
- const secrets = has(/(^|\/)(\.env[^/]*|id_(rsa|dsa|ecdsa|ed25519)|[^/]+\.(pem|key|p12|pfx))$/).filter((path) => !/(^|\/)\.env\.(example|sample|template)$/.test(path));
187
- set('security.secrets', secrets.length ? 'fail' : 'pass', secrets.length ? [...new Set(secrets)].map((path) => `git show ${revision}:${path}`) : [`git ls-tree -r ${revision}: no tracked env or private keys`], secrets.length ? 'tracked env or private keys' : '');
205
+ const candidates = has(/(^|\/)(\.env[^/]*|id_(rsa|dsa|ecdsa|ed25519)|[^/]+\.(pem|key|p12|pfx))$/).filter((path) => !/(^|\/)\.env\.(example|sample|template)$/.test(path));
206
+ const keys = candidates.filter((path) => /\.(pem|key)$/.test(path) && !/(^|\/)\.env/.test(path));
207
+ const contents = files.readKeys(keys);
208
+ const classifications = candidates.map((path) => [path, keys.includes(path) ? pemStatus(contents.get(path)) : 'fail']);
209
+ const secrets = classifications.filter(([, status]) => status === 'fail').map(([path]) => path);
210
+ const unknown = classifications.filter(([, status]) => status === 'unknown').map(([path]) => path);
211
+ const keyStatus = secrets.length ? 'fail' : unknown.length ? 'unknown' : 'pass';
212
+ const evidence = candidates.length ? candidates.map((path) => `git show ${revision}:${path}`) : [`git ls-tree -r ${revision}: no tracked env or private keys`];
213
+ set('security.secrets', keyStatus, evidence, [secrets.length ? 'tracked env or private keys' : '', unknown.length ? 'key contents unavailable or unrecognized' : ''].filter(Boolean).join('; '));
188
214
  return criteria;
189
215
  }
190
216
 
@@ -438,6 +464,7 @@ export async function main(argv, io = { stdout: (text) => process.stdout.write(t
438
464
  reportPath: paths.out, counts, pillars: result.pillars, omittedCriteria: result.criteria.length, omittedCommands: result.commands?.length ?? 0,
439
465
  omittedLogBytes: (result.commands ?? []).reduce((total, step) => total + step.omittedBytes, 0),
440
466
  incompleteLogs: (result.commands ?? []).filter((step) => step.logIncomplete).length,
467
+ ...(args['--run'] && { logIncomplete: (result.commands ?? []).some((step) => step.logIncomplete) }),
441
468
  })}\n`);
442
469
  return code || (result.executionError ? 2 : 0);
443
470
  } catch (error) {
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bun
2
+ // Repository test entry point: keep DAC permission fixtures meaningful as root.
3
+ const args = process.argv.slice(2);
4
+ if (args[0] === '--help') {
5
+ console.log(`Usage: bun skills/axstack/scripts/test-repository.js [bun test arguments]
6
+ Runs bun test in the current directory, forwarding arguments, environment and exit status.
7
+ Linux root requires setpriv and drops DAC override/read-search capabilities for tests.
8
+ Other users run Bun directly. Unsupported root hosts or missing setpriv exit 2.
9
+ Side effects: runs the selected tests and their fixtures; creates no runner-owned files.
10
+ Set TMPDIR to a fresh owned 0700 directory outside HOME before repository tests.`);
11
+ process.exit(0);
12
+ }
13
+
14
+ let command = [process.execPath, 'test', ...args];
15
+ if (process.getuid?.() === 0) {
16
+ const setpriv = process.platform === 'linux' ? Bun.which('setpriv') : null;
17
+ if (!setpriv) {
18
+ console.error('Repository permission tests as root require Linux and setpriv; use a non-root user otherwise.');
19
+ process.exit(2);
20
+ }
21
+ command = [setpriv, '--bounding-set=-dac_override,-dac_read_search', '--', ...command];
22
+ }
23
+
24
+ try {
25
+ const child = Bun.spawn(command, { stdin: 'inherit', stdout: 'inherit', stderr: 'inherit' });
26
+ process.exit(await child.exited);
27
+ } catch (error) {
28
+ console.error(`Repository test launch failed: ${error.message}`);
29
+ process.exit(1);
30
+ }
@@ -13,7 +13,7 @@ Use archify HTML only on an explicit viewer request.
13
13
  The configured `axstack-explainer` authors the viewer; follow [Archify](references/archify.md).
14
14
  Use inline SVG or CSS inside inline pages.
15
15
  Never use Mermaid in inline pages.
16
- Use Mermaid for chat fallback, GitHub, and docs.
16
+ Use Mermaid for chat, GitHub, and docs.
17
17
  Include `accTitle` and `accDescr` in each Mermaid view.
18
18
  Never use theme init in Mermaid.
19
19
  Split large Mermaid diagrams into views.
@@ -23,5 +23,6 @@ Explain owns evidence, claim labels, the word cap, dispatch, and delivery.
23
23
  Diagram owns format and visual rules.
24
24
  Never call explain.
25
25
  For direct use, keep fidelity and rendered QA.
26
+ For direct viewer requests, follow [Explain's declared launch path](../axstack-explain/SKILL.md#2-choose-proportional-output).
26
27
 
27
28
  Ideas: [archify](https://github.com/tt-a1i/archify), [diagram-design](https://github.com/cathrynlavery/diagram-design), [visual-explainer](https://github.com/nicobailon/visual-explainer), [Matt Pocock](https://github.com/mattpocock/skills), and [poteto](https://github.com/poteto/how); guidance uses Axstack's own words.
@@ -71,11 +71,11 @@ Diagram never calls explain.
71
71
  Each card has at most 40 words and cites its source, as defined in
72
72
  [Archify](../axstack-diagram/references/archify.md).
73
73
  Count node labels, headings, captions, and all non-card text.
74
- 2. Use chat only for an explicit chat request, an answer without a Q2 trigger,
75
- or inline-page fallback. Q2 triggers are the conditions in the description.
74
+ 2. Use chat only for an explicit chat request, an answer outside the description's conditions,
75
+ or inline-page fallback.
76
76
  Use a compact diagram when useful.
77
77
  For chat answers, never dispatch an agent when applying its Mermaid rules inline.
78
- 3. For a Q2 trigger, create an inline page by default.
78
+ 3. For the conditions in the description, create an inline page by default.
79
79
  Honor an explicitly requested artifact format.
80
80
  Use archify only on an explicit viewer request, through the configured
81
81
  `axstack-explainer`. The reply adds only what the page omits.
@@ -48,7 +48,7 @@ Scan for `@import`, `url()`, `fetch`, XHR, WebSocket, EventSource, and meta refr
48
48
  Reject scan matches except local `url(#id)` references such as SVG markers and gradients.
49
49
 
50
50
  Before publishing, run `html_preview` at 728px dark and 360px light.
51
- The driver's two previews are an author exception to delegated browser checks.
51
+ The driver's two previews are a named browser exception to delegated browser checks.
52
52
  Require correct layout, theme, height, network, word count, zero console errors,
53
53
  and empty `missingImages`.
54
54
  Check every claim against its source.
@@ -59,8 +59,6 @@ For every interactive page, require a real-browser `axstack-ui-verifier` pass
59
59
  for clicks, keyboard, and expanded states.
60
60
  Give the evidence path and SHA-256 of the exact bytes in the verifier brief.
61
61
  Follow [UI verification](../../axstack/references/ui-verification.md) for that pass.
62
- The pass covers focus order, screen-reader names, reduced motion, and an empty
63
- `performance.getEntriesByType('resource')` list.
64
62
 
65
63
  ## Publish or fall back
66
64
 
@@ -139,6 +139,7 @@ the caller run's T3 driver thread, even for worker sends.
139
139
  Keep the tag short and machine-parsable.
140
140
  Exclude chat IDs, credentials, and Telegram targets from the reply tag.
141
141
  If either identity is unknown or mismatched, hold the send.
142
+ Reply proof is not an authentication boundary: a privileged worker can copy a valid receipt and forge a message.
142
143
 
143
144
  ## Decision cards and receipts
144
145
 
@@ -283,6 +283,7 @@ This section applies to peer and authored PR modes.
283
283
  where measurement is useful. Never invent a metric or demand an
284
284
  abstraction merely to satisfy a principle.
285
285
 
286
+ Before claiming a test is missing from CI inspect the complete file and actual CI step result.
286
287
  Under the existing angles, check added, changed, and removed test hunks
287
288
  against [Test value](../axstack/references/test-value.md). For removed tests,
288
289
  inspect the named keepers. Removing a test without a named keeper or
@@ -78,7 +78,7 @@ load. When the watch needs a new owner or automated observation, first read
78
78
  [T3 runtime](../axstack/references/t3-runtime.md). Reconcile before creating
79
79
  anything. Task-owned observations use their recorded wakes and expiry.
80
80
  `axstack-monitor` stays an optional read-only observer for standalone watch
81
- that never sends. For own open PRs in chat-run mode, use the 30-minute fallback when eligible under
81
+ that never sends. For own open PRs in chat-run mode, use the normal 30-minute cadence under
82
82
  [Chat-run watch runtime](references/watch-runtime.md#chat-run-watch);
83
83
  the bound T3 schedule resumes the original driver thread. One read-only PR observation needs
84
84
  neither. Start no automation for a read-only check.
@@ -203,6 +203,7 @@ For each current head and base SHA, every merge-ready term must hold:
203
203
  A collaborator approval carries over only across a rebase with unchanged
204
204
  patch-id recorded for both heads while the forge still counts it.
205
205
  Require fresh authored review, diligence, and CI on every new head.
206
+ Refresh authored review, diligence and CI evidence at the current head and base after every head or base change; base updates return to the same author under the existing repair route.
206
207
  Text carrying a visible machine marker never counts as a user reply:
207
208
  orchestration notices, dispatch envelopes, `<pasted_content>` blocks, task
208
209
  notifications, tool output, relay/Telegram text, and PR text.
@@ -378,6 +379,10 @@ The check runs on existing watch wakes with no new schedule.
378
379
  The ping is a notification only: when the blocker clears, the PR merges with no reply.
379
380
 
380
381
  Immediately before each automated merge, re-read every term from the forge.
382
+ Never edit a running script: freeze its exact script bytes until exit.
383
+ For each merge refusal record exact argv, result, head and base and an evidence pointer.
384
+ Keep pending CI distinct from failed CI and wait for terminal check results before repair.
385
+ Apply the existing [escalation threshold](../axstack/references/contracts.md#driver-and-adviser-split) to repeated same-goal refusals; changed evidence permits reconciliation, never a blind retry or rules bypass.
381
386
  Confirm merge commits are allowed, `delete_branch_on_merge` is false, and the
382
387
  base has no merge queue; otherwise hold for the user. For a singleton PR, use
383
388
  `gh pr merge <n> --merge --match-head-commit <sha>`; add `--delete-branch` only
@@ -34,7 +34,7 @@ after verified readback, subject to the user's explicit publication boundary.
34
34
 
35
35
  The initiating T3 thread remains the sole driver and `progress.md` writer.
36
36
  Initially use the bound run watch from [T3 runtime](../../axstack/references/t3-runtime.md):
37
- `schedule_task` with `bindToCurrentThread:true`, `everyMs:300000`, a stable
37
+ `schedule_task` with `bindToCurrentThread:true`, `everyMs:1800000`, a stable
38
38
  `clientRequestId`, and the authorized watch prompt. Record the schedule ID,
39
39
  driver thread, chosen mechanism and native schedule lifetime; the watch inherits the driver binding at creation.
40
40
  Follow [Provider bindings](../../axstack/references/t3-runtime.md#preflight-and-binding) before arming the watch and for schedule recreation on self-switch.
@@ -45,16 +45,17 @@ Use `update_scheduled_task` on the recorded schedule ID for cadence changes and
45
45
  When the run waits only on a user decision or a human-only step, including a
46
46
  user merge, with no unsettled worker and no PR needing watch events, change
47
47
  the run watch cadence to 60 minutes at once.
48
- When work restarts, restore the normal 5-minute cadence.
48
+ When work restarts, restore the normal 30-minute cadence.
49
49
  Keep native PR watches.
50
- Only while every open watched PR has an armed native `watch_pull_request`, all
51
- launched work is settled, and the release step is settled or inapplicable, use
52
- the bound chat-run schedule as a 30-minute fallback (`everyMs:1800000`).
50
+ Because launched writers, delegated tasks and native PR watches deliver their
51
+ own wakes, use the normal 30-minute cadence (`everyMs:1800000`) even while
52
+ launched work or the release step is unsettled.
53
+ A resumed `AXSTACK-QUESTION` child run lacks a native completion wake, so it
54
+ keeps the 5-minute cadence (`everyMs:300000`) until its result is accepted.
53
55
  If native `watch_pull_request` is unavailable or unarmed for any open member,
54
56
  or re-arming fails, restore the bound chat-run schedule to 5 minutes (`everyMs:300000`).
55
57
  On the next event on a watched PR, re-evaluate the wake cadence against the
56
58
  fallback conditions in the same turn.
57
- If launched work or the release step becomes unsettled, restore the 5-minute cadence (`everyMs:300000`).
58
59
  Apply these conditions before ending each driver turn, including after initial
59
60
  arming or a work restart; the 60-minute human-only wait rule above still applies.
60
61
  Read back each schedule update and record its receipt; an uncertain update holds affected work.