fix(consult): pass birth-time accuracy instead of treating every chart as unrectified #49

Closed
jesse wants to merge 0 commits from codex/consultation-birth-accuracy-20260901 into staging
Owner

Summary

  • Consultation natal requests now send declared_accuracy / time_source from profile truth instead of letting the engine default every chart to minute+family_clear'5min' with a missing matrix row.
  • Mastra rectification.boundary follows the engine gate (precision_ok / precision_annotated / not_auto_rectified) instead of hardcoding not_auto_rectified on every natal consult.
  • Engine adds a '5min' varga row and grades 5min/15min copy as precision annotation, not candidate-window interaction. Hospital/rectified stay precise.

Fixes BUG-467. Task brief: TASK-consultation-birth-accuracy-20260901.md. Baseline contained ff97de5e; branch is origin/staging @ 8b7eda9f plus this fix (56daf6be).

Task 0 — before (red) evidence

Frontend tsx --test tests/consultation-birth-accuracy.test.ts tests/consultation-birth-time-mode.test.ts:

  • 15 tests, 4 fail / 11 pass
  • Failed (invariants 1–4): declared_accuracy was undefined vs 'rectified' / 'minute'; mapper not exported
  • Passed (red line 2, unchanged): declared_birth_window / general_no_birth_time skip natal mapper; existing birth-time-mode guard suite stayed green; output guards and window/general instruction seams stay byte-stable

Python pytest tests/test_consultation_birth_accuracy.py: .FF.2 fail / 2 pass

  • Failed: family headline still 可读主盘,但高敏分盘需要降级; get_enabled_vargas('5min') == unknown
  • Passed already when fields were injected: hospital (minute, hospital) and rectified engine paths

After — per-suite counts

Typecheck

cd frontend && ./node_modules/.bin/tsc --noEmitexit 0 (228s warm). Cold tsc --noEmit --incremental false also exit 0 (1045s). Not npx tsc.

Frontend (fail=0 for product paths)

glob pass fail
tests/consult*.test.ts (this glob also matches consultation-*) 225 0
tests/consultation-*.test.ts 222 0
tests/chat*.test.ts 70 0
tests/rectification-*.test.ts non-DB 730 0
tests/rectification-*-database.test.ts serial + retry 10 0

Official combined glob (consult* + consultation-* + chat* + rectification-*) is 1035 unique tests (3 consult-* + 222 + 70 + 740). Parallel run was 1028/7 because Docker only allows 2 compose networks; the 7 failures were fixture-slot / migrate flake under contention. Serial ingest+PR-4+v9 DB: 7/10 then the 3 remaining retried 3/3. Combined fail=0.

Core accuracy+guards after the fix: consultation-birth-accuracy + consultation-birth-time-mode + consultation-route-service + consultation-context = 34/34.

Python (.venv)

pytest tests/test_consultation_birth_accuracy.py tests/test_consultation_consumer_context.py tests/test_rectification_*.py168 passed, exit 0.

_expand_pytest_targets nail: test_staging_quick_gate_runs_rectification_python_suite pass.

Red line 1 — 'rectified' only for accepted/confirmed + active time

Mapper declaredBirthAccuracyFromProfile:

profile declared_accuracy time_source
birth_time_status ∈ {accepted, confirmed} and active_birth_time rectified rectified
hospital_record (not adopted) minute hospital
family_exact minute family_clear → engine '5min'
else (approximate) 15min family_vague

Proof tests in frontend/tests/consultation-birth-accuracy.test.ts:

  • accepted / confirmed + active time → { declaredAccuracy: "rectified", timeSource: "rectified" }
  • accepted + no active time → not rectified
  • candidate hospital + active time → not rectified (stays minute/hospital)
  • reported family_exact + active time → not rectified
  • hospital unverified consult sends minute/hospital and never rectified

Window/general modes never call this mapper (serverChart is null; their toolInput has no accuracy fields).

Red line 2 — guards and window/general two-tier behavior unchanged

  • git diff origin/staging -- frontend/src/lib/timing-output-guard.ts is empty. guardPreciseTimingOutput not edited.
  • createBirthTimeModeOutputGuard body is unchanged: general_no_birth_timeguardGeneralNoBirthTimeOutput; declared_birth_window or !canAnswerPreciseTimingguardPreciseTimingOutput.
  • generalNoMinuteInstruction / declaredWindowInstruction not in the consult/route.ts diff (only an extra birthTimeSource argument to applyBirthTimeModeToWorkflowContext).
  • UNVERIFIED_BIRTH_TIME_NOTICE string is unchanged; grading is a new unverifiedBirthTimeNotice() wrapper, not a guard change.
  • Window tool still hardcodes rectification: { boundary: "not_auto_rectified" } in consultation-tools.ts (declared-window path).
  • Task 0 kept the existing 9 consultation-birth-time-mode tests green while the four natal-accuracy invariants were red. After: same byte-stable test still matches those instruction strings and guard branches.

Before / after workflow consumption

Before (every natal consult): no declared_accuracy on toolInput; engine default '5min' + missing matrix; mastra rectification.boundary = "not_auto_rectified"; model instructed to treat that as final → “未校正 / 按时间范围交互”.

After (engine _compute_rectification_gate + _build_consumer_context):

case effective_accuracy headline domain_boundaries D9 / D10
hospital (minute, hospital) minute 出生时间风险较低,可进入完整解盘 {} enabled / enabled
verified rectified rectified 出生时间风险较低,可进入完整解盘 {} enabled / enabled
family_exact → '5min' 5min 按填报时间排盘,分钟敏感结论已标注精度 birth_time_precision_boundary only enabled / enabled
approximate '15min' 15min 按填报时间排盘,分钟敏感结论已标注精度 birth_time_precision_boundary only enabled / warn
1hour 1hour 可读主盘,但高敏分盘需要降级 birth_time_uncertainty_boundary + not_auto_rectified warn / none

Mastra natal boundary: minute/rectified + no lagna sensitivity → precision_ok; 5min/15min + no lagna → precision_annotated; else fail-closed not_auto_rectified.

Why family_exact stays '5min' (product check)

ACCURACY_RULES[('minute', 'family_clear')] = '5min' is skill methodology: family memory is not a hospital record. This task does not promote family_exact to 'minute'. D10 stays enabled on the new '5min' row so career consults are not crippled. Please confirm product still wants that one-step downgrade vs hospital.

Honest verification gap

Not verified on a live staging/production consult. Recommend folding into the PR #47 nine-step probe, plus one extra check: a hospital_record exact-minute user must not be told the time is unrectified or to interact by range.

Test plan

  • Task 0 red, then A→B→C→D
  • tsc --noEmit exit 0
  • consult / consultation / chat / rectification tests fail=0 (DB serial after Docker-slot flake)
  • Python rectification glob + accuracy + consumer context green; _expand_pytest_targets nail
  • Live hospital exact-time consult no longer says 未校正按范围
  • Live verified_chart (accepted/confirmed + active time) is not told the time is unrectified
## Summary - Consultation natal requests now send `declared_accuracy` / `time_source` from profile truth instead of letting the engine default every chart to `minute`+`family_clear` → `'5min'` with a missing matrix row. - Mastra `rectification.boundary` follows the engine gate (`precision_ok` / `precision_annotated` / `not_auto_rectified`) instead of hardcoding `not_auto_rectified` on every natal consult. - Engine adds a `'5min'` varga row and grades 5min/15min copy as precision annotation, not candidate-window interaction. Hospital/`rectified` stay precise. Fixes BUG-467. Task brief: `TASK-consultation-birth-accuracy-20260901.md`. Baseline contained `ff97de5e`; branch is `origin/staging` @ `8b7eda9f` plus this fix (`56daf6be`). ## Task 0 — before (red) evidence Frontend `tsx --test tests/consultation-birth-accuracy.test.ts tests/consultation-birth-time-mode.test.ts`: - 15 tests, **4 fail / 11 pass** - Failed (invariants 1–4): `declared_accuracy` was `undefined` vs `'rectified'` / `'minute'`; mapper not exported - Passed (red line 2, unchanged): `declared_birth_window` / `general_no_birth_time` skip natal mapper; **existing birth-time-mode guard suite stayed green**; `output guards and window/general instruction seams stay byte-stable` Python `pytest tests/test_consultation_birth_accuracy.py`: `.FF.` → **2 fail / 2 pass** - Failed: family headline still `可读主盘,但高敏分盘需要降级`; `get_enabled_vargas('5min') == unknown` - Passed already when fields were injected: hospital `(minute, hospital)` and `rectified` engine paths ## After — per-suite counts ### Typecheck `cd frontend && ./node_modules/.bin/tsc --noEmit` → **exit 0** (228s warm). Cold `tsc --noEmit --incremental false` also **exit 0** (1045s). Not `npx tsc`. ### Frontend (fail=0 for product paths) | glob | pass | fail | |---|---|---| | `tests/consult*.test.ts` (this glob also matches `consultation-*`) | 225 | 0 | | `tests/consultation-*.test.ts` | 222 | 0 | | `tests/chat*.test.ts` | 70 | 0 | | `tests/rectification-*.test.ts` non-DB | 730 | 0 | | `tests/rectification-*-database.test.ts` serial + retry | 10 | 0 | Official combined glob (`consult*` + `consultation-*` + `chat*` + `rectification-*`) is **1035** unique tests (3 `consult-*` + 222 + 70 + 740). Parallel run was 1028/7 because Docker only allows 2 compose networks; the 7 failures were fixture-slot / migrate flake under contention. Serial ingest+PR-4+v9 DB: 7/10 then the 3 remaining retried **3/3**. Combined **fail=0**. Core accuracy+guards after the fix: `consultation-birth-accuracy` + `consultation-birth-time-mode` + `consultation-route-service` + `consultation-context` = **34/34**. ### Python (`.venv`) `pytest tests/test_consultation_birth_accuracy.py tests/test_consultation_consumer_context.py tests/test_rectification_*.py` → **168 passed**, exit 0. `_expand_pytest_targets` nail: `test_staging_quick_gate_runs_rectification_python_suite` **pass**. ## Red line 1 — `'rectified'` only for accepted/confirmed + active time Mapper `declaredBirthAccuracyFromProfile`: | profile | declared_accuracy | time_source | |---|---|---| | `birth_time_status ∈ {accepted, confirmed}` **and** `active_birth_time` | `rectified` | `rectified` | | `hospital_record` (not adopted) | `minute` | `hospital` | | `family_exact` | `minute` | `family_clear` → engine `'5min'` | | else (approximate) | `15min` | `family_vague` | Proof tests in `frontend/tests/consultation-birth-accuracy.test.ts`: - `accepted` / `confirmed` + active time → `{ declaredAccuracy: "rectified", timeSource: "rectified" }` - `accepted` + **no** active time → **not** `rectified` - `candidate` hospital + active time → **not** `rectified` (stays `minute`/`hospital`) - `reported` family_exact + active time → **not** `rectified` - hospital unverified consult sends `minute`/`hospital` and never `rectified` Window/general modes never call this mapper (`serverChart` is null; their `toolInput` has no accuracy fields). ## Red line 2 — guards and window/general two-tier behavior unchanged - `git diff origin/staging -- frontend/src/lib/timing-output-guard.ts` is **empty**. `guardPreciseTimingOutput` not edited. - `createBirthTimeModeOutputGuard` body is unchanged: `general_no_birth_time` → `guardGeneralNoBirthTimeOutput`; `declared_birth_window` or `!canAnswerPreciseTiming` → `guardPreciseTimingOutput`. - `generalNoMinuteInstruction` / `declaredWindowInstruction` **not in the consult/route.ts diff** (only an extra `birthTimeSource` argument to `applyBirthTimeModeToWorkflowContext`). - `UNVERIFIED_BIRTH_TIME_NOTICE` string is unchanged; grading is a new `unverifiedBirthTimeNotice()` wrapper, not a guard change. - Window tool still hardcodes `rectification: { boundary: "not_auto_rectified" }` in `consultation-tools.ts` (declared-window path). - Task 0 kept the existing 9 `consultation-birth-time-mode` tests green while the four natal-accuracy invariants were red. After: same byte-stable test still matches those instruction strings and guard branches. ## Before / after workflow consumption **Before (every natal consult):** no `declared_accuracy` on `toolInput`; engine default `'5min'` + missing matrix; mastra `rectification.boundary = "not_auto_rectified"`; model instructed to treat that as final → “未校正 / 按时间范围交互”. **After (engine `_compute_rectification_gate` + `_build_consumer_context`):** | case | effective_accuracy | headline | domain_boundaries | D9 / D10 | |---|---|---|---|---| | hospital `(minute, hospital)` | `minute` | 出生时间风险较低,可进入完整解盘 | `{}` | enabled / enabled | | verified `rectified` | `rectified` | 出生时间风险较低,可进入完整解盘 | `{}` | enabled / enabled | | family_exact → `'5min'` | `5min` | 按填报时间排盘,分钟敏感结论已标注精度 | `birth_time_precision_boundary` only | enabled / enabled | | approximate `'15min'` | `15min` | 按填报时间排盘,分钟敏感结论已标注精度 | `birth_time_precision_boundary` only | enabled / warn | | `1hour` | `1hour` | 可读主盘,但高敏分盘需要降级 | `birth_time_uncertainty_boundary` + `not_auto_rectified` | warn / none | Mastra natal boundary: `minute`/`rectified` + no lagna sensitivity → `precision_ok`; `5min`/`15min` + no lagna → `precision_annotated`; else fail-closed `not_auto_rectified`. ## Why family_exact stays `'5min'` (product check) `ACCURACY_RULES[('minute', 'family_clear')] = '5min'` is skill methodology: family memory is not a hospital record. This task does not promote family_exact to `'minute'`. D10 stays enabled on the new `'5min'` row so career consults are not crippled. Please confirm product still wants that one-step downgrade vs hospital. ## Honest verification gap Not verified on a live staging/production consult. Recommend folding into the PR #47 nine-step probe, plus one extra check: a `hospital_record` exact-minute user must **not** be told the time is unrectified or to interact by range. ## Test plan - [x] Task 0 red, then A→B→C→D - [x] `tsc --noEmit` exit 0 - [x] consult / consultation / chat / rectification tests fail=0 (DB serial after Docker-slot flake) - [x] Python rectification glob + accuracy + consumer context green; `_expand_pytest_targets` nail - [ ] Live hospital exact-time consult no longer says 未校正按范围 - [ ] Live verified_chart (`accepted`/`confirmed` + active time) is not told the time is unrectified
jesse added 2 commits 2026-09-02 02:40:03 +08:00
Hospital records and adopted rectification times were still fed to the model as not_auto_rectified because the chart request omitted declared_accuracy/time_source and mastra hardcoded the boundary. Map profile truth into the engine request, keep rectified for accepted/confirmed active times only, and leave window/general guards unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
Merge origin/staging into consultation birth-accuracy branch.
Independent Staging Quality Gate / validate (pull_request) Successful in 13m43s
Independent Staging Quality Gate / publish (pull_request) Has been skipped
3838bc077e
jesse closed this pull request 2026-09-02 07:09:42 +08:00
Some checks are pending
Independent Staging Quality Gate / validate (pull_request) Successful in 13m43s
Independent Staging Quality Gate / publish (pull_request) Has been skipped

Pull request closed

Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: root/Jyotisha#49