decision(P00): clarification — v0.2 MVP ambiguities resolved (D-007..D-020)

---ci---
phase: 0
milestone: v0.2
status: clarify
decisions:
  - id: D-007
    decision: Backend layout = backend/ package at repo root, src-layout researchx with config/, methodologies/, adapters/, engine/, evidence/, trust/, api/ per ARCHITECTURE §Components
    rationale: Matches ARCHITECTURE.md component map 1:1; src-layout avoids import shadowing; uv manages the venv + Python 3.12
    confidence: 0.95
    alternatives: [flat single-package, api-first layout]
  - id: D-008
    decision: Provision uv + uv-managed Python 3.12 (official installer, user-local ~/.local/bin); system Python is 3.11.2
    rationale: REQ-33 mandates uv-managed Python 3.12; uv fetches a managed CPython so the system interpreter is irrelevant
    confidence: 0.9
    alternatives: [system 3.11 venv + pip (deviation from spec)]
  - id: D-009
    decision: Backend serves on 127.0.0.1:8000; frontend dev on :3000 calls backend via NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8000; FastAPI CORS allows localhost:3000
    rationale: FastAPI/uvicorn defaults for a local-first single-user tool; frontend env var keeps the ApiClient boundary intact
    confidence: 0.85
    alternatives: [single-origin reverse proxy, fixed port 3001]
  - id: D-010
    decision: Run IDs = 8-char nanoid-style lowercase alnum slug (e.g. r-3f9k2q7z), generated by the API layer; storage layout = <storage_dir>/runs/<run_id>/
    rationale: Human-referenceable in URLs and folders; nanoid avoids UUID's file-name collisions with 8 chars at 62-alphabet entropy (~10^14)
    confidence: 0.8
    alternatives: [uuid4 hex, monotonic counter]
  - id: D-011
    decision: Ollama endpoints resolve from secrets scopes: base URLs via OLLAMA_BASE_URL (default https://ollama.com/v1) and fallback http://localhost:11434/v1; API key via OLLAMA_API_KEY from .ciagent/.env.secrets only
    rationale: config.json secrets.model scope already declares OLLAMA_API_KEY + OLLAMA_BASE_URL; env-file secrets-loader is the only resolution path per ship/verify credential rules
    confidence: 0.9
    alternatives: [config.json fields, interactive setup]
  - id: D-012
    decision: DuckDuckGo keyless fallback = HTML-lite endpoint (html.duckduckgo.com/html) scraped conservatively (top results, title+url+snippet); Tavily remains the default search adapter via TAVILY_API_KEY
    rationale: Keyless fallback must need zero credentials; the html endpoint requires none and is the standard keyless DDG surface; Tavily default per REQ-09
    confidence: 0.75
    alternatives: [duckduckgo_search python lib, searx instance]
  - id: D-013
    decision: OpenAPI client generation = openapi-typescript (types) + openapi-fetch (typed fetch client) into frontend/src/lib/api/generated/, bound behind the existing ApiClient interface in P3
    rationale: Zero-runtime overhead, TS-strict, produces a typed fetch function that wraps cleanly behind ApiClient; avoids heavyweight SDK codegen
    confidence: 0.8
    alternatives: [orval, @openapitools/openapi-generator-cli]
  - id: D-014
    decision: SSE transport = GET /api/runs/{id}/events via sse-starlette EventSourceResponse; typed event bus is asyncio.Queue-fed with pydantic event models (run_started, query_fired, source_visited, passage_extracted, state_changed, run_done, run_error)
    rationale: sse-starlette is the maintained FastAPI SSE standard; pydantic events give the typed bus REQ-16 demands; matches the MockApiClient replay contract already in the frontend
    confidence: 0.85
    alternatives: [websockets, polling]
  - id: D-015
    decision: Quote-anchoring fuzzy match = normalized token-subset + difflib SequenceMatcher ratio >= 0.85 as the primary check; exact substring match short-circuits; mismatch flagged in the ledger, never silently dropped
    rationale: difflib is stdlib (zero deps), 0.85 threshold balances recall/precision on quoted prose; flagged mismatches preserve trust-loop transparency
    confidence: 0.75
    alternatives: [rapidfuzz, embedding similarity]
  - id: D-016
    decision: URL liveness pass = HTTP HEAD/GET (follow redirects, 10s timeout) classifying 200-299=live, 3xx-stale-after-redirect-limit/stale-content=stale, 4xx/5xx/timeout/dead-url=dead; runs post-run per REQ-19; recheck at export
    rationale: Simple, deterministic classification REQ-19 names live/stale/dead; single GET with redirect following captures the common moved-page case
    confidence: 0.8
    alternatives: [HEAD-only, wayback fallback]
  - id: D-017
    decision: Engine durability = JSON state file per run (runs/<id>/state.json) rewritten atomically (tmp+rename) after every state transition + EventBus subscribers receive replay on resume; asyncio tasks checkpointed via state machine position, not mid-task
    rationale: REQ-15 demands pause/cancel/resume across restarts; atomic rewrite prevents torn files; transitions are natural checkpoint boundaries
    confidence: 0.85
    alternatives: [sqlite, append-only journal]
  - id: D-018
    decision: Run termination semantics for THIS pipeline run: phases ship P1..P4 + final P5; the milestone → main merge HALTS at the owner approval gate (merge_to_main escalation hook) exactly as v0.1 did — the run presents v0.2.5 and stops
    rationale: Owner-approved run directive (this session): 'P0–P5, halt at owner gate'; config.json escalation_hooks includes merge_to_main; v0.1 precedent (v0.1.8 halted, owner recorded approval in a follow-up)
    confidence: 1.0
    alternatives: [autonomous merge at full autonomy]
  - id: D-019
    decision: Deploy policy for this run = none; no LXC staging without an explicit owner directive (deploy escalation hook)
    rationale: Owner-approved run directive (this session): 'No deploys without owner directive'
    confidence: 1.0
    alternatives: [stage v0.2.x-uat at P5 gate]
  - id: D-020
    decision: Frontend stays on the v0.1 ApiClient boundary in P1/P2 (MockApiClient), P3 adds a generated OpenAPI client + ApiClient binding switch (env-flag RESEARCHX_API_MODE=mock|live with mock default), no component rewrites (REQ-26/REQ-54)
    rationale: Keeps REQ-54 zero-throwaway promise explicit; live binding only activated when the backend runs, mock remains default for dev/tests
    confidence: 0.9
    alternatives: [hard swap in P3, dual clients per component]
---/ci---

CLARIFY stage, milestone v0.2: 14 ambiguities resolved at full
autonomy (defaults accepted). Key resolutions: backend/ src-layout
package, uv + managed Python 3.12, run IDs as short slugs, DDG
html-lite keyless fallback, openapi-typescript client gen, sse-starlette
event transport, difflib quote anchoring at 0.85, JSON durable state
with atomic rewrites, owner-gate halt at P5, no deploys without
directive. PROJECT.md + REQUIREMENTS.md updated with the clarified
constraints.
This commit is contained in:
coreci
2026-09-27 10:43:05 +00:00
parent 9a005f5c64
commit e79102b901
+2 -2
View File
@@ -1,8 +1,8 @@
{
"phase": 0,
"stage": "specify",
"stage": "clarify",
"milestone": "v0.2",
"phase_role": "pre_execution",
"attempts": 0,
"updated_at": "2026-09-27T10:20:00Z"
"updated_at": "2026-09-27T10:30:00Z"
}