Phase 1: Application spine (Reflex + reflex-local-auth + worker + admin auth) - #2
Merged
noelsaw1 merged 36 commits intoJul 21, 2026
Merged
Conversation
Same "one phase at a time" pattern as Phase 0's MARATHON-p0.yaml. Added a note to the p1 brief: extend Phase 0's existing pyproject.toml into the uv workspace root rather than overwrite it, and keep its tests/test_database_smoke.py 4/4 passing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Gitignoring these in the earlier cleanup broke marathon-drive.sh itself: it commits its own relay file under phases/<name>--<id>/ RELAY.md as part of firing a phase, and refuses when that path is gitignored (git add: "paths are ignored ... use -f"). Confirmed by Phase 1's first fire attempt failing before any build turn started (marathon-drive exit 1). Keep .tick/ ignored (genuinely per-device, never committed by the driver); handle phases/relay-system/ cleanup manually before a marathon branch merges to main instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…everted by a turn-taker))
First fire attempt escalated (exit 6, "off-lane edits") even though every file Codex touched (30 total, checked via the turn transcript) was semantically within apps/,packages/,migrations/,.github/, docs/architecture/,scripts/. Root cause: the harness's containment check (relay-turn-lib.sh rtl_in_allow) only matches an EXACT literal path, or a directory git reports as entirely new/untracked in one collapsed porcelain line -- it has no general prefix/glob matching. .github/ and docs/architecture/ already had Phase 0 files (database-image.yml, database.md), so new siblings there (.github/workflows/ci.yaml, two ADR .md files) were reported by git as INDIVIDUAL new-file entries, not a collapsed directory -- and neither matched the bare .github/ / docs/architecture/ allowlist tokens. Fixed by pinning exact expected paths instead of bare directories: .github/workflows/ci.yml (PROJECT.md's own canonical name -- also fixes a .yml/.yaml mismatch) and docs/architecture/decisions/ (a brand-new subdirectory, so it collapses and matches directly -- also the structurally correct ADR location per PROJECT.md's required layout, not loose files under docs/architecture/). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…everted by a turn-taker))
Same containment-allowlist class of bug as the .github//docs architecture fix, missed on the first pass: scripts/ also already has Phase 0 content (verify-extensions), so a new scripts/bootstrap-admin* file there is reported by git as an individual new-file entry, not a collapsed new directory, and fails the bare scripts/ allowlist token's exact-match check. Fixed architecturally, not just by allowlisting another exact path: PROJECT.md's own bootstrap-admin invocation (`uv run starter-admin create-initial-admin`) names a registered console-script entry point, not a standalone script file -- so it belongs inside packages/core/ (fully open, brand-new scope) via [project.scripts], never under the pre-populated scripts/ directory. Dropped scripts/ from Phase 1's artifact scope entirely since nothing in the brief now needs it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…everted by a turn-taker))
…everted by a turn-taker))
Codex's build turn produced a real, working uv workspace: apps/web (Reflex + reflex-local-auth + FastAPI), apps/worker (ARQ), packages/core (models, db, auth, config, services, analysis, worker, logging, CLI), Alembic migration scaffolding, a full docker-compose stack (postgres + redis + web + worker), a CI workflow, and 2 ADRs (worker choice, ARQ; reflex-local-auth security review) numbered per PROJECT.md's own pre-planned ADR scheme (0004, 0006). Every marathon fire attempt escalated on a containment violation before reaching the reviewer round (agy never reviewed this), so this landed via direct hands-on verification, not an approved relay -- same discipline as Phase 0's manual close-out, but Phase 1's scope is much larger and this verification is intentionally BOUNDED, not exhaustive: Verified: - `uv sync --all-packages` resolves the whole workspace cleanly. - starter_core, starter_web, starter_worker, and every starter_core submodule import without error. - Migration files compile (py_compile) without a syntax error. - Phase 0's own gate is unaffected: uv run pytest -m 'database or integration' -> still 4 passed after the pyproject.toml/uv.lock change. - `starter-admin = "starter_core.cli:main"` is a registered [project.scripts] entry point, per the brief's redirect. NOT verified (real gaps, not swept under the rug): - migrations/alembic.ini is MISSING -- `alembic upgrade head` cannot run yet, so "migrations run" (an exit-gate criterion) is unmet. - No fresh-clone bootstrap attempted. - No health/readiness endpoint actually exercised. - No auth flow (login/logout/bootstrap-admin/role-enforcement) actually run end-to-end -- only that the code imports. - No mock-analysis vertical-slice job actually executed. - Redis wiring in apps/worker/main.py not exercised against a live Redis. Next: author migrations/alembic.ini, then work through the remaining exit-gate criteria in briefs/p1-application-spine.md one at a time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
README replaces the "Coming soon" placeholder with a real stack/layout overview; LICENSE is Apache 2.0 (c) Neochrome, Inc. Also marks the exact trigger in the build plan (Phase 1 exit gate verified + merged to main) for when to rewrite the README against actual repo contents and run /front-door + /shakedown before calling onboarding solid. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… progress Frontmatter/status table were stuck at "Phase 0 not yet started" despite Phase 0 being closed and merged to main. Updated the Status table, added a Status column to the phase table of contents, and converted Phase 0 and Phase 1's deliverable/ exit-gate bullet lists to checkboxes reflecting what's actually verified vs. only code-written — so a fresh agent can pick up Phase 1 from the unchecked items instead of re-deriving the gap list (missing alembic.ini, no app-level tests, no reviewer approval, vertical slice not exercised end-to-end). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Configured alembic.ini for psycopg - Added reflex-local-auth tables to Alembic migration - Registered localuser in SQLAlchemy metadata - Added unit tests for auth and vertical slice - Marked README.md as Alpha (Phase 1 Complete)
FRONTDOOR.md marked FD-01/02/03 as fixed but the fixes never landed: README lacked the uv prereq + alembic upgrade head step, and pytest required manually exporting DATABASE_URL. Verified: uv run pytest now passes 5/5 out of the box via the documented steps. Also fixes phase-status drift across README/ROADMAP/marathon docs — README claimed "Phase 1 Complete" while PROJECT.md (source of truth) records Phase 0 done/merged and Phase 1 in progress/unverified/unmerged. ROADMAP.md's Phase 0 gate line was stale for the same reason. Adds missing PDDA frontmatter, Status tables, and ROADMAP coverage (roadmap_exempt for sub-briefs) to MARATHON-PLAN.md and its 7 phase briefs, clearing all 25 errors from `utils/pdda/pdda.sh run` — the plan's stale "Draft — not fired" status is corrected to reflect that Phase 0 was fired and escalated (turn-timeout-or-hang). Also lands the .claude/skills/pdda/SKILL.md path-anchor fix found by a prior /shakedown run (utils/pdda/pdda.sh was invoked CWD-relative, breaking outside the repo root). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
REPO_MAP.md and .aider/studio/repomap-cache.json are regenerated by Aider Studio and already gitignored going forward; untrack them so they stop showing as dirty. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
.github/workflows/ci.yml runs `uv run ruff check` and `uv run pyright` but neither was declared anywhere in the workspace — CI's last 3 runs failed on "Failed to spawn: ruff: No such file or directory" before any lint, type-check, or test evidence was produced. Note: with the tools actually installed, ruff finds 46 real E701/E702 violations (compound one-liner statements, all in packages/core and apps/*) and pyright finds 2 real type errors in auth_adapter.py. Not fixed here — that's app-code changes beyond the dependency gap; left for a separate pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…n, export working-directory - Split 46 E701/E702 compound one-liners (if/raise, semicolon-chained statements) across auth_adapter.py, migrations/env.py, 0001_application_spine.py, analysis.py, auth.py, cli.py, models.py, services.py, worker.py — cosmetic, no behavior change. - auth_adapter.py: add missing @rx.event on do_logout (fixes pyright reportIncompatibleVariableOverride — parent's do_logout is @rx.event, the override wasn't); capture require()'s return in create_user instead of discarding it (fixes reportOptionalMemberAccess on principal.user_id, and was a real narrowing bug, not just a type-checker complaint). - rxconfig.py: add app_module_import="starter_web.app" — Reflex's default convention expects starter_web/starter_web.py, but the app lives in src/starter_web/app.py (src-layout). Without this, `reflex export`/`reflex run` never worked at all: "Module starter_web.starter_web not found". This was never caught because no CI run ever got past the earlier ruff/pyright gap to reach this step. - ci.yml: add working-directory: apps/web to the reflex export step — rxconfig.py lives there, not at repo root, so the step 404'd on "rxconfig.py not found" even with the module fix applied. - apps/web/.gitignore, apps/web/reflex.lock/: first successful `reflex init` in this repo's history generated these; committing the lockfile for reproducible frontend builds (same rationale as uv.lock). Verified locally: ruff check, pyright, pytest -m "not database", and reflex export --frontend-only --no-zip (from apps/web) all pass clean.
PROJECT.md's Phase 1 exit gate said 0/9 items verified and alembic.ini was
missing. Both were stale: alembic.ini exists, and with CI now actually green
it was possible to run real commands instead of re-reading checkboxes.
Verified this pass, with commands recorded in PROJECT.md:
- alembic upgrade head / alembic current -> 0001_application_spine (head)
- reflex run --backend-only starts; /api/v1/{health,readiness,version} all
respond correctly over real HTTP
- tests/test_vertical_slice.py passes (dataset -> job -> worker -> mock
engine -> SUCCEEDED)
- uv run pytest -v: 7/7 passing
5 of 9 exit-gate items flip from unverified to verified. The 4 that remain
open are real and named explicitly: fresh-clone bootstrap, admin-bootstrap
idempotency, login/logout/disabled-user session tests, and
orphan-profile-resolves-unauthorized. No reviewer has approved Phase 1 --
that is still the actual blocker to merge, not the exit gate itself.
ROADMAP.md and CHANGELOG.md updated to match. pdda.sh run: all checks pass.
Real, running tests against a live Postgres session -- not just imports: - tests/test_admin_bootstrap.py: starter-admin create-initial-admin succeeds once, is refused on a second run while an admin exists, and rejects a too-short password. - tests/test_auth_sessions.py: login success + logout ends the session (asserted against localauthsession row count, not just is_authenticated), wrong password rejected, disabled user rejected with no session created, principal resolves the correct role after login, and a LocalUser with no user_profiles row resolves to unauthorized (principal is None) -- the orphan-profile gate PROJECT.md calls out explicitly. - tests/test_registration_gating.py: the register route is absent from the compiled app when ALLOW_OPEN_REGISTRATION is off (the default); login route is present. Both new integration files instantiate reflex_local_auth's real State classes rather than re-implementing their logic: building a proper root state tree via rx.State(_reflex_internal_init=True) + get_substate(...) so inherited vars (auth_token) resolve through a real parent_state, instead of the "'NoneType' object has no attribute 'auth_token'" crash a bare AppAuthState()/LoginState() hits. rx.session()'s DB engine resolves through reflex's own config (rxconfig.py), discoverable only with CWD=apps/web, so both files briefly chdir there for the triggering import and restore CWD. Also removed an unused `import os` in test_vertical_slice.py (unrelated, noticed in passing -- tests/ isn't in CI's ruff/pyright scope so it was never caught, but there's no reason to leave it). 16/16 tests pass, ruff and pyright both clean (CI scope: apps packages migrations). Remaining exit-gate item: a real fresh-clone bootstrap dry run (next commit).
added 6 commits
July 21, 2026 16:25
Fixed README.md: plain 'uv sync' doesn't install this uv workspace's members (only --all-packages does), so migrations broke immediately on a genuine fresh clone with ModuleNotFoundError: No module named 'starter_core'. Confirmed the fix by re-cloning and running the documented steps verbatim a second time: 12/12 database/integration tests pass. Reconciled PROJECT.md/ROADMAP.md/CHANGELOG.md against the new test evidence (see prior commit) and this fix. Also corrected a miscount in the earlier same-day exit-gate update: it's 10 items, not 9. pdda.sh run: all checks pass. Full suite: 16/16.
phases/ (marathon-drive.sh's own turn logs from this branch's 6 fires) and relay-system/ (this session's p1-alpha-merge-qa relay thread + a stray codex-turn log) are build-tooling transcripts, not application deliverables -- same rationale as f1c89d6, and the documented policy in .gitignore's own comment: "before merging a marathon branch, manually git rm -r phases/ relay-system/". PROJECT.md's pointer to the relay thread is updated to a permalink at the last commit where the file existed (a6635e2), since the relative link would otherwise 404 on main once this lands.
…ations Discovered opening PR #2: this workflow only triggers on pull_request (and push to main), so it never ran against this branch until the PR was opened -- meaning this bug was pre-existing and unrelated to today's new tests. uv run pytest implicitly syncs, but not with --all-packages, so starter_core/reflex/reflex_local_auth were never installed; even pre-existing tests/test_auth.py and tests/test_vertical_slice.py failed collection here, not just the new auth/bootstrap tests. Added uv sync --all-packages (matching ci.yml) and uv run alembic upgrade head (the app-level tables test_vertical_slice.py and the new auth tests need don't exist without it -- this workflow never ran migrations). Verified locally against a truly fresh container (docker compose down -v, rebuild, up) running the exact new step sequence: 12/12 database or integration tests pass.
noelsaw1
pushed a commit
that referenced
this pull request
Jul 22, 2026
Replaced the example block's placeholder values with the real release: Phase 0 (Postgres 16 + AGE + pgvector) plus Phase 1 (Reflex app, reflex-local-auth roles, worker, mock engine) -- exit gate 10/10 verified, merged via PR #2, relay-approved. Kept the QA-gate fields to the contract's exact Yes/No format (PROJECT/PDDA.md's "RELEASES.md -- release ledger" section) rather than the free-text detail from an earlier draft, which broke pdda.sh releases' block parser. `pdda.sh releases`: 0 errors, 0 warnings.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 1 of the Reflex Hybrid Analytics Starter build plan — the application spine on top of Phase 0's proven PostgreSQL 16 + AGE + pgvector image (merged in PR #1). Ships:
apps/*+packages/*), shared config, SQLAlchemy session management, Alembic migrations.UserProfile,Dataset,AnalysisJob,AuditEvent)./api/v1/{health,readiness,version}endpoints.reflex-local-authintegration: app-owneduser_profilestable carrying role, admin/member/viewer authorization matrix, bootstrap-admin CLI, registration gated off by default.Full Phase 1 exit gate (10/10 items) is met — see
PROJECT/2-WORKING/PROJECT.md's Phase 1 section for the itemized checklist with commands and evidence for each. Independently reviewed and approved via a cli-driven relay with Agy (verdict Round 3: "The branch is safe and appropriate to merge intomainas an alpha"; content preserved ata6635e2).What changed in this session (beyond the marathon-driven Phase 1 code)
CI had never once passed on this branch before this session. Fixed, in order:
ruff/pyrightdev dependencies (CI failed before reaching any check).E701/E702ruff violations and 2 real pyright errors — one of which (auth_adapter.py'sdo_logoutmissing@rx.event, andcreate_userdiscardingrequire()'s return value) were live bugs, not just lint noise.reflex exporthad never once succeeded — wrong Reflex module convention (app_module_importmissing) plus a missingworking-directory: apps/webon the CI step.README.mdsaiduv sync, but thisuvworkspace requires--all-packagesorpackages/corenever installs — migrations failed immediately withModuleNotFoundErroron a genuine fresh clone..github/workflows/database-image.yml(Phase 0's compatibility check,pull_request-triggered) never ranuv sync --all-packagesoralembic upgrade head— the exact same class of bug as Technical assessment — implementation state, defects, and test coverage (read-only review of main @ e1bb8c6) #4, but pre-existing and unrelated to this session's new tests. It never surfaced before because that workflow only triggers onpull_request/push-to-main, so it never ran against this branch until this PR opened. Even pre-existingtests/test_auth.pyandtests/test_vertical_slice.pyfailed collection there. Fixed and verified against a truly fresh container (docker compose down -v+ rebuild).Then closed all 5 remaining Phase 1 exit-gate gaps with real, running tests (not just imports):
tests/test_admin_bootstrap.py,tests/test_auth_sessions.py(real login/logout/disabled-user/role-resolution/orphan-profile behavior through reflex_local_auth's actual state classes, not reimplemented logic),tests/test_registration_gating.py.Known, still-open gaps (not blockers for this alpha merge)
web+worker+redisDocker Compose stack has never been brought up together (onlycompose.test.yaml'spostgresservice has).These are documented in
PROJECT.md's Deliverables list as open, separate from the exit gate this PR closes.Test plan
uv run ruff check apps packages migrations— cleanuv run pyright apps packages— 0 errorsuv run pytest -v— 16/16 pass (compose.test.yamlpostgres up)uv run alembic upgrade head— clean,0001_application_spine (head)uv run reflex run --backend-only+ curl on all 3 API endpoints — correct responsesquality×2,compatibility)🤖 Generated with Claude Code