ship: v0.3 phase 0 — pre-execution (specify, clarify, research, plan, grill, mvp/ux) #16

Merged
coreci-bot merged 5 commits from phase/00-pre-execution into milestone/v0.3-hosted 2026-09-29 17:13:33 +00:00

5 Commits

Author SHA1 Message Date
coreci 3ea96c0984 docs(grill): adversarial review — 11 challenges, 10 binding decisions, 1 escalation
---ci---
project: researchx
phase: 0
milestone: v0.3
status: grill
decisions:
  - id: G-101
    decision: "Startup sweep handles PLANNING runs: C3-11's 'verified' CANCELLED-legality claim is false (states.py PLANNING to {AWAITING_APPROVAL,FAILED}); add PLANNING to CANCELLED in _LEGAL_TRANSITIONS or document a bypass; pinned by sweep test in T-1.8"
    rationale: "A crash mid-PLANNING leaves a persisted state the sweep cannot legally cancel — restart-path corruption at the first hosted redeploy"
    confidence: 0.90
  - id: G-102
    decision: "Stale out-of-scope rows fixed (REQUIREMENTS 'Auth/multi-tenancy' + PROJECT 'multi-tenant/auth') to match the recorded supersession"
    rationale: "Same-file contradictions with the owner-recorded v0.3 supersession"
    confidence: 0.95
  - id: G-103
    decision: "REQ-75 split bound (delegation-end P3, hosted-default P4); traceability row fixed; PLAN's pitfall-phase mapping authoritative over C3-19"
    rationale: "G-001-class triple-mapping contradiction across three documents"
    confidence: 0.85
  - id: G-104
    decision: "Exposure hardening: password min length 12 at register + admin-create; per-IP login rate limit at HAProxy front; audit asserts throttle + uniform 401s"
    rationale: "Public URL + argon2 ~50-100ms CPU per verify = DoS amplifier; no policy existed anywhere"
    confidence: 0.80
  - id: G-105
    decision: "KEK-loss posture stated: loss = per-user keys unrecoverable, honest degradation, re-entry under re-provisioned KEK; owner backs up off-repo; rotation out of v0.3 (recorded)"
    rationale: "KEK becomes load-bearing at P2; loss posture was stated nowhere"
    confidence: 0.85
  - id: G-106
    decision: "deploy.sh: free-disk assert + dangling-image prune + documented 4-container host budget"
    rationale: "P4 doubles image families on a single LXC host; current script has neither"
    confidence: 0.75
  - id: G-107
    decision: "Staged deploy defined: candidate pair on the host, health green, four smoke assertions host-locally; public cutover is a discrete owner-directive act with -prev armed"
    rationale: "P4's DoD used an undefined term; the host runs one container today"
    confidence: 0.75
  - id: G-108
    decision: "P3 pressure valve (G-006 precedent): may ship as P3a backend + P3b frontend patches without scope change"
    rationale: "P3 is the heaviest phase (7 REQs + standalone client + all swaps) with no pre-agreed valve"
    confidence: 0.70
  - id: G-109
    decision: "Task-inbox honesty: live-mode status derived (answered-iff DONE); checkbox display-honest in live mode; persisted check state is v0.4+; pinned by test"
    rationale: "REQ-78 promises 'checkable' but the derived GET-only read-model silently reverts user checks"
    confidence: 0.75
  - id: G-110
    decision: "Cost posture stated: server-tier runs bill the owner's key; bound = admin-managed accounts; per-user counters surface usage; no hard cap in v0.3"
    rationale: "Server-key-as-fallback (owner-picked) with the cost ceiling stated nowhere"
    confidence: 0.70
escalations:
  - "[G-111] P4's DoD (staged smoke on the LXC host) requires a host deployment D-019 forbids without a directive — owner must pre-authorize staged deploys for P4 DoD (no public cutover) or accept host proofs deferred to the gate"
requirements:
  covered: []
  partial: []
---/ci---

Verdict: Proceed (0.68). 11/12 sampled research claims verified
exact; the one false claim (sweep legality) bound as G-101 with a
test pin. G-102/G-103 applied to REQUIREMENTS/PROJECT/PLAN in this
commit; G-101/104..110 encoded as PLAN must-haves; G-111 visible via
ciagent audit.
2026-09-29 17:12:28 +00:00
coreci dca86a74ad docs(P00): create phase plans — v0.3 hosted P1..P4 + P5 note
---ci---
project: researchx
phase: 0
milestone: v0.3
status: plan
requirements:
  covered: []
  partial: []
message: >-
  PLAN.md: vertical-slice plans for P1 (auth+sessions+scoping, T-1.1..
  T-1.12), P2 (per-user settings+model keys+factory, T-2.1..T-2.11),
  P3 (live surfaces, read-models, report.json/evidence.json,
  standalone live client, T-3.1..T-3.13), P4 (hosting, live default,
  smoke+exposure audit, T-4.1..T-4.9); P5 final-review note. MVP/UX
  sections written (User-Facing Surface, Happy Path, UX Acceptance
  Criteria). C3-18 ApiClient extension list documented exhaustively
  before P1 (G-002 lesson). Pitfall ledger P-1..P-14 assigned and
  closable by v0.3.4. Carrying gates: G-005 snapshot+drift per
  route-touching phase, G-007 owner-keyed counters, G-008 DDG fixtures,
  D-018/D-019 halt+deploy.
---/ci---
2026-09-29 16:55:40 +00:00
coreci 6f5c4d5aa1 docs(P00): research findings — v0.3 hosted auth, key store, partitioning, hosting
---ci---
project: researchx
phase: 0
milestone: v0.3
status: research
requirements:
  covered: []
  partial: []
message: >-
  RESEARCH-v03.md: 12 topics, 20 conclusions (C3-01..C3-20), 14
  verified multi-user pitfalls (P-1..P-14). Key findings: shared-adapter
  fallback-mutation bug confirmed (ollama.py:73-79) — per-run factory
  mandatory; config_status reads private runs._actors (first multi-user
  breakage); all read endpoints die on restart — disk read-models are
  load-bearing for hosting; CORS lacks allow_credentials; module-level
  create_app() would serve fakes in production — new researchx.api.main
  entry required; frontend marker regex forces [p-N]→[^pN] normalization
  at the report.json boundary. ARCHITECTURE.md rewritten for v0.3 (new
  components: Identity & Sessions, Per-User Key Store, Model Adapter
  Factory; deployment topology). PERSONAS.md v0.3 roster: security-
  engineer phase-specific P1/P2/P4. config.json gains the 'keys'
  secrets scope (RESEARCHX_KEK).
---/ci---
2026-09-29 16:49:12 +00:00
coreci 33f4fa003a decision(P00): clarification — v0.3 hosted auth, key store, partitioning, live surfaces
---ci---
phase: 0
milestone: v0.3
status: clarify
decisions:
  - id: D-101
    decision: "Sessions: server-side store (in-process dict + durable sessions.json), httpOnly SameSite=Lax cookies, opaque tokens, 14-day sliding expiry, immediate revocation"
    rationale: "Single uvicorn worker makes in-process sound; server-side is the only posture satisfying REQ-70 deactivation"
    confidence: 0.90
  - id: D-102
    decision: "argon2id via argon2-cffi, verify via asyncio.to_thread"
    rationale: "REQ-69 names argon2id; standard maintained implementation"
    confidence: 0.95
  - id: D-103
    decision: "401 guard on all /api/* except login/register; CSRF = SameSite=Lax + custom header on non-GET; /healthz outside /api"
    rationale: "Same-origin proxy + JSON fetch + custom header kills cross-site CSRF without token round-trips"
    confidence: 0.82
  - id: D-104
    decision: "Register open only while user store empty (first user = admin); then admin-created accounts with admin-set passwords"
    rationale: "Only mechanism without CLI/mail; race-safe under single asyncio worker"
    confidence: 0.85
  - id: D-105
    decision: "user_id = 8-char slug, email login, no email verification"
    rationale: "House ID style; filesystem-safe partition key"
    confidence: 0.75
  - id: D-106
    decision: "User-prefixed storage runs/<user_id>/<run_id>/ (supersedes D-010); owner_id in RunState; no migration"
    rationale: "Defense in depth; per-user library = single directory scan"
    confidence: 0.88
  - id: D-107
    decision: "Data root = RESEARCHX_DATA_DIR (dev backend/var/researchx; container /var/lib/researchx)"
    rationale: "cwd-dependent storage breaks under container workdirs"
    confidence: 0.85
  - id: D-108
    decision: "KEK = RESEARCHX_KEK (env-file 'keys' scope), AES-256-GCM with user_id as AAD; disabled honestly when unconfigured"
    rationale: "REQ-73 mandate; graceful degradation keeps zero-key dev green"
    confidence: 0.83
  - id: D-109
    decision: "Key store scope = model key only in v0.3"
    rationale: "REQ-73/74 name model keys; minimal encrypted surface"
    confidence: 0.80
  - id: D-110
    decision: "Model adapter constructed per run via factory resolving user→server→keyless; probe per resolved tier"
    rationale: "Shared adapter mutates _active_base globally (verified bug); per-user keys impossible on shared instance"
    confidence: 0.90
  - id: D-111
    decision: "GET /api/config stays read-only status; new GET/PUT /api/config/settings for per-user settings; storage path display-only hosted"
    rationale: "Two read models already split in the ApiClient"
    confidence: 0.87
  - id: D-112
    decision: "Code default stays mock; hosted image built with NEXT_PUBLIC_RESEARCHX_API_MODE=live; no live delegation to mock after P3"
    rationale: "Build-time inlining of NEXT_PUBLIC vars; zero-key discipline preserved"
    confidence: 0.85
  - id: D-113
    decision: "REQ-82 in scope as backend work: persist report.json + evidence.json at DONE; endpoints enriched; live exports use real data"
    rationale: "Evidence is in-memory only today; hover quotes impossible without persisted artifacts"
    confidence: 0.90
  - id: D-114
    decision: "Open questions = plan sub-questions answered-iff DONE; no synthetic suggestions; empty state for new users"
    rationale: "Real, persisted, user-scoped, honest, zero extra model cost"
    confidence: 0.65
  - id: D-115
    decision: "Read-models read disk artifacts, not RunRegistry; in-flight runs marked cancelled on restart; rehydration out of v0.3 scope"
    rationale: "RunRegistry is in-process; disk-backed read-models mandatory for hosted restarts"
    confidence: 0.80
requirements:
  covered: []
  partial: []
---/ci---

REQUIREMENTS.md REQ-69..85 refined with the decision outcomes;
PROJECT.md carries the D-101..D-115 decision log.
2026-09-29 16:12:36 +00:00
coreci a4afcccd7f docs(init): validate specification — v0.3 hosted milestone
---ci---
project: researchx
phase: 0
milestone: v0.3
status: specify
requirements:
  covered: []
  partial: []
message: >-
  v0.3 'hosted' scope established (owner-directed 2026-09-29): auth +
  sessions (user/pass, admin-managed, OAuth deferred), per-user model
  API keys (encrypted at rest, resolution chain user→server→keyless),
  live-bound user-scoped surfaces (library, settings, suggestions,
  refine, graph/inbox, profile), backend hosting (SSE-safe), real
  inference as product default. Requirement set REQ-69..85 written;
  single-user/no-auth constraint superseded; config.json milestone_name
  set; ROADMAP v0.3 section added (P0..P5).
---/ci---
2026-09-29 16:06:47 +00:00