feat: persist dynamic rectification turns

This commit is contained in:
Jesse_Chen
2026-07-19 06:42:17 +08:00
parent 25d5552a05
commit 723d05c623
30 changed files with 2082 additions and 243 deletions
+86
View File
@@ -0,0 +1,86 @@
# Task 5 Report: Durable v2 Persistence and Legacy Isolation
## Outcome
Implemented durable `dynamic-choice-v2` persistence with a public/private data split. Public case rows contain only the public dynamic turn projection; the exact candidate model, persisted question binding, answers, server evidence, control state, and bounded Agent context are kept in a service-role-only companion row. Versioned turn and scoring-job writes are transactionally coordinated by service-role-only RPCs and never update `active_birth_time`.
New assessments initialize the public case, private state, and profile pointer in one database RPC transaction. Owner-scoped resume parsing requires complete v2 public/private rows and returns a strict legacy/v2 union; only absent protocol data from old legacy rows retains compatibility defaults. Active legacy cases can be upgraded without losing evidence or audit data, terminal legacy cases remain unchanged, and every legacy guided mutation entry point rejects v2 cases before writing.
## Files
- `frontend/supabase/migrations/20260718090000_dynamic_choice_birth_time_rectification.sql`
- `frontend/supabase/migrations/20260718091000_dynamic_choice_birth_time_transitions.sql`
- `frontend/src/lib/birth-time-evidence-service.ts`
- `frontend/src/lib/birth-time-guided-candidate.ts`
- `frontend/src/lib/birth-time-guided-draft-revision.ts`
- `frontend/src/lib/birth-time-journey-actions.ts`
- `frontend/src/lib/birth-time-journey-case-loader.ts`
- `frontend/src/lib/birth-time-journey-dynamic-case.ts`
- `frontend/src/lib/birth-time-journey-dynamic-persistence.ts`
- `frontend/src/lib/birth-time-journey-dynamic-state.ts`
- `frontend/src/lib/birth-time-journey-errors.ts`
- `frontend/src/lib/birth-time-journey-service.ts`
- `frontend/src/lib/birth-time-journey-store-errors.ts`
- `frontend/src/lib/birth-time-journey-stored-protocol.ts`
- `frontend/src/lib/birth-time-journey-store.ts`
- `frontend/src/lib/birth-time-journey-turn-persistence.ts`
- `frontend/src/lib/birth-time-scoring-service.ts`
- `frontend/tests/birth-time-dynamic-persistence-fixture.ts`
- `frontend/tests/birth-time-dynamic-persistence.test.ts`
- `frontend/tests/birth-time-journey-memory-store.ts`
- `frontend/tests/birth-time-journey-legacy-isolation.test.ts`
- `tests/test_birth_time_dynamic_persistence_contract.py`
- `tests/test_birth_time_journey_contract.py`
## TDD Evidence
- Migration RED: `.omo/evidence/task-5-python-red.log`
- Persistence RED: `.omo/evidence/task-5-ts-red.log`
- SQL null-state regression RED: `.omo/evidence/task-5-null-state-red.log`
- Unknown-time initialization RED: `.omo/evidence/task-5-unknown-range-red.log`
- Atomic creation RED: `.omo/evidence/task-5-atomic-create-red.log`, `.omo/evidence/task-5-atomic-create-ts-red.log`
- Legacy isolation RED: `.omo/evidence/task-5-guided-isolation-red.log`, `.omo/evidence/task-5-all-legacy-isolation-red.log`
- Memory replay RED: `.omo/evidence/task-5-memory-replay-red.log`
- Migration GREEN: `.omo/evidence/task-5-python-green.log`
- Persistence GREEN: `.omo/evidence/task-5-ts-green.log`
- SQL null-state regression GREEN: `.omo/evidence/task-5-null-state-green.log`
- Unknown-time initialization GREEN: `.omo/evidence/task-5-unknown-range-green.log`
- Atomic creation GREEN: `.omo/evidence/task-5-atomic-create-green.log`
- Legacy isolation and replay GREEN: `.omo/evidence/task-5-memory-and-isolation-green.log`
The additional regressions prove that a missing current scoring action cannot pass a SQL `NOT IN` guard through three-valued `NULL` logic, and that the supported unknown-time assessment initializes a valid full-day dynamic range instead of failing on its intentional null reported bounds.
## Verification
- Focused persistence and legacy-isolation TypeScript: 30/30 passed.
- Relevant Python contracts and engine/API regressions: 36/36 passed.
- Full birth-time frontend suite: 255/255 passed.
- Full frontend suite: 330/330 passed.
- Changed-file ESLint: passed.
- Changed-file Ruff: passed.
- `git diff --check`: passed.
- All changed TypeScript, test, and ordered migration modules are at most 250 pure LOC; maximum is 247.
- Full TypeScript check reports only the known unrelated baseline at `frontend/tests/profile-persistence.test.ts:7` (`TS1501`, ES2018 regex under the existing target).
Evidence:
- `.omo/evidence/task-5-python-relevant.log`
- `.omo/evidence/task-5-birth-time-suite.log`
- `.omo/evidence/task-5-frontend-full.log`
- `.omo/evidence/task-5-eslint.log`
- `.omo/evidence/task-5-ruff.log`
- `.omo/evidence/task-5-diff-check.log`
- `.omo/evidence/task-5-loc-final.log`
- `.omo/evidence/task-5-tsc.log`
## Review
Fresh review: **CLEAR / APPROVE**, with no blockers. Artifact: `.omo/evidence/task-5-code-review.md`.
Live PostgreSQL execution was not available: Docker CLI is installed, but the daemon socket does not exist. `.omo/evidence/task-5-live-postgres-unavailable.log` records the exact failure. SQL checks are therefore described only as static migration contracts; TypeScript fakes execute the RPC boundary and failure/replay semantics without claiming database execution.
Task 6 remains responsible for routing public `assess`/`resume` responses and dynamic actions through the v2 transition service. Task 5 establishes and verifies the durable store boundary those transitions use.
## Commit
Commit message: `feat: persist dynamic rectification turns`