axstack 0.36.1 → 0.38.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/README.md +5 -4
- package/bin/axstack.js +4 -18
- package/docs/getting-started.md +6 -6
- package/docs/host-operations.md +9 -5
- package/docs/installation.md +20 -41
- package/docs/workflows.md +12 -9
- package/package.json +1 -1
- package/profiles/presets/claude-only.json +3 -3
- package/profiles/presets/codex-only.json +3 -3
- package/profiles/presets/mixed.json +5 -4
- package/skills/axstack/references/automations.md +7 -2
- package/skills/axstack/references/lifecycle.md +2 -1
- package/skills/axstack/references/review-manager-prompt.md +44 -3
- package/skills/axstack/references/role-roster.md +13 -6
- package/skills/axstack/references/routing.md +2 -1
- package/skills/axstack/references/run-record.md +2 -0
- package/skills/axstack/references/ui-verification.md +6 -10
- package/skills/axstack-anatomy/ANATOMY.md +172 -0
- package/skills/axstack-anatomy/CHANGELOG.md +70 -0
- package/skills/axstack-anatomy/LICENSE +21 -0
- package/skills/axstack-anatomy/PROVENANCE.md +21 -0
- package/skills/axstack-anatomy/README.md +316 -0
- package/skills/axstack-anatomy/SKILL.md +74 -0
- package/skills/axstack-anatomy/SOURCE-PROOF.json +107 -0
- package/skills/axstack-anatomy/examples/arcade-cabinet/arcade-cabinet.css +44 -0
- package/skills/axstack-anatomy/examples/arcade-cabinet/build.mjs +714 -0
- package/skills/axstack-anatomy/examples/arcade-cabinet/live.js +597 -0
- package/skills/axstack-anatomy/examples/arcade-cabinet/parts.mjs +89 -0
- package/skills/axstack-anatomy/examples/arcade-cabinet/screen.mjs +114 -0
- package/skills/axstack-anatomy/examples/desk-computer/build.mjs +435 -0
- package/skills/axstack-anatomy/examples/dial-indicator/build.mjs +275 -0
- package/skills/axstack-anatomy/examples/raptor-engine/build.mjs +938 -0
- package/skills/axstack-anatomy/examples/raptor-engine/live.js +907 -0
- package/skills/axstack-anatomy/examples/raptor-engine/raptor.css +359 -0
- package/skills/axstack-anatomy/examples/ripple-tank/build.mjs +558 -0
- package/skills/axstack-anatomy/examples/ripple-tank/live.js +579 -0
- package/skills/axstack-anatomy/examples/ripple-tank/model.mjs +131 -0
- package/skills/axstack-anatomy/examples/ripple-tank/ripple.css +103 -0
- package/skills/axstack-anatomy/examples/spot-plate/art.tsx +110 -0
- package/skills/axstack-anatomy/examples/spot-plate/drops.ts +541 -0
- package/skills/axstack-anatomy/examples/spot-plate/fan.ts +84 -0
- package/skills/axstack-anatomy/examples/spot-plate/figure.tsx +25 -0
- package/skills/axstack-anatomy/examples/spot-plate/geometry.ts +280 -0
- package/skills/axstack-anatomy/examples/spot-plate/live.tsx +339 -0
- package/skills/axstack-anatomy/examples/spot-plate/rig.ts +230 -0
- package/skills/axstack-anatomy/examples/spot-plate/spot-plate.css +92 -0
- package/skills/axstack-anatomy/examples/test-rig/art.tsx +196 -0
- package/skills/axstack-anatomy/examples/test-rig/figure.tsx +23 -0
- package/skills/axstack-anatomy/examples/test-rig/geometry.ts +305 -0
- package/skills/axstack-anatomy/examples/test-rig/live.tsx +511 -0
- package/skills/axstack-anatomy/examples/test-rig/pose.ts +107 -0
- package/skills/axstack-anatomy/examples/test-rig/rig.ts +170 -0
- package/skills/axstack-anatomy/examples/test-rig/stretch.css +80 -0
- package/skills/axstack-anatomy/examples/test-rig/view.ts +99 -0
- package/skills/axstack-anatomy/examples/turning-dial/build.mjs +479 -0
- package/skills/axstack-anatomy/examples/turning-dial/dial.css +10 -0
- package/skills/axstack-anatomy/examples/turning-dial/live.js +342 -0
- package/skills/axstack-anatomy/examples/turning-dial/stress.mjs +269 -0
- package/skills/axstack-anatomy/kit/audit.mjs +1913 -0
- package/skills/axstack-anatomy/kit/canvas-painter.mjs +722 -0
- package/skills/axstack-anatomy/kit/gl-shared.mjs +184 -0
- package/skills/axstack-anatomy/kit/gl.mjs +184 -0
- package/skills/axstack-anatomy/kit/iso-kit.mjs +674 -0
- package/skills/axstack-anatomy/kit/iso-kit.ts +842 -0
- package/skills/axstack-anatomy/kit/iso.css +106 -0
- package/skills/axstack-anatomy/kit/lathe.mjs +1184 -0
- package/skills/axstack-anatomy/kit/react/draw.tsx +205 -0
- package/skills/axstack-anatomy/kit/react/live.tsx +140 -0
- package/skills/axstack-anatomy/kit/tube.mjs +306 -0
- package/skills/axstack-anatomy/kit/turn-audit.mjs +1096 -0
- package/skills/axstack-anatomy/kit/turn-build.mjs +1355 -0
- package/skills/axstack-anatomy/kit/turn-fixed.mjs +3800 -0
- package/skills/axstack-anatomy/kit/turn.mjs +3610 -0
- package/skills/axstack-anatomy/references/3d.md +352 -0
- package/skills/axstack-anatomy/references/craft.md +325 -0
- package/skills/axstack-anatomy/references/kit.md +419 -0
- package/skills/axstack-anatomy/references/motion.md +165 -0
- package/skills/axstack-anatomy/references/react.md +42 -0
- package/skills/axstack-anatomy/references/verify.md +351 -0
- package/skills/axstack-anatomy/references/walkthrough.md +75 -0
- package/skills/axstack-anatomy/references/webgl.md +473 -0
- package/skills/axstack-anatomy/scripts/audit-affine-bounds.test.mjs +54 -0
- package/skills/axstack-anatomy/scripts/audit-cache.test.mjs +37 -0
- package/skills/axstack-anatomy/scripts/audit-clearance-envelope.test.mjs +39 -0
- package/skills/axstack-anatomy/scripts/audit-empty-clipping.test.mjs +38 -0
- package/skills/axstack-anatomy/scripts/audit-order-convergence.test.mjs +55 -0
- package/skills/axstack-anatomy/scripts/audit-path-origin.test.mjs +26 -0
- package/skills/axstack-anatomy/scripts/audit-profile.test.mjs +41 -0
- package/skills/axstack-anatomy/scripts/audit-projection.test.mjs +36 -0
- package/skills/axstack-anatomy/scripts/audit-slide-clearance.test.mjs +58 -0
- package/skills/axstack-anatomy/scripts/audit-svg-attributes.test.mjs +46 -0
- package/skills/axstack-anatomy/scripts/audit-taper.test.mjs +29 -0
- package/skills/axstack-anatomy/scripts/capture-selector.test.mjs +131 -0
- package/skills/axstack-anatomy/scripts/capture.mjs +228 -0
- package/skills/axstack-anatomy/scripts/capture.test.mjs +42 -0
- package/skills/axstack-anatomy/scripts/chrome-port.mjs +36 -0
- package/skills/axstack-anatomy/scripts/chrome-startup.test.mjs +38 -0
- package/skills/axstack-anatomy/scripts/drive-actions.test.mjs +46 -0
- package/skills/axstack-anatomy/scripts/drive.mjs +302 -0
- package/skills/axstack-anatomy/scripts/drive.test.mjs +72 -0
- package/skills/axstack-anatomy/scripts/enter-key.test.mjs +38 -0
- package/skills/axstack-anatomy/scripts/fidelity-cut.test.mjs +54 -0
- package/skills/axstack-anatomy/scripts/fidelity-tube-coverage.test.mjs +40 -0
- package/skills/axstack-anatomy/scripts/inline-kit.mjs +26 -0
- package/skills/axstack-anatomy/scripts/line-ends.mjs +306 -0
- package/skills/axstack-anatomy/scripts/lines-arcs.test.mjs +50 -0
- package/skills/axstack-anatomy/scripts/lines-bezier.test.mjs +28 -0
- package/skills/axstack-anatomy/scripts/lines-round.test.mjs +56 -0
- package/skills/axstack-anatomy/scripts/lines-rounded-rect.test.mjs +32 -0
- package/skills/axstack-anatomy/scripts/lines.mjs +31 -0
- package/skills/axstack-anatomy/scripts/lines.test.mjs +38 -0
- package/skills/axstack-anatomy/scripts/navigation.test.mjs +55 -0
- package/skills/axstack-anatomy/scripts/orbit-sampling.test.mjs +50 -0
- package/skills/axstack-anatomy/scripts/react-dots-opacity.test.mjs +51 -0
- package/skills/axstack-anatomy/scripts/react-readout-first-write.test.mjs +37 -0
- package/skills/axstack-anatomy/scripts/react-solid-seamless.test.mjs +47 -0
- package/skills/axstack-anatomy/scripts/react-visibility-target.test.mjs +38 -0
- package/skills/axstack-anatomy/scripts/turn-bench.mjs +74 -0
- package/skills/axstack-anatomy/scripts/turn-check.mjs +641 -0
- package/skills/axstack-anatomy/scripts/turn-fidelity.mjs +413 -0
- package/skills/axstack-anatomy/scripts/turn-group-reference.test.mjs +40 -0
- package/skills/axstack-anatomy/scripts/turn-split-owner.test.mjs +62 -0
- package/skills/axstack-anatomy/theme.js +56 -0
- package/skills/axstack-explain/SKILL.md +28 -15
- package/skills/axstack-explain/references/delivery.md +22 -0
- package/skills/{axstack-diagram → axstack-explain}/references/fidelity.md +1 -7
- package/skills/axstack-explain/references/inline-pages.md +8 -8
- package/skills/axstack-explain/references/visual-qa.md +3 -5
- package/skills/axstack-explain/references/whiteboard-defense.md +59 -0
- package/skills/axstack-research/SKILL.md +18 -6
- package/src/archify-legacy.js +70 -0
- package/src/capabilities.js +1 -29
- package/src/installer.js +11 -31
- package/skills/axstack-diagram/SKILL.md +0 -28
- package/skills/axstack-diagram/references/archify.md +0 -76
- package/src/archify-pin.js +0 -5
- package/src/archify.js +0 -151
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Delivery and holds
|
|
2
|
+
|
|
3
|
+
Use this single fallback rule for Explain and direct Anatomy delivery.
|
|
4
|
+
A whiteboard-defense explanation covers a whole system, PR, run or change
|
|
5
|
+
under [Whiteboard defense](whiteboard-defense.md).
|
|
6
|
+
If the reviewer is unavailable, hold the whiteboard-defense explanation and name the unavailable reviewer.
|
|
7
|
+
Hold a whiteboard-defense explanation still failing claim review after two repair rounds with
|
|
8
|
+
the same author, naming unresolved findings. Never deliver it unreviewed or as reviewed.
|
|
9
|
+
Visual-only findings follow the visual rule, and claim findings hold the whiteboard-defense explanation.
|
|
10
|
+
|
|
11
|
+
For a missing preview or render tool, unavailable verifier or check, or page
|
|
12
|
+
absent after ambiguous `html_render`, hold the page and answer in plain prose
|
|
13
|
+
naming the held page and the reason.
|
|
14
|
+
Whiteboard-defense plain prose still requires `axstack-explainer-review`.
|
|
15
|
+
If a required author or reviewer seat is unavailable, hold the visual and
|
|
16
|
+
answer in plain prose naming the held visual.
|
|
17
|
+
Replace a visual still failing checks after two repair rounds with the same
|
|
18
|
+
author by plain prose with the reason.
|
|
19
|
+
The driver never writes a substitute visual.
|
|
20
|
+
Each repair round returns findings to the same author and rechecks the exact final bytes.
|
|
21
|
+
An anatomy page above the size cap follows [Anatomy](../../axstack-anatomy/SKILL.md)
|
|
22
|
+
for oversized delivery, with the same author and reviewer bindings.
|
|
@@ -6,14 +6,8 @@ For uncommitted sources, use the content hash and mark their pins unknown.
|
|
|
6
6
|
Keep the source map outside the reader view.
|
|
7
7
|
Display the short SHA.
|
|
8
8
|
|
|
9
|
-
Give planned and unknown nodes a visible marker in the node label
|
|
10
|
-
Use the literal marker `planned` or `unknown` for all five types.
|
|
9
|
+
Give planned and unknown nodes a visible marker in the node label.
|
|
11
10
|
Give planned and unknown edges a visible `planned` or `unknown` marker in the edge label.
|
|
12
|
-
For dashed styling, use only properties the pinned schema accepts and verify with `validate`.
|
|
13
|
-
This is an extra cue, such as a dashed relationship where supported.
|
|
14
|
-
The pinned node collections reject `variant`.
|
|
15
|
-
Add a planned or unknown legend entry only for sequence and dataflow.
|
|
16
|
-
Architecture, workflow, and lifecycle reject a `dashed` legend entry.
|
|
17
11
|
In Mermaid, use the same visible markers and a dashed cue where its syntax permits.
|
|
18
12
|
|
|
19
13
|
Treat about 9 core nodes as a signal to split.
|
|
@@ -9,10 +9,11 @@ Use T3 theme variables and follow the live theme.
|
|
|
9
9
|
Only an explicit user theme request overrides the page theme.
|
|
10
10
|
Use `--background`, `--foreground`, `--muted-foreground`, `--ring`,
|
|
11
11
|
`--font-sans`, and `--font-mono` for their named purposes.
|
|
12
|
-
The dark default stays archify-only.
|
|
13
12
|
|
|
14
13
|
Use fluid width, with zero outer horizontal padding.
|
|
15
14
|
Never use an outer frame, banner, or viewport heights.
|
|
15
|
+
Anatomy may retain its figure card as an inner panel with at least 16px padding
|
|
16
|
+
and `var(--radius)`; its page wrapper still has zero gutters and no background.
|
|
16
17
|
Let content set the page height. Use fixed chart heights.
|
|
17
18
|
Set frame `height` to the larger contentHeight of the two previews, capped at 2000.
|
|
18
19
|
|
|
@@ -44,7 +45,9 @@ Pages never use storage, cookies, modals, or popups.
|
|
|
44
45
|
|
|
45
46
|
Keep page bytes, SHA-256, and attachmentId in run evidence.
|
|
46
47
|
Scan the exact bytes of every page for remote `src`, `srcset`, and `href` on loaded resources.
|
|
47
|
-
Scan for `@import`, `url(
|
|
48
|
+
Scan for `@import`, CSS `url(` tokens at a word boundary with non-`#` targets, `fetch`, XHR, WebSocket, EventSource, and meta refresh.
|
|
49
|
+
A word boundary before `url(` excludes identifiers such as `knurl(`; local `url(#id)` remains allowed.
|
|
50
|
+
Trim CSS URL target whitespace and quotes before checking whether it starts with `#`.
|
|
48
51
|
Reject scan matches except local `url(#id)` references such as SVG markers and gradients.
|
|
49
52
|
|
|
50
53
|
Before publishing, run `html_preview` at 728px dark and 360px light.
|
|
@@ -63,13 +66,10 @@ Follow [UI verification](../../axstack/references/ui-verification.md) for that p
|
|
|
63
66
|
## Publish or fall back
|
|
64
67
|
|
|
65
68
|
Publish one page per answer.
|
|
66
|
-
|
|
69
|
+
Only the driver publishes with `html_render` from exactly the checked bytes.
|
|
67
70
|
Fix a failed check and rerun on the new bytes.
|
|
68
71
|
A byte change invalidates affected checks and review.
|
|
69
|
-
|
|
70
|
-
to chat with Mermaid and state the reason.
|
|
71
|
-
If a check, verifier, or required reviewer is unavailable or still fails, fall
|
|
72
|
-
back to chat with Mermaid and state the reason.
|
|
72
|
+
Follow [Delivery](delivery.md) for missing tools, seats, failed checks and repair holds.
|
|
73
73
|
After an ambiguous `html_render` result, never retry.
|
|
74
74
|
After an ambiguous `html_render` result, read `t3_thread_read`, keep the page
|
|
75
|
-
if present, and otherwise
|
|
75
|
+
if present, and otherwise hold the page and follow Delivery.
|
|
@@ -8,18 +8,16 @@ rendering matters.
|
|
|
8
8
|
Browser and visual checks must run in the delegated `axstack-ui-verifier` in its own detached checkout; outputs go to its private evidence folder, never the driver worktree.
|
|
9
9
|
|
|
10
10
|
1. Identify the final artifact bytes and theme. The explicit user theme wins;
|
|
11
|
-
otherwise use
|
|
12
|
-
For archify output, bind the `axstack-ui-verifier` pass to the finalize receipt's `artifact.sha256`.
|
|
13
|
-
For archify output, bind `axstack-explainer-review` to the same finalize receipt's `artifact.sha256`.
|
|
11
|
+
otherwise use `prefers-color-scheme`.
|
|
14
12
|
2. Delegate the rendered pass through [UI verification](../../axstack/references/ui-verification.md).
|
|
15
13
|
Record actual desktop and mobile observations, or name the missing layout
|
|
16
14
|
check.
|
|
17
15
|
3. Have the verifier exercise relevant interactions, keyboard and screen-reader
|
|
18
16
|
accessibility, and reduced-motion behavior. Report each unavailable check
|
|
19
17
|
honestly.
|
|
20
|
-
4. The explainer reviewer checks text and source fidelity
|
|
18
|
+
4. The explainer reviewer checks text and source fidelity, including every node
|
|
19
|
+
and edge against source. Keep source
|
|
21
20
|
correctness, tests, rendered behavior, independent review, and publication
|
|
22
21
|
evidence separate.
|
|
23
|
-
For archify output, require `axstack-explainer-review` of every node and edge against source.
|
|
24
22
|
5. Bind review to the exact artifact identity. Any byte change invalidates the
|
|
25
23
|
affected approval and requires fresh QA and review.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Whiteboard defense
|
|
2
|
+
|
|
3
|
+
Apply whiteboard defense to a whole system, PR, run or change, whether shipped,
|
|
4
|
+
in review or planned. Narrow questions stay proportional.
|
|
5
|
+
Triggers include "help me defend X", "whiteboard defense for PR N", and
|
|
6
|
+
"prep me for questions on X".
|
|
7
|
+
Extend §2.1's base for every explanation with the five elements below.
|
|
8
|
+
Use the §1.6 decision trail as evidence for decisions.
|
|
9
|
+
Spec-readback identity wins when defending a spec.
|
|
10
|
+
Show-your-work and blast-radius requests keep their rules unless the user also
|
|
11
|
+
asks to understand or defend the whole.
|
|
12
|
+
|
|
13
|
+
1. Cover how it works at a high level and where data flows.
|
|
14
|
+
2. Cover key decisions as "why X instead of Y".
|
|
15
|
+
3. Cover the main data structures or state and why they were chosen.
|
|
16
|
+
4. Cover what happens if a user, dependency, agent or attacker misbehaves or
|
|
17
|
+
acts maliciously.
|
|
18
|
+
5. Cover where it fails and how that failure shows.
|
|
19
|
+
|
|
20
|
+
Traced means source, tests, observation or recorded design (spec, run-record
|
|
21
|
+
Decisions, ADR, PR), each with its §1.3 label.
|
|
22
|
+
Label design-only claims planned/proposed.
|
|
23
|
+
For shipped or in-review work, list design-only (4) or (5) as an open gap:
|
|
24
|
+
"implementation not traced".
|
|
25
|
+
List a missing alternative, reason, data-structure rationale, or untraced
|
|
26
|
+
misbehaviour or failure as open gaps. Never invent alternatives, reasons,
|
|
27
|
+
threats or failures.
|
|
28
|
+
Name each trust boundary on the flow traced in (1), with an evidence pointer
|
|
29
|
+
and the actors crossing it; an unclear boundary is an open gap.
|
|
30
|
+
Answer each actor the user names or list it open.
|
|
31
|
+
State in one line that other actors are out of scope.
|
|
32
|
+
Omissions of (2) and (3) require "does not apply", a one-line reason and
|
|
33
|
+
an evidence pointer. Never omit (1), (4) or (5); answer them or mark them open.
|
|
34
|
+
Never drop an element silently.
|
|
35
|
+
(5) does not trigger the blast-radius proof; that runs only on "what could this break".
|
|
36
|
+
Use function names and line-level detail only as evidence pointers.
|
|
37
|
+
|
|
38
|
+
Close with likely whiteboard questions beyond (1)-(5), each with an
|
|
39
|
+
evidence-backed labelled answer or marked open.
|
|
40
|
+
Give each open gap one line naming the missing evidence and saying to resolve
|
|
41
|
+
it before defending the work.
|
|
42
|
+
Every whiteboard-defense explanation is consequential for review, in chat too.
|
|
43
|
+
Follow [Delivery](delivery.md) for reviewer-outage and two-round holds.
|
|
44
|
+
|
|
45
|
+
If five elements and closing questions exceed 700 words, keep the top level,
|
|
46
|
+
every element and the open gaps as at least one line each.
|
|
47
|
+
Offer per-component explanations for closing questions that cannot fit and
|
|
48
|
+
count how many moved. Never drop questions silently.
|
|
49
|
+
If mandatory lines exceed 700 words, give the top-level explanation first and
|
|
50
|
+
split the rest into per-component explanations; never exceed the cap.
|
|
51
|
+
The delivered view keeps (1), one line each for (4) and (5), and the open-gap
|
|
52
|
+
count with the components holding them.
|
|
53
|
+
State how many elements and open gaps moved.
|
|
54
|
+
|
|
55
|
+
When the user or stated records (run record, spec, PR body or README) identify
|
|
56
|
+
a PoC, demo or experiment, use an optional checklist and proportional answer
|
|
57
|
+
under the ordinary review rule. Never infer that status.
|
|
58
|
+
An intent to ship overrides the PoC exemption.
|
|
59
|
+
An explicit checklist request requires full whiteboard defense.
|
|
@@ -28,10 +28,20 @@ is part of research.
|
|
|
28
28
|
gathering evidence only after the target and stopping boundary are explicit.
|
|
29
29
|
|
|
30
30
|
2. **Fan out research:** A single factual lookup stays in the current chat.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
For other runs, dispatch through T3 `delegate_task` only branches whose
|
|
32
|
+
source class (requirements, code, execution, web, X) an open answer-changing claim needs.
|
|
33
|
+
Dispatch requirements only when scope is unclear.
|
|
34
|
+
Dispatch execution when a claim depends on runtime behavior.
|
|
35
|
+
Dispatch X only when a claim depends on X posts.
|
|
36
|
+
Record each skipped branch as `not needed (<reason>)`.
|
|
37
|
+
For each needed source class, retain two isolated branches where configured.
|
|
38
|
+
In mixed, each needed source class retains branches from different providers.
|
|
39
|
+
The code pair is `axstack-research-code` +
|
|
40
|
+
`axstack-research-code-sol`; the web pair is `axstack-research-web` +
|
|
41
|
+
`axstack-research-web-google`; the execution pair is `axstack-explore-execution` +
|
|
42
|
+
`axstack-explore-execution-sol`. Requirements, code, and web use Sonnet high
|
|
43
|
+
in mixed/claude-only and Codex in codex-only; web-google uses
|
|
44
|
+
Gemini/Antigravity with Google Search built in, and X uses Grok.
|
|
35
45
|
Give each branch one owner, allow no cross-reading, and require a cited note
|
|
36
46
|
with a URL and access date per claim; re-open sources and never trust a search
|
|
37
47
|
summary. The driver reconciles agreements/disagreements per claim.
|
|
@@ -61,8 +71,10 @@ is part of research.
|
|
|
61
71
|
never averaging findings. Record an intentionally absent pair and proceed
|
|
62
72
|
with the base seat alone. The `-sol` pair is optional: fence a launch failure,
|
|
63
73
|
trust/login prompt, or prompt block; record `absent (<reason>)`, and continue
|
|
64
|
-
with the base seat.
|
|
65
|
-
|
|
74
|
+
with the base seat. A sized mixed research fan-out requires dispatched
|
|
75
|
+
branches spanning at least two different providers; otherwise hold that fan-out.
|
|
76
|
+
Every other mixed fan-out, including arena and peer review, retains at least one
|
|
77
|
+
Codex and one Claude seat; otherwise hold that fan-out.
|
|
66
78
|
|
|
67
79
|
3. **Gather primary source evidence.** Inspect the actual documentation, code,
|
|
68
80
|
or tool output for every answer-changing claim. Apply the source standards
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { lstat, readFile, rm } from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
export const ARCHIFY_RECORD = 'axstack-diagram/archify.json';
|
|
4
|
+
|
|
5
|
+
function archifyGit(path, ...args) {
|
|
6
|
+
try {
|
|
7
|
+
const result = Bun.spawnSync(['git', ...(path ? ['-C', path] : []), ...args], {
|
|
8
|
+
stdout: 'pipe', stderr: 'pipe', timeout: 60000,
|
|
9
|
+
env: { ...Bun.env, GIT_TERMINAL_PROMPT: '0' },
|
|
10
|
+
});
|
|
11
|
+
if (result.exitCode !== 0) throw new Error(result.stderr.toString().trim() || `git exit ${result.exitCode}`);
|
|
12
|
+
return result.stdout.toString().trim();
|
|
13
|
+
} catch (err) {
|
|
14
|
+
throw new Error(`git: ${err.message}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function ownersSnapshot(path) {
|
|
19
|
+
const st = await lstat(path).catch((err) => {
|
|
20
|
+
if (err.code === 'ENOENT') return null;
|
|
21
|
+
throw err;
|
|
22
|
+
});
|
|
23
|
+
if (st && (!st.isFile() || st.isSymbolicLink())) throw new Error(`unsafe archify owners file: ${path}`);
|
|
24
|
+
const bytes = st ? await readFile(path) : null;
|
|
25
|
+
// JSON.parse coerces its argument to a string; preserve the existing Buffer coercion.
|
|
26
|
+
const owners = bytes ? /** @type {(text: {toString(): string}) => unknown} */ (JSON.parse)(bytes) : [];
|
|
27
|
+
if (!Array.isArray(owners) || owners.some((owner) => typeof owner !== 'string' || !owner.startsWith('/'))) {
|
|
28
|
+
throw new Error(`invalid archify owners file: ${path}`);
|
|
29
|
+
}
|
|
30
|
+
return { bytes, owners, mode: st ? st.mode & 0o777 : null };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Drop ownership transactionally; defer destructive cleanup until the skills
|
|
34
|
+
// manifest commits. A failed manifest write can then restore the owner list.
|
|
35
|
+
export async function planArchifyRelease({ record, skillsRoot, writeAtomic, log }) {
|
|
36
|
+
const ownersFile = `${record.path}.owners.json`;
|
|
37
|
+
const before = await ownersSnapshot(ownersFile);
|
|
38
|
+
const remaining = before.owners.filter((owner) => owner !== skillsRoot);
|
|
39
|
+
if (!before.owners.includes(skillsRoot)) {
|
|
40
|
+
return { rollback: async () => {}, finish: async () => log(`archify: kept ${record.path} (owner is absent)`) };
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
await writeAtomic(ownersFile, JSON.stringify(remaining) + '\n', { mode: 0o600 });
|
|
44
|
+
} catch (err) {
|
|
45
|
+
const current = await readFile(ownersFile);
|
|
46
|
+
if (!current.equals(before.bytes)) {
|
|
47
|
+
try { await writeAtomic(ownersFile, before.bytes, { mode: before.mode }); }
|
|
48
|
+
catch (restoreErr) { err.message += ` (archify owner restore failed: ${restoreErr.message})`; }
|
|
49
|
+
}
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
rollback: () => writeAtomic(ownersFile, before.bytes, { mode: before.mode }),
|
|
54
|
+
finish: async () => {
|
|
55
|
+
try {
|
|
56
|
+
if (remaining.length) throw new Error('other skills roots still own this copy');
|
|
57
|
+
const st = await lstat(record.path);
|
|
58
|
+
if (!st.isDirectory() || st.isSymbolicLink()) throw new Error('copy is not a real directory');
|
|
59
|
+
if (archifyGit(record.path, 'rev-parse', 'HEAD') !== record.sha) throw new Error('HEAD differs from the recorded SHA');
|
|
60
|
+
if (archifyGit(record.path, 'status', '--porcelain', '--ignored')) throw new Error('copy has tracked, untracked, or ignored changes');
|
|
61
|
+
await rm(record.path, { recursive: true });
|
|
62
|
+
await rm(ownersFile);
|
|
63
|
+
log(`archify: removed ${record.path}`);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
log(`archify: kept ${record.path} (${err.message})`);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
package/src/capabilities.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
// Host capability checks. Injected execution keeps tests off live runtimes.
|
|
2
|
-
import { lstat, readFile } from 'node:fs/promises';
|
|
3
|
-
import { join } from './posixpath.js';
|
|
4
|
-
import { ARCHIFY_RECORD, archifyGit } from './archify.js';
|
|
5
|
-
import { ARCHIFY_PIN } from './archify-pin.js';
|
|
6
2
|
export const BUN_FLOOR = '1.3.14';
|
|
7
3
|
export const T3_FLOOR = '0.0.46-nightly.20261003.2610';
|
|
8
4
|
|
|
@@ -109,28 +105,4 @@ export async function checkCapabilities(exec) {
|
|
|
109
105
|
return { checks, gaps, limitations: [...PROBE_LIMITATIONS] };
|
|
110
106
|
}
|
|
111
107
|
|
|
112
|
-
|
|
113
|
-
const recordFile = join(skillsDir, ARCHIFY_RECORD);
|
|
114
|
-
const override = Bun.env.ARCHIFY_CHROME;
|
|
115
|
-
const chrome = override ? Bun.which(override) :
|
|
116
|
-
['google-chrome', 'google-chrome-stable', 'chromium', 'chromium-browser', 'chrome']
|
|
117
|
-
.map((name) => Bun.which(name)).find(Boolean);
|
|
118
|
-
let record = null;
|
|
119
|
-
let actual = null;
|
|
120
|
-
let reason = null;
|
|
121
|
-
try {
|
|
122
|
-
if ((await lstat(recordFile)).isSymbolicLink()) throw new Error('record is a symlink');
|
|
123
|
-
record = JSON.parse(await readFile(recordFile, 'utf8'));
|
|
124
|
-
if (!record || typeof record.path !== 'string' || !record.path.startsWith('/') ||
|
|
125
|
-
!/^[a-f0-9]{40}$/.test(record.sha)) throw new Error('invalid record');
|
|
126
|
-
const copy = await lstat(record.path);
|
|
127
|
-
if (!copy.isDirectory() || copy.isSymbolicLink()) throw new Error('copy is not a real directory');
|
|
128
|
-
actual = archifyGit(record.path, 'rev-parse', 'HEAD');
|
|
129
|
-
if (actual !== record.sha || record.sha !== ARCHIFY_PIN.sha) {
|
|
130
|
-
throw new Error(`SHA mismatch: recorded ${record.sha}, checked-out ${actual}, expected ${ARCHIFY_PIN.sha}`);
|
|
131
|
-
}
|
|
132
|
-
} catch (err) {
|
|
133
|
-
reason = err.code === 'ENOENT' ? 'missing record or copy' : err.message;
|
|
134
|
-
}
|
|
135
|
-
return { recordFile, record, actual, chrome: chrome ?? null, reason };
|
|
136
|
-
}
|
|
108
|
+
|
package/src/installer.js
CHANGED
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
writeManifest,
|
|
39
39
|
} from './manifest.js';
|
|
40
40
|
import { planInstallClaudeSettings, planUninstallClaudeSettings } from './claude-settings.js';
|
|
41
|
-
import { ARCHIFY_RECORD, planArchifyRelease
|
|
41
|
+
import { ARCHIFY_RECORD, planArchifyRelease } from './archify-legacy.js';
|
|
42
42
|
import {
|
|
43
43
|
applyInstructionPlan,
|
|
44
44
|
findLegacyRoutingLines,
|
|
@@ -485,14 +485,13 @@ async function assertFileSnapshot(dest, expected) {
|
|
|
485
485
|
*/
|
|
486
486
|
|
|
487
487
|
/**
|
|
488
|
-
* @param {InstallerOptions & {bundleDir?: string, preset?: string,
|
|
488
|
+
* @param {InstallerOptions & {bundleDir?: string, preset?: string,
|
|
489
489
|
* inheritedInstructions?: {path: string, hash: string}}} [options]
|
|
490
490
|
*/
|
|
491
491
|
export async function installBundle({
|
|
492
492
|
bundleDir,
|
|
493
493
|
skillsDir,
|
|
494
494
|
preset,
|
|
495
|
-
toolsDir = null,
|
|
496
495
|
instructionsPath = null,
|
|
497
496
|
inheritedInstructions = null,
|
|
498
497
|
force = false,
|
|
@@ -511,14 +510,6 @@ export async function installBundle({
|
|
|
511
510
|
? await canonicalInstructionFile(instructionsPath)
|
|
512
511
|
: null;
|
|
513
512
|
assertOutsideHome(skillsRoot, { yes, kind: 'skills directory' });
|
|
514
|
-
const toolsPath = toolsDir ?? (homeDir() ? join(homeDir(), '.axstack', 'tools') : null);
|
|
515
|
-
if (!toolsPath) throw new Error('HOME is unavailable; pass --tools-dir explicitly');
|
|
516
|
-
await assertNoSymlinksBelow('/', resolve(toolsPath));
|
|
517
|
-
const toolsRoot = await canonicalTargetDir(toolsPath);
|
|
518
|
-
if (withinRoot(toolsRoot, skillsRoot) || withinRoot(skillsRoot, toolsRoot)) {
|
|
519
|
-
throw new Error('tools directory overlaps the skills root; refusing');
|
|
520
|
-
}
|
|
521
|
-
assertOutsideHome(toolsRoot, { yes, kind: 'tools directory' });
|
|
522
513
|
if (instructionsFile) assertOutsideHome(instructionsFile, { yes, kind: 'instruction file' });
|
|
523
514
|
const claudePlan = await planInstallClaudeSettings({ claude, skillsRoot });
|
|
524
515
|
if (claudePlan.report.path) {
|
|
@@ -586,11 +577,6 @@ export async function installBundle({
|
|
|
586
577
|
// manifest entry resolves its destination, current bytes, and pristine
|
|
587
578
|
// flag through the same guard before Phase 2 mutates anything.
|
|
588
579
|
const desired = [];
|
|
589
|
-
const archifyTarget = await readOwnedTarget(skillsRoot, ARCHIFY_RECORD);
|
|
590
|
-
if (archifyTarget.current !== null &&
|
|
591
|
-
hashContent(archifyTarget.current) !== ownedFiles[ARCHIFY_RECORD]) {
|
|
592
|
-
throw new Error('archify record is edited or unowned; refusing to replace it');
|
|
593
|
-
}
|
|
594
580
|
for (const { rel, abs, content: inlineContent } of bundle.files) {
|
|
595
581
|
const { dest, current, mode } = await readOwnedTarget(skillsRoot, rel);
|
|
596
582
|
const content = inlineContent ?? await readFile(abs);
|
|
@@ -608,7 +594,6 @@ export async function installBundle({
|
|
|
608
594
|
}
|
|
609
595
|
|
|
610
596
|
const bundleRels = new Set(bundle.files.map((file) => file.rel));
|
|
611
|
-
bundleRels.add(ARCHIFY_RECORD);
|
|
612
597
|
const stalePlan = [];
|
|
613
598
|
for (const rel of Object.keys(ownedFiles)) {
|
|
614
599
|
if (bundleRels.has(rel)) continue;
|
|
@@ -638,7 +623,6 @@ export async function installBundle({
|
|
|
638
623
|
const installedHashes = {};
|
|
639
624
|
const claudeWritten = [];
|
|
640
625
|
let instructionWritten = false;
|
|
641
|
-
let archify = null;
|
|
642
626
|
let archifyRelease = null;
|
|
643
627
|
const manifestFile = join(skillsRoot, '.axstack-manifest.json');
|
|
644
628
|
const manifestBefore = await readFile(manifestFile).catch((err) => {
|
|
@@ -649,12 +633,6 @@ export async function installBundle({
|
|
|
649
633
|
let manifestAttempted = false;
|
|
650
634
|
log('plan complete');
|
|
651
635
|
try {
|
|
652
|
-
archify = await provisionArchify({ toolsRoot, skillsRoot, writeAtomic, log });
|
|
653
|
-
const archifyRecord = archify.record ?? prevManifest.archify ?? null;
|
|
654
|
-
if (archifyRecord) {
|
|
655
|
-
desired.push({ rel: ARCHIFY_RECORD, ...archifyTarget,
|
|
656
|
-
content: Buffer.from(JSON.stringify(archifyRecord, null, 2) + '\n') });
|
|
657
|
-
}
|
|
658
636
|
if (instructionsFile) {
|
|
659
637
|
await assertFileSnapshot(instructionsFile, existingInstructionsRaw);
|
|
660
638
|
}
|
|
@@ -771,7 +749,11 @@ export async function installBundle({
|
|
|
771
749
|
)
|
|
772
750
|
: null;
|
|
773
751
|
|
|
774
|
-
|
|
752
|
+
const legacyRecord = await readOwnedTarget(skillsRoot, ARCHIFY_RECORD);
|
|
753
|
+
if (legacyRecord.current !== null) {
|
|
754
|
+
log(`archify: kept ${prevManifest.archify?.path ?? 'unbound copy'} (preserved record ${legacyRecord.dest})`);
|
|
755
|
+
}
|
|
756
|
+
if (prevManifest.archify && legacyRecord.current === null) {
|
|
775
757
|
archifyRelease = await planArchifyRelease({
|
|
776
758
|
record: prevManifest.archify, skillsRoot, writeAtomic, log,
|
|
777
759
|
});
|
|
@@ -785,7 +767,6 @@ export async function installBundle({
|
|
|
785
767
|
path: claudePlan.settingsPath ?? boundClaudeSettingsPath,
|
|
786
768
|
},
|
|
787
769
|
instructions: nextInstructions,
|
|
788
|
-
archify: archifyRecord,
|
|
789
770
|
});
|
|
790
771
|
if (archifyRelease) await archifyRelease.finish();
|
|
791
772
|
if (legacyProfileNote || legacyInstructionNote) {
|
|
@@ -837,10 +818,6 @@ export async function installBundle({
|
|
|
837
818
|
try { await archifyRelease.rollback(); }
|
|
838
819
|
catch (restoreErr) { rollbackErrors.push(`old archify owner: ${restoreErr.message}`); }
|
|
839
820
|
}
|
|
840
|
-
if (archify) {
|
|
841
|
-
try { await archify.rollback(); }
|
|
842
|
-
catch (restoreErr) { rollbackErrors.push(`archify: ${restoreErr.message}`); }
|
|
843
|
-
}
|
|
844
821
|
for (const write of claudeWritten.reverse()) {
|
|
845
822
|
try {
|
|
846
823
|
if (write.before === null) await rm(write.path);
|
|
@@ -1052,6 +1029,9 @@ export async function uninstallBundle({
|
|
|
1052
1029
|
summary.claudeSettings = claudePlan.report;
|
|
1053
1030
|
|
|
1054
1031
|
const remainingArchify = ARCHIFY_RECORD in remainingFiles ? manifest.archify : null;
|
|
1032
|
+
if (remainingArchify) {
|
|
1033
|
+
log(`archify: kept ${remainingArchify.path} (preserved record ${join(skillsRoot, ARCHIFY_RECORD)})`);
|
|
1034
|
+
}
|
|
1055
1035
|
if (manifest.archify && !remainingArchify) {
|
|
1056
1036
|
archifyRelease = await planArchifyRelease({ record: manifest.archify, skillsRoot, writeAtomic, log });
|
|
1057
1037
|
}
|
|
@@ -1070,10 +1050,10 @@ export async function uninstallBundle({
|
|
|
1070
1050
|
await writeManifest(skillsRoot, {
|
|
1071
1051
|
version: MANIFEST_VERSION,
|
|
1072
1052
|
files: remainingFiles,
|
|
1053
|
+
...(remainingArchify ? { archify: remainingArchify } : {}),
|
|
1073
1054
|
profiles: legacyProfiles,
|
|
1074
1055
|
claudeSettings: { path: remainingClaudeSettingsPath },
|
|
1075
1056
|
instructions: remainingInstructions,
|
|
1076
|
-
archify: remainingArchify,
|
|
1077
1057
|
});
|
|
1078
1058
|
}
|
|
1079
1059
|
if (archifyRelease) await archifyRelease.finish();
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: axstack-diagram
|
|
3
|
-
description: When an explanation needs a diagram, use axstack-diagram for inline SVG or CSS in pages, Mermaid in chat, GitHub, and docs, or archify only on an explicit viewer request.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Diagram
|
|
7
|
-
|
|
8
|
-
Usage: `/axstack-diagram <question + destination>` or load this skill from explain.
|
|
9
|
-
Load [Standing contracts](../axstack/references/contracts.md) before acting.
|
|
10
|
-
Follow [Fidelity](references/fidelity.md) for every format.
|
|
11
|
-
|
|
12
|
-
Use archify HTML only on an explicit viewer request.
|
|
13
|
-
The configured `axstack-explainer` authors the viewer; follow [Archify](references/archify.md).
|
|
14
|
-
Use inline SVG or CSS inside inline pages.
|
|
15
|
-
Never use Mermaid in inline pages.
|
|
16
|
-
Use Mermaid for chat, GitHub, and docs.
|
|
17
|
-
Include `accTitle` and `accDescr` in each Mermaid view.
|
|
18
|
-
Never use theme init in Mermaid.
|
|
19
|
-
Split large Mermaid diagrams into views.
|
|
20
|
-
T3 chat renders the Mermaid fence once the reply settles.
|
|
21
|
-
|
|
22
|
-
Explain owns evidence, claim labels, the word cap, dispatch, and delivery.
|
|
23
|
-
Diagram owns format and visual rules.
|
|
24
|
-
Never call explain.
|
|
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).
|
|
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.
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# Archify
|
|
2
|
-
|
|
3
|
-
Archify turns a typed JSON intermediate representation (IR) into an HTML viewer.
|
|
4
|
-
Follow [Fidelity](fidelity.md) before authoring.
|
|
5
|
-
|
|
6
|
-
## Bind the tool
|
|
7
|
-
|
|
8
|
-
Read `archify.json` beside the skill entrypoint for its `{path, sha}` record.
|
|
9
|
-
Verify that `git -C <path> rev-parse HEAD` equals the recorded SHA.
|
|
10
|
-
If the record or copy is missing, HEAD mismatches the recorded SHA, or Chrome is missing, hold and ask the user.
|
|
11
|
-
Honor `ARCHIFY_CHROME` when set.
|
|
12
|
-
Otherwise find Chrome or Chromium on `PATH`.
|
|
13
|
-
Set `ARCHIFY_UPDATE_CHECK_DISABLED=1` on every archify call.
|
|
14
|
-
Never substitute Mermaid or unverified HTML when the viewer is required.
|
|
15
|
-
|
|
16
|
-
## Author the IR
|
|
17
|
-
|
|
18
|
-
If the pinned archify procedure conflicts with this file, this file takes precedence.
|
|
19
|
-
Read `<path>/archify/SKILL.md` and follow its named references for IR authoring and repair.
|
|
20
|
-
Run the referenced archify commands with Bun.
|
|
21
|
-
|
|
22
|
-
Set `meta.title` and a portable relative HTML filename in `meta.output`.
|
|
23
|
-
Use `meta.quality_profile: "showcase"`.
|
|
24
|
-
Set `meta.animation: "none"`.
|
|
25
|
-
Keep trace motion, exports, share cards, and brand marks off unless the user asks.
|
|
26
|
-
Deliver the viewer with `?theme=dark` unless the user names a theme.
|
|
27
|
-
Theme is viewer state rather than an IR field; without the query, localStorage and `prefers-color-scheme` determine it.
|
|
28
|
-
Preserve explain's claim labels in `meta.subtitle` or the companion explanation.
|
|
29
|
-
Set `meta.repository` to the inspected repository URL and full `revision`.
|
|
30
|
-
Keep per-node source pins in each node's `sources` entries for inspected paths and line ranges.
|
|
31
|
-
Keep the private source map for relationships that have no source field.
|
|
32
|
-
Keep proposed sources distinct from factual pins.
|
|
33
|
-
Use [Fidelity](fidelity.md) for visible planned and unknown markers.
|
|
34
|
-
For a separate check, run `bun <path>/archify/bin/archify.mjs validate <type> <ir.json> --json --repo-root <repo>`.
|
|
35
|
-
When sources are declared, require `--repo-root <repo>` on both `validate` and `finalize`.
|
|
36
|
-
For sequence and dataflow, a schema-valid legend cue is `meta.legend.entries.dashed: {"label":"Planned or unknown"}`.
|
|
37
|
-
|
|
38
|
-
Use one top-level `cards[]` entry with `{dot, title, items[]}` per key node as its node detail card.
|
|
39
|
-
Each node detail card has at most 40 words and cites its source.
|
|
40
|
-
Count the title plus all items toward each card's 40-word cap.
|
|
41
|
-
Cite each card's source inline as `file:lines` in its `items[]`.
|
|
42
|
-
Node detail cards are exempt from explain's 700-word cap.
|
|
43
|
-
Count node labels, headings, captions, and all non-card text.
|
|
44
|
-
|
|
45
|
-
## Build and hold
|
|
46
|
-
|
|
47
|
-
Write the IR, HTML, and receipt only inside the private evidence folder.
|
|
48
|
-
For direct invocation, create a private run evidence folder first.
|
|
49
|
-
Override archify's default `.archify/` folder with an evidence output path and `--out-dir <evidence dir>`.
|
|
50
|
-
Run `bun <path>/archify/bin/archify.mjs finalize <type> <ir.json> <out.html> --quality showcase --json --out-dir <evidence dir> --repo-root <repo>`.
|
|
51
|
-
This is the author's headless build gate.
|
|
52
|
-
Read the saved receipt named by the JSON result's `evidence.receipt`.
|
|
53
|
-
Accept only receipt `status: "pass"` as success.
|
|
54
|
-
Never trust exit codes as proof of a pass.
|
|
55
|
-
When a receipt is missing or remains non-pass after two repair rounds, hold delivery and ask the user.
|
|
56
|
-
If Chrome is missing, report "viewer not verified: Chrome missing".
|
|
57
|
-
Chrome absence yields `status: "skipped"` and exit 2.
|
|
58
|
-
Follow archify's receipt fixes and references for layout and composition repairs.
|
|
59
|
-
After every repair edit, rerun the complete `finalize` command.
|
|
60
|
-
Count one repair round as one complete `finalize` rerun after an edit.
|
|
61
|
-
Validate runs never count as repair rounds.
|
|
62
|
-
Do not require validate before finalize.
|
|
63
|
-
Run at most two repair rounds, then hold with remaining defects for the user's decision.
|
|
64
|
-
Never use archify's extra evidence-based retry.
|
|
65
|
-
|
|
66
|
-
## Rendered and source checks
|
|
67
|
-
|
|
68
|
-
Follow [UI verification](../../axstack/references/ui-verification.md) for dispatch.
|
|
69
|
-
Keep `finalize` as the only author browser check.
|
|
70
|
-
Delegate `visual-check`, browser opening, preview, and first-screen inspection to `axstack-ui-verifier`.
|
|
71
|
-
Require `axstack-ui-verifier` checks of the receipt's `artifact.sha256` bytes on desktop, 390px mobile, keyboard, reduced motion, and the text alternative.
|
|
72
|
-
Include theme, search or focus, and directional reach interactions.
|
|
73
|
-
Require `axstack-ui-verifier` checks with the theme set explicitly.
|
|
74
|
-
Require `explainer-review` of every node and edge against source for archify output.
|
|
75
|
-
Every byte change requires fresh checks.
|
|
76
|
-
Keep these independent verdicts with the build receipt before delivery.
|