Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
de0fe07
Extract the runtime seam from the target model
suffro Aug 26, 2026
4743ecf
Untrack the staging copy npm run types leaves behind
suffro Aug 27, 2026
e76783d
Break the wire to version 3: a declared runtime, per-asset embed, dec…
suffro Aug 27, 2026
525e6b8
Carry the version 3 break through the Node tests, fixtures and examples
suffro Aug 27, 2026
46d5c91
Rewrite the documentation for version 3
suffro Aug 27, 2026
4110087
Bring the Rust consumer to version 3
suffro Aug 27, 2026
16a1da6
Bring the Python consumer to version 3
suffro Aug 27, 2026
7230fb1
Record the version 3 format break in the changelog
suffro Aug 27, 2026
97c0088
Update the repository's own instructions for version 3
suffro Aug 27, 2026
4371ea3
Make the Rust extractor set the mode explicitly, like the other two
suffro Aug 27, 2026
10cbba3
Drop the removed --weights flag from the four box-building workflows
suffro Aug 28, 2026
be04c58
Implement the native and node runtimes, and declare bundled licences
suffro Aug 28, 2026
e0fb472
Bring the Rust consumer to the native and node runtimes
suffro Aug 28, 2026
e54da85
Bring the Python consumer to the native and node runtimes
suffro Aug 28, 2026
e832751
Add worked node and native example boxes, and document both runtimes
suffro Aug 28, 2026
c6c46c6
Refuse a superseded envelope by version, not as a shape error
suffro Aug 28, 2026
12a48ce
Build the node and native examples in CI, and record phase C
suffro Aug 28, 2026
9fbb66a
Make the CI determinism check able to fail
suffro Aug 28, 2026
946e62b
Name the publish base URL for what it is, and make publishing optional
suffro Sep 1, 2026
80ab74a
Merge remote-tracking branch 'origin/main' into v3-phase-a-runtime-seam
suffro Sep 1, 2026
67e0a40
Fix the two Windows failures the first CI run found
suffro Sep 1, 2026
b222eba
Drop a redundant unit return type from the Windows umask stub
suffro Sep 1, 2026
f4defc5
Fix browser export, add v2-to-v3 guide
suffro Sep 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
# So: no conversion for anything a scroll can name. The example entry points are the files in this
# repository that a scroll actually names; keep this list in step with `localFiles` if that changes.
examples/**/entrypoint.py -text
examples/**/entrypoint.js -text
# Reference data a box ships and answers questions about. `codon-demo` pins its SHA-256, so a
# checkout that rewrote its newlines would fail the build rather than ship different data.
examples/**/codons.csv -text
# `dataset-demo` ships a binary HDF5 file and pins its hash; `readings.txt` is the text it was
# generated from, and a rewritten newline there would silently change what regeneration produces.
examples/**/readings.h5 -text
examples/**/readings.txt -text

# Committed locks and licence inventories are compared byte for byte across platforms in CI.
examples/**/pixi.lock -text
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/demo-box.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ jobs:
run: |
node src/cli.mjs build "$BOX_ID/${{ matrix.target }}" \
--scrolls-dir examples \
--weights embed \
--private-key "$RUNNER_TEMP/demo-signing-private.pem" \
--public-key examples/keys/example-signing-public.json

Expand Down
75 changes: 60 additions & 15 deletions .github/workflows/example-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ jobs:
run: node src/cli.mjs keygen

- name: Build
run: node src/cli.mjs build hello-box/${{ matrix.target }} --scrolls-dir examples --weights embed
run: node src/cli.mjs build hello-box/${{ matrix.target }} --scrolls-dir examples

# The check that matters: extract the box and import with the interpreter inside it.
- name: Verify with self-test
Expand Down Expand Up @@ -132,23 +132,68 @@ jobs:
console.log(`extracted ${mb(payload.installedSizeBytes)}`);
' "$release"

# Determinism is a promise the repository makes that nothing else verifies end to end. The
# archive is named by its own SHA-256, so a rebuild that differs lands beside the first rather
# than replacing it — the file count is the assertion.
# The other two runtimes, on the one target their example scrolls declare. They are the only
# thing in CI that builds a box with no interpreter, and a box whose interpreter is not
# Python — the paths where a Python assumption would hide, and where the unit suite's fake
# toolchain cannot follow. Each is built, verified with its own self-test, and run.
- name: Build, verify and run the node and native examples
if: matrix.target == 'macos-aarch64-metal'
shell: bash
run: |
set -euo pipefail
release_for() {
ls .scrollcase/dist/boxes/"$1"/1.0.0/${{ matrix.target }}/*.release.json
}

node src/cli.mjs build hello-box-node/${{ matrix.target }} --scrolls-dir examples
node src/cli.mjs verify "$(release_for hello-box-node)" --self-test
node src/cli.mjs run "$(release_for hello-box-node)"

node src/cli.mjs build hello-box-native/${{ matrix.target }} --scrolls-dir examples
node src/cli.mjs verify "$(release_for hello-box-native)" --self-test
node src/cli.mjs run "$(release_for hello-box-native)" -- --version

# A node box doing real work rather than proving it starts: reference data pinned by hash,
# queried through `node:sqlite`. It is the one example whose payload carries data the box
# is expected to answer questions about, so a wrong answer here is a build failure.
node src/cli.mjs build codon-demo/${{ matrix.target }} --scrolls-dir examples
node src/cli.mjs verify "$(release_for codon-demo)" --self-test
node src/cli.mjs run "$(release_for codon-demo)" -- Leucine

# The native counterpart: a large compiled program with a long tail of codec libraries,
# whose self-test runs a real encode rather than a version check. Slower than the rest of
# this job put together, and the only thing here that would catch a break in the one case
# `native` exists for.
node src/cli.mjs build transcode-demo/${{ matrix.target }} --scrolls-dir examples
node src/cli.mjs verify "$(release_for transcode-demo)" --self-test
node src/cli.mjs run "$(release_for transcode-demo)" -- -version

# The other native shape: small compiled tools reading a data file the box ships, rather
# than one large program driven by flags. Its self-test reads the shipped dataset both
# ways — structure and values — so a box whose data or reader stopped agreeing fails here.
node src/cli.mjs build dataset-demo/${{ matrix.target }} --scrolls-dir examples
node src/cli.mjs verify "$(release_for dataset-demo)" --self-test
node src/cli.mjs run "$(release_for dataset-demo)" -- -H readings.h5

# Determinism is a promise the repository makes that nothing else verifies end to end.
#
# The assertion is the archive's *name*, not the file count. The archive is named by its own
# SHA-256, but a build clears its own object directory before writing into it, so a rebuild
# replaces the first archive rather than landing beside it — counting the files afterwards
# always yields one and could never fail. Measured: changing the scroll's sourceRevision
# produced a completely different archive and left the count at one.
- name: Rebuild and compare
shell: bash
run: |
set -euo pipefail
dist=".scrollcase/dist/boxes/hello-box/1.0.0/${{ matrix.target }}"
before=$(ls "$dist"/*.zip | wc -l)
if [ "$before" -ne 1 ]; then
echo "Expected exactly one archive after the first build, found $before" >&2
exit 1
fi
node src/cli.mjs build hello-box/${{ matrix.target }} --scrolls-dir examples --weights embed
after=$(ls "$dist"/*.zip | wc -l)
if [ "$after" -ne 1 ]; then
echo "Rebuild was not byte-identical: $after distinct archives" >&2
ls -l "$dist" >&2
before=$(basename "$(ls "$dist"/*.zip)")
node src/cli.mjs build hello-box/${{ matrix.target }} --scrolls-dir examples
after=$(basename "$(ls "$dist"/*.zip)")
if [ "$before" != "$after" ]; then
echo "Rebuild was not byte-identical:" >&2
echo " first $before" >&2
echo " second $after" >&2
exit 1
fi
echo "Rebuild produced a byte-identical archive."
echo "Rebuild produced a byte-identical archive: $after"
1 change: 0 additions & 1 deletion .github/workflows/llm-demo-box.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ jobs:
df -h . || true
node src/cli.mjs build "$BOX_ID/${{ matrix.target }}" \
--scrolls-dir examples \
--weights embed \
--private-key "$RUNNER_TEMP/demo-signing-private.pem" \
--public-key examples/keys/example-signing-public.json

Expand Down
1 change: 0 additions & 1 deletion .github/workflows/sentiment-demo-box.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ jobs:
run: |
node src/cli.mjs build "$BOX_ID/${{ matrix.target }}" \
--scrolls-dir examples \
--weights embed \
--private-key "$RUNNER_TEMP/demo-signing-private.pem" \
--public-key examples/keys/example-signing-public.json

Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ vite.config.ts.timestamp-*
# scrollcase build state
.scrollcase/

# `npm run types` stages a copy of src/ here before running tsc over it. One escaped into a commit
# once, leaving a stale second copy of the contract in the tree.
.scrollcase-runtime-types-*/

# Python development and packaging state
__pycache__/
*.py[cod]
Expand Down
Loading
Loading