packaging: terminal distribution, portable monitor bundle and the acceptance matrix

packaging/package_terminal.py builds the self-contained terminal archive with its own
Python runtime, monitor_bundle.py the portable monitor, acceptance_matrix.py maps the 67
acceptance rows to evidence, README.md documents the target matrix. The built archive
(release/artifacts/*.tar.gz, 42 MB) is not in git.

From the owner's rebuild branch strata/rebuild-2026-10.
This commit is contained in:
Niko1221 committed 2026-10-15 10:24:09 +02:00
1 parent 48fc04a425
commit cef6027dcb
4 files changed
+1277

No files matched your search

+105
View File
@@ -0,0 +1,105 @@
# Strata packaging: targets, recipes and locks
Revision: Phase A. This directory holds the per-target build recipes and the machine-readable
manifests. **It describes what can be built where, and it is explicit about what has *not* been
built or tested.** Nothing here is a support claim.
## The target matrix
A backend name alone is not a target. Each row is an OS × CPU ABI × backend tuple, and a row is
`qualified` only after the acceptance checklist has actually run on it.
| Target tuple | App artifact | Engine package | Status on this Mac |
| --- | --- | --- | --- |
| `macos-arm64-metal` | `.app` + `.dmg` (Tauri) | source-built `strata` | **built.** App self-test 8/8 from inside the mounted DMG; engine built from source and 17/17 kernel-parity suites pass on the real M4 GPU. Real-model inference **not run** (no pack fits 16 GiB / 21 GiB free). |
| Terminal `darwin-arm64` | self-contained `.tar.gz` (42 MB) | any | **built and verified.** Runs from a moved directory in a clean environment with no usable system Python; serving, `status`, `timeskip` and `tune` all exercised on the bundled interpreter. |
| `windows-x86_64-cuda` | NSIS `.exe` (Tauri) | `setup.py` / `tools/windows` | **not built here.** Config present; needs a Windows host with an NVIDIA GPU. |
| `windows-x86_64-hip` | NSIS `.exe` (Tauri) | `tools/hip/build_windows.bat` | **not built here.** Needs Windows + AMD. |
| `linux-x86_64-cuda` | AppImage (Tauri) | `setup.py` | **not built here.** Needs a Linux host with an NVIDIA GPU. |
| `linux-x86_64-hip` | AppImage (Tauri) | `cmake -DSTRATA_ENABLE_HIP=ON` | **not built here.** Needs Linux + ROCm. |
| `linux-x86_64-sycl` | AppImage (Tauri) | `sycl/setup_intel.py` | **not built here.** Needs Linux + Intel Arc. Documented upstream as experimental. |
| `windows-x86_64-sycl` | — | — | **gated.** No native Windows SYCL path exists upstream; it needs a separate feasibility gate, not a recipe. |
| `macos-x86_64-*` | — | — | **unsupported.** The macOS engine is Apple-Silicon only (`metal/CMakeLists.txt` requires Apple Silicon for the Metal runtime). |
**Cross-compilation is not attempted for the desktop app.** Tauri bundles are per-OS and the
Windows installer must be produced on Windows. What *is* portable, and therefore tested here, is
everything below the carrier: the Python contracts, the owner transport, the checks, and the engine
build recipes.
## The private Node runtime
The desktop bundle ships a pinned standalone Node beside the App Host, because the packaging
requirement is that an end user supplies no runtime. It is **not committed**: it is a ~117 MB
platform binary, it cannot be reviewed in a diff, and it is an input rather than source. Fetch and
verify it instead:
```sh
apps/desktop/scripts/fetch-node.sh # host platform
apps/desktop/scripts/fetch-node.sh --target linux-x64
apps/desktop/scripts/fetch-node.sh --check # verify what is installed
```
The script refuses an archive whose SHA-256 does not match the digest recorded for that target. The
digests are taken from nodejs.org's own `SHASUMS256.txt` for the pinned version, so they are not
derived from the same response as the payload. **To bump the Node version: change
`STRATA_NODE_VERSION`, add the new digests, and re-run `--check`** — the script will refuse an
unknown target's archive rather than installing it unverified.
## Desktop app
```sh
cd apps/desktop
./scripts/fetch-node.sh # the sidecar (once)
npm install --prefix ../host && npm --prefix ../host run build # the UI
cargo tauri build --bundles app # -> Strata.app
# macOS DMG (headless: Tauri's default step needs a Finder session)
./scripts/make-dmg.sh
# Windows (on Windows): produces an NSIS per-user installer
cargo tauri build --bundles nsis
# Linux (on Linux): AppImage plus a deb
cargo tauri build --bundles appimage deb
```
Windows installer policy is set in `tauri.conf.json`: `installMode: currentUser`, so the installer
is **per-user and needs no elevation**. Uninstall retention (checklist P10) is a property of *where
the app keeps its data*, not of an installer flag: models, profiles and sessions live outside the
install directory (`~/.strata` and the platform state directories), so removing the application
cannot remove them. The packaging config was validated against this Tauri version's schema — an
earlier draft used `perMachine`/`oneClick`/`deleteAppDataOnUninstall`, which `tauri-utils` 2.10 does
not declare and which are rejected outright rather than ignored.
**The `dmg`/`app` targets were rebuilt and the packaged self-test re-run (8/8) after the Windows and
Linux targets were added**, so the added targets do not affect the macOS artifact.
## What every platform must produce before it can be called qualified
1. The app launches and the window serves the supplied UI from the host's origin.
2. The owner transport works: one owner per installation, a second client attaches, a stale owner
can be recovered, and no client can stop a process it does not own (R03/R04).
3. Real inference on a supported pack, with the engine build hash and the model hashes recorded.
4. `docs/work/QUALIFICATION-KIT.md` run end to end, with raw evidence retained.
## Honest gaps in this directory
* There is no CI. Every recipe here is written to be run by hand; making them automated is separate
work.
* No signing or notarization assets, and no update-feed bootstrap trust. Both need owner-supplied
credentials (work plan D04).
* No font or asset redistribution evidence has been gathered (D05). The app currently ships the
brand assets from the supplied UI ZIP, which is fine for an internal candidate and not sufficient
for redistribution.
* The terminal distribution **does** now bundle a private Python runtime and its dependencies
(42 MB, built by `package_terminal.py`), and was verified running from a moved directory in a
clean environment with no usable system Python. What remains is per-platform work: the current
archive is `darwin-arm64` only. `PYTHON_PIN` in that script needs the matching
python-build-standalone archive and digest for each target, and the launcher already prefers a
bundled runtime, so a Linux or Windows archive is a pin change plus a build on that OS.
* `.gitignore` coverage for build output was added late. `apps/desktop/src-tauri/target` and the
Node sidecar were briefly committed, which left ~324 MB in the git object store. The files are
untracked now and the ignore rules are in place, but the history is not rewritten: that is a
deliberate choice, since rewriting published history is worse than a large local repository. A
developer cloning this branch gets the current tree, and the sidecar arrives via
`fetch-node.sh`.
+514
View File
@@ -0,0 +1,514 @@
#!/usr/bin/env python3
"""Generate the acceptance evidence matrix.
`strata-acceptance-checklist.md` has 67 requirement rows, and Phase A is only complete when **every
one of them has an explicit result** — passed, failed, pending hardware, pending signing/release
evidence, or genuinely unsupported with a reason. This script holds that mapping as data so it can
be regenerated as the results change, and so a missing row is a build error rather than an oversight.
The statuses are deliberately conservative:
* ``passed`` means the check ran on this Mac and its evidence is retained;
* ``pending-hardware`` means the code exists and the check needs a machine this is not;
* ``pending-release`` means it needs signing, keys or rights evidence;
* ``unsupported`` means the combination is not implemented, with the reason;
* ``not-implemented`` means the work is not written yet.
A code defect is never ``pending-hardware``: if the implementation is wrong or absent, that is said
plainly, because deferring a defect to another machine is how defects become permanent.
Usage:
python packaging/acceptance_matrix.py [--out docs/work/ACCEPTANCE-EVIDENCE.md]
"""
from __future__ import annotations
import argparse
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
STATUS_ORDER = ("passed", "failed", "pending-hardware", "pending-release", "unsupported",
"not-implemented")
#: id -> (status, observation, evidence). Written as data so `--check` can prove the set is complete.
ROWS: dict[str, tuple[str, str, str]] = {
# ---------------------------------------------------------------- packaging and reuse
"P01": ("passed",
"The macOS .app builds, launches, serves the supplied UI from the host's origin and "
"passes its own 8-step self-test from inside the mounted DMG. No global Node, Python, "
"npm, pip or compiler is required: the bundle carries a pinned Node runtime. Real "
"generation through it is NOT proven -- no Strata pack fits this Mac (16 GiB RAM, "
"~21 GiB free); that leg is pending hardware.",
"docs/work/evidence/tauri-selftest-from-dmg.json, tauri-artifact.txt"),
"P02": ("pending-hardware",
"The NSIS per-user installer target and policy are configured and the config validates "
"against this Tauri version, but a Windows installer must be produced on Windows. Not "
"built here.",
"packaging/README.md"),
"P03": ("pending-hardware",
"The AppImage target is configured; an AppImage must be built on Linux. Not built here.",
"packaging/README.md"),
"P04": ("passed",
"The terminal distribution is self-contained: it carries a pinned standalone CPython "
"3.12.15 and its dependencies. Verified from a moved directory with a PATH whose only "
"Python is macOS's 3.9, which the launcher correctly rejects as below the 3.10 floor. "
"Serving, status, timeskip and tune all ran on the bundled interpreter. Only the "
"darwin-arm64 archive is built; Linux/Windows archives need a pin plus a build there.",
"docs/work/evidence/terminal-dist.txt"),
"P05": ("unsupported",
"No Metal/CUDA/SYCL/HIP target row is delivered as a package yet. One engine is built "
"and qualified as a *source build* on this Mac (macos-arm64-metal); the other families "
"have recipes only. No blanket cross-platform claim is made.",
"packaging/README.md"),
"P06": ("passed",
"A component's cache key is the digest of its artifact bytes, not its version label, so a rebuilt artifact with an unchanged version is a different entry and a cached payload is never reused on the strength of a string. The cache is never consumed by activation (the active directory is a copy), so a compatible application update reuses the installed engine instead of downloading it again. `prune_cache` keeps whatever the active and rollback versions refer to."
,
"strata_control/components.py, tests/tuning/test_components.py"),
"P07": ("passed",
"Components are acquired by kind: the manifest declares `application`, `runtime`, `agent-runtime`, `tuning`, `engine` or `prepared-data`, each with its own platform and backend gates, and `strata components plan` reports whether a package may be used here without fetching anything. The backend families are held to the packet's own matrix, so a Windows SYCL or Intel-Mac Metal package is refused as unsupported rather than attempted."
,
"strata_control/components.py, `strata components backends`"),
"P08": ("passed",
"Desktop and Terminal read the same owner state and the same model directory; the "
"terminal's `models list` reads the setup-written configs that Desktop's model surfaces "
"use. Imported files are never copied (import is preview-only by design).",
"docs/work/evidence/serve-suite.txt, terminal-suite.txt"),
"P09": ("not-implemented",
"No offline bundle format exists yet. The engine build and model preparation are "
"documented as commands, not as a signed bundle.",
"docs/work/QUALIFICATION-KIT.md 3.2"),
"P10": ("pending-hardware",
"Uninstall retention is designed in (data lives outside the install directory, and the "
"Windows installer does not delete app data), but uninstall/reinstall cannot be "
"exercised without an installed Windows or a signed macOS build.",
"packaging/README.md"),
"P11": ("passed",
"Every artifact is verified against its manifest SHA-256 and declared size before anything is unpacked, and unpacking validates every member first, so a refusal writes nothing. Refused: parent segments (including backslash and nested forms), absolute paths, Windows drive letters, UNC paths, NUL bytes, symlinks and hard links, device and FIFO members, an artifact name that is a path, and any member whose resolved destination leaves the target. Setuid/setgid bits from an archive are never trusted. Each refusal is asserted to leave the destination empty."
,
"tests/tuning/test_components.py (TestSafeExtraction)"),
"P12": ("passed",
"An interrupted transfer keeps its partial bytes for a resume; a range request that the server ignores restarts rather than appending to a prefix; a partial larger than the declared size is discarded as stale; a completed download that is short is an error with the bytes retained; and nothing is promoted to the cache until its digest has been verified, with the `.complete` marker written last so an interrupted install is never mistaken for a finished one."
,
"tests/tuning/test_components.py (TestInterruptedTransfer)"),
"P13": ("passed",
"Activation moves the displaced version aside rather than deleting it, and the rollback record is written *before* the activation so a crash between the two cannot leave it pointing at the new version. `rollback` restores the previous version exactly and refuses when there is nothing to restore, so 'rollback succeeded' and 'the component is gone' are never the same outcome. An incompatible component is refused before anything is written or activated."
,
"tests/tuning/test_components.py (TestActivation)"),
# ---------------------------------------------------------------- runtime, models and control
"R01": ("passed",
"Serving with installed inputs invokes no downloader, package manager, git or compiler: "
"the server's import graph is stdlib plus relative modules, and the server starts and "
"answers from the bundled runtime with no network access attempted.",
"docs/work/evidence/terminal-dist.txt"),
"R02": ("passed",
"Pure serving and the read-only CLI run with no agent runtime, no tuning packages, no "
"Textual and no UI present. Verified by running the server from the standalone bundle "
"and by importing strata_control.management and strata_cli.cli on a bare interpreter.",
"docs/work/evidence/terminal-dist.txt"),
"R03": ("passed",
"One owner per installation is enforced by an O_EXCL state file plus an flock held for "
"the host's lifetime; a second client attaches over the private channel; a stale owner "
"is only recovered after the socket is proven dead. Fifteen transport tests cover it.",
"docs/work/evidence/owner-transport-suite.txt"),
"R04": ("unsupported",
"**The Windows owner transport is not a named pipe with an ACL.** The Python standard "
"library cannot create one, so Windows uses a loopback-only TCP listener whose "
"authority rests on a 256-bit control token plus a private state directory. That is "
"weaker than the checklist's stated condition and is recorded as such. Console stop and "
"process-tree cleanup for engine children ARE implemented (serve/winjob.py, tested). "
"The Windows branch of the transport is exercised on macOS but has never run on "
"Windows.",
"strata_control/owner_transport.py, docs/work/evidence/owner-transport-suite.txt"),
"R05": ("passed",
"Preflight refuses before load: the engine rejects a model whose metadata geometry "
"differs from the compiled shape, naming the differing key; the GGUF preflight checks "
"shard naming, magic, version and tokenizer identity; the tuning plan refuses a "
"dimension whose flag the engine does not accept.",
"docs/work/evidence/tuning-suite.txt, docs/work/IMPLEMENTATION_STATUS.md 1.12"),
"R06": ("pending-hardware",
"The catalog refuses unknown selections and reports a reason, and the plan refuses "
"unsupported dimensions. Whether Qwen3.6 runs at all on this backend is answerable only "
"with the model present: the engine documents that Qwen3.6 does not run on the Mac "
"engine, and NVFP4 is NVIDIA-only.",
"docs/MAC.md, docs/work/evidence/tuning-plan.txt"),
"R07": ("pending-hardware",
"The reviewed downloader is resumable and keeps verified bytes, and import is "
"preview-only so it never moves files. The cancel/resume behaviour cannot be exercised "
"without a model large enough to interrupt, which does not fit here.",
"docs/work/QUALIFICATION-KIT.md 3.2"),
"R08": ("passed",
"Saving settings preserves unknown fields and masks secrets (the reviewed NATIVE_FIELDS "
"table with `••••••••` round-tripping), writes atomically via a temp file and rename, "
"and reports restartNeeded rather than applying silently. The tuning job refuses to "
"touch the active profile at all.",
"docs/work/evidence/serve-suite.txt, docs/work/IMPLEMENTATION_STATUS.md 1.12"),
"R09": ("passed",
"Readiness requires the engine's own READY line: max_context stays 0 until it arrives, "
"so a pre-READY 'ready' is impossible, and a timeout is reported as uncertain rather "
"than as success. Verified against a stub engine.",
"docs/work/evidence/tuning-suite.txt"),
"R10": ("pending-hardware",
"Stop and unload handle active and queued work explicitly in the reviewed server, and "
"the ten-cycle orphan check is an executable procedure. It cannot be run without a "
"loadable model.",
"docs/work/QUALIFICATION-KIT.md 5"),
"R11": ("passed",
"All three dialects are served and their documented behaviour is covered by the "
"reviewed server suite: 801 tests pass, including streams, tool fragments, telemetry "
"and error shapes.",
"docs/work/evidence/serve-suite.txt"),
"R12": ("passed",
"The agent bridge cancels per session and the host owns request cancellation for "
"serving; the queue releases only the affected slot. The end-to-end cancellation *turn* "
"assertion is inconclusive against the instant mock engine and is reported as such "
"rather than as a pass.",
"docs/work/evidence/serve-suite.txt, docs/work/IMPLEMENTATION_STATUS.md 1.8"),
"R13": ("pending-hardware",
"Group allocation, reservations and readiness-before-routing are reviewed features of "
"the server, and multi-device behaviour cannot be exercised on a single-GPU machine.",
"docs/work/QUALIFICATION-KIT.md 6"),
"R14": ("pending-hardware",
"Vision and image-generation paths exist in the reviewed server and refuse when "
"unavailable; neither can be exercised without a real model and its mmproj.",
"docs/work/QUALIFICATION-KIT.md 6"),
# ---------------------------------------------------------------- agent, session, permissions
"A01": ("passed",
"The supplied UI drives a turn through the pinned Harness against the local Strata API "
"worker on loopback, with no cloud route declared in the profile and therefore no "
"DeepSeek account required. The model is the built-in mock: this proves the route, not "
"inference quality.",
"docs/work/evidence/bridge-e2e.txt, integrations/harness/profile/cordis.patch.yml"),
"A02": ("passed",
"Framing is verified at the byte level: a multi-byte character split across reads "
"survives intact with no replacement character, an SSE line split mid-JSON is "
"reassembled, a stream ending without a trailing newline still yields its last delta, a "
"malformed frame is skipped without losing its neighbours, and an error frame reaches "
"the caller in the provider's own words. Content, reasoning, usage, finish and error "
"are all carried.",
"apps/host/tests/stream-framing.test.mjs"),
"A05": ("passed",
"Verified end to end against a live turn. The scripted worker runs with a token delay so the turn is still running when Stop is sent, and the test waits for turn/start and fails if the turn was not live -- without that it passed trivially on the first attempt, because the scripted turn finished in milliseconds and Stop arrived after turn/end. Stop is acknowledged as `{accepted: true}`, which is admission only; the turn then settles as `aborted` in the durable record, which is what actually settles it, and the tool does not run. Also asserts the marker the tool would have written does not exist, so a turn that reported cancellation while letting the tool finish would fail.",
"docs/work/evidence/tool-flow.txt, integrations/harness/tool_flow_e2e.py"),
"A06": ("passed",
"Sessions are durable in the Harness format, and resume re-reads them rather than "
"replaying effects: the interrupted-turn repair synthesises closers instead of "
"re-running tools. Verified by reading the durable session file across its concatenated "
"frames.",
"integrations/harness/bridge_e2e.py, strconv note in docs/work/IMPLEMENTATION_STATUS.md 1.8"),
"A07": ("passed",
"The same durable session is reachable from the App Host and from a CLI-started Harness "
"with the same DSH_HOME, sharing history, model identity and workspace. The bridge "
"reads the identical file the UI's host would.",
"docs/work/evidence/bridge-e2e.txt"),
"A08": ("pending-hardware",
"Two sessions run independently in the bridge (each gets its own session), but the "
"cross-session isolation assertions (no shared tool results, no credential leakage, no "
"shared-runtime kill) need a real model and a tool that runs.",
"docs/work/QUALIFICATION-KIT.md 7"),
"A09": ("pending-hardware",
"Subagents are a reviewed Harness feature with real depth and concurrency bounds; the "
"UI already renders child-agent frames. Nothing has been observed end to end, and the "
"supplied UI's agent events are not emitted by the host yet.",
"docs/work/QUALIFICATION-KIT.md 7"),
"A10": ("pending-hardware",
"Bounded goals are a reviewed Harness feature (maxGoalRounds, round-limit blocking). Not "
"exercised in this integration.",
"docs/work/QUALIFICATION-KIT.md 7"),
"A11": ("pending-hardware",
"Compaction and long-context handling are reviewed Harness features. The route declares "
"the real local window (32K) rather than inheriting a cloud default, and refuses a "
"request that does not fit rather than truncating silently -- observed. The "
"compaction-preserves-tool-pairing assertion needs a real model.",
"docs/work/IMPLEMENTATION_STATUS.md 1.8"),
"A12": ("passed",
"The reviewed task service validates schedules with DST-correct nextRun, prevents "
"overlapping runs, and has no catch-up burst or duplicate scheduler. Covered by the "
"reviewed test suite and by route tests.",
"docs/work/evidence/serve-suite.txt"),
"A13": ("pending-hardware",
"Skills resolve against the reviewed workspace/policy roots. That prompts and inference "
"keys cannot invoke installer or owner actions needs an adversarial test against a "
"running agent, which needs a real model.",
"docs/work/QUALIFICATION-KIT.md 7"),
"A14": ("passed",
"The Strata profile declares exactly one route, on loopback, and no cloud route at all, "
"so a local failure cannot fall back to a hosted model -- there is nothing to fall back "
"to. The host additionally refuses to simulate in a packaged build.",
"integrations/harness/profile/cordis.patch.yml, docs/work/IMPLEMENTATION_STATUS.md 1.7"),
"A15": ("passed",
"Conversations live in the browser store by design, and browser-origin export/import is "
"idempotent with attachments and preferences retained; historical tool records are data "
"and are never executed. Covered by the reviewed UI suite.",
"docs/work/evidence/serve-suite.txt"),
"A03": ("passed",
"Verified end to end, and only after four defects were cleared. A scripted Strata worker "
"emits a bash tool call; the test's own peer opens the gateway's `$events` stream on "
"`/api/remote.mux` and grants the escalation with `allowed-once`. Asserted: the turn "
"completes, the tool ran exactly once (`marker_lines == 1`, so a replayed effect would "
"fail), an approval was both asked and answered with the recorded outcome `allowed-once`, "
"and the worker recorded two requests while executing nothing itself -- so the tool ran in "
"the Harness and the transcript, not the tool, went to the API. The defects cleared to get "
"here: the session-title request consumed the scripted tool call; the call was missing a "
"required `description`; the launch-URL `interactions` parameter is decorative and the "
"default is answerable; and a workspace-write command is not gated, so the call now "
"requests a sandbox escalation, which is the only path that consults the approval gate.",
"docs/work/evidence/tool-flow.txt, integrations/harness/tool_flow_e2e.py"),
"A04": ("passed",
"Verified with the denial observed, not assumed. Same scripted call as A03, but the peer "
"answers `next` (do not decide, delegate), which is this vocabulary\'s way of saying no "
"answerer is available. Asserted: the escalation reached the gate (`approval/asked` "
"present), every recorded decision is the fail-closed `unavailable`, zero grants were "
"recorded, the turn settled `completed`, and the marker does not exist. Each assertion "
"fails for a different way the flow could otherwise pass while proving nothing -- the tool "
"never reaching the gate, the gate granting, or the turn hanging with the request left "
"pending. An earlier version of this row was marked passed and has been withdrawn: in that "
"run the approval was never consulted at all, the tool failed argument validation for an "
"unrelated reason, and the mechanism it relied on does not exist.",
"docs/work/evidence/tool-flow.txt, integrations/harness/tool_flow_e2e.py"),
# ---------------------------------------------------------------- time-skip and tuning
"S01": ("passed",
"Real baseline and candidate directories with content-addressed checkpoints replace the "
"timer entirely. Every checkpoint and decision persists by SHA-256 digest of its file "
"inventory, not by a label.",
"tests/time_skip/test_time_skip.py, docs/work/evidence/timeskip-harness.txt"),
"S02": ("passed",
"A previously passing deterministic check that regresses rejects the candidate and the "
"last good checkpoint stays selected; verified end to end, where the attempt was "
"rejected and the baseline retained.",
"docs/work/evidence/timeskip-harness.txt"),
"S03": ("passed",
"An unevaluable check is `unknown` and never a pass; a run whose checks cannot justify a "
"change ends `inconclusive` with `needsUserReview`, and a model's opinion is declared "
"advice and cannot gate.",
"tests/time_skip/test_time_skip.py"),
"S04": ("passed",
"Open/Compare/Keep/Restore operate on the actual retained checkpoint; applying previews, "
"keeps a rollback copy, refuses with 409 when the destination changed, and restores.",
"docs/work/evidence/timeskip-http.txt"),
"S05": ("passed",
"Cancellation and failure retain the last complete checkpoint, attempts are bounded, and "
"no revision can touch the workspace, so there is no tool replay or external side "
"effect from a rejected attempt.",
"tests/time_skip/test_time_skip.py"),
"T01": ("passed",
"Tuning is optional and pinned: it lives in a separate module, the CLI imports it only "
"for its own commands, and serving, status and monitor run without it. Nothing is "
"installed by the tuning path.",
"docs/work/evidence/terminal-dist.txt"),
"T02": ("passed",
"The sweep uses only flags the selected engine binary accepts, holds an exclusive "
"device lease, fixes the workload (greedy, seed 1) for every candidate, and never "
"writes the active profile.",
"docs/work/evidence/tuning-sweep.txt"),
"T03": ("passed",
"Boot failure, timeout, no-tokens and correctness failure are each recorded with a "
"status and excluded from selection; the lease is released even when the job fails; "
"cancellation stops new trials and keeps the evidence collected.",
"docs/work/evidence/tuning-suite.txt"),
"T04": ("passed",
"Two measurement modes, labelled so neither can stand in for the other. `protocol` reads "
"the engine's own serve protocol; `api` starts the real worker from the candidate's "
"configuration and measures a streamed chat completion over HTTP -- the full path a user "
"waits for -- with a required expectation so a fast wrong answer cannot win. Both record "
"peak resident memory of the process tree, so a speed win bought by spilling is visible. "
"Repeats, a paired baseline re-measurement and drift grading are unchanged. 11 tests "
"cover the API mode, including that a reasoning-only stream, a missing usage block and a "
"non-matching answer are refused rather than scored. Runs against the mock worker are "
"labelled as harness tests, never as performance results.",
"strata_control/tune_api.py, tests/tuning/test_tune_api.py"),
"T05": ("passed",
"Benchmark-only levers are measured but stripped from the exported args, and "
"verify_exported() re-measures the configuration as it would actually be written; a "
"configuration whose advantage came from a stripped lever is withdrawn.",
"tests/tuning/test_tuning.py"),
"T06": ("passed",
"Identity is a content digest, so a re-quantised model with the same name is a different "
"model. The plan records the engine digest, the model digest, the digest method, the "
"backend, the workload, the context and the machine; a completed sweep records itself "
"and its report states how many earlier sweeps are comparable and, for each that is "
"not, the reason in plain words. An unknown identity is never treated as a match, and "
"incomparable history is retained rather than deleted. Twenty-one tests cover each "
"element of the key separately.",
"tests/tuning/test_tune_history.py, strata_control/tune_history.py"),
"T07": ("passed",
"`strata tune apply` revalidates the report hash, re-checks the sweep's artifact identity "
"against what is installed, refuses an incomplete sweep, a no-change result, material "
"drift or a report edited after review, snapshots the previous bytes, and writes "
"atomically; `strata tune rollback` restores those exact bytes and refuses if the "
"configuration was edited since. Nothing applies automatically. The one gap is stated by "
"the command itself: the candidate start is not verified against a running model because "
"no supported model fits this Mac, so `--verify` is wired and tested but the CLI passes "
"None and says so. 31 tests, and a refused apply is asserted to leave the file "
"byte-identical.",
"tests/tuning/test_tune_apply.py, docs/work/evidence/tuning-apply.txt"),
"T08": ("unsupported",
"No real tuning qualification exists for any backend, because no supported model fits "
"this Mac. Stub-engine results are labelled as stub results everywhere they appear and "
"are never presented as a backend qualification.",
"docs/work/evidence/tuning-sweep.txt"),
# ---------------------------------------------------------------- interface, security, evidence
"U01": ("passed",
"The supplied UI is used unmodified: 54 components, 10 screens, 8 locales, the design "
"kit's tokens and motion. The tuning panel added this round uses the kit's own "
"primitives and tokens rather than introducing a parallel style.",
"docs/work/evidence/serve-suite.txt, apps/host/src/components/TunePanel.tsx"),
"U02": ("pending-hardware",
"Keyboard and focus behaviour, reduced motion, long translations and OS file/font "
"permission flows are designed in and the UI suite covers parts of them. Verifying them "
"in a *packaged* Desktop on multiple OS versions needs those hosts.",
"docs/work/evidence/tauri-selftest.json"),
"U03": ("passed",
"The terminal's six views render and the operator flows work at full width; the "
"reviewed monitor suite covers compact fallbacks and the terminal is restored on exit. "
"Verified against a live worker.",
"docs/work/evidence/terminal-suite.txt, serve-suite.txt"),
"U04": ("passed",
"The monitor attaches over HTTP read-only, with an allow-list of exactly four "
"read-only endpoints, refuses redirects, caps the body and never starts or stops "
"anything; detaching leaves work alive.",
"docs/work/evidence/terminal-suite.txt"),
"U05": ("passed",
"Telemetry carries a sample age and a source; a missing or stale reading renders as a "
"dash rather than as zero, unified memory is counted once, and the packaged build "
"refuses to fabricate readings at all.",
"docs/work/evidence/serve-suite.txt, docs/work/IMPLEMENTATION_STATUS.md 1.7"),
"U06": ("passed",
"Rendered artifacts cannot reach renderer or owner privileges: the window is confined "
"to the host origin, navigation elsewhere is handed to the system browser, permissions "
"are denied by default, and the renderer runs sandboxed with context isolation and no "
"Node integration.",
"apps/desktop/src-tauri/src/main.rs, docs/work/evidence/tauri-selftest.json"),
"U07": ("passed",
"A non-loopback bind requires an API key (the reviewed server enforces it and the test "
"suite covers the refusal); management stays on the private channel; secrets are masked "
"in projections and never written to logs or URLs.",
"docs/work/evidence/serve-suite.txt"),
"U08": ("passed",
"The two claims are separated and tested separately, because a passing runtime-only test says nothing about the strict one. `runtime_only_no_download` is that the server fetches nothing while serving; `strict_offline` is that nothing in the process tree reaches the network. Strict offline is enforced through the environment every child receives (HF_HUB_OFFLINE, TRANSFORMERS_OFFLINE, PIP_NO_INDEX, DO_NOT_TRACK, emptied proxy variables and NO_PROXY=*), applied last so a profile cannot remove it by naming the same variable; through one `check_url` predicate for our own fetches, which allows local schemes and loopback because talking to this machine is not a download; and through a preflight that refuses a profile declaring MCP servers, a before_load command or remote image sources, naming which. Verified against a real child process. A test asserts every child_env call site passes the policy -- which caught a real leak in the engine's own environment. `limits()` states what is not enforced: a child that opens a socket directly is not stopped, because that needs an OS sandbox this product does not have on all three platforms.",
"tests/tuning/test_offline.py, docs/work/evidence/offline.txt"),
"U09": ("not-implemented",
"Connector operations exist in the reviewed host (Cloudflare tunnel, LAN share) but the "
"explicit-only and never-widen-on-failure assertions are not tested.",
"apps/host/server/cloudflare.mjs, local-share.mjs"),
"U10": ("pending-hardware",
"A thirty-minute real soak needs a running model. The reviewed suite has shorter "
"monitor soak evidence from the terminal handover, which is not a substitute.",
"docs/work/QUALIFICATION-KIT.md 5"),
"U11": ("pending-release",
"No signing or notarization, no SBOM, no notices bundle and no font/asset "
"redistribution evidence. Signing credentials and rights evidence must come from the "
"owner; the artifacts produced here are explicitly labelled unsigned internal "
"candidates.",
"packaging/README.md, docs/work/evidence/tauri-artifact.txt"),
"U12": ("passed",
"A packaged build has no success path that uses demo responses or demo metrics: the "
"host refuses to simulate when STRATA_PACKAGED is set and returns a typed error "
"instead, the monitor reports `none` rather than invented readings, and this matrix "
"records every failure, skip and remaining item.",
"docs/work/IMPLEMENTATION_STATUS.md 1.7, this file"),
}
def main() -> int:
ap = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--out", default="docs/work/ACCEPTANCE-EVIDENCE.md")
ap.add_argument("--check", action="store_true",
help="verify that every checklist row has a result, and exit non-zero if not")
args = ap.parse_args()
expected = _checklist_ids()
missing = sorted(expected - set(ROWS))
extra = sorted(set(ROWS) - expected)
if missing or extra:
print(f"the mapping does not cover the checklist: missing={missing} extra={extra}",
file=sys.stderr)
return 1
if args.check:
print(f"all {len(expected)} requirement rows have an explicit result")
return 0
revision = subprocess.run(["git", "rev-parse", "HEAD"], cwd=ROOT, capture_output=True,
text=True).stdout.strip() or "unknown"
counts = {status: sum(1 for s, _, _ in ROWS.values() if s == status) for status in STATUS_ORDER}
out = [
"# Acceptance evidence matrix",
"",
f"Generated by `packaging/acceptance_matrix.py` at "
f"{datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')} for revision `{revision}`.",
"",
"Every row of `strata-acceptance-checklist.md` appears exactly once, with an explicit "
"result. `--check` fails if a row is missing, so a gap is a build error rather than an "
"oversight.",
"",
"| Result | Count |",
"| --- | --- |",
]
out += [f"| {status} | {counts[status]} |" for status in STATUS_ORDER]
out += [f"| **total** | **{len(ROWS)}** |", ""]
by_section = {
"Packaging and reuse": [f"P{i:02d}" for i in range(1, 14)],
"Runtime, models and control": [f"R{i:02d}" for i in range(1, 15)],
"Agent, session and permissions": [f"A{i:02d}" for i in range(1, 16)],
"Time-skip and tuning": [f"S{i:02d}" for i in range(1, 6)] + [f"T{i:02d}" for i in range(1, 9)],
"Interface, security and evidence": [f"U{i:02d}" for i in range(1, 13)],
}
for section, ids in by_section.items():
out += [f"## {section}", "", "| ID | Result | Observation | Evidence |", "| --- | --- | --- | --- |"]
for rid in ids:
status, observation, evidence = ROWS[rid]
out.append(f"| {rid} | **{status}** | {observation} | `{evidence}` |")
out.append("")
out += [
"## Reading this table",
"",
"* `passed` means the check ran on this Mac and its evidence is retained. It does not mean "
"the feature works on a machine this is not.",
"* `failed` is an implementation gap. It is never recorded as `pending-hardware`: deferring "
"a defect to another machine is how defects become permanent.",
"* `unsupported` names a combination that is not implemented, with the reason.",
"* Nothing here is a support claim. The macOS candidate is an **unsigned internal "
"development candidate** and is **real-model validation pending**.",
"",
]
path = ROOT / args.out
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("\n".join(out), encoding="utf-8")
print(f"wrote {path}")
for status in STATUS_ORDER:
print(f" {status:<18} {counts[status]}")
return 0
def _checklist_ids() -> set[str]:
"""The requirement IDs the handoff's checklist actually contains."""
checklist = Path("/Users/nikoveit/Documents/deepseek-harness/ENDING PLAN/"
"strata-acceptance-checklist.md")
if not checklist.exists():
# Resolve the containing directory of the handoff if it has moved, per the packet's own rule.
for candidate in Path("/Users/nikoveit/Documents/deepseek-harness").rglob(
"strata-acceptance-checklist.md"):
checklist = candidate
break
if not checklist.exists():
raise SystemExit("cannot find strata-acceptance-checklist.md")
import re
return set(re.findall(r"^\|\s*([A-Z][0-9]{2})\s*\|", checklist.read_text(encoding="utf-8"),
re.MULTILINE))
if __name__ == "__main__":
sys.exit(main())
+386
View File
@@ -0,0 +1,386 @@
#!/usr/bin/env python3
"""Build and manage the thin, read-only Strata monitor zipapp.
The bundle contains no engine, model, installer or terminal framework. One-shot
JSON status uses stdlib only; interactive mode needs the explicit Textual add-on.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import shutil
import stat
import tempfile
import zipapp
import zipfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SOURCES_V2 = (
"strata_cli/__init__.py",
"strata_cli/client.py",
"strata_cli/connections.py",
"strata_cli/render.py",
"strata_cli/monitor.py",
"strata_cli/monitor_commands.py",
"strata_cli/logs.py",
)
SOURCES_V3 = (*SOURCES_V2, "strata_cli/dashboard.py")
SOURCES = (*SOURCES_V3, "strata_cli/detail_dashboard.py")
BOOTSTRAP = b"from strata_cli.monitor_commands import main\nraise SystemExit(main())\n"
MEMBERS_V1 = ("__main__.py", *SOURCES_V2)
LICENSE_NOTICE = "LICENSE"
MEMBERS_V2 = (*MEMBERS_V1, LICENSE_NOTICE)
MEMBERS_V3 = (*MEMBERS_V2, "strata_cli/dashboard.py")
MEMBERS = (*MEMBERS_V3, "strata_cli/detail_dashboard.py")
COMPATIBILITY = {"python_min": "3.10", "telemetry_schema": [1]}
MAX_BUNDLE_BYTES = 1024 * 1024
MAX_MANIFEST_BYTES = 64 * 1024
MAX_MEMBER_BYTES = 256 * 1024
def digest(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def manifest_path(bundle: Path) -> Path:
return bundle.with_name(bundle.name + ".manifest.json")
def build(bundle: Path) -> dict:
if bundle.exists() or manifest_path(bundle).exists():
raise FileExistsError("bundle or manifest already exists; choose a new output path")
bundle.parent.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(prefix=".strata-monitor-build-", dir=bundle.parent) as directory:
stage = Path(directory) / "source"
stage.mkdir()
(stage / "__main__.py").write_bytes(BOOTSTRAP)
for name in SOURCES:
target = stage / name
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(ROOT / name, target)
shutil.copyfile(ROOT / LICENSE_NOTICE, stage / LICENSE_NOTICE)
temp_bundle = Path(directory) / bundle.name
zipapp.create_archive(stage, target=temp_bundle, interpreter="/usr/bin/env python3",
compressed=True)
with zipfile.ZipFile(temp_bundle) as archive:
names = archive.namelist()
if set(names) != set((*MEMBERS, "strata_cli/")) or len(names) != len(MEMBERS) + 1:
raise ValueError("bundle member list changed during build")
files = {name: digest(archive.read(name)) for name in MEMBERS}
report = {"schema_version": 4, "kind": "strata-monitor-bundle",
"bundle": bundle.name, "sha256": digest(temp_bundle.read_bytes()),
"files": files, "interactive_dependency": "textual==8.2.8",
"compatibility": COMPATIBILITY, "license": "MIT", "license_file": LICENSE_NOTICE,
"publisher_signature": None}
temp_manifest = Path(directory) / manifest_path(bundle).name
temp_manifest.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n")
os.replace(temp_bundle, bundle)
try:
os.replace(temp_manifest, manifest_path(bundle))
except OSError:
bundle.unlink(missing_ok=True)
raise
return report
def verify(bundle: Path) -> dict:
for path, limit in ((bundle, MAX_BUNDLE_BYTES), (manifest_path(bundle), MAX_MANIFEST_BYTES)):
if not stat.S_ISREG(path.lstat().st_mode) or path.stat().st_size > limit:
raise ValueError("bundle and manifest must be bounded regular files")
report = json.loads(manifest_path(bundle).read_text())
if (not isinstance(report, dict) or type(report.get("schema_version")) is not int or
report["schema_version"] not in (1, 2, 3, 4) or
report.get("kind") != "strata-monitor-bundle" or
report.get("publisher_signature") is not None or
report.get("bundle") != bundle.name or report.get("sha256") != digest(bundle.read_bytes())):
raise ValueError("bundle manifest or SHA-256 mismatch")
members = {1: MEMBERS_V1, 2: MEMBERS_V2, 3: MEMBERS_V3,
4: MEMBERS}[report["schema_version"]]
if report["schema_version"] >= 2:
if (report.get("compatibility") != COMPATIBILITY or report.get("license") != "MIT" or
report.get("license_file") != LICENSE_NOTICE):
raise ValueError("bundle compatibility or license notice mismatch")
with zipfile.ZipFile(bundle) as archive:
names = archive.namelist()
if len(names) != len(members) + 1 or set(names) != set((*members, "strata_cli/")):
raise ValueError("bundle contains missing, duplicate or unexpected files")
if any(info.flag_bits & 1 for info in archive.infolist()):
raise ValueError("encrypted bundle members are unsupported")
if any(info.file_size > MAX_MEMBER_BYTES for info in archive.infolist()):
raise ValueError("bundle member exceeds the size limit")
if archive.testzip() is not None:
raise ValueError("bundle CRC failure")
files = {name: digest(archive.read(name)) for name in members}
if report["schema_version"] >= 2 and not archive.read(LICENSE_NOTICE).startswith(b"MIT License\n"):
raise ValueError("bundle license notice is invalid")
if report.get("files") != files:
raise ValueError("bundle member checksum mismatch")
return report
def _target(target: Path) -> Path:
target = target.expanduser()
if not target.is_absolute() or target.name in ("", ".", ".."):
raise ValueError("install target must be an absolute new directory")
parent = target.parent
parent_info = parent.lstat()
if not stat.S_ISDIR(parent_info.st_mode) or \
(os.name == "posix" and (parent_info.st_uid != os.getuid() or parent_info.st_mode & 0o022)):
raise ValueError("install target parent must be an existing owner-controlled directory")
return target
def _present(path: Path) -> bool:
return path.exists() or path.is_symlink()
def _previous(target: Path) -> Path:
return target.with_name(target.name + ".previous")
def _installed(target: Path) -> tuple[Path, dict]:
if not stat.S_ISDIR(target.lstat().st_mode):
raise ValueError("install target must be a regular directory")
candidates = [path for path in target.iterdir() if path.name.endswith(".pyz")]
if len(candidates) != 1:
raise ValueError("install target has no unique monitor bundle; inspect it manually")
bundle = candidates[0]
expected = {bundle.name, manifest_path(bundle).name}
if {path.name for path in target.iterdir()} != expected:
raise ValueError("install target has unexpected or missing files; inspect it manually")
return bundle, verify(bundle)
def _stage_copy(bundle: Path, report: dict, target: Path) -> Path:
stage = Path(tempfile.mkdtemp(prefix=".strata-monitor-install-", dir=target.parent))
try:
staged_bundle = stage / bundle.name
shutil.copyfile(bundle, staged_bundle, follow_symlinks=False)
shutil.copyfile(manifest_path(bundle), manifest_path(staged_bundle), follow_symlinks=False)
os.chmod(staged_bundle, 0o600)
os.chmod(manifest_path(staged_bundle), 0o600)
if verify(staged_bundle)["sha256"] != report["sha256"]:
raise ValueError("bundle changed during copy")
return stage
except Exception:
shutil.rmtree(stage)
raise
def _remove_install(bundle: Path) -> None:
manifest_path(bundle).unlink()
bundle.unlink()
bundle.parent.rmdir()
def install(bundle: Path, target: Path, *, plan: bool = False,
if_candidate_sha256: str | None = None) -> dict:
"""Copy a verified client into a new directory without touching the server."""
report = verify(bundle)
target = _target(target)
if _present(target) or _present(_previous(target)):
raise FileExistsError("install target already exists; choose a new directory")
result = {"target": str(target), "sha256": report["sha256"],
"bundle": bundle.name, "would_install": plan}
if plan:
return result
if if_candidate_sha256 != report["sha256"]:
raise ValueError("monitor install digest changed or is missing; preview again")
stage = _stage_copy(bundle, report, target)
try:
if _present(target) or _present(_previous(target)):
raise FileExistsError("install target appeared during copy; inspect it manually")
os.rename(stage, target)
finally:
if stage.exists():
shutil.rmtree(stage)
return {**result, "installed": True, "would_install": False}
def update(bundle: Path, target: Path, *, plan: bool = False,
if_current_sha256: str | None = None, if_candidate_sha256: str | None = None) -> dict:
"""Promote a verified candidate and retain one verified rollback copy."""
target = _target(target)
current_bundle, current = _installed(target)
candidate = verify(bundle)
previous = _previous(target)
if _present(previous):
raise FileExistsError("previous monitor version exists; rollback or commit it before another update")
changed = candidate["sha256"] != current["sha256"]
result = {"target": str(target), "previous": str(previous),
"current_sha256": current["sha256"], "candidate_sha256": candidate["sha256"],
"current_bundle": current_bundle.name, "candidate_bundle": bundle.name,
"would_update": plan and changed}
if plan:
return result
if if_current_sha256 != current["sha256"] or if_candidate_sha256 != candidate["sha256"]:
raise ValueError("monitor update digests changed or are missing; preview again")
if not changed:
return result
stage = _stage_copy(bundle, candidate, target)
try:
if _present(previous):
raise FileExistsError("previous monitor version appeared during copy")
if _installed(target)[1]["sha256"] != current["sha256"]:
raise ValueError("active monitor changed during copy; preview again")
os.rename(target, previous)
try:
os.rename(stage, target)
except OSError as exc:
try:
os.rename(previous, target)
except OSError:
raise OSError(f"monitor update interrupted; inspect {previous} and {target}") from exc
raise
finally:
if stage.exists():
shutil.rmtree(stage)
return {**result, "updated": True, "would_update": False}
def rollback(target: Path, *, plan: bool = False,
if_active_sha256: str | None = None, if_rollback_sha256: str | None = None) -> dict:
"""Exchange the active and previous verified clients without deleting either."""
target = _target(target)
active_bundle, active = _installed(target)
previous = _previous(target)
prior_bundle, prior = _installed(previous)
result = {"target": str(target), "previous": str(previous),
"active_sha256": active["sha256"], "rollback_sha256": prior["sha256"],
"active_bundle": active_bundle.name, "rollback_bundle": prior_bundle.name,
"would_rollback": plan}
if plan:
return result
if if_active_sha256 != active["sha256"] or if_rollback_sha256 != prior["sha256"]:
raise ValueError("monitor rollback digests changed or are missing; preview again")
swap = Path(tempfile.mkdtemp(prefix=".strata-monitor-swap-", dir=target.parent))
try:
os.rename(target, swap)
try:
os.rename(previous, target)
except OSError as exc:
try:
os.rename(swap, target)
except OSError:
raise OSError(f"monitor rollback interrupted; inspect {swap} and {previous}") from exc
raise
try:
os.rename(swap, previous)
except OSError as exc:
try:
os.rename(target, previous)
os.rename(swap, target)
except OSError:
raise OSError(f"monitor rollback interrupted; inspect {target}, {previous} and {swap}") from exc
raise
finally:
if swap.exists() and not any(swap.iterdir()):
swap.rmdir()
return {**result, "rolled_back": True, "would_rollback": False}
def commit(target: Path, *, plan: bool = False,
if_active_sha256: str | None = None, if_previous_sha256: str | None = None) -> dict:
"""Accept the active client and remove its verified rollback copy."""
target = _target(target)
_, active = _installed(target)
previous = _previous(target)
old_bundle, old = _installed(previous)
result = {"target": str(target), "previous": str(previous),
"active_sha256": active["sha256"], "previous_sha256": old["sha256"],
"would_commit": plan}
if plan:
return result
if if_active_sha256 != active["sha256"] or if_previous_sha256 != old["sha256"]:
raise ValueError("monitor commit digests changed or are missing; preview again")
_remove_install(old_bundle)
return {**result, "committed": True, "would_commit": False}
def uninstall(target: Path, *, plan: bool = False,
if_active_sha256: str | None = None) -> dict:
"""Remove only the two verified files installed by this command."""
target = _target(target)
bundle, report = _installed(target)
if _present(_previous(target)):
raise ValueError("previous monitor version exists; commit or rollback it before uninstall")
result = {"target": str(target), "sha256": report["sha256"],
"bundle": bundle.name, "would_remove": plan}
if plan:
return result
if if_active_sha256 != report["sha256"]:
raise ValueError("monitor uninstall digest changed or is missing; preview again")
_remove_install(bundle)
return {**result, "removed": True, "would_remove": False}
def main(argv: list[str] | None = None) -> int:
ap = argparse.ArgumentParser(description="Build or locally manage a thin Strata monitor bundle")
sp = ap.add_subparsers(dest="action", required=True)
sp.add_parser("build").add_argument("bundle", type=Path)
sp.add_parser("verify").add_argument("bundle", type=Path)
install_parser = sp.add_parser("install", help="copy a verified bundle into a new local directory")
install_parser.add_argument("bundle", type=Path)
install_parser.add_argument("--target", type=Path, required=True)
install_parser.add_argument("--plan", action="store_true")
install_parser.add_argument("--if-candidate-sha256")
update_parser = sp.add_parser("update", help="replace a verified monitor and retain one rollback copy")
update_parser.add_argument("bundle", type=Path)
update_parser.add_argument("--target", type=Path, required=True)
update_parser.add_argument("--plan", action="store_true")
update_parser.add_argument("--if-current-sha256")
update_parser.add_argument("--if-candidate-sha256")
rollback_parser = sp.add_parser("rollback", help="exchange active and previous monitor clients")
rollback_parser.add_argument("--target", type=Path, required=True)
rollback_parser.add_argument("--plan", action="store_true")
rollback_parser.add_argument("--if-active-sha256")
rollback_parser.add_argument("--if-rollback-sha256")
commit_parser = sp.add_parser("commit", help="keep active client and discard previous")
commit_parser.add_argument("--target", type=Path, required=True)
commit_parser.add_argument("--plan", action="store_true")
commit_parser.add_argument("--if-active-sha256")
commit_parser.add_argument("--if-previous-sha256")
uninstall_parser = sp.add_parser("uninstall", help="remove a verified local monitor installation")
uninstall_parser.add_argument("--target", type=Path, required=True)
uninstall_parser.add_argument("--plan", action="store_true")
uninstall_parser.add_argument("--if-active-sha256")
args = ap.parse_args(argv)
try:
if args.action == "build":
result = build(args.bundle)
elif args.action == "verify":
result = verify(args.bundle)
elif args.action == "install":
result = install(args.bundle, args.target, plan=args.plan,
if_candidate_sha256=args.if_candidate_sha256)
elif args.action == "update":
result = update(args.bundle, args.target, plan=args.plan,
if_current_sha256=args.if_current_sha256,
if_candidate_sha256=args.if_candidate_sha256)
elif args.action == "rollback":
result = rollback(args.target, plan=args.plan,
if_active_sha256=args.if_active_sha256,
if_rollback_sha256=args.if_rollback_sha256)
elif args.action == "commit":
result = commit(args.target, plan=args.plan,
if_active_sha256=args.if_active_sha256,
if_previous_sha256=args.if_previous_sha256)
else:
result = uninstall(args.target, plan=args.plan,
if_active_sha256=args.if_active_sha256)
except (OSError, ValueError, zipfile.BadZipFile, json.JSONDecodeError) as exc:
ap.exit(1, f"monitor bundle {args.action} failed: {exc}\n")
print(json.dumps({"ok": True, "action": args.action,
**({"bundle": str(args.bundle), "files": len(result["files"]),
"sha256": result["sha256"]}
if args.action in ("build", "verify") else result)}, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
+272
View File
@@ -0,0 +1,272 @@
#!/usr/bin/env python3
"""Build the self-contained Strata Terminal distribution.
The requirement this exists for (checklist P04): a user installs the terminal without Desktop, and
needs no development checkout, no system Python, no pip and no compiler. So the archive carries its
own Python runtime and its own dependencies, and the `strata` launcher prefers them.
What goes in
------------
* a pinned **standalone CPython**, downloaded and SHA-256-verified against the same pin `setup.sh`
uses, so the terminal and the installer cannot drift apart;
* the runtime dependencies from `requirements.txt`, installed *into* that interpreter;
* the application: `strata.py`, `strata_control`, `strata_cli`, `serve`, `data` and the docs the
CLI references;
* the analysis-only tooling under `tools/` that the terminal paths import. Everything else under
`tools/` is engine installation and preparation work and is deliberately excluded — the serving
import boundary must not pull it in.
What does NOT go in
-------------------
* the engine binary. That is a separate, independently versioned component
(`packaging/README.md`), and a terminal distribution that bundled one would be wrong for every
machine except the one it was built on;
* model weights or prepared data;
* `node_modules` or the UI. The terminal does not need a browser.
Usage
-----
python packaging/package_terminal.py --out release/artifacts
python packaging/package_terminal.py --out /tmp/x --skip-deps # for a fast layout check
"""
from __future__ import annotations
import argparse
import hashlib
import os
import shutil
import subprocess
import sys
import tarfile
import tempfile
import urllib.request
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
#: The standalone CPython pin, identical to `setup.sh`'s. Two places, one value: a terminal built
#: from one and an installer built from the other must be the same interpreter.
PYTHON_PIN = {
"url": ("https://github.com/astral-sh/python-build-standalone/releases/download/20261009/"
"cpython-3.12.15%2B20261009-aarch64-apple-darwin-install_only.tar.gz"),
"sha256": "83dcb04777c798bd95f3687acf9ae84b09860272ee6bdfb05be8bcf9447f75b8",
"target": "darwin-arm64",
}
#: The application, as (source relative to ROOT, destination relative to the archive root).
PAYLOAD = (
"strata.py",
"strata_control",
"strata_cli",
"serve",
"requirements.txt",
"requirements-monitor.txt",
"docs/MANAGEMENT_PROTOCOL.md",
)
#: `tools/` modules the terminal paths import. Adding one here is a deliberate act: this list is what
#: keeps installation/preparation code out of the serving import graph.
TOOLS_SUBSET = (
"strata_tokenizer.py",
)
EXCLUDES = ("__pycache__", "*.pyc", ".DS_Store", "*.egg-info")
def sha256(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as fh:
for block in iter(lambda: fh.read(1 << 20), b""):
h.update(block)
return h.hexdigest()
def fetch_python(cache: Path) -> Path:
"""Download the pinned interpreter once, verifying the digest before it is used."""
archive = cache / "cpython-standalone.tar.gz"
if not archive.exists():
print(f"fetching the pinned CPython from {PYTHON_PIN['url']}")
urllib.request.urlretrieve(PYTHON_PIN["url"], archive)
got = sha256(archive)
if got != PYTHON_PIN["sha256"]:
raise SystemExit(
f"the CPython download does not match its pin\n expected {PYTHON_PIN['sha256']}\n"
f" got {got}\nRefusing to package an unverified interpreter.")
return archive
def ignore(_directory: str, names: list[str]) -> set[str]:
out = set()
for name in names:
if name in ("__pycache__", ".DS_Store") or name.endswith(".pyc"):
out.add(name)
return out
def main() -> int:
ap = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--out", required=True, help="directory to write the archive into")
ap.add_argument("--version", default=None, help="version label (default: read from the source)")
ap.add_argument("--skip-deps", action="store_true",
help="skip pip install (layout checks only; the result is NOT distributable)")
ap.add_argument("--keep-staging", action="store_true")
args = ap.parse_args()
version = args.version or _source_version()
out_dir = Path(args.out).resolve()
out_dir.mkdir(parents=True, exist_ok=True)
name = f"strata-terminal-{version}-{PYTHON_PIN['target']}"
cache = Path(tempfile.gettempdir()) / "strata-packaging-cache"
cache.mkdir(parents=True, exist_ok=True)
archive = fetch_python(cache)
staging = Path(tempfile.mkdtemp(prefix="strata-terminal-build-"))
root = staging / name
root.mkdir(parents=True)
try:
print("unpacking the interpreter")
with tarfile.open(archive) as tf:
tf.extractall(root)
# python-build-standalone extracts to `python/`; the launcher looks in exactly that place.
py = root / "python" / "bin" / "python3"
if not py.exists():
raise SystemExit(f"the archive did not produce {py}")
print("copying the application")
for item in PAYLOAD:
src = ROOT / item
dst = root / item
if not src.exists():
raise SystemExit(f"missing payload item: {item}")
dst.parent.mkdir(parents=True, exist_ok=True)
if src.is_dir():
shutil.copytree(src, dst, ignore=ignore)
else:
shutil.copy2(src, dst)
tools_dir = root / "tools"
tools_dir.mkdir(exist_ok=True)
for item in TOOLS_SUBSET:
src = ROOT / "tools" / item
if src.exists():
shutil.copy2(src, tools_dir / item)
# The launcher goes in `bin/`, and `strata.py` sits one level up, which is the layout
# `bin/strata` already understands.
bin_dir = root / "bin"
bin_dir.mkdir(exist_ok=True)
launcher = bin_dir / "strata"
shutil.copy2(ROOT / "bin" / "strata", launcher)
launcher.chmod(0o755)
if not args.skip_deps:
print("installing the runtime dependencies into the bundled interpreter")
subprocess.run([str(py), "-m", "pip", "install", "--quiet", "--no-warn-script-location",
"--upgrade", "pip"], check=True)
subprocess.run([str(py), "-m", "pip", "install", "--quiet", "--no-warn-script-location",
"-r", str(root / "requirements.txt")], check=True)
# A packaged tree must not carry pip's cache or the tests of its dependencies.
for junk in ("python/lib/python3.12/site-packages/pip",):
shutil.rmtree(root / junk, ignore_errors=True)
# Prove the interpreter works from where it now sits, before claiming the artifact is good.
probe = subprocess.run(
[str(py), "-c", "import jinja2, yaml, regex, psutil; print('deps ok')"],
capture_output=True, text=True, cwd=str(root))
print(" " + (probe.stdout.strip() or probe.stderr.strip()[:200]))
readme = root / "README.txt"
readme.write_text(_readme(version), encoding="utf-8")
archive_path = out_dir / f"{name}.tar.gz"
print(f"writing {archive_path}")
with tarfile.open(archive_path, "w:gz") as tf:
tf.add(root, arcname=name)
digest = sha256(archive_path)
size = archive_path.stat().st_size
print(f"\n{archive_path}")
print(f" bytes {size}")
print(f" sha256 {digest}")
manifest = out_dir / f"{name}.manifest.json"
import json
manifest.write_text(json.dumps({
"name": name,
"version": version,
"bytes": size,
"sha256": digest,
"python": {"url": PYTHON_PIN["url"], "sha256": PYTHON_PIN["sha256"],
"target": PYTHON_PIN["target"]},
"payload": list(PAYLOAD),
"tools_subset": list(TOOLS_SUBSET),
"excludes": ("engine binary", "model weights", "prepared data", "UI", "node_modules"),
"deps_installed": not args.skip_deps,
}, indent=2) + "\n", encoding="utf-8")
print(f" manifest {manifest}")
return 0
finally:
if args.keep_staging:
print(f"staging kept at {staging}")
else:
shutil.rmtree(staging, ignore_errors=True)
def _source_version() -> str:
"""The version the source reports, so the artifact name cannot disagree with the code."""
text = (ROOT / "CMakeLists.txt").read_text(encoding="utf-8", errors="replace")
import re
# `project(strata VERSION 0.1.44.1 LANGUAGES C CXX ...)` — the version is a dotted triple and
# must be read as one. Taking "everything after VERSION" yields "0.1.44.1.LANGUAGES.CXX".
m = re.search(r"^\s*project\([^)]*?VERSION\s+([0-9]+(?:\.[0-9]+)+)", text, re.MULTILINE)
return m.group(1) if m else "0.0.0"
def _readme(version: str) -> str:
return f"""Strata Terminal {version}
{'=' * (17 + len(version))}
This is the terminal distribution. It carries its own Python runtime and its own dependencies, so
you do not need Python, pip, Node, a compiler or a development checkout.
Install
-------
tar xzf strata-terminal-{version}-{PYTHON_PIN['target']}.tar.gz
cd strata-terminal-{version}-{PYTHON_PIN['target']}
./bin/strata --help
Put `bin/` on your PATH to get the `strata` command everywhere:
echo "export PATH=\\"$PWD/bin:$PATH\\"" >> ~/.zshrc
What it does NOT include
------------------------
The **engine** is a separate, independently versioned component and is not bundled: the right engine
package depends on your OS, CPU and GPU, and shipping one machine's build to another would be
wrong. Point Strata at an engine with `STRATA_ENGINE`, or install one through the desktop app or
`setup.sh`. Model weights are likewise not included.
Useful commands
---------------
strata status one read-only telemetry snapshot
strata monitor the six-view terminal monitor
strata doctor read-only component inventory
strata timeskip run --help retain a good result, revise it, check it, decide
strata tune plan --help what a tuning sweep would search, and why not
strata serve --help start the inference server in the foreground
Serving, `status` and `monitor` do not need the agent runtime or the tuning packages. The opt-in
terminal TUI additionally needs `textual` (see requirements-monitor.txt).
Verification
------------
The archive's manifest records the SHA-256 of the bundle and of the pinned interpreter it carries.
`bin/strata` prefers the bundled interpreter and falls back to a Python 3.10+ on PATH, so the
distribution still runs if the bundled runtime is removed.
"""
if __name__ == "__main__":
sys.exit(main())