Mechanical sweep of all .sx sources and plan docs (PLAN.md, current/,
.agents/) for the sx language rename (s8/s16/s32/s64 -> i8/i16/i32/i64).
Verified: make build + make test, 14/14.
The June stdlib restructure deleted the flat library modules; the old
paths kept resolving only through a stale zig-out/library install
snapshot. Verified green with that snapshot removed. Drop the empty
src/infra/ — the planned hash/json/cli shims shipped as sx std modules
instead.
Close the remaining publish aggregate-consistency edges (review round 2, F1
continued):
- chan.name == release.channel — the promoted channel must be the one the
release declares as its target; promoting a "beta" channel for a release
whose channel is "stable" committed an edge contradicting the release's own
target. Now rejected with Integrity + rollback.
- release.id must be new — a colliding id would shadow the existing release
(get_release resolves to the OLD one), so the channel edge would silently
point at a different release than the one published. Now rejected with
Integrity + rollback.
tests/repo_transaction.sx: add a channel-name-mismatch case and a
release-id-collision case (both assert Integrity + model unchanged); existing
fully-consistent publish still commits. Both new cases fail on the pre-fix
repo.sx and pass after.
Repo.publish validated entities individually and checked
artifact.release_id == release.id, but never verified the published
aggregate forms one consistent identity graph. It could commit a channel
whose app_id differs from the release's app (a channel of app B pointing
at app A's release) or artifacts whose app_id differs from the release's
app — exactly the dangling/cross-app edge the acceptance forbids.
Add Integrity preconditions to the publish transaction (reusing the
existing len-reset/channel-restore rollback so the model is unchanged on
failure): the release's app must exist, the promoted channel must belong
to that app (chan.app_id == release.app_id), and every artifact must
belong to that app AND name this release (a.app_id == release.app_id and
a.release_id == release.id).
Extend tests/repo_transaction.sx with cross-app channel and cross-app
artifact cases asserting publish raises Integrity and leaves the model
unchanged; the existing rollback and no-dangling assertions stay green.
Adds the in-memory repository over the P2.1 domain and whole-model
persistence to <root>/db.json via std.json (subplan-02 Slice 1, the part
P2.1's mapping deferred).
src/repo/repo.sx
- Repo over App/Release/Artifact/Channel/AuditEvent, each a growable
List scanned LINEARLY (no index — Slice 1).
- create/get/list/update per entity; find_app_by_slug;
find_artifact_by_digest (the P2.2 content-address key).
- publish(): atomic-ish transaction (release + artifacts + channel
pointer). A failure midway rolls the model back by snapshot/restore —
no half-inserted entities and no channel left pointing at a release
that isn't in the repo.
- Long-lived-container rule: init captures own_allocator :=
context.allocator and every List growth forwards it explicitly, so the
backing stores outlive any single call's transient context allocator.
src/repo/db.sx
- save()/load() the whole model to/from <root>/db.json via std.json.
- Stable (insertion-order) field order: entities emit in declaration
order; top-level order is apps, releases, artifacts, channels,
audit_events. Re-saving an unchanged model is byte-identical.
- Enums serialize as their variant name. Read-back is strict: a missing
field, wrong JSON type, or unknown enum name -> typed LoadErr.BadShape.
Loaded strings are copied into the new repo's own allocator.
tests/
- repo_roundtrip.sx: save -> reparse (valid JSON) -> reload into a fresh
repo, asserting every field round-trips and a re-save is byte-identical.
- repo_transaction.sx: a publish that fails midway leaves the model
unchanged (no dangling release/channel), in memory and after reload.
- repo_owns_allocator.sx: deterministic proof that every owned list grows
through the captured allocator, not the call-site context allocator.
Gate: make build + make test both green (6/6).