A Rails 8.1 ingestion engine, real-time dashboard, and REA value-flow attestation store — built non-destructively and fully revertibly, so it can be explored without ever touching a load-bearing production system.
01 — What it is
Aethelgard tracks breaking marketplace signals and parses them through the lens of structural gaps and REA (Resource-Event-Agent) value-flows. The vault and git history stay canonical for base facts; Oxigraph, the Report Studio, and the Letta Witness keep their roles. Aethelgard's genuine net-new value is ingestion — the Report Studio is generation, not ingestion.
SerpApi sweeps public Google News into deduplicated signals, asynchronously via Sidekiq.
When a structural gap closes, it records an REA value-event — the seed of a bioavailable attestation layer.
A live Hotwire dashboard streams rows and re-ranks the SBPI projection in real time over Turbo Streams.
02 — The isolation contract
The load-bearing design decision. Two independent layers of safety mean the prototype is physically incapable of damaging the live stack in its default posture.
A standalone git repo at ~/Documents/projects/aethelgard, outside the Obsidian vault and outside the live React / Cloudflare / Turso / Oxigraph stack. Never tracked by the vault's git.
Soft revert
git reset --hard 37fb5fb
Hard revert rm -rf the repo +
dropdb the namespaced databases. Nothing else is touched.
Every load-bearing touchpoint goes through an adapter gated by a single master switch, AETHELGARD_LIVE (default false). Going live is a per-adapter opt-in: flip the switch and fill one endpoint URL at a time.
Default runtime posture — what each adapter actually does:
Read-only Google News. Gated by key presence, not the master switch. Not load-bearing.
Deterministic replay fixtures. No socket opened.
The only write path. Logs what it would attest, returns a stub receipt.
Replay stack rank from fixtures.
No writes. Canonical stores keep their roles.
03 — The pipeline
Ingestion is async — the controller returns a 303 in ~18ms while the sweep runs in Sidekiq. Each new signal threads the bridge pipeline and broadcasts its row live.
A query term is submitted; the controller enqueues a sweep and returns immediately.
Calls SerpApiIngestor — the one deliberate live integration (read-only public Google News).
One row per article persisted to local Postgres. (Named MarketSignal — see the gotcha below.)
Runs InfranodusBridge.analyze (stub). If a structural gap closes → an REA ValueEvent + LettaWitness.post_pattern (no-op).
The row appears / updates live on the dashboard and the SBPI projection re-ranks in-memory.
BrandRegistry is the single source of truth for both the stack rank and actor extraction — which reads the company named in the headline, not the news source. So ingesting brand news re-ranks the projection. The delta is bounded: +0.3 per closed gap, capped at +14, projection capped at 100, so a burst reads as momentum without breaking the 0–100 scale.
Demo recipe — ingest in sequence and watch the stream fill and the projection re-rank: Vanta → Drata → Sprinto.
04 — Data model
Honoring the vault's corrections: Resource-Event-Agent value-flows across three temporal layers, mapped to the sbpi.ttl upper ontology.
| column | meaning |
|---|---|
| source_urn | unique signal identity (dedup key) |
| actor_urn | company named in the headline |
| engine_source | google_news, … |
| payload_body | title + snippet |
| metadata_dump | full API JSON (jsonb) |
| processing_state | ingested · processed · failed |
| column | meaning |
|---|---|
| event_type | EconomicEvent · Transformation |
| temporal_layer | observation · plan · recipe |
| resource_class | Capital · Intelligence/Narrative · Relational/Network · Infrastructure |
| bridged_gap_id | the InfraNodus gap that closed |
| quantity_modifier | weight on the SBPI delta |
05 — Project map
Cold-start read order: README → the isolation core → the brand registry → the process job → the ingestor → the three adapters → the dashboard view.
~/Documents/projects/aethelgard/ ├── CLAUDE.md agent orientation + non-negotiable rules ├── README.md canonical run / revert / isolation summary ├── config/initializers/ │ └── aethelgard.rb ISOLATION CORE — posture + adapter wiring (read first) ├── lib/aethelgard/ │ ├── brand_registry.rb single source of truth: stack rank + actor extraction │ └── fixtures.rb deterministic replay fixtures for the stubs ├── app/jobs/ │ ├── ingest_signals_job.rb enqueues a SerpApi sweep │ └── process_signal_job.rb per-signal bridge → ValueEvent → broadcast ├── app/services/ │ ├── serp_api_ingestor.rb live Google News (the one live integration) │ └── aethelgard/adapters/ │ ├── infranodus_bridge.rb stub — replay fixtures, no socket │ ├── letta_witness.rb no-op the only write path │ └── report_studio.rb stub — replay stack rank ├── app/models/{market_signal,value_event}.rb ├── app/controllers/{dashboards,ingestions}_controller.rb ├── app/views/dashboards/{show,_signal_row}.html.erb └── db/migrate · schema.rb · seeds.rb
Signal shadows Ruby's stdlib ::Signal and breaks Signal.trap (Puma/Sidekiq) → the model is MarketSignal with self.table_name = "signals".
Sidekiq 7.3.x ✗ connection_pool 3.x — positional pop(timeout) removed → connection_pool pinned ~> 2.5.
Foreman tears down the whole stack when any process exits; tailwindcss:watch exits without a TTY → for headless verification run web + sidekiq as separate processes.
06 — Stage-2 roadmap
Stage-1 is complete and paused. It is not wired into any cron, automation, or deploy, and stays a sandbox until a Stage-2 decision is made.
Read-only /analyze first — gap verdicts become real structural analysis instead of deterministic stubs. The registered next_action; safe because /analyze does not write.
Real per-vertical stack ranks from the Worker-API instead of replay fixtures.
The one true mutation. Stays a no-op until everything else is proven.
Onboarding intake → ingestion classifier agent → custom stream engine: Farcaster Hub, GitHub commits/issues, specialized SerpApi queries.
Corrections to carry — do not regress
07 — Agent read-in
A second Claude Code agent, rooted in the totem-terminal vault, can get fully caught up from one document. The code is reachable from the vault via a gitignored symlink — read access without breaking physical isolation.
Project map, pipeline, isolation recap, REA model, Stage-2 roadmap, and a consolidated index of every scattered source.
projects/Totem-framework/AETHELGARD-AGENT-ONBOARDING.md
Gitignored symlink → the standalone repo. Read any file through it without the vault's git ever tracking the code.
projects/Totem-framework/aethelgard-code/ → ~/Documents/projects/aethelgard