Engineering
Built for
the record.
How Stonewall stores the record, searches it, cites its answers, and tests every change before release. Open any chapter for the detail.
This page, one line per chapter
- 1One record, two lanes. The full record stays private; only counts and neutral labels reach this site.
- 2Answers cite the record. Each fact is cited by document ID, and the documents an answer cites are listed beside it.
- 3Patterns, measured. How often two of the ten most common patterns share a document.
- 4Three checks, one queue. Every change passes three required checks before it reaches production.
- 5Six services, one cloud. Each part deploys on its own, and rolls back on its own if a live check fails.
- 6The production bar. Reliability, security, performance, operations and cost, each tied to a check.
One record, two lanes. The full record stays private. Only counts and neutral labels reach this site.
Every document gets an ID, a type, a date and the people and patterns it touches. The private side holds the full text, searchable by word and by meaning. The public side gets counts and neutral labels only, and a build check refuses anything else.
Inside the data plane
- Corpus projection
- One row per manifest artifact, full source bodies, and the pattern taxonomy, all full-text indexed.
- Semantic layer
- Every canonical text file, chunked, with 1536-dimension embeddings under an HNSW cosine index.
- Memory
- Conversation threads and a long-term operator memory, distilled in the background. The workspace and the Telegram bot keep their own threads and share the long-term memory.
- Access
- Row-level security, default-deny for generic roles, a read-only query role, and an idempotent, schema-versioned migrator.
PostgreSQL 17 · pgvector · HNSW cosine · numbered migrations
Answers cite the record. Shelby searches the documents before it answers and cites each fact by its document ID.
Shelby answers from the record, not from what the model remembers. Every passage its search returns carries the document’s ID and file, and Shelby is told to cite each fact by that ID and never to invent one. In the workspace, the documents an answer cites are listed beside the conversation, and each answer is saved with its citations. A fictional example, cited claim by claim, is on the landing page.
What is checked, and what is not yet
Checked on every change: the search tools still attach a document ID and file to each passage, and the IDs they return point at real catalog entries. When search by meaning is unavailable, it falls back to word search and says so.
Not automatic yet: a check that compares each answer’s citations with the passages Shelby actually retrieved, and a link from each citation to its source.
How documents become searchable text
Each document’s text is transcribed in two grades.
- Searchable floor
- Machine-read text: good enough to find the page, not to quote.
- page image at 300 dpi or more
- machine reading (OCR)
- searchable text
- Gold
- A verbatim transcript: every word, marker and reaction.
- page image at 500 dpi or more
- visual transcription
- second machine check
- verbatim transcript
Speaker B @ 09:16 > [quoted: Speaker A @ 09:14] > Can you send the exhibit list before the call? Sent. Two attachments below. [IMAGE: exhibit list, page 1 of 3, four-column table] [reaction: thumbs up · Speaker A]
Patterns, measured. How often two of the record’s ten most common patterns turn up in the same document. The counts are real; the names are stand-ins.
Strongest pairs
- Motion Posture Witness SequencingJaccard 0.27
- Calendar Control Billing HygieneJaccard 0.23
- Witness Sequencing Billing HygieneJaccard 0.23
- Exhibit Chain Service of ProcessJaccard 0.17
- Calendar Control Deadline CadenceJaccard 0.16
How the score works
The score, Jaccard similarity, divides the documents two patterns share by the documents that carry either one: 1 means they always appear together, 0 means they never do. Pairs that share fewer than two documents are not drawn.
Three checks, one queue. Every change passes three required checks, then waits its turn in the merge queue.
The three checks, and the rules behind them
verify- Checks follow the change scope. Code changes run lint, types and tests; changes affecting the public app, cockpit or command worker also run that surface's build and checks, including applicable bundle budgets. Corpus-only changes run the record checks without the lint/test stack. Record checks cover manifest consistency, the ontology, freshness of gated tracked indexes, content-hash stamps, and identity and rendering audits. Corpus cross-reference freshness is advisory in ordinary verification.
protected code/config scan- Secret scanning on code and configuration, on every pull request and every queue entry. An advisory scan runs over the corpus beside it.
CodeQL gate- Three analyzers: GitHub Actions, JavaScript and TypeScript, Python. The gate passes only when all three finish.
The rules it was built around
- A search result points to a document; it is not proof of what the document says.
- A correct record can still be displayed wrongly, so displays are checked too.
- If a fact decides whether code merges, a machine checks it.
And every check must be one a change can pass, one that can actually fail, and one that fails only on a real problem.
What the lane refuses
Every service deploys through the same eight-stage lane. The numerals mark where it can refuse a deploy.
- Build the image, then a filesystem gate
- Scan Trivy, fixable high and critical findingsi
- Guard the main tip is unchangedii
- Lease one build per serviceiii
- Candidate tagged, at zero traffic
- Probe the exact commit, readyiv
- Promote full traffic, production probesv
- Finalize rollback held, lease released
| Mark | Condition | Caught by | Result |
|---|---|---|---|
| i | A fixable high- or critical-severity finding in the image | Trivy, before push | The build stops; nothing is published. |
| ii | Main advanced with changes to this service | The main-tip guard | The build stops before publishing; the newer build deploys. |
| iii | Another build holds the service | The deploy lease | This build waits for the owner to finish. |
| iv | The candidate serves the wrong commit or fails readiness | The candidate probe | Traffic never moves; production is untouched. |
| v | A production probe fails after promotion | Promote and finalize | The prior revision is restored. |
| — | Derived artifacts sit behind their sources | verify, on the pull request and in the queue | Refused until they are regenerated and committed. |
Six services, one cloud. Each part of Stonewall runs and deploys on its own, in one Google Cloud region.
stonewall-public- stnwl.ai — this site
stonewall-cockpit- app.stnwl.ai — the operator cockpit and Shelby
stonewall-portal- api.stnwl.ai — the workspace’s document API, behind a service key
stonewall-bot- Shelby on Telegram
knowledge-base-webhook- Signed ingest callbacks
chat-agent-service-c2e9- The operator’s Google Chat agent, bounded tools
stonewall-command- The operator’s command agent, behind Cloudflare Access
Triggers, edge and uptime
The roster above is read from the one surface file CI probes live. One more surface runs beside the fleet on Cloudflare Workers, deployed by hand. The apex sits behind a global external HTTPS load balancer with managed certificates. The stnwl.ai DNS zone uses Google Cloud DNS; stonewall.esq retains Cloudflare DNS-only nameservers. Eleven uptime checks run at a five-minute cadence, with alert policies for uptime-check failures.
| Service | Deploy config | Trigger |
|---|---|---|
stonewall-public | cloudbuild.public.yaml | stonewall-public-main-push |
stonewall-cockpit | web/ | stonewall-cockpit-main-push |
stonewall-portal | cloudbuild.yaml | stonewall-main-push |
stonewall-bot | scripts/ | stonewall-bot-main-push |
knowledge-base-webhook | cloudbuild.webhook.yaml | stonewall-kb-webhook-main-push |
chat-agent-service-c2e9 | apps/ | stonewall-chat-agent-main-push |
stonewall-command | apps/ | wrangler deploy, operator-run |
The full stack
- Languages
- Python 3.12 / 3.13 / 3.14, by service · TypeScript · Node 24 · Markdown
- Canon
- catalog/manifest.md · Markdown + JSON · stdlib stonewall.py CLI
- Data plane
- Neon Postgres 17 · pgvector, HNSW · Row-level security
- Embeddings
- Gemini embedding, the embedding model · gemini-embedding-001, 1536 dimensions
- Public site
- Next.js 16 standalone · Distroless Node 24 · Self-hosted Geist + Newsreader · GA4, deferred
- Corpus API
- FastAPI · Uvicorn · Repo JSON + in-memory search index
- Cockpit
- Next.js 16 · React 19 · Tailwind · Clerk · AI SDK
- Shelby
- Anthropic Claude · Claude Fable 5.1, the deploy-lane pin · A smaller Claude tier for distillation
- Voice and audio
- Whisper · Gemini 2.5 Pro, listening · TTS-1-HD
- Chat agent
- Gemini 2.5 Flash on Vertex AI · Bounded tools, read-only SQL
- Intake
- pypdfium2 · Tesseract · extract-msg · Two-pass vision
- CI
- GitHub Actions · pytest · Vitest · Playwright · CodeQL · gitleaks · Trivy
- Infrastructure
- Cloud Run · Cloud Build · Artifact Registry · Cloud DNS · HTTPS load balancer · Terraform · Cloud Monitoring
The production bar. Five areas, each tied to the file or check that enforces it. The files are in our private repository.
- Reliability
- Zero-traffic candidates, rollback held, hashed assets, live probes.
- Security
- Secret and image scanning, CodeQL, an export guard, default-deny data.
- Performance
- Invariants in CI, per-route bundle budgets, self-hosted type.
- Operations
- One service per workload; triggers and merge rules as code.
- Cost
- Scope-aware CI, frozen lockfiles, duplicate runs cancelled.
Files and gates
Reliability
- Zero-traffic candidate, rollback held
scripts/Cloud Build probes, every lanecloud_run_safe_promote.py - Content-hashed assets
scripts/verifyasset_version_stamp.py - Declared surfaces, probed live
catalog/audit_surface_state.pyintake/ state/ surfaces.toml - Cockpit reads with the API cold
web/bundled snapshot fallbacklib/ api.ts
Security
- Secret scanning
.github/protected code/config scanworkflows/ gitleaks.yml - Static analysis
.github/CodeQL gateworkflows/ codeql.yml - Public export guard
scripts/fails the buildassert_public_portal_payload.py - Operator boundary
web/allowlist, fails closedmiddleware.ts - Default-deny data access
web/row-level securitymigrations/ postgres/ 0028-data-api-default-deny.sql - Security headers, pinned actions
next.config.jsactionlint and zizmor in verify
Performance
- Performance invariants
scripts/verifycheck_web_perf_invariants.py - Per-route bundle budgets
web/pnpm check:bundlesperf-budgets.json - Self-hosted type, deferred analytics
tests/verifytest_public_perf_invariants.py
Operations
- Triggers declared in code
terraform/trigger roster audit, liveedge.tf - Merge rules as code
docs/apply_ruleset_policy.pyruleset-policy.json - Gates tested on a clean tree
scripts/--check and --canaryaudit_gate_lane_parity.py
Cost
- Corpus-only verify for prose
scripts/verify scopedetect_pr_change_scope.py - Frozen lockfiles
Dockerfile.publicpnpm install --frozen-lockfile - Duplicate runs cancelled
.github/janitorworkflows/ cancel-runs-on-janitor-close.yml - Warm capacity per service
docs/public 2 · portal, cockpit, bot 1 · webhook, chat agent 0deployment-current-state.md
How it was built
How the system grew, corrected to how it runs today; the milestones follow.
- The catalog is the database. Every document has a numbered row: its type, its date, the people and patterns it involves, and whether it has been analyzed. A small command-line tool queries it, and the build checks the indexes against it.
catalog/manifest.md scripts/stonewall.py - Verification is scoped to each change. A change to prose runs the quick record checks; a change to code runs everything. Names, displays and drift between pages each have their own audit.
scripts/verify_all.py scripts/pre_pr_check.py - One file lists every live service. A single file names every live service, hostname, trigger and probe; the deployment docs are generated from it, and CI probes it live.
catalog/intake/ state/ surfaces.toml scripts/render_surface_docs.py - Two grades of transcription. Page images at 500 dpi or better, transcribed and then checked by machine, give the verbatim transcripts; machine reading at 300 dpi or better is the searchable floor. Reply chains nest; nothing is summarized.
references/bubble_render_doctrine.md scripts/batch_sidecar_gold_uplift.py - The workspace keeps reading when a service is down. Sign-in gated, with a fallback from the live API to a bundled snapshot to public data, so a document still opens when an upstream service is cold.
web/lib/ api.ts scripts/build_cockpit_operator_snapshot.py - A scan flags pages that cite changed code. An on-demand scanner lists the pages that still cite changed paths or names; a scheduled documentation pass opens ordinary pull requests through the queue, and a janitor keeps one per topic.
scripts/docs_api_drift.py .github/workflows/ close-duplicate-doc-prs.yml
- Groundwork: the document reader, task reading lists, duplicate detection and drift checks.
- Google Cloud Run becomes the deploy plane.
- The merge queue settles on main.
- The last external hosts retire; stnwl.ai DNS moves to Google Cloud DNS. The compatibility domain retains Cloudflare DNS.
- The public site is consolidated into fewer pages.
- A sixth service joins the fleet: the operator’s chat agent.
- Review threads leave the merge gate; three checks and the queue are the whole contract.
- Every service deploys through the guarded lane.
- Search by meaning moves to Google’s Gemini embeddings; the record is re-embedded.
These checks describe how the system is built; they are not a certification.