Care-plan conflict surfacing, AI Transparency IG, and round-bound review gate (#169) - #173
Open
samschifman wants to merge 17 commits into
Open
Care-plan conflict surfacing, AI Transparency IG, and round-bound review gate (#169)#173samschifman wants to merge 17 commits into
samschifman wants to merge 17 commits into
Conversation
WS1 — Contracts (planning_brief.py): - Replace placeholder ConflictEntry with full model + ConflictSeverity/ ConflictCategory/ConflictStatus/ConflictSource. - Add conflict_id(): semantic, index-free id so a conflict's identity survives a request-changes regeneration (composer reorders items). - Add coerce_conflicts(): relocated/upgraded _sanitize_conflicts; handles legacy shapes (bare strings, sources:list[str], recommendation_ids). WS2 — conflict_analyst node (nodes/ + prompts/): - New LLM node detecting plan-level conflicts (overlap/contradiction/ divergent_target/divergent_schedule); numbered items, one retry, graceful degradation, index clamping, composer-conflict dedupe, and carry-forward of clinician status/resolution across re-runs. - Wire into pipeline between brief-review loop and fhir_bundle_generator; FHIR review loop does not pass back through it. - plan_composer uses coerce_conflicts + stashes rendered prompt; drop the conflict-flagging rule and anti-training "conflicts": [] template. - state.py: add conflict_prompt / plan_composer_prompt. Tests: 69 pass (test_planning_brief, test_plan_composer, test_pipeline, new test_conflict_analyst). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
WS3: new services/ai_transparency.py with IG-conformant builders (AI-Device, AI-Provenance, AI-InputPrompt/ModelCard DocRefs, AIconfidence, AIAST, stock targetPath). Fixes canonical base uv/ai-transparency -> uv/aitransparency, adds occurredDateTime + AI-agent role. fhir_bundle_builder refactored onto the module; threads model_id + captured prompts. WS4: one AI-Provenance per conflict (targetPath into affected activities/goals, source entities, device rationale note, conflict-id/-description/-severity/ -category/-status + AIconfidence extensions) and a single careplan-conflict- detected marker on the CarePlan. Tests: new test_ai_transparency.py, extended test_fhir_builder.py; reconciled the R4 .detail activity convention. Full suite green except the pre-existing DMN-model-dependent guideline_resolver test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
WS5 — Reviewer identity (SMART-on-FHIR-ready seam): - New services/reviewer.py: ReviewerContext + default_reviewer (ACP_REVIEWER_* env) + reviewer_from_payload (request override); source stays opaque to downstream, so a future SMART launch reuses the same path. - approve_care_plan appends the verifier Humanagent (reference + identifier) to every AI-Provenance and flips each conflict Provenance's conflict-status to acknowledged; back-compat clinician-string shim retained. Wired through api.py and services/fhir_server.py on PUT status=active. WS6 — BFF mapping + persisted read-back: - ai_transparency.py: is_conflict_provenance + plan_conflict_from_provenance reconstruct a PlanConflict from extensions + entity[] only (never note text); _source_display/parse_source_display round-trip source labels. - artifact_resolver.plan_conflict_from_entry maps ConflictEntry (snake) -> PlanConflict (camel); replaces the raw pass-through. - bff._extract_view_from_bundle reads conflicts from Provenances (was []). WS7 — UI: - ConflictAlert.tsx: category-varying titles + de-duplicated "From:" CPG line. - fixtures one-per-category; extended ConflictAlert tests. Contract + deploy: - bff-openapi.yaml: PlanConflict gains category/status/confidence/sources; new ConflictSource; ReviewerRef on ReviewAction. Restored SystemStatus decisions/cpgs (pre-existing drift from #161 that gen:api surfaced). Regenerated ui/src/api/types.ts. - Env vars added to compose.yml, both Helm charts, and openshell/deploy.sh (fhir-gen: ACP_CAPTURE_PROMPTS/LLM_MODEL_CARD_URL; fhir-srv: ACP_REVIEWER_*). Deferred (tracked for a later milestone): request_changes-per-conflict resolution (itemId -> resolution note + status resolved + brief carry) — needs SonataFlow structured-feedback plumbing that does not exist yet. Tests: 470 py pass (only unrelated JVM-less test_matches_hypertension_cpg fails); UI 29 pass + 3 pre-existing unrelated failures; npm build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…(issue #169) WS8 — fixtures + verification: - Add tests/integration/fixtures/htn-cpg.json (SYN-HTN-2026-001) engineered to conflict with the diabetes CPG: overlapping diet advice, BP target <140/90 vs <130/80, and titrate-lisinopril-up. - Add dm2-rec-005 (reduce lisinopril dose) to diabetes-cpg.json — the contradiction half. - Add acp-writer/tests/test_conflict_repeatability.py: gated on LLM_BASE_URL (falls back to LITELLM_URL); builds the brief deterministically from both fixtures, runs the real conflict_analyst 3x, asserts every run flags >=1 conflict, overlap in >=2/3, contradiction|divergent_target in >=2/3 — on categories/index-sets, never wording. - Extend test_e2e.py with test_comprehensive_patient_surfaces_conflicts: ingests both fixture CPGs, runs the comprehensive patient full-pipeline, asserts >=1 conflict Provenance + exactly one CarePlan marker; module gate now honors LLM_BASE_URL. WS9 — documentation: - acp-writer/README.md: Mermaid pipeline with conflict_analyst (node 7), expanded AI Transparency section, conflict-surfacing + config-env tables. - New docs/ai-transparency.md: IG conformance inventory, conflict-Provenance Mermaid + pattern, ACP_EXT_BASE extension table, reviewer/SMART seam; linked from docs/README.md. Documentation hyperlinks use the browsable build URL; the FHIR canonical (http://hl7.org/fhir/uv/aitransparency) is kept as resource identity. - AGENTS.md: IG in Technology Context + acp-writer conflict-Provenance line. - platform/mlflow/README.md: conflict_analyst in the tracing inventory. - api/bff-openapi.yaml + api/openapi.yaml: field descriptions for the WS1/WS5 conflict + reviewer additions; regenerated ui types.ts. Deferred per-conflict resolution recording is tracked in #172. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
conflict_analyst was wired only into the monolith pipeline.py, so the pod-split cluster path (BFF -> SonataFlow -> compose/generate-bundle endpoints) never ran it and deployed care plans surfaced zero conflicts. Fold conflict_analyst into both the sync /api/v1/compose and async /api/v1/compose-async handlers, right after the brief-review loop converges. The detected conflicts are annotated onto the planning brief, which flows unchanged into generate-bundle -> fhir_bundle_generator where the conflict Provenances and CarePlan marker are emitted. No SonataFlow workflow change is needed; the node stays in the llm-reasoning pod where it already has the recommendations, applicable CPGs, and LLM creds. Add tests/test_split_compose_conflicts.py to guard the split path, and note the ComposePlan-embedded ordering in the README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Even with conflict_analyst running, no conflicts surfaced because the plan_composer naturally reconciles disagreeing guidelines into one coherent plan before the analyst sees the brief (e.g. merging divergent BP targets into a single goal and dropping the reduce-dose activity in favour of titrate-up). The analyst then finds a clean brief. Add a "Preserving conflicts" directive to the composer system prompt: when recommendations conflict (contradictory directives, divergent targets/schedules, duplicate activities across CPGs) it must emit BOTH items as separate goals/activities, each with its own source_cpg, and must not merge, reconcile, subordinate, or editorialise them away. The Conflict Analyst then flags them and the clinician resolves at the review gate -- the intended #169 behaviour ("every conflicting item stays in the plan"). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
samschifman
marked this pull request as draft
August 27, 2026 15:25
…back loop (#169) Code-review fixes for PR #173, plus the Sam-approved F16 enhancement. Batch A — pipeline robustness (conflict_analyst / planning_brief / bundle gen): - F3: _build_entry/_parse_conflicts total — malformed LLM conflict items are skipped-and-logged instead of aborting the run - F14: retry with original messages on transport error (no empty assistant turn) - F7: coerce_conflicts total — enum synonyms + camelCase sources + drop-bad-source, never blanks the brief - F4: coerce conflicts via PlanningBrief before-validator so every validation site is covered; empty bundle from a validation failure now surfaces an error flag - F6: content key for every conflict category + deterministic id uniquification Batch B — approval + split-path parity: - F1: single apply_approval_transition() (CLINAST swap, verifier agent, conflict acknowledge) shared by monolith approve and deployed WriteFHIR; reviewer threaded through the workflow - F2: split compose ferries captured prompts to fhir-generation so cluster bundles carry AI-InputPrompt DocRefs - F12: single reviewer_from_payload(); removed duplicate reviewer shims F16 — "resolve all identified conflicts as you suggested" now works: - F16a: prior brief conflicts fed back into regeneration (render_conflicts_feedback, prior_brief_ref threaded through the workflow + llm_reasoning) - F16b: analyst emits a conservative suggested_resolution per conflict, surfaced end-to-end (prompt -> ConflictEntry -> conflict-suggested-resolution extension + both read paths -> BFF contract -> UI) Deferred (documented): F5 server-side Provenance persistence -> #174; per-conflict structured feedback -> #172. Batch C (F8/F9/F10/F13/F11-remove/F15) not included. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Conflict-surfacing hardening from the PR #173 review: - F8: invert the certainty-rating map on read-back so a "medium" confidence stored as "moderate" reads back as "medium". - F9: store conflict sources structurally on the Provenance entity (cpg-id / recommendation-id / excerpt extensions) so read-back no longer parses the human-readable display string, which corrupted cpg ids / excerpts containing the display delimiters. Legacy Provenances still fall back to display-string parsing. - F10: strip DocumentReference attachment payloads from the FHIR semantic reviewer prompt; inject the DMN audit trail into the brief in code instead of round-tripping it through the plan-composer LLM. - F11: remove the conflict "detected_by" field and the composer carry-forward/merge path. The analyst re-runs on a freshly composed brief and is authoritative; per-conflict resolution carry-over is deferred to #172 (rebuilt against the conflict Provenances). - F13: add @mlflow.trace to the conflict normalize / provenance / read-back functions and update the MLflow tracing inventory. - F15: retry `openshell service expose` and fail the deploy with a visible error on persistent failure instead of swallowing it (shared expose_service helper, used by both component deploys). Each fix carries a fail-before/pass-after test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
C1: extract shared llm_json (strip_code_fence/loads_json), dedup 5 sites C2: named ReviewerRef schema + $ref in openapi.yaml C3: dedup conflict test scaffolding into tests/_conflict_fixtures.py C4: keep ReviewerContext.source with SMART-launch/audit comment C5: drop unused category param + dead bounds-checks in _content_key C6: drop redundant is_conflict_provenance() pre-check in bff C7: fix docs — AIconfidence is conflict-Provenance-only, not per-activity C8: conflict_prompt captures user_prompt only (matches composer) C9: run compose pipeline via asyncio.to_thread so /compose doesn't block loop Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ry (#169) On a care-plan request_changes revision, the composer and conflict analyst now revise an approved base plan instead of re-authoring from scratch: - F17a: dedicated REVISION prompt mode for plan_composer (shared HEAD/EXAMPLES with a mode-specific AUTHORING|REVISION section). Composer is state-driven — revision iff prior_planning_brief has goals/activities — so the monolith path is unchanged and authoring output stays byte-identical. - F17b: accumulated feedback history — every revision sees ALL prior clinician comments (oldest-first, newest marked as the round to address), so standing constraints don't expire. Workflow accumulates careplanReviewHistory; brief records revision_history. - F17c: analyst resolution continuity — prior conflicts + the clinician comment are fed to the analyst on a revision pass. Resolved conflicts re-emit with the SAME id + status "resolved" + a resolution note; surviving conflicts keep their id. A conflict-resolution Provenance extension carries the note through the FHIR round-trip and read-back. - brief_reviewer: notes when a brief is a minimal revision of an approved base so it judges the changes rather than demanding authoring-style rewrites. - deploy: force `oc rollout restart deployment/acpwriter` after the workflow re-apply (the operator does not always roll the pod on a flow-only change); verify.sh greps the running pod's mounted flow for a current-version marker; workflow version bumped 0.2.2 -> 0.3.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ced (#169) Fixes the feedback-channel collision that made "resolve all conflicts as suggested" a no-op (run 1dfa716dedbe): clinician directives were seeded into brief_review_feedback, which the internal reviewer overwrites every iteration, and the revision base was frozen at the prior brief, so later iterations reverted any applied resolutions by construction. - F18a: durable "Clinician-directed changes" prompt section rendered every composer iteration from state (instruction, unresolved conflicts with suggestions, resolved-keep-resolved, enforcement note); _seed_feedback deleted — brief_review_feedback is reviewer-only again - F18b: revision base evolves to the latest draft on iterations >= 2 ("Care Plan Base"), so internal-review fixes build on directed changes - F18c: analyst reports clinician_directed per carried-forward conflict; unapplied directives trigger one composer retry with them spelled out, then flag the brief naming what could not be applied - sync + background compose consolidated into one _compose_pipeline - new local multi-round harness tests/test_revision_flow.py: 6 structural tests through the real pipeline (scripted role LLMs) + LLM-gated live two-round driver; live-validated against gpt-5.6-terra via MaaS Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Run 32402d542276 proved the F18 pipeline works on the cluster (all conflicts came back same-id status=resolved with resolutions recorded) but the display made it look like nothing happened: - workflow: InitCarePlanReview injected careplanReviewCount: 0 via inject `data`, which merges into state BEFORE the null-guard stateDataFilter runs — clobbering the counter every request_changes loop, so the UI said "round 1" forever. Initialize via the guarded filter only. - UI: ConflictAlert ignored status/resolution, so resolved records rendered identically to open conflicts. Resolved/acknowledged now render as compact collapsed green rows (PatternFly expandable Alert) — expand to review description, applied resolution, and sources — sorted after open conflicts, with the tab label split "N open · M resolved". types.ts regenerated for the resolution field. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
samschifman
marked this pull request as ready for review
August 28, 2026 00:26
) All three are timing-dependent failures observed on 2026-08-27/28; the scripts' set -e + 2>/dev/null pattern made them silent and intermittent: - lib.sh label_pod: unguarded `oc label` — a transient conflict with the sandbox controller's concurrent metadata writes killed the whole deploy between "ready" and "labeled" with stderr discarded. Now retries 3x, then warns and continues (a missing cosmetic label must not abort). - openshell/deploy.sh teardown: sandbox deletion was not awaited, so wait_for_pod_ready could match the old Terminating pod ("ready after 1s") and follow-up oc calls raced the deletion. Teardown now waits for the old pods to be fully gone before recreation. - deploy.sh orchestrator: a pod created seconds after the workflow CM apply can mount a stale kubelet-cached copy; the delayed projection swap later triggers a Quarkus devmode live-reload that wipes in-memory workflow instances, orphaning any in-flight run (run 5af4a6e5a58c). After rollout, wait for the pod's mounted workflow to match the CM by content hash, then trigger the lazy reload while nothing is running. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # acp-writer/deploy/chart-pods/values.yaml # acp-writer/deploy/openshell/deploy.sh
) Clinicians saw Approve/Submit buttons "not work the first few times": every click succeeded (202) but the UI gave no acknowledgment and re-enabled the buttons, so users clicked again during the 6-24s window before the devmode engine moved the run off the review gate. Repeat clicks' events were then silently dropped, and a changed decision between clicks was silently discarded. - ReviewPanel: add a terminal `submitted` state (persistent "Review submitted" info alert, buttons hidden) and stop swallowing errors (inline danger alert + retry). This structurally prevents double-submit and changed-decision loss. - api.ts: surface the JSON error body's `.message` centrally so alerts show the friendly text, not raw JSON. - bff.submit_review: return 503 (friendly message) when the engine is briefly unavailable instead of a bare 500; track in-flight submits per (run_id, gate) and return 409 on a duplicate instead of letting the engine drop it silently. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bind each care-plan review submission to the round it was made against (reviewRound == reviewIteration) and validate it in the workflow engine, the only authoritative component. A submission whose round no longer matches the armed gate is discarded and the gate re-arms, so a stale browser tab can't approve a superseded plan or double-apply a change. This replaces the BFF's timing/dedupe machinery: - Contract: reviewRound added to ReviewAction (bff-openapi.yaml), types regenerated. Single camelCase spelling end to end — no translation point to disagree. - Workflow: ValidateReviewRound switch + DiscardStaleReview inject between the gate and the counter. Fails open on reviewRound == null (old clients / manual curl events). - BFF: removed _pending_reviews tracker, _clear_stale_pending_reviews, and the duplicate-409 branch. send_review forwards the review verbatim. Added _resolve_sf_id so runs are still found after a BFF restart (business-key lookup; a raw run_id is never a valid instance id). - UI: submit payloads stamp reviewRound; RunDetailPage keys the panel by round so it remounts fresh each gate; ReviewPanel offers a neutral ~30s retry (at-most-once delivery can drop a submit before the gate arms — retry is safe by construction). - Docs: README "Care-plan review gate" subsection with a two-round Mermaid sequence diagram. Tests: BFF review 6/6, ReviewPanel 8/8, UI build clean, types idempotent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The UI can render a new review round a few seconds before the engine arms its gate (the data-index leads on content, lags on state); a submission in that window is dropped by at-most-once delivery. Round-binding (P1) makes retrying a same-round submission provably safe — consumed-first or engine- discarded, never a double-apply — so the panel now rides out that race automatically instead of making the clinician wait 30s and retry by hand. While `submitted` and still at the same gate/round, a self-rescheduling loop waits REVIEW_RETRY_INTERVAL_MS, silently re-submits the same action, and schedules the next wait — up to MAX_REVIEW_RETRIES (both constants at the top of ReviewPanel.tsx). Exactly one timer is ever pending. The loop ends when the run leaves the gate (panel unmount/remount → cleanup cancels), a retry errors (fall back to the manual/error path), or the retries are exhausted (stalled → manual retry affordance). Defaults: retry at ~10/20/30s, then hand off to the manual retry. UI-only; no contract/BFF/workflow change. Tests (fake timers): loop cadence, stop-after-max + manual retry, unmount cancels the loop, an errored retry stops the loop and surfaces the error. ReviewPanel 11/11; full UI suite 45 passed / 3 pre-existing baselines; build clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Aug 31, 2026
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.
Implements #169. What began as plan-level conflict surfacing + adoption of the HL7 AI Transparency on FHIR IG has grown, over the life of the branch, into the full care-plan review-gate story: detecting conflicts, feeding them back to the composer under clinician direction, and hardening the human review gate itself. Three bodies of work, plus deploy hardening.
1. Conflict surfacing + AI Transparency IG
acp-writer detects plan-level conflicts across the goals/activities composed from multiple CPGs and surfaces them to the reviewing clinician — without ever mutating the plan. Detection is a generic LLM judgment task (no DMN, no clinical knowledge base).
Categories:
overlap·contradiction·divergent_target·divergent_schedule(+other).FHIR recording:
CarePlan.activity[].detail/GoalviatargetPath, lists source recommendations as entities, carries an AI-authored rationale note, and storesconflict-id/-description/-severity/-category/-status/-suggested-resolution+AIconfidenceextensions.careplan-conflict-detectedmarker extension on the CarePlan.detected → acknowledgedand appends averifierhuman agent.plan_composerno longer harmonizes guideline conflicts away, and theconflict_analystruns on the split/SonataFlow compose path as well as the monolith.AI Transparency IG adoption: AI-Device (AIKind=LLM), bundle-level AI-Provenance (AIAST reason, fixed AI agent role, occurredDateTime), AIAST labels on all AI-produced resources, AI-InputPrompt DocumentReferences (opt-in), AI-ModelCard (opt-in), and AIconfidence — all under the correct
http://hl7.org/fhir/uv/aitransparencycanonical.2. Conflict-resolution feedback loop (request-changes → recompose)
When a clinician requests changes, the composer runs in revision mode: the prior brief is the authoritative base and only clinician-directed changes are applied.
render_clinician_directives) is rendered on every composer iteration from state — the clinician's instruction + accumulated feedback history + the prior brief's unresolved conflicts (with suggestions and source ids) + conflicts already resolved in earlier rounds. It survives the internal brief-review loop (separate from the internal reviewer's channel, which is overwritten each iteration). (F17)resolved(recording the clinician's instruction) or still-present.review_status: flagged) naming what couldn't be applied — an honest "could not apply" instead of silently re-presenting the same conflicts. (F18)3. Review Gate v2 — round-bound submissions
A series of live incidents at the human review gate (silent slow submits → multi-click; a GraphQL-replica gate pre-check accepting a submit before the next round's gate armed → engine drop; an in-memory dedupe tracker that then deadlocked the run; a business-key lookup bug that 404'd runs after any BFF restart). Root cause: the review submission had no identity. The fix gives it one — the review round — and validates it in the engine, the only component with authoritative state.
ReviewActionwithreviewRound = reviewIteration; a newValidateReviewRoundswitch in the workflow discards any submission whosereviewRoundno longer matchescareplanReviewCountand re-arms the gate (DiscardStaleReview) — so a clinician can never approve a plan version they didn't see. One camelCase spelling end-to-end;reviewRound == nullfails open for old clients/manual curl, guarded by an exact-key BFF passthrough test + the cluster verify script.get_instance_by_business_key, so they still load/accept reviews after a BFF restart.submittedand still at the same gate, a self-rescheduling loop re-submits the same round-bound action (~10/20/30s, then hands off to a manual retry) — riding out the brief window where the UI shows a new round before the engine arms its gate. Safe by construction (round-binding: consumed-first or engine-discarded, never double-applied). UI-only.4. Deploy hardening
d7ef4dd— hardened three latent races that could abort or orphan deploys.Testing
conflict_analyst, AI-transparency builders, conflict read-back, split-compose conflicts, revision flow, reviewer identity, BFF review-submit passthrough, plan-composer, FHIR builder/server-writer, e2e. Known baseline: the JVM-lesstest_matches_hypertension_cpg/test_pipeline_integrationfail locally (DMN engine unavailable — no JVM), unrelated to these changes.test_conflict_repeatability.py): passes on gpt-5.6 — overlap, contradiction, divergent_target in 3/3 runs.ReviewPanel11/11 incl. P6 fake-timer loop tests,ConflictAlertrendering; full UI suite 45 passed / 3 pre-existing unrelated baselines;npm run buildclean.working/issue-169-conflict-surfacing/verify-review-gate.sh): posts a stale-round event (must be discarded, gate re-arms, count unchanged — the fail-open detector) and a matching-round event (must advance) — run post-deploy.Deployed & verified
Live in namespace
sschifma-cpg-acpat this branch tip: theValidateReviewRound/DiscardStaleReviewstates are present in the running SonataFlow workflow; all acp-writer sandboxes healthy. (Deploy surfaced unrelated infra bugs tracked separately: #179, #180.)Follow-ups
Docs
New
docs/ai-transparency.md(IG conformance inventory, conflict-Provenance pattern + Mermaid, conflict-resolution feedback loop + Mermaid, custom-extension table, reviewer/SMART seam); updatedacp-writer/README.md(pipeline diagram, conflict surfacing, round-bound review-gate section + two-round Mermaid sequence, config);AGENTS.md;platform/mlflow/README.md; and API-contract field descriptions (bff-openapi.yaml, incl.reviewRound).🤖 Generated with Claude Code