Conversation
… md/FHIR/PDF hardening, F5 chain-wiring, KR font portability Remediates round-1 review blockers on the F5 clinical hand-off pipeline, preserving the original deterministic/ZERO-LLM design: - handoff_generator: _event_risk returns MAX of parseable severity signals (risk_level + ctrs via CTRS_TO_RISK) so a contradictory pair never resolves downward (issue #21); RiskEvent-or-dict normalization via _event_view. - schemas/handoff: new RiskEvent model — strict risk_level/ctrs_level validation at the API boundary (rejects null + unicode-confusable CTRS like '①'), extra keys preserved, max_length=100. - f5: A6->A8 leak guard NFKC+casefold normalized (case/fullwidth dodge closed); generated_at timezone-aware. - services/f5_report: markdown/HTML injection hardening (_md_inline/_md_cell/_md_block); PDF exporter isolation + newline-dense paragraph cap; FHIR null-primitive omission w/ dataAbsentReason, XHTML escaping, well-formed-div + null-value validation. - services/trend_plotter: bundled SHA-pinned Korean font assets are first-class candidates (portable across macOS/slim containers). - continuous_test: F5 runs from the post-ledger path in BOTH single- and multi-session chains (was bypassed/stale). - tests: adversarial coverage for all of the above + test_handoff_input_validation.py.
…nsistency + PDF partial-export signaling codex review (round-2) surfaced two P2 regressions in the round-1 remediation; both fixed while preserving the deterministic post-ledger design: - continuous_test._main: defer F4 (like F5) out of the in-chain run so BOTH read the post-append ledger snapshot in the single-session path — previously F4 ran pre-append (N-1 entries) while F5 ran post-append (N), an inconsistent longitudinal window. Now mirrors run_multi_session_chain's append-then-F4-then-F5 ordering. - continuous_test.run_f5_stage: report F5 'warn' (not 'pass') when PDF export failed but markdown+FHIR were written, so a missing clinical artifact is never masked as complete. - continuous_test._run_f5_replay_cli: print an explicit WARNING and return a non-zero exit on partial export (PDF missing). - tests: strengthen the single-session wiring test to assert F4 AND F5 observe the SAME 1-entry ledger snapshot (ordered F4->F5->F6); add PDF partial-export regression (warn when pdf absent, pass when present).
…omplete md neutralization, PDF pagination (no content drop) codex review (round-3) surfaced 1 P1 + 2 P2, all in the round-1/2 hardening; fixed while preserving the deterministic design: - [P1] Gate F5 on F4 =='pass' (both single- and multi-session): F4 writes its *_temporal.json ONLY on 'pass'; a 'warn'/'skip' produces no fresh longitudinal output, so gating on 'warn' let F5 silently consume a STALE temporal and emit a passing report mixing the current header with old data. - [P2] _md_block now escapes Setext headings (=), code fences (` and ~), ordered lists (1./1)) and the inline-link seam ]( in addition to ATX/blockquote/table/bullet tokens — opt-in narrative text can no longer forge report structure. - [P2] PDF long free-text (주호소/현병력/A8 narrative/상세 부록/슬롯 이력) is paginated across flowables via _paras/_pdf_line_chunks instead of being truncated at 40 lines with an omission marker — clinical facts are no longer dropped from the PDF while md/FHIR kept them. - tests: F4-warn->F5-skip; _md_block Setext/fence/list/link neutralization; _pdf_line_chunks preserves every line; pypdf extraction proves a >40-line narrative's last line survives in the PDF.
…tale-PDF cleanup Two of three codex round-3 P2 findings (the third, shared-helper PDF escaping, follows): - [P2] continuous_test._chain_return_code: a partial F5 export (md/FHIR written, PDF missing -> F5 'warn') now makes both single- and multi-session runs exit 2 (was 0), matching the --f5-from-artifacts replay CLI, so automation detects the missing clinical artifact even without a hard stage failure. Benign non-F5 warns still exit 0. - [P2] save_f5_result: on PDF export failure, unlink(missing_ok) the target PDF path so a same-second-prefix collision (or partial write) can't leave a previous run's stale PDF beside freshly overwritten md/FHIR. - tests: _chain_return_code matrix (partial->2, fail->1, clean->0, benign warn->0); stale PDF removed on failed export.
…zation (no double-escape) Third codex round-3 P2: shared render helpers (_truncate, _mse_lines, and the summary/risk-prose/risk-table chain) baked Markdown escaping (_md_cell/_md_inline) into text consumed by BOTH exporters, so the PDF double-escaped it — reportlab's own P() then produced literal <… / \| artifacts. - Thread a sanitize: Callable[[object], str] param through those helpers. Default = the Markdown escaper the helper already used, so build_markdown_report behavior is IDENTICAL (injection protection intact); build_pdf_report passes _pdf_raw (identity) so reportlab's P() is the SOLE escaper for PDF-bound text. - _truncate anchors the full text into each renderer's own _AppendixCollector in the form correct for that renderer (md-escaped vs raw). - test: adversarial clinical text with < and | renders clean in the PDF (no </\| artifacts); all Markdown-injection tests unchanged.
…OpenAPI enum/non-null schema Both codex round-4 P2 findings: - [P2] save_f5_result: append a unique per-run token to the artifact prefix (<vp>_<ts>_<8hex>) so two same-second exports of the same VP never share filenames. The round-4 on-failure unlink can no longer delete a DIFFERENT run's valid PDF, and no stale PDF is left paired with mismatched md/FHIR; the unlink now only targets THIS run's own (unique) path. - [P2] schemas/handoff RiskEvent: publish risk_level/ctrs_level as non-nullable, enum-constrained (Literal) OpenAPI fields that remain omittable — the generated /openapi.json schema now matches the runtime validators, so schema-legal payloads no longer hit surprise 422s. The validator label sets are derived from the Literals so schema and validators cannot drift. - tests: failed export preserves a prior run's PDF (unique prefixes); RiskEvent schema encodes enum/non-null/optional and matches validator sets; F5 naming regex updated for the per-run token.
…ent round-trip serialization
Both codex round-5 P2 findings:
- [P2] f5 A8 leak guard: match A6 candidate disease names on ASCII token boundaries (preserving the NFKC/casefold hardening) so a short Latin candidate ('AD') no longer falsely matches inside 'had' and wrongly disables the opt-in narrative; CJK names still match across a following Korean particle (우울증 in 우울증이).
- [P2] schemas/handoff RiskEvent: a model_serializer omits absent severity fields (never emits null), so an unlabeled event round-trips model_dump()->model_validate() (cache/retry/forward) without the validators rejecting a null they never should have emitted. The published schema stays enum + non-null.
- tests: leak-guard false-positive + real-leak (standalone Latin, Korean particle); unlabeled/labeled/extra-key round-trip.
codex round-6 P1: a valid A6 candidate with surrounding whitespace (e.g. 'PTSD ') had the whitespace baked into re.escape(), so the ASCII boundary anchor landed after the space and failed to match 'PTSD symptoms' — the disease name leaked into the opt-in A8 narrative (the old substring guard caught it). _disease_leaks now strips the candidate before building the pattern, restoring the A6->A8 clinical-isolation guarantee. +regression test.
Continues the F5 clinical hand-off report review remediation with three security-hardening boundaries plus a FHIR-validity fix. - Narrative guard (A6->A8): reject Unicode-confusable disease leaks — Cyrillic/Greek script when an ASCII candidate exists, interstitial combining marks, fullwidth and Cf format characters. Comparison-only normalization; stored narrative bytes are never mutated. - XML 1.0 safety: sanitize invalid scalars one-for-one to U+FFFD across every FHIR Narrative and ReportLab paragraph path; validate the FHIR Bundle before HTTP return and before persistence behind a generic, PHI-free 500 / zero-artifacts boundary (Markdown left byte-exact). - Artifact reservation cleanup: attempt every unlink/close, retry failed unlinks once after closes, preserve the primary exception with a count-only note, else raise a typed content-free error; extracted into a typed helper. - Emit a valid Patient.id (fall back to vp_id) so sparse-persona bundles stay structurally valid FHIR — fixes an F1->F5 continuous-test regression the new pre-persistence validation surfaced. Verified: ai-server suite 2383 passed / 2 skipped; ruff clean; basedpyright changed-src errors 17->5 (0 added, 12 removed); mypy clean on new modules; real-surface Unicode/XML/PDF/FHIR/cleanup QA 28/28.
Member
|
보안 관련 내용 수용, 브랜치 최신화 이후 조화 가능 부분들 재정합 진행 예정 |
Member
|
보존 안내 (filemanager, ADR-041 이후 Master 대이동) Master가 #77→#79→#80→#83→#78→#81→#82 순으로 머지되어 (base였던)
close 금지 확인, draft 유지. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Completes the security-critical portion of the F5 clinical hand-off report review remediation. This branch carries 24 commits over
origin/Masterthat resolve the clinical-safety, grounding, and boundary blockers raised across the multi-round Codex + five-lane reviews.Review remediation on this branch
Grounding & contracts (prior remediation commits
0c09f7d…c921a35)/ai/handoff/generateshared contract adopted losslessly; production A8 narrative structurally gated (422).generated_at(no wall-clock); type-safeRiskEventomission; official-boundary risk validation.__cause__/__context__areNone).Safety & security boundaries — completed this session (Todos 18–20 + regression)
U+FFFDacross every FHIR Narrative and ReportLab paragraph path; the FHIR Bundle is validated before HTTP return and before persistence behind a generic, PHI-free500/ zero-artifacts boundary (Markdown left byte-exact).Patient.idfalls back tovp_idso sparse-persona bundles stay structurally valid — fixes an F1→F5 continuous-test regression the new pre-persistence validation surfaced (TDD RED→GREEN, new unit test).O_EXCL/O_NOFOLLOW), mode-0600artifact writes; PHI-safe PDF/persistence failures.Verification — candidate
efea8f9uv run pytest -q)src tests)0E/0WU+FFFD· malformed-FHIR reject (content-free, no files) · exhaustive cleanup (no PHI leak) ·0600exportDeferred to a follow-up PR (tracked)
Intentionally not in this PR, to keep it focused and behavior-preserving:
f5_report.pyinto typed<200-LOC modules (markdown / PDF / FHIR / thin facade). Pure structural refactor.f5_report.pybasedpyright errors (reportMissingTypeArgument/ one comparison) are legacy debt cleared by the Todo 22–24 extraction — not introduced here.Notes for reviewers
.omo/planning/evidence metadata is intentionally untracked and absent from every commit.Masteradvanced (fix(alembic): resolve duplicate revision 0008 — renumber questionnair… #75) after this branch was cut;8c34d79remains a common ancestor.apps/api/shared-contractsfull-suite gate is the pre-merge step (hence draft).