feat(report): birth-sky report cover bound to the report's birth fingerprint (F1–F3)

Product decision A (TASK-birth-sky-followup-20260928):
- every generation path writes subject.birthFingerprint (stored-row
  fingerprint, same function as the chart page's profileFingerprint);
  never into the evidence bundle the model reads
- GET /api/reports/:id compares on the server and returns only
  report.coverSubject (subject id or null); legacy reports never get one
- report page shows the night cover above the body, paper cover alone on
  the first printed A4 page; silent on any failure, no waiting state
- TS / JSON schema / Python contract accept the optional field
- BLOCKED T5 struck through; DESIGN §17, CHANGELOG, checklist, PROGRESS

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
Jesse_Chen
2026-09-28 21:47:44 +08:00
co-authored by Claude Opus 5.5
parent 14ef18ac24
commit a9a917d50c
27 changed files with 739 additions and 27 deletions
@@ -295,6 +295,10 @@
"type": "string",
"minLength": 1,
"maxLength": 200
},
"birthFingerprint": {
"type": "string",
"pattern": "^[0-9a-f]{32}$"
}
},
"required": ["displayName", "birthTimeStatus", "birthPlaceLabel"]
@@ -704,6 +704,10 @@
"type": "string",
"minLength": 1,
"maxLength": 200
},
"birthFingerprint": {
"type": "string",
"pattern": "^[0-9a-f]{32}$"
}
},
"required": [