axstack 0.53.1 → 0.55.0
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 +4 -1
- package/package.json +1 -1
- package/profiles/presets/claude-only.json +9 -0
- package/profiles/presets/codex-only.json +9 -0
- package/profiles/presets/mixed.json +9 -0
- package/skills/axstack/references/diligence.md +4 -1
- package/skills/axstack/references/fleet-route-schema.md +314 -0
- package/skills/axstack/references/role-roster.md +5 -0
- package/skills/axstack/references/routing.md +2 -0
- package/skills/axstack/references/t3-runtime.md +1 -1
- package/skills/axstack/references/ui-verification.md +67 -4
- package/skills/axstack/scripts/dispatch-plan.js +34 -7
- package/skills/axstack/scripts/fleet-config.js +78 -0
- package/skills/axstack/scripts/fleet-dispatch.js +41 -0
- package/skills/axstack/scripts/fleet-facts.js +150 -0
- package/skills/axstack/scripts/fleet-proc.js +31 -0
- package/skills/axstack/scripts/fleet-route-cli.js +78 -0
- package/skills/axstack/scripts/fleet-route.js +85 -0
- package/skills/axstack/scripts/fleet-sample-cli.js +81 -0
- package/skills/axstack/scripts/fleet-sample-io.js +59 -0
- package/skills/axstack/scripts/fleet-sample-manifest.js +34 -0
- package/skills/axstack/scripts/fleet-sample.js +48 -0
- package/skills/axstack/scripts/resolve-models.js +8 -4
- package/skills/axstack-review/references/review-candidate.md +1 -1
- package/skills/axstack-spec/SKILL.md +5 -3
- package/skills/axstack-spec/references/format.md +88 -0
- package/skills/axstack-verify/SKILL.md +17 -6
- package/src/roles.js +2 -2
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { parseProc, pressure } from './fleet-proc.js';
|
|
2
|
+
import { validateManifest, sampleOptions } from './fleet-sample-manifest.js';
|
|
3
|
+
import { readProc, wait } from './fleet-sample-io.js';
|
|
4
|
+
export { parseProc, pressure, validateManifest };
|
|
5
|
+
|
|
6
|
+
// Read functions and clocks are injectable for deterministic offline verification.
|
|
7
|
+
export async function sampleFleet(manifest, hosts, options, dependencies = {}) {
|
|
8
|
+
validateManifest(manifest, hosts);
|
|
9
|
+
const { intervalMs, timeoutMs } = sampleOptions(options);
|
|
10
|
+
const { clock = Date.now, monotonic = () => performance.now(), read = readProc, wait: pause = wait,
|
|
11
|
+
signal } = dependencies;
|
|
12
|
+
const telemetry = await Promise.all(manifest.hosts.map(async (host) => {
|
|
13
|
+
const controller = new AbortController();
|
|
14
|
+
const cancel = () => controller.abort(Error('cancelled'));
|
|
15
|
+
signal?.addEventListener('abort', cancel, { once: true });
|
|
16
|
+
if (signal?.aborted) cancel();
|
|
17
|
+
const timer = setTimeout(() => controller.abort(Error('timeout')), timeoutMs);
|
|
18
|
+
const windowStartedAt = clock(); const started = monotonic();
|
|
19
|
+
const source = host.transport === 'local' ? 'linux-proc-local' : 'linux-proc-ssh';
|
|
20
|
+
const provenance = { hostId: host.hostId, environmentId: host.environmentId, scope: 'whole-host', source,
|
|
21
|
+
intervalMs, timeoutMs, windowStartedAt, windowKind: 'collector-bracket' };
|
|
22
|
+
const check = () => { if (controller.signal.aborted) throw controller.signal.reason; };
|
|
23
|
+
try {
|
|
24
|
+
check();
|
|
25
|
+
const first = parseProc(await read(host, controller.signal));
|
|
26
|
+
check();
|
|
27
|
+
await pause(intervalMs, controller.signal);
|
|
28
|
+
check();
|
|
29
|
+
const last = parseProc(await read(host, controller.signal));
|
|
30
|
+
check();
|
|
31
|
+
const windowEndedAt = clock(); const durationMs = Math.floor(monotonic() - started);
|
|
32
|
+
if (durationMs >= timeoutMs) throw Error('timeout');
|
|
33
|
+
if (durationMs < intervalMs || windowEndedAt < windowStartedAt
|
|
34
|
+
|| windowEndedAt - windowStartedAt < durationMs
|
|
35
|
+
|| windowEndedAt - windowStartedAt > durationMs + 1000) throw Error('collector-clock');
|
|
36
|
+
return { ...provenance, status: 'ok', ...pressure(first, last), durationMs,
|
|
37
|
+
windowEndedAt, receivedAt: windowEndedAt };
|
|
38
|
+
} catch (error) {
|
|
39
|
+
const reason = controller.signal.aborted ? controller.signal.reason?.message : error.message;
|
|
40
|
+
return { ...provenance, status: ['timeout', 'cancelled', 'malformed', 'inconsistent-counters', 'collector-clock']
|
|
41
|
+
.includes(reason) ? reason : 'failed', durationMs: Math.max(0, Math.floor(monotonic() - started)), receivedAt: clock() };
|
|
42
|
+
} finally {
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
signal?.removeEventListener('abort', cancel);
|
|
45
|
+
}
|
|
46
|
+
}));
|
|
47
|
+
return { schemaVersion: 1, telemetry };
|
|
48
|
+
}
|
|
@@ -3,6 +3,7 @@ import { readFileSync } from 'node:fs';
|
|
|
3
3
|
const bindings = { codex: 'codex', claude: 'claudeAgent', grok: 'grok' };
|
|
4
4
|
const effortIds = { codex: 'reasoningEffort', claude: 'effort', grok: 'reasoningEffort' };
|
|
5
5
|
const nonempty = (value) => typeof value === 'string' && value.trim().length > 0;
|
|
6
|
+
const optionValues = (option) => option.values ?? option.options?.map((value) => value.id);
|
|
6
7
|
|
|
7
8
|
function argumentsFrom(args) {
|
|
8
9
|
const options = { excluded: [] };
|
|
@@ -37,9 +38,11 @@ function providerFrom(catalog, provider) {
|
|
|
37
38
|
}
|
|
38
39
|
const instance = matches[0];
|
|
39
40
|
if (!Array.isArray(instance.models) || instance.models.some((model) =>
|
|
40
|
-
!model || !nonempty(model.id) || !Array.isArray(model.options) || model.options.some((option) =>
|
|
41
|
+
!model || !nonempty(model.id) || !Array.isArray(model.options ?? []) || (model.options ?? []).some((option) =>
|
|
41
42
|
!option || !nonempty(option.id) || (option.options !== undefined &&
|
|
42
|
-
(!Array.isArray(option.options) || option.options.some((value) => !value || !nonempty(value.id))))
|
|
43
|
+
(!Array.isArray(option.options) || option.options.some((value) => !value || !nonempty(value.id))))
|
|
44
|
+
|| (option.values !== undefined && (!Array.isArray(option.values)
|
|
45
|
+
|| option.values.some((value) => typeof value !== 'boolean' && !nonempty(value))))))) {
|
|
43
46
|
throw new Error('malformed capabilities models or options');
|
|
44
47
|
}
|
|
45
48
|
return instance;
|
|
@@ -58,6 +61,7 @@ function modelFrom(models, { provider, model: pin, class: modelClass, excluded }
|
|
|
58
61
|
}
|
|
59
62
|
const model = models[0];
|
|
60
63
|
if (!model || excluded.includes(model.id)) throw new Error('missing first listed model');
|
|
64
|
+
if (!model.id.startsWith('grok-')) throw new Error(`first listed model ${model.id} is not grok-family`);
|
|
61
65
|
return model;
|
|
62
66
|
}
|
|
63
67
|
if (!['codex', 'claude'].includes(provider) || !/^[a-z]+$/.test(modelClass)) {
|
|
@@ -90,8 +94,8 @@ try {
|
|
|
90
94
|
if ((options.class === 'terra' || /^gpt-\d+(?:\.\d+)*-terra$/.test(model.id)) && effort !== 'high') {
|
|
91
95
|
throw new Error('Terra requires exactly high effort');
|
|
92
96
|
}
|
|
93
|
-
const effortOptions = model.options.filter((option) => option.id === effortIds[provider]);
|
|
94
|
-
if (effortOptions.length !== 1 || !effortOptions[0]
|
|
97
|
+
const effortOptions = (model.options ?? []).filter((option) => option.id === effortIds[provider]);
|
|
98
|
+
if (effortOptions.length !== 1 || !optionValues(effortOptions[0])?.includes(effort)
|
|
95
99
|
|| (provider === 'grok' && effort === 'max')) {
|
|
96
100
|
throw new Error(`unsupported effort ${effort} for ${provider}/${model.id}`);
|
|
97
101
|
}
|
|
@@ -137,7 +137,7 @@ This section applies to peer and authored PR modes.
|
|
|
137
137
|
For an `axstack-verify create` PR, the reviewer drives the candidate's recipe because the base has none.
|
|
138
138
|
The author never performs this review drive.
|
|
139
139
|
The reviewer drives each affected feature and records that drive as the `Final-head check`.
|
|
140
|
-
Delegate browser drives to `axstack-ui-verifier` with the feature file path.
|
|
140
|
+
Delegate browser drives to `axstack-ui-verifier` with the feature file path; device drives follow [UI verification](../../axstack/references/ui-verification.md#android-device-and-webpwa-dual-run)'s dual-run trigger.
|
|
141
141
|
Running the applicable surface counts as spin-up; a CLI run qualifies.
|
|
142
142
|
Use loopback and isolated instances under [axstack-verify](../../axstack-verify/SKILL.md#isolate-every-attempt).
|
|
143
143
|
|
|
@@ -43,8 +43,9 @@ and the lifecycle's [audit skill](../axstack-audit/SKILL.md) hook.
|
|
|
43
43
|
access before any issue write. Record the repository and identity checked.
|
|
44
44
|
Missing access preserves the GitHub selection and stops the phase without
|
|
45
45
|
mutation or fallback. Markdown mode skips external access preflight.
|
|
46
|
-
3. **Draft with decision evidence.**
|
|
47
|
-
|
|
46
|
+
3. **Draft with decision evidence.** Follow the
|
|
47
|
+
[readable specification format](references/format.md) when drafting in the
|
|
48
|
+
selected store. Only when the scope identity
|
|
48
49
|
carries a sketch, include the [design lens](../axstack/references/design-lens.md)
|
|
49
50
|
sketch in the approved revision's `Design` section and its `Usage` line in
|
|
50
51
|
acceptance. First record the driver's
|
|
@@ -90,7 +91,8 @@ and the lifecycle's [audit skill](../axstack-audit/SKILL.md) hook.
|
|
|
90
91
|
[Diligence](../axstack/references/diligence.md) to check the draft against
|
|
91
92
|
the Align decisions for anything dropped, added, or softened.
|
|
92
93
|
5. **Snapshot the baseline.** Record the approved revision identity and a
|
|
93
|
-
concise repository Markdown counterpart
|
|
94
|
+
concise repository Markdown counterpart pointing to the same requirements,
|
|
95
|
+
not a second human or agent contract. In Linear mode, the native
|
|
94
96
|
document remains authoritative. In GitHub mode, the approved issue body is
|
|
95
97
|
authoritative and its GitHub issue URL plus SHA-256 body digest identifies
|
|
96
98
|
the exact approved revision. In Markdown mode, the agreed repository path does.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Readable specification format
|
|
2
|
+
|
|
3
|
+
Use this structure inside the selected authoritative store. It changes layout,
|
|
4
|
+
not store selection, adviser review, diligence, approval or snapshot authority.
|
|
5
|
+
|
|
6
|
+
## Decision brief
|
|
7
|
+
|
|
8
|
+
Begin with a decision brief before execution detail and history.
|
|
9
|
+
Keep identity and status compact, with review-round history after current requirements.
|
|
10
|
+
For example: title, revision, draft or approved status, authoritative location.
|
|
11
|
+
|
|
12
|
+
- In the opening, explain the problem, why it matters and the proposed change.
|
|
13
|
+
- When useful, include a concrete before/after example or flow in the opening.
|
|
14
|
+
- State included and excluded scope in the brief.
|
|
15
|
+
- Surface consequential risks, trade-offs and unverified boundaries in the brief.
|
|
16
|
+
- Collect all decisions needed from the user in the opening, with options and
|
|
17
|
+
consequences; say explicitly when none remain.
|
|
18
|
+
|
|
19
|
+
Give enough context for an informed decision. Link each consequential choice
|
|
20
|
+
or risk to its precise execution requirement. Never bury a material decision
|
|
21
|
+
or risk only in supporting detail.
|
|
22
|
+
|
|
23
|
+
## Execution requirements
|
|
24
|
+
|
|
25
|
+
Group precise requirements and observable acceptance by capability, using stable
|
|
26
|
+
IDs where useful. Include behavior, interfaces, data, invariants, failure cases
|
|
27
|
+
and validation that determine success. Put routine file-by-file task and PR
|
|
28
|
+
sequencing in Tickets, retaining meaningful design and rollout constraints in the spec.
|
|
29
|
+
State each rule once and reference it from other sections.
|
|
30
|
+
Technical execution detail remains binding wherever stored, including linked detail.
|
|
31
|
+
Human and agent views share one authoritative specification.
|
|
32
|
+
Resolve brief/detail conflicts before approval rather than declaring one view expendable.
|
|
33
|
+
|
|
34
|
+
Use headings, short requirement blocks or lists for scanning. Use a table when
|
|
35
|
+
it helps compare choices, rather than putting whole paragraphs in cells.
|
|
36
|
+
Do not impose word caps, mandatory tables, an extra artifact or ceremony.
|
|
37
|
+
The example below is adaptable guidance, not another required document.
|
|
38
|
+
The existing readback identifies and explains this same revision; it is not
|
|
39
|
+
a duplicate whole spec with separate requirements.
|
|
40
|
+
|
|
41
|
+
## Evidence and history
|
|
42
|
+
|
|
43
|
+
Keep history, evidence and adviser receipts separate from current requirements.
|
|
44
|
+
Place provenance, commands, review-round dispositions and change logs here or
|
|
45
|
+
link their existing records. They explain the requirements, not override them.
|
|
46
|
+
When amending a draft, update current criteria in place and keep the change log
|
|
47
|
+
as history. Preserve approved snapshots immutable and mark supersession through
|
|
48
|
+
metadata or links outside the approved bytes. Use the existing material-revision
|
|
49
|
+
and snapshot procedure for a changed baseline; a new draft is not approval.
|
|
50
|
+
|
|
51
|
+
## Readability review
|
|
52
|
+
|
|
53
|
+
For readability review, verify a human can identify what changes, why, decisions
|
|
54
|
+
and risks from the opening and an implementer can find exact requirements without
|
|
55
|
+
reconstructing history. Assess this alongside completeness before the existing
|
|
56
|
+
specification checkpoint. Return unclear or conflicting passages for revision
|
|
57
|
+
under the existing review and approval rules.
|
|
58
|
+
|
|
59
|
+
## Compact example
|
|
60
|
+
|
|
61
|
+
```markdown
|
|
62
|
+
# Export the filtered view — r2, draft
|
|
63
|
+
Authoritative location: <selected store URL or repository path>
|
|
64
|
+
|
|
65
|
+
## Decision brief
|
|
66
|
+
Problem/why: exports ignore the table filter, forcing users to clean the file.
|
|
67
|
+
Change: export what the user is viewing.
|
|
68
|
+
Before: export downloads the whole dataset. After: it downloads filtered rows.
|
|
69
|
+
Included: CSV export from this table. Excluded: scheduled exports and new formats.
|
|
70
|
+
Trade-off/risk: an active filter can omit rows users expect; label the action
|
|
71
|
+
“Export filtered rows” (EXP-1). Unverified: large exports on low-memory devices.
|
|
72
|
+
Decision needed: approve filtered export with the explicit label, or retain
|
|
73
|
+
whole-dataset export and its cleanup cost. Proposed choice: filtered export.
|
|
74
|
+
|
|
75
|
+
## Execution requirements
|
|
76
|
+
### Filtered export
|
|
77
|
+
EXP-1: On click, capture the active filter and visible column order, label the
|
|
78
|
+
action “Export filtered rows”, and export only matching rows in those columns.
|
|
79
|
+
Acceptance for EXP-1: with a two-row dataset and a filter matching one row,
|
|
80
|
+
the CSV contains that row and the visible columns in order; with a filter
|
|
81
|
+
matching zero rows, the CSV contains the header only.
|
|
82
|
+
|
|
83
|
+
## Evidence and history
|
|
84
|
+
Source: <observed current behavior and revision>.
|
|
85
|
+
Adviser receipts: <links bound to r2>; approval: pending.
|
|
86
|
+
Change log: r2 incorporates the zero-row case into EXP-1 acceptance.
|
|
87
|
+
Previous approved baseline, if any: <immutable ref>; supersession: pending approval.
|
|
88
|
+
```
|
|
@@ -49,6 +49,13 @@ Name the app, surface and invocation conditions in the description.
|
|
|
49
49
|
- Cleanup: stop only processes started by this run and remove owned scratch.
|
|
50
50
|
- Helpers: explain each helper's purpose and working directory.
|
|
51
51
|
|
|
52
|
+
For a device surface, require a Device section in generated `verify-<app>` recipes.
|
|
53
|
+
The Device section must use device tools to install and launch the pinned build,
|
|
54
|
+
drive features, capture evidence and shutdown owned devices.
|
|
55
|
+
The Device section must follow [UI verification](../axstack/references/ui-verification.md#android-device-and-webpwa-dual-run)
|
|
56
|
+
for dual-run independence and reconciliation, fleet routing and desktop safety.
|
|
57
|
+
Record unavailable device surfaces as unverified.
|
|
58
|
+
|
|
52
59
|
Write `.agents/skills/verify-<app>/features/README.md` as an index of the top 3-5 user features.
|
|
53
60
|
Write per-feature files in `.agents/skills/verify-<app>/features/` and link them from the index.
|
|
54
61
|
Give each feature file its entry points, prerequisites, drive steps and observable proof.
|
|
@@ -79,10 +86,12 @@ Report an unavailable candidate run as unverified.
|
|
|
79
86
|
|
|
80
87
|
Execute launch, doctor, drive one mapped feature, capture evidence, clean up and confirm evidence survives.
|
|
81
88
|
Run doctor before each drive.
|
|
82
|
-
The author drives only non-browser surfaces.
|
|
83
|
-
Delegate
|
|
84
|
-
|
|
89
|
+
The author drives only non-browser, non-device surfaces.
|
|
90
|
+
Delegate a recipe's device checks and explicitly requested app/PWA verification
|
|
91
|
+
to the UI verifier roles for a dual run through
|
|
92
|
+
[UI verification](../axstack/references/ui-verification.md#android-device-and-webpwa-dual-run)
|
|
85
93
|
with the feature file path in the brief.
|
|
94
|
+
Delegate other recipe browser drives to one `axstack-ui-verifier` pass.
|
|
86
95
|
Give the verifier the pinned candidate, isolated instance and private evidence path.
|
|
87
96
|
Clean up after every failed attempt.
|
|
88
97
|
Retry once after a drift fix.
|
|
@@ -124,10 +133,12 @@ Classify each gap as doc drift, harness gap or product regression:
|
|
|
124
133
|
Drive every mapped feature live even when source looks clean.
|
|
125
134
|
Use the skill's Launch recipe for an isolated instance.
|
|
126
135
|
Run doctor before each drive.
|
|
127
|
-
The author drives only non-browser surfaces.
|
|
128
|
-
Delegate
|
|
129
|
-
|
|
136
|
+
The author drives only non-browser, non-device surfaces.
|
|
137
|
+
Delegate a recipe's device checks and explicitly requested app/PWA verification
|
|
138
|
+
to the UI verifier roles for a dual run through
|
|
139
|
+
[UI verification](../axstack/references/ui-verification.md#android-device-and-webpwa-dual-run)
|
|
130
140
|
with the feature file path in the brief.
|
|
141
|
+
Delegate other recipe browser drives to one `axstack-ui-verifier` pass.
|
|
131
142
|
Capture actions, resulting states and side effects under its proof standards.
|
|
132
143
|
Report unreachable prerequisites with attempted routes separately from passes.
|
|
133
144
|
Fix a missing prerequisite in the map as doc drift.
|
package/src/roles.js
CHANGED
|
@@ -92,8 +92,8 @@ export function assessRoleReadiness(roles, preset) {
|
|
|
92
92
|
const isIntentionalAbsence = (role) => role.model === null && (
|
|
93
93
|
(preset === 'mixed' && role.id === 'axstack-research-x' && role.provider === 'grok') ||
|
|
94
94
|
(preset === 'mixed' && role.id === 'axstack-arena-candidate-grok' && role.provider === 'grok') ||
|
|
95
|
-
(preset === 'codex-only' && ['axstack-advisor-opus', 'axstack-escalation-fable', 'axstack-arena-judge-opus', 'axstack-research-x', 'axstack-arena-candidate-grok', 'axstack-research-web-sol'].includes(role.id)) ||
|
|
96
|
-
(preset === 'claude-only' && ['axstack-advisor-astra', 'axstack-arena-judge-astra', 'axstack-research-x', 'axstack-arena-candidate-grok', 'axstack-auditor-sol', 'axstack-research-code-sol', 'axstack-explore-execution-sol', 'axstack-explore-codebase-luna', 'axstack-research-web-sol'].includes(role.id))
|
|
95
|
+
(preset === 'codex-only' && ['axstack-advisor-opus', 'axstack-escalation-fable', 'axstack-arena-judge-opus', 'axstack-research-x', 'axstack-arena-candidate-grok', 'axstack-research-web-sol', 'axstack-ui-verifier-sonnet'].includes(role.id)) ||
|
|
96
|
+
(preset === 'claude-only' && ['axstack-advisor-astra', 'axstack-arena-judge-astra', 'axstack-research-x', 'axstack-arena-candidate-grok', 'axstack-auditor-sol', 'axstack-research-code-sol', 'axstack-explore-execution-sol', 'axstack-explore-codebase-luna', 'axstack-research-web-sol', 'axstack-ui-verifier-sonnet'].includes(role.id))
|
|
97
97
|
);
|
|
98
98
|
for (const role of roles) {
|
|
99
99
|
if (!bounds.has(role.provider)) {
|