Skip to content

Build Journal

The primary view of this site. Every Ralph loop iteration is explained here — newest first — as a card you can expand: what the story is trying to achieve, what the loop actually did (in plain language), how CI ruled on it, and exactly what changed. The terse pass/fail tables live on Loop Stats.

22 loop iterations so far · 21 of 21 stories complete. Expand any card for the full explanation.

⏭ Loop 22 · US-021 — Progress/docs site buildable for Cloudflare Pages

⏭ ci-skipped · attempt 1 · 20m 18s ·

Goal: Progress/docs site buildable for Cloudflare Pages — As the operator, I want a static docs/progress site that renders the PRD and live story progress so I can track the Ralph loop, deployable to Cloudflare Pages.

What this loop did: Iteration 22 [2026-05-26T12:04:05Z]: Progress/docs site buildable for Cloudflare Pages (US-021) completed (no CI workflows found -- pre-CI bootstrap story). Local gate: pass. Attempt 1.

Outcome: CI skipped for this iteration (pre-CI work, e.g. scaffolding before the pipeline existed). Not gated.

Changed: commit not identified in metrics for this iteration.

⏭ Loop 21 · US-020 — Failure-modes catalog, runbooks, and threat model docs

⏭ ci-skipped · attempt 1 · 12m 47s ·

Goal: Failure-modes catalog, runbooks, and threat model docs — As the on-call homeowner, I need every failure mode and operator action documented so a 2 a.m.

What this loop did: Iteration 21 [2026-05-26T11:43:47Z]: Failure-modes catalog, runbooks, and threat model docs (US-020) completed (no CI workflows found -- pre-CI bootstrap story). Local gate: pass. Attempt 1.

Outcome: CI skipped for this iteration (pre-CI work, e.g. scaffolding before the pipeline existed). Not gated.

Changed: commit not identified in metrics for this iteration.

⏭ Loop 20 · US-019 — Hardened docker-compose deployment

⏭ ci-skipped · attempt 1 · 20m 17s ·

Goal: Hardened docker-compose deployment — As the operator, I need the compose deployment to enforce least privilege so a compromised container cannot reach the host, other containers, or the public internet.

What this loop did: Iteration 20 [2026-05-26T11:31:01Z]: Hardened docker-compose deployment (US-019) completed (no CI workflows found -- pre-CI bootstrap story). Local gate: pass. Attempt 1.

Outcome: CI skipped for this iteration (pre-CI work, e.g. scaffolding before the pipeline existed). Not gated.

Changed: commit not identified in metrics for this iteration.

⏭ Loop 19 · US-018 — Containerization (Dockerfile, digest-pinned, non-root, read-only rootfs)

⏭ ci-skipped · attempt 1 · 19m 47s ·

Goal: Containerization (Dockerfile, digest-pinned, non-root, read-only rootfs) — As a developer, I need a hardened container image so the service can deploy to the home-lab VM with verifiable provenance.

What this loop did: Iteration 19 [2026-05-26T11:10:43Z]: Containerization (Dockerfile, digest-pinned, non-root, read-only rootfs) (US-018) completed (no CI workflows found -- pre-CI bootstrap story). Local gate: pass. Attempt 1.

Outcome: CI skipped for this iteration (pre-CI work, e.g. scaffolding before the pipeline existed). Not gated.

Changed: commit not identified in metrics for this iteration.

⏭ Loop 18 · US-017 — Home Assistant configuration artifacts and drift check

⏭ ci-skipped · attempt 1 · 15m 17s ·

Goal: Home Assistant configuration artifacts and drift check — As the operator, I need the HA zones, high-accuracy automation, and broker ACL defined so the service receives timely location and the broker is scoped.

What this loop did: Iteration 18 [2026-05-26T10:50:57Z]: Home Assistant configuration artifacts and drift check (US-017) completed (no CI workflows found -- pre-CI bootstrap story). Local gate: pass. Attempt 1.

Outcome: CI skipped for this iteration (pre-CI work, e.g. scaffolding before the pipeline existed). Not gated.

Changed: commit not identified in metrics for this iteration.

✅ Loop 17 · US-016 — End-to-end simulation/replay harness (CI completion gate)

✅ CI pass · attempt 1 · 22m 16s ·

Goal: End-to-end simulation/replay harness (CI completion gate) — As a developer, I need to replay GPS traces end-to-end so behavior is validated without hardware and serves as the integration gate.

What this loop did: Iteration 17 [2026-05-26T10:35:37Z]: End-to-end simulation/replay harness (CI completion gate) (US-016) COMPLETED. CI passed (run 26447183173). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26447183173)

Changed: commit not identified in metrics for this iteration.

✅ Loop 16 · US-015 — Degraded-operation behavior

✅ CI pass · attempt 1 · 22m 54s ·

Goal: Degraded-operation behavior — As the system, I must fail safe when dependencies are unavailable.

What this loop did: Iteration 16 [2026-05-26T10:13:17Z]: Degraded-operation behavior (US-015) COMPLETED. CI passed (run 26446168610). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26446168610)

Changed: commit not identified in metrics for this iteration.

✅ Loop 15 · US-014 — State recovery on restart

✅ CI pass · attempt 1 · 17m 46s ·

Goal: State recovery on restart — As the system, I must behave safely if Home Assistant or the service restarts mid-session.

What this loop did: Iteration 15 [2026-05-26T09:50:20Z]: State recovery on restart (US-014) COMPLETED. CI passed (run 26445102183). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26445102183)

Changed: commit not identified in metrics for this iteration.

✅ Loop 14 · US-013 — Alerting via Home Assistant notification topic

✅ CI pass · attempt 1 · 17m 44s ·

Goal: Alerting via Home Assistant notification topic — As the operator, I want deduped, runbook-linked alerts so I notice failures and anomalies without alert storms.

What this loop did: Iteration 14 [2026-05-26T09:32:31Z]: Alerting via Home Assistant notification topic (US-013) COMPLETED. CI passed (run 26444261265). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26444261265)

Changed: commit not identified in metrics for this iteration.

✅ Loop 13 · US-012 — Runtime configuration and health API

✅ CI pass · attempt 1 · 22m 44s ·

Goal: Runtime configuration and health API — As the operator (and a future C2 site), I want an authenticated REST API to read/update config at runtime and check health.

What this loop did: Iteration 13 [2026-05-26T09:14:43Z]: Runtime configuration and health API (US-012) COMPLETED. CI passed (run 26443405553). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26443405553)

Changed: commit not identified in metrics for this iteration.

✅ Loop 12 · US-011 — Safety caps and structured audit logging

✅ CI pass · attempt 1 · 23m 14s ·

Goal: Safety caps and structured audit logging — As the operator, I need hard limits and a full decision trail so the system can never runaway-pulse and I can answer why the gate opened.

What this loop did: Iteration 12 [2026-05-26T08:51:55Z]: Safety caps and structured audit logging (US-011) COMPLETED. CI passed (run 26442327730). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26442327730)

Changed: commit not identified in metrics for this iteration.

✅ Loop 11 · US-010 — Prometheus metrics and liveness/readiness probes

✅ CI pass · attempt 1 · 18m 15s ·

Goal: Prometheus metrics and liveness/readiness probes — As the operator, I need a metrics registry, a /metrics endpoint, and livez/readyz probes so the service is observable and a Docker healthcheck can target it.

What this loop did: Iteration 11 [2026-05-26T08:28:38Z]: Prometheus metrics and liveness/readiness probes (US-010) COMPLETED. CI passed (run 26441244980). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26441244980)

Changed: commit not identified in metrics for this iteration.

✅ Loop 10 · US-009 — Pulse coordinator and actuator publisher

✅ CI pass · attempt 1 · 11m 39s ·

Goal: Pulse coordinator and actuator publisher — As the system, I must keep the gate open across the approach by re-pulsing under the auto-close timer, deduped across both phones.

What this loop did: Iteration 10 [2026-05-26T08:10:20Z]: Pulse coordinator and actuator publisher (US-009) COMPLETED. CI passed (run 26440426467). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26440426467)

Changed: commit not identified in metrics for this iteration.

✅ Loop 9 · US-008 — Singleton publish guard via flock

✅ CI pass · attempt 1 · 7m 37s ·

Goal: Singleton publish guard via flock — As the operator, I need at most one process able to publish gate/cmd so a duplicate instance cannot double-pulse the gate.

What this loop did: Iteration 9 [2026-05-26T07:58:38Z]: Singleton publish guard via flock (US-008) COMPLETED. CI passed (run 26439906550). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26439906550)

Changed: commit not identified in metrics for this iteration.

✅ Loop 8 · US-007 — Per-entity state machine

✅ CI pass · attempt 1 · 11m 37s ·

Goal: Per-entity state machine — As the system, I need a per-entity state machine so each phone independently progresses IDLE->ARMED->TRIGGERING->ARRIVED/STALE and re-arms correctly.

What this loop did: Iteration 8 [2026-05-26T07:50:57Z]: Per-entity state machine (US-007) COMPLETED. CI passed (run 26439566416). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26439566416)

Changed: commit not identified in metrics for this iteration.

✅ Loop 7 · US-006 — Geometry module: zone membership, distance, and ground speed

✅ CI pass · attempt 1 · 7m 6s ·

Goal: Geometry module: zone membership, distance, and ground speed — As the system, I need geometry primitives so I can decide which zone an entity is in and how fast it is moving.

What this loop did: Iteration 7 [2026-05-26T07:39:16Z]: Geometry module: zone membership, distance, and ground speed (US-006) COMPLETED. CI passed (run 26439056290). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26439056290)

Changed: commit not identified in metrics for this iteration.

✅ Loop 6 · US-005 — Location ingest with freshness and accuracy guards

✅ CI pass · attempt 1 · 9m 7s ·

Goal: Location ingest with freshness and accuracy guards — As the system, I must reject unreliable location fixes so stale or drifting GPS cannot cause bad decisions.

What this loop did: Iteration 6 [2026-05-26T07:32:06Z]: Location ingest with freshness and accuracy guards (US-005) COMPLETED. CI passed (run 26438752854). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26438752854)

Changed: commit not identified in metrics for this iteration.

✅ Loop 5 · US-004 — MQTT client wrapper with TLS, auth, reconnect, and retain=false

✅ CI pass · attempt 1 · 13m 7s ·

Goal: MQTT client wrapper with TLS, auth, reconnect, and retain=false — As a developer, I need a resilient, authenticated MQTT client so the service survives broker restarts and cannot leak or replay commands.

What this loop did: Iteration 5 [2026-05-26T07:22:55Z]: MQTT client wrapper with TLS, auth, reconnect, and retain=false (US-004) COMPLETED. CI passed (run 26438350924). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26438350924)

Changed: commit not identified in metrics for this iteration.

✅ Loop 4 · US-003 — Persistence layer with migrations and append-only audit

✅ CI pass · attempt 1 · 10m 6s ·

Goal: Persistence layer with migrations and append-only audit — As a developer, I need a SQLite-backed repository with migrations so config, latches, and audit rows persist behind a swappable interface.

What this loop did: Iteration 4 [2026-05-26T07:09:44Z]: Persistence layer with migrations and append-only audit (US-003) COMPLETED. CI passed (run 26437799049). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26437799049)

Changed: commit not identified in metrics for this iteration.

✅ Loop 3 · US-002 — Typed configuration model with validation and secret-by-reference

✅ CI pass · attempt 1 · 14m 6s ·

Goal: Typed configuration model with validation and secret-by-reference — As a developer, I need a validated, typed config model so all tunables live in one place and unsafe configs are rejected.

What this loop did: Iteration 3 [2026-05-26T06:59:35Z]: Typed configuration model with validation and secret-by-reference (US-002) COMPLETED. CI passed (run 26437382944). Local gate: pass. Attempt 1.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26437382944)

Changed: commit not identified in metrics for this iteration.

✅ Loop 2 · US-001 — Project scaffold and GitHub Actions CI pipeline

✅ CI pass · attempt 2 · 6m 59s ·

Goal: Project scaffold and GitHub Actions CI pipeline — As a developer, I need a Python project skeleton and a CI pipeline so every later story is gated on green CI.

What this loop did: Iteration 2 [2026-05-26T06:45:26Z]: Project scaffold and GitHub Actions CI pipeline (US-001) COMPLETED. CI passed (run 26436816924). Local gate: pass. Attempt 2.

Outcome: CI passed — lint, typecheck, and pytest green on GitHub Actions. (run 26436816924)

Changed: commit not identified in metrics for this iteration.

❌ Loop 1 · US-001 — Project scaffold and GitHub Actions CI pipeline

❌ CI fail · attempt 1 · 12m 12s ·

Goal: Project scaffold and GitHub Actions CI pipeline — As a developer, I need a Python project skeleton and a CI pipeline so every later story is gated on green CI.

What this loop did: Iteration 1 [2026-05-26T05:57:41Z]: Project scaffold and GitHub Actions CI pipeline (US-001) CI FAILED (attempt 1). 1 lane(s) failed. Local gate: pass. Story NOT marked complete. The next iteration should debug this failure. Per-lane CI failure tails (last 50 lines per failing lane): === Workflow: CI (run 26435103611, conclusion=failure) === log not found: 77816014277

Outcome: CI failed — CI FAILED (attempt 1). 1 lane(s) failed. Local gate: pass. See run 26435103611.

Changed: commit not identified in metrics for this iteration.

Retry sagas

Stories that took more than one loop attempt before CI went green (or are still being retried). Each attempt has its own explained card above.

Story Attempts Latest result
US-001 2 ✅ CI pass

Last regenerated: .