axstack 0.34.1 → 0.34.3
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/docs/workflows.md +7 -1
- package/package.json +3 -3
- package/skills/axstack/references/automations.md +7 -4
- package/skills/axstack/references/t3-runtime.md +5 -0
- package/skills/axstack/references/workspace-hygiene.md +11 -0
- package/skills/axstack/scripts/dispatch-plan.js +25 -10
- package/skills/axstack/scripts/readiness.js +29 -2
- package/skills/axstack/scripts/test-repository.js +30 -0
- package/skills/axstack-relay/SKILL.md +34 -3
- package/skills/axstack-review/SKILL.md +1 -0
- package/skills/axstack-watch/SKILL.md +5 -0
- package/skills/axstack-watch/references/watch-runtime.md +4 -0
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
|
|
@@ -259,7 +260,12 @@ for explicit ownership transfer.
|
|
|
259
260
|
archive the exact eligible thread, then remove its exact worktree without
|
|
260
261
|
force and delete only eligible local branches. T3 metadata actions do not
|
|
261
262
|
remove worktrees. Accepted writer or delegated completion settles thread
|
|
262
|
-
metadata only
|
|
263
|
+
metadata only after receipt acceptance, which is distinct from T3 sidebar
|
|
264
|
+
settlement. Every writer follow-up, including rebase, base update, conflict,
|
|
265
|
+
body and receipt changes, follows same-turn settlement and readback under
|
|
266
|
+
[Settlement](../skills/axstack/references/workspace-hygiene.md#settlement).
|
|
267
|
+
Fresh native run inventory gates zero-unsettled and cadence claims; incomplete
|
|
268
|
+
inventory is UNKNOWN. Author threads and worktrees remain retained and unarchived
|
|
263
269
|
until their PR merges or closes; the current pass, unsettled descendants,
|
|
264
270
|
and user-taken-over threads remain protected.
|
|
265
271
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "axstack",
|
|
3
|
-
"version": "0.34.
|
|
3
|
+
"version": "0.34.3",
|
|
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.
|
|
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,
|
|
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
|
-
|
|
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.
|
|
@@ -242,6 +242,11 @@ With an unsettled launched thread, the driver turn must end only while a bound
|
|
|
242
242
|
its ID recorded. Each wake must reconcile all unsettled runs, including a
|
|
243
243
|
writer that died without sending; failed runs hold incomplete work. The watch
|
|
244
244
|
inherits the driver model/workspace and adds no runtime of Axstack's own.
|
|
245
|
+
T3 queues a scheduled wake even while the driver is busy, so copies pile up.
|
|
246
|
+
At the start of each run-watch wake, the driver must cancel every other queued
|
|
247
|
+
copy of its own run-watch prompt with `t3_queue_list` and `t3_queue_cancel`,
|
|
248
|
+
keeping every other queued message, including user and worker messages.
|
|
249
|
+
One wake reconciles all those copies.
|
|
245
250
|
|
|
246
251
|
Once nothing remains unsettled the driver must `delete_scheduled_task` for the
|
|
247
252
|
run watch and use `list_scheduled_tasks` to read back its absence; an uncertain
|
|
@@ -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
|
|
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 })
|
|
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
|
|
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) {
|
|
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
|
-
|
|
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
|
|
146
|
-
emit(
|
|
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
|
|
187
|
-
|
|
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
|
+
}
|
|
@@ -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
|
|
|
@@ -155,20 +156,36 @@ reissuing only if a decision is still needed.
|
|
|
155
156
|
Each card must carry exactly one decision.
|
|
156
157
|
The card ID must be `<run id>/<n>`, never reused in the driver thread.
|
|
157
158
|
The only sender of reply-dependent cards must be the run's driver.
|
|
159
|
+
|
|
160
|
+
Decision cards and explain answers must use short paragraphs with blank lines
|
|
161
|
+
between thoughts for reading on a phone.
|
|
162
|
+
Decision cards and explain answers must use ordinary words and briefly define
|
|
163
|
+
necessary technical terms.
|
|
164
|
+
Each decision card must explain what is happening and why the choice matters.
|
|
165
|
+
Each numbered option must state the concrete action, scope, expected outcome
|
|
166
|
+
and material trade-off.
|
|
167
|
+
Defer and Cancel must describe the withheld action and what remains unresolved.
|
|
168
|
+
Any recommendation must include a brief evidence-supported reason.
|
|
169
|
+
A recommendation must leave the choice to the user.
|
|
170
|
+
|
|
158
171
|
Use plain ASCII for the card layout below, as one message:
|
|
159
172
|
|
|
160
173
|
```text
|
|
161
174
|
<project> - <decision title>
|
|
162
175
|
Card <run id>/<n>[, confirms card <run id>/<m>]
|
|
176
|
+
|
|
163
177
|
Context: <what we were doing, one line>
|
|
164
178
|
Decision: <the question>
|
|
179
|
+
|
|
180
|
+
1 <action> - <scope; outcome; trade-off>
|
|
181
|
+
2 <action> - <scope; outcome; trade-off>
|
|
182
|
+
3 Defer - <deferred action and scope; outcome; trade-off> (optional)
|
|
183
|
+
|
|
165
184
|
Driver environment: <value | not supplied>
|
|
166
185
|
Execution machine: <value | not supplied>
|
|
167
186
|
Affected targets: <values | not supplied>
|
|
168
187
|
Revision: <repo#PR @ full SHA | spec rev N sha256 | none>
|
|
169
|
-
|
|
170
|
-
2 <action> - <scope>
|
|
171
|
-
3 Defer (optional)
|
|
188
|
+
|
|
172
189
|
Reply 1, 2, 3 or explain. Open T3: <link | not available>
|
|
173
190
|
T3 reply: <env label> thread <driver threadId>
|
|
174
191
|
```
|
|
@@ -250,6 +267,13 @@ Run these four checks in order, stopping at the first failure:
|
|
|
250
267
|
Free text is user input, not a numbered option, and gets the same authority
|
|
251
268
|
revalidation and confirmation checks before it can authorize an action.
|
|
252
269
|
|
|
270
|
+
Explain answers must directly address the reader's confusion.
|
|
271
|
+
Explain answers must expand meaningful context and each option's consequences.
|
|
272
|
+
Explain answers must give a justified recommendation when evidence supports it.
|
|
273
|
+
Explain answers must state material risks and unknown facts plainly.
|
|
274
|
+
Never merely restate IDs or SHAs in an explain answer.
|
|
275
|
+
The same one-message size and final reply tag rules apply to explain answers.
|
|
276
|
+
|
|
253
277
|
## Confirm, acknowledge and act
|
|
254
278
|
|
|
255
279
|
Any forwarded reply, including free text and `(confirm)` options, authorizing
|
|
@@ -257,6 +281,13 @@ a destructive, production or fleet-wide action must first get a confirmation car
|
|
|
257
281
|
A confirmation card must have its own ID, name the card it confirms
|
|
258
282
|
(`confirms card`), bind the exact action, execution machine, affected targets
|
|
259
283
|
and revision, and offer `1 Confirm` and `2 Cancel`.
|
|
284
|
+
Use the same detail fields for confirmation option rows:
|
|
285
|
+
|
|
286
|
+
```text
|
|
287
|
+
1 Confirm - <bound action and scope; outcome; trade-off>
|
|
288
|
+
2 Cancel - <withheld action and scope; outcome; trade-off>
|
|
289
|
+
```
|
|
290
|
+
|
|
260
291
|
Sending a confirmation must record the original card decided, pending
|
|
261
292
|
confirmation with no authority.
|
|
262
293
|
The driver must allow only a verified `1` on the open confirmation to authorize
|
|
@@ -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
|
|
@@ -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
|
|
@@ -50,6 +50,10 @@ Keep native PR watches.
|
|
|
50
50
|
Only while every open watched PR has an armed native `watch_pull_request`, all
|
|
51
51
|
launched work is settled, and the release step is settled or inapplicable, use
|
|
52
52
|
the bound chat-run schedule as a 30-minute fallback (`everyMs:1800000`).
|
|
53
|
+
Because delegated tasks deliver native completion wakes, unsettled delegated
|
|
54
|
+
tasks alone keep the 30-minute fallback eligible.
|
|
55
|
+
A resumed `AXSTACK-QUESTION` child run lacks a native completion wake, so it
|
|
56
|
+
keeps the 5-minute cadence (`everyMs:300000`) until its result is accepted.
|
|
53
57
|
If native `watch_pull_request` is unavailable or unarmed for any open member,
|
|
54
58
|
or re-arming fails, restore the bound chat-run schedule to 5 minutes (`everyMs:300000`).
|
|
55
59
|
On the next event on a watched PR, re-evaluate the wake cadence against the
|