The pinned digest predated src/mock_provider, so mock mode required
`up --build`. That module is now on main and published, so the sandbox runs
from a released image again and plain `up` works.
The module check stays. A digest without the module resolves and pulls fine,
then crash-loops one service, so the guard is still the difference between a
clear message and an obscure failure — its wording is just no longer tied to
an unmerged PR.
Verified against the new digest from clean volumes: `up` in 44s with no
--build, all three Honcho services on the pinned digest, 4 explicit
conclusions as documented; a junk peer added then `reset` in 1.1s restored
exactly the seeded state; and pinning the old digest still refuses to start
with the rewritten message.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A local Honcho that comes up already seeded and resets to that exact state in
under a second, so harness integration testing stops depending on machine state.
`docker-compose.yml.example` gave the topology but was an example, unseeded, and
had no teardown path, so every harness change was verified against whatever state
a given laptop was in. The blocker to fixing that was the deriver's provider call,
which made a sandbox neither free nor deterministic; src/mock_provider removes it.
Reset restores a Postgres template database rather than re-deriving: `seed`
snapshots the finished state, `reset` drops the live database and recreates it
from that snapshot, then flushes Redis. Nothing is stopped or restarted --
DROP DATABASE ... WITH (FORCE) evicts the connection pools and both services
reconnect on their own. Measured 0.86s in mock mode, 1.29s in real mode against
a 361s seed.
Two provider modes, both first class. Mock is the default: deterministic, free,
no egress. Real points the same stack at a configured provider, reading
credentials from one gitignored file rather than ambient environment. The
difference matters and is documented: mock embeddings are hash-derived and carry
no semantic similarity, so vector-recall assertions are impossible there, and
mock conclusions come out explicit-only. On the committed fixture, mock yields 4
synthetic explicit conclusions and real yields 22 across all three levels.
The sandbox is configured only by what Compose injects. PYTHON_DOTENV_DISABLED
and HONCHO_CONFIG_TOML_DISABLED are both set because src/config.py calls
load_dotenv(override=True) at import and the Dockerfile bakes any local
config.toml into the image; without them a developer's own provider config wins
silently. Deriver scheduling is pinned for the same reason: on stock settings a
sandbox seeded with a handful of messages produces zero conclusions and gives no
indication why, because work units wait for a 512-token batch or 30 minutes and
startup jitter delays the first poll by up to 30s.
Snapshots can go stale, so each carries a fingerprint -- Alembic revision,
fixture hash, provider mode -- and reset refuses on a mismatch instead of
restoring a state that predates a migration. Templates are per-mode, so both can
coexist.
Note: the pinned digest in sandbox/image.env predates the mock provider, so
`--build` is required until that lands and a new image is published. sandbox.sh
detects this and says so rather than crash-looping on a missing module.