Skip to content

Commit b92dd09

Browse files
Add production-readiness execution pack (work orders + fleet runner) (#36)
Adds `.claude/tasks/prod-readiness/` — 15 self-contained work orders from the 2026-07-10 ten-dimension audit, each runnable by a fresh Claude session with **per-task Opus/Sonnet/Haiku routing** — plus `.claude/workflows/prod-readiness.js`, a fleet runner that routes each task to its model and runs implement → adversarial-verify → open-PR. ### Contents - **Wave 1** (basic functioning): W1-1 neural-boundary guardrail (**P0**), W1-2 degenerate-input guards, W1-3 router coverage, W1-6 zig FFI, W1-7 agda proofs. *(W1-4/#34, W1-5/#35, hygiene/#33 already merged — marked done.)* - **Wave 2** (beyond): release (Registrator+TagBot+SBOM), buildable guix, runnable containers, experimental reframe, extended reference validation, structured logging, prompt-injection delimiting, polish sweep. ### Why The wave-1 fleet completed setup (Julia 1.10.11 / Zig 0.16 / Agda 2.6.4.3 in WSL, baseline 4404 tests green) and landed 3 PRs, but hit a monthly spend limit before the other 5 packages pushed. This pack captures every remaining item as durable, context-free briefs so any session (or a re-run of the fleet) can execute them — carrying the user's decisions of record and verified toolchain facts. Docs/config only; no code or CI behavior changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 39713fd commit b92dd09

17 files changed

Lines changed: 1015 additions & 0 deletions
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<!--
2+
SPDX-License-Identifier: MPL-2.0
3+
Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
4+
-->
5+
# Production-Readiness — Execution Pack
6+
7+
Self-contained work orders from the 2026-07-10 ten-dimension production-readiness
8+
audit of statistikles. Each file is a standalone brief runnable by a fresh Claude
9+
session with **no dependency on the originating conversation**, with a recommended
10+
model (**Opus / Sonnet / Haiku**) for implementation and for adversarial verification.
11+
12+
- **Wave 1** = "basically running & functioning acceptably" (the P0 guarantee + the
13+
crash/robustness/coverage/install fixes). Do these **first**.
14+
- **Wave 2** = everything beyond that (release engineering, packaging, honesty
15+
reframes, deeper test coverage, observability, polish).
16+
17+
Package 8 of wave 1 (hygiene/security-templates) is **already merged** (PR #33).
18+
Everything else below is open work.
19+
20+
## How to run
21+
22+
**One task, any session:**
23+
> "Execute the work order in `.claude/tasks/prod-readiness/w1-1-neural-guardrail.md`.
24+
> Use the model it names; follow the execution contract in the README."
25+
26+
**A whole wave as a fleet (Claude Code, budget permitting):**
27+
> `Workflow({ name: "prod-readiness" })` — the runner reads this folder, routes each
28+
> task to its model, and does implement → adversarial-verify → open-PR. See
29+
> `.claude/workflows/prod-readiness.js`.
30+
31+
## Model routing
32+
33+
### Wave 1 — basic functioning (do first, in order)
34+
35+
**Status (2026-07-11):** packages 4, 5, and 8 are **MERGED** (PRs #34, #35, #33). The
36+
five below with a status of OPEN are the remainder — their fleet agents died on a spend
37+
limit before pushing. Do them next, **W1-1 (P0) first**.
38+
39+
| # | Task | Branch | Impl | Verify | Status |
40+
|---|------|--------|------|--------|--------|
41+
| 1 | [Neural boundary guardrail (**P0**)](w1-1-neural-guardrail.md) | `fix/neural-boundary-guardrail` | **opus** | **opus** | **OPEN** |
42+
| 2 | [Degenerate-input guards + `@assert``ArgumentError`](w1-2-stats-degenerate-inputs.md) | `fix/stats-degenerate-inputs` | sonnet | opus | **OPEN** |
43+
| 3 | [Table-driven router tests + CI coverage](w1-3-executor-router-coverage.md) | `test/executor-router-coverage` | sonnet | sonnet | **OPEN** |
44+
| 4 | [Real install path (Justfile, quickstarts, smoke CI)](w1-4-documented-install-path.md) | `fix/documented-install-path` | sonnet | haiku |#34 |
45+
| 5 | [Pin compute half + prune Dependabot + threat model](w1-5-supply-chain-pinning.md) | `fix/supply-chain-pinning` | sonnet | sonnet |#35 |
46+
| 6 | [Make Zig FFI compile + `zig build test` CI](w1-6-zig-ffi-compiles.md) | `fix/zig-ffi-compiles` | **opus** | sonnet | **OPEN** |
47+
| 7 | [Type-checking Agda proofs + `agda --safe` CI](w1-7-agda-proofs-ci.md) | `fix/agda-proofs-ci` | **opus** | sonnet | **OPEN** |
48+
| 8 | Hygiene / security-templates | `chore/hygiene-security-templates` | haiku | haiku |#33 |
49+
50+
### Wave 2 — beyond basic functioning
51+
52+
| # | Task | Branch | Impl | Verify |
53+
|---|------|--------|------|--------|
54+
| 1 | [Release: JuliaRegistrator + TagBot + SBOM](w2-1-release-pipeline.md) | `feat/release-registrator-tagbot` | **opus** | **opus** |
55+
| 2 | [Buildable guix.scm](w2-2-guix-package.md) | `feat/guix-real-package` | **opus** | sonnet |
56+
| 3 | [Runnable Containerfile + devcontainer](w2-3-containers.md) | `feat/containers-runnable` | sonnet | sonnet |
57+
| 4 | [Experimental reframe: FFI + proofs docs](w2-4-experimental-reframe.md) | `docs/experimental-reframe` | sonnet | sonnet |
58+
| 5 | [Extend ground-truth reference validation](w2-5-reference-validation.md) | `test/reference-validation-extension` | sonnet | **opus** |
59+
| 6 | [Structured logging + audit trail](w2-6-observability.md) | `feat/structured-observability` | sonnet | sonnet |
60+
| 7 | [Prompt-injection delimiting](w2-7-prompt-injection.md) | `fix/prompt-injection-delimiting` | sonnet | haiku |
61+
| 8 | [Polish sweep](w2-8-polish-sweep.md) | `chore/polish-sweep` | haiku | haiku |
62+
63+
**Routing rationale.** Opus for design-sensitive, safety-critical, or niche-toolchain
64+
work (the guarantee guardrail, release engineering, Guix, Zig/Agda, statistical
65+
ground-truth). Sonnet for well-specified implementation. Haiku for mechanical sweeps.
66+
Verification is adversarial — the verifier tries to *refute* the implementation against
67+
the work order's acceptance criteria — and is deliberately assigned the model that
68+
should be able to follow the spec: if the verify-model can't confirm it, the spec or
69+
the code isn't done.
70+
71+
## Decisions of record (user-approved 2026-07-10 — do not relitigate)
72+
73+
1. **Release = JuliaRegistrator + TagBot** (General registry), not artifact-only.
74+
2. **FFI (Zig/C-ABI) and Agda proofs are EXPERIMENTAL** — make them compile & CI-check
75+
(wave 1), reframe docs (wave 2); do NOT invest in the Idris2 ABI or proofs-over-ℝ yet.
76+
3. **guix.scm gets made real** (buildable), not deleted — governance has a "Guix primary"
77+
policy check.
78+
4. **Merge gate is active**: the Base ruleset requires the "E2E — Julia Test Suite"
79+
status check. A PR cannot merge red.
80+
81+
## Verified toolchain facts (WSL Debian, set up 2026-07-10)
82+
83+
- **Julia 1.10.11** via juliaup; on PATH only in a **login shell** (`bash -lc`).
84+
Baseline `Pkg.test()` = **4404 tests green** (Full 424 / E2E 145 / Property 3800 /
85+
Reference 35). ALWAYS serialize Julia runs: `flock /tmp/statistikles-julia.lock -c '…'`
86+
(16 GB RAM ceiling). Warm depot exists; `instantiate+precompile` ≈ 1m46s, test ≈ 36s
87+
after precompile.
88+
- **Zig 0.16.0** tarball at `/home/hyperpolymath/zig/zig-x86_64-linux-0.16.0/zig`
89+
(also `~/.local/bin/zig` in login shells). ⚠ 0.15+/0.16 has breaking std changes
90+
(Io writer/reader redesign) — if the repo's Zig targets ≤0.14, fetch an older
91+
tarball into the same dir.
92+
- **Agda 2.6.4.3 + agda-stdlib 2.1** (apt); stdlib wired via `~/.agda/libraries` +
93+
`~/.agda/defaults`. ⚠ stdlib 2.1 renamed some modules vs 1.x — expect import tweaks.
94+
95+
## Execution contract (every task)
96+
97+
- Branch from up-to-date `origin/main` using the named branch. If a wave-1 prerequisite
98+
is unmerged and you touch the same files, branch from that branch and say so in the PR.
99+
- Commits SSH-signed (environment is pre-configured). End messages with the
100+
`Co-Authored-By` trailer for your model.
101+
- Run the work order's **Local verification** before pushing; on 16 GB RAM, wrap Julia
102+
in `flock /tmp/statistikles-julia.lock`. Never claim verification you didn't run.
103+
- Diffs surgical; match surrounding style. GitHub Actions **SHA-pinned** with a version
104+
comment. Never job-level `hashFiles()`/`secrets` conditionals (silent startup_failure)
105+
— step-level only.
106+
- **Do not open GitHub issues** and **do not merge/delete/force-push.** Open exactly one
107+
PR to `main` per task, titled per the work order; body = what changed & why (file
108+
refs) + verification run & result + anything skipped. Stop there.
109+
- Python is banned in repo code (governance-enforced). Deriving test constants with
110+
Python/R locally is fine; only the constants + a derivation note enter the repo.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# W1-1 · Neural boundary guardrail (P0) + hardening
2+
3+
**Model:** impl=opus · verify=opus · **Branch:** `fix/neural-boundary-guardrail`
4+
**Priority: P0 — this is the product's raison d'être made real.**
5+
6+
## Context
7+
8+
The flagship guarantee — "no number is ever produced by the LLM" — is currently
9+
**prompt-only** (`src/tools/chat.jl` SYSTEM_PROMPT ~lines 29-66) with zero output
10+
validation. `chat.jl` (~114-119) prints assistant content verbatim; nothing checks
11+
that numerals in the reply came from a tool result. Surrounding this are crash/robustness
12+
gaps: unguarded tool-call JSON parsing, no HTTP timeout, silent-null unknown sub-types,
13+
dropped `tools` on follow-up calls (no multi-step chaining), and `@assert`-based
14+
resource assumptions.
15+
16+
## Requirements (implement in `src/tools/`)
17+
18+
**(a) NEW `src/tools/guardrail.jl`:**
19+
- `collect_numbers(x)` — recursively harvest every numeric value from tool-result
20+
Dicts/Vectors/nested structures.
21+
- `extract_numeric_tokens(text::String)` — find numeric literals in assistant prose
22+
(ints, decimals, scientific notation, percentages).
23+
- `validate_numeric_provenance(text, tool_results, user_numbers; rtol=1e-6) -> (ok::Bool, orphans::Vector{String})`.
24+
A token is legitimate if it approx-matches (rtol) any harvested tool-result number,
25+
OR its ÷100 / ×100 variant matches (percent phrasing), OR it appears in the user's own
26+
input numbers, OR it is a small structural integer 0..12.
27+
28+
**(b) `chat.jl`:** record all tool results for the turn; after the final assistant
29+
content, run the guardrail (parse user-message numbers as `user_numbers`). If orphans
30+
exist AND tool calls happened: ONE retry asking the model to restate using only
31+
tool-result numbers; if orphans persist, print the reply with a clear warning block
32+
listing the unverified numbers. If the reply has numeric tokens but NO tool call was
33+
made: same retry-once-then-warn with a "no symbolic computation was performed" message.
34+
**Never silently rewrite model text — flag, never fabricate.**
35+
36+
**(c) `lmstudio.jl` `process_tool_calls`:** wrap the per-tool-call body (nested key
37+
access + `JSON3.read` of arguments, ~lines 88-101) in try/catch; on failure push a
38+
`role:"tool"` message with a clean `Dict("error"=>...)` so the model recovers. **Pass
39+
the `tools` parameter on the follow-up call** (currently dropped ~line 104) and iterate
40+
tool-call rounds in a bounded loop (max 5) until a reply has no tool_calls. Add HTTP
41+
timeouts to `call_lm_studio` and the `chat.jl` HTTP call: `connect_timeout=10`,
42+
`readtimeout=120`, `retry=false` (see `echidna_adapter.jl` for the existing pattern);
43+
timeout → return the existing error-Dict shape.
44+
45+
**(d) `chat.jl` REPL while-loop:** try/catch around the turn body — print a concise
46+
error and continue; one bad turn must never kill the session.
47+
48+
**(e) `executor.jl`:** clamp caller-supplied `n_reps`/`n_permutations` to ≤100_000 and
49+
component counts `k` to ≤20 (return `Dict("error"=>...)` when exceeded); add a trailing
50+
`else return Dict("error"=>"Unknown type '…' for <tool>")` to EVERY inner sub-type
51+
dispatch (grep every inner if/elseif chain: t_test, time_series, information_theory,
52+
survival_analysis, robust_stats, causal_inference, spatial_stats, advanced_modeling,
53+
algebraic_stats, nonparametric_test, and any others); gate the `trace` backtrace field
54+
in the catch-all behind ENV `STATISTIKLES_DEBUG` (default off), keeping a concise stable
55+
error string.
56+
57+
**(f) tests — NEW `test/guardrail_test.jl`:** unit tests for the three guardrail
58+
functions incl. a **clean fixture** (all numbers from tool results, with rounding/percent
59+
variants) that passes and an **injected-fabrication fixture** that MUST be flagged; tests
60+
for `process_tool_calls` recovery with malformed tool_call dicts (missing keys, non-JSON
61+
arguments) — construct response Dicts directly, no HTTP; tests for clamps and
62+
unknown-sub-type else-errors via `execute_tool`. Wire into `test/runtests.jl`.
63+
64+
## Acceptance criteria
65+
66+
- [ ] Guardrail flags an injected fabricated number and passes a clean reply (both tested).
67+
- [ ] Malformed tool-call dicts recover instead of crashing (tested).
68+
- [ ] Every inner sub-type dispatch has an `else`-error; clamps enforced (tested).
69+
- [ ] HTTP calls have timeouts; follow-up call passes `tools`; loop is bounded.
70+
- [ ] Full suite green locally (was 4404 tests) + the new tests.
71+
72+
## Local verification
73+
74+
`flock /tmp/statistikles-julia.lock -c 'cd <repo> && julia --project=. -e "using Pkg; Pkg.test()"'`
75+
in a WSL login shell (`wsl.exe -d Debian -u hyperpolymath -- bash -lc '…'`).
76+
77+
## Out of scope
78+
79+
Prompt-injection input delimiting (that is W2-7, which builds on this guardrail).
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# W1-2 · Degenerate-input guards + `@assert``ArgumentError`
2+
3+
**Model:** impl=sonnet · verify=opus · **Branch:** `fix/stats-degenerate-inputs`
4+
5+
## Context
6+
7+
The compute core is correct on the happy path and reference-validated, but degenerate
8+
inputs leak `NaN`/`Inf` verbatim to the user — violating the finiteness + JSON-
9+
serialisability contract and the "nothing fabricated" guarantee. Separately, ~26
10+
validation `@assert` sites across ~13 modules are the sole guard on user/LLM data;
11+
Julia documents `@assert` as disable-able, so mismatched-length/out-of-range inputs
12+
could proceed to `BoundsError` or silently-wrong numbers.
13+
14+
## Requirements
15+
16+
**(a) Degenerate guards** (`src/stats/*.jl`): skewness requires n≥3; kurtosis n≥4;
17+
pearson/regression guard zero denominators (`den>0`, `ss_tot>0`, `(1-r^2)>0`); t-tests
18+
guard `se==0` and `n<2`; zero-variance groups. Follow the guard patterns already in
19+
`spearman`/`one_way_anova`. On a degenerate case return `nothing` (JSON null) for that
20+
field plus, where the Dict shape allows, a short `"note"` explaining why — never
21+
NaN/Inf. Also convert misleading sentinels `harmonic_mean=0.0`, `cv=Inf`,
22+
`geometric_mean=NaN` to `nothing`+note.
23+
24+
**(b) Replace validation `@assert`s** that guard user/LLM data with
25+
`throw(ArgumentError("…"))` carrying a precise message (grep `@assert` under `src/`;
26+
known sites include `correlation_regression.jl:17,54,206`, `descriptive.jl:132`,
27+
`information_theory.jl:26`, `representations.jl:15`, `bridge/typell_levels.jl:59`). Add
28+
NEW `src/stats/validation.jl` with small helpers (`require_equal_length`,
29+
`require_nonempty`, `require_positive`, `require_probability`, …) and use them. Keep
30+
`@assert` only for true internal invariants.
31+
32+
**(c) tests — NEW `test/degenerate_input_test.jl`:** n=1/2/3 vectors, constant vectors,
33+
zero-variance two-group cases; assert no `NaN`/`Inf` anywhere in returned Dicts (reuse
34+
the serialisability walk from `e2e_test.jl`); `@test_throws ArgumentError` for the
35+
converted validations. Wire into `runtests.jl`.
36+
37+
**NOTE:** `descriptive_stats`' `outlier_fences` was already fixed to a `Vector` (PR #31,
38+
merged). Do not touch it.
39+
40+
## Acceptance criteria
41+
42+
- [ ] No core stat function returns NaN/Inf on degenerate input (tested).
43+
- [ ] Converted validations throw `ArgumentError` (tested).
44+
- [ ] Full suite green + new tests.
45+
46+
## Local verification
47+
48+
`flock /tmp/statistikles-julia.lock -c 'cd <repo> && julia --project=. -e "using Pkg; Pkg.test()"'`
49+
(WSL login shell). Sibling W1-1 also adds guards; if both are unmerged and overlap on a
50+
file, note it — do not duplicate.
51+
52+
## Out of scope
53+
54+
The guardrail/provenance layer (W1-1); reference-value coverage (W2-5).
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# W1-3 · Table-driven router tests + CI coverage
2+
3+
**Model:** impl=sonnet · verify=sonnet · **Branch:** `test/executor-router-coverage`
4+
5+
## Context
6+
7+
`src/tools/executor.jl` has ~75 dispatch arms but `execute_tool` is tested exactly once
8+
(anova, `reference_validation_test.jl:103`). The string→function mapping and argument
9+
coercion — the precise layer the LLM drives — is unverified for 74/75 tools; `lmstudio.jl`
10+
has zero test references. Coverage is entirely unmeasured (no flag, no reporter, no gate)
11+
across ~90 source files.
12+
13+
## Requirements
14+
15+
**(a) NEW `test/executor_router_test.jl`:** programmatically enumerate every registered
16+
tool name from `src/tools/definitions.jl` (parse the definitions structure — do NOT
17+
hand-copy the list); maintain a table of minimal valid arguments per tool; for each,
18+
call `execute_tool(name, args)` and assert the result is a `Dict` NOT containing
19+
`"error"`. Maintain an explicit skip-list with a reason string for tools genuinely
20+
needing external services/files, and assert the skip-list stays small. For ≥5
21+
high-traffic tools (t_test, descriptive_stats, correlation, regression, mann_whitney)
22+
cross-check key numbers against the direct Julia function call (the anova pattern). **The
23+
test must FAIL if a new tool is registered without a table entry or skip reason** — that
24+
is the point. Wire into `runtests.jl`.
25+
26+
**(b) `.github/workflows/e2e.yml`:** switch to `Pkg.test(coverage=true)`; add a
27+
SHA-pinned `julia-actions/julia-processcoverage` step; compute total % and echo to
28+
`GITHUB_STEP_SUMMARY`; upload an lcov artifact. **INFORMATIONAL only** (no threshold
29+
gate yet). Match existing SHA-pinning style (every action pinned by full commit SHA +
30+
version comment).
31+
32+
**Sibling caution:** W1-1 adds `else`-error branches for unknown sub-types and clamps —
33+
do NOT assert unknown sub-types return `nothing`, and use argument values within clamp
34+
bounds.
35+
36+
## Acceptance criteria
37+
38+
- [ ] Every registered tool is exercised or explicitly skipped-with-reason (tested).
39+
- [ ] ≥5 tools cross-checked vs direct calls.
40+
- [ ] e2e.yml runs with coverage and reports a %; actions SHA-pinned.
41+
- [ ] Full suite green + new test.
42+
43+
## Local verification
44+
45+
`flock /tmp/statistikles-julia.lock -c 'cd <repo> && julia --project=. -e "using Pkg; Pkg.test()"'`
46+
(WSL login shell). YAML: actionlint if available, else careful review.
47+
48+
## Out of scope
49+
50+
Enforcing a coverage threshold (informational first); extending ground-truth reference
51+
values (W2-5).
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# W1-4 · Make the documented install path real
2+
3+
**Model:** impl=sonnet · verify=haiku · **Branch:** `fix/documented-install-path`
4+
5+
## Context
6+
7+
`QUICKSTART-USER.adoc` drives `just setup`/`just run`/`just deps`, but `setup` doesn't
8+
exist, `run` prints "Run not configured yet" (`Justfile:347-349`), `deps`/`build` are
9+
no-op echoes with TODOs. A new user following the documented quickstart cannot install
10+
or run. Only the raw README `julia --project=.` path works. Four quickstarts diverge;
11+
`docs/QUICKSTART.md` even clones a different repo.
12+
13+
## Requirements
14+
15+
**(a) Justfile** — real recipes wrapping the working commands:
16+
`setup: julia --project=. -e 'using Pkg; Pkg.instantiate()'`; `deps:` alias/dependency
17+
of setup; `build: julia --project=. -e 'using Pkg; Pkg.precompile()'`;
18+
`run: julia --project=. -e 'using Statistikles; main()'`. Keep the test recipe correct.
19+
If docs reference `stapeln-run`, either alias it to `run` or drop the reference — be
20+
consistent.
21+
22+
**(b) Quickstarts** — replace remaining template placeholder content in
23+
`QUICKSTART-USER.adoc` (token list ~line 4, "See README.adoc" body) with a real minimal
24+
quickstart matching the new recipes; correct expected-output claims (check what
25+
`src/Statistikles.jl` `main()` actually prints — don't promise unprinted strings).
26+
`QUICKSTART-DEV.adoc`: replace `{{BUILD_CMD}}`/`{{TEST_CMD}}`/`{{LANG_STACK}}` with real
27+
Julia commands; fix references to nonexistent `just setup-dev`/`panic-scan`/`flake.nix`/
28+
`tests/` (tests live in `test/`). `QUICKSTART-MAINTAINER.adoc`: trim references to
29+
missing recipes. `docs/QUICKSTART.md`: reconcile to statistikles or reduce to a pointer.
30+
**One canonical, tested command story.**
31+
32+
**(c) `.tool-versions`:** add `julia` pinned to the CI minor (check `e2e.yml` — 1.10.x).
33+
34+
**(d) `README.adoc`:** fix the module-count claim (says 17; count `src/stats/*.jl` and
35+
state the real number — audit found ~40).
36+
37+
**(e) NEW `.github/workflows/install-smoke.yml`:** ubuntu-latest; SHA-pinned
38+
`actions/checkout` + `julia-actions/setup-julia` (1.10) matching repo pinning style;
39+
install `just` (SHA-pinned action or apt); run `just setup && julia --project=. -e 'using Statistikles' && just --list`.
40+
NO job-level `hashFiles()`/`secrets` conditionals — step-level only.
41+
42+
## Acceptance criteria
43+
44+
- [ ] Every recipe referenced by any quickstart exists in the Justfile (grep-verified).
45+
- [ ] No `{{placeholders}}` remain in touched docs.
46+
- [ ] install-smoke.yml is schema-valid and SHA-pinned.
47+
- [ ] README module count matches reality.
48+
49+
## Local verification
50+
51+
In WSL: `sudo apt-get install -y just` (NOPASSWD sudo), then `just --list` in the repo
52+
parses; grep that every quickstart-referenced recipe exists. Optionally `just setup`
53+
under the Julia lock.
54+
55+
## Out of scope
56+
57+
Container/devcontainer install (W2-3); release (W2-1).

0 commit comments

Comments
 (0)