Harden traceability + self-learning tail: deterministic graph assembly, safe SDTM staging, gated skill-PR#1
Open
Griphu wants to merge 2 commits into
Open
Harden traceability + self-learning tail: deterministic graph assembly, safe SDTM staging, gated skill-PR#1Griphu wants to merge 2 commits into
Griphu wants to merge 2 commits into
Conversation
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
Hardens the tail of the USDM→TFL pipeline by moving correctness-critical, deterministic work out of LLM agent steps and into pinned, tested Python scripts, gating the self-learning loop so a clean run doesn't pay for it, and making the skill-lesson PR non-fatal. Net: the pipeline grows from 13 to 15 steps, but the two new steps are cheap deterministic scripts that remove non-determinism and cost from the parts of the flow where an LLM added risk, not value.
All changes came out of a critical review of the workflow against the Mediforce workflow-authoring golden rules (executor-choice / "is that really an agent step?"). Each is independently motivated below.
Why these changes
F1b — Split the traceability graph assembly out of the agent (correctness)
build-traceabilitywas a single ~25-min agent that both assembled the objective→endpoint→SDTM→ADaM→TLF graph (a pure join over five JSON artifacts + coverage math + the issues feed) and rendered the interactive HTML explorer. A traceability graph assembled by an LLM can silently drop an edge or miscount endpoint coverage — which defeats the entire purpose of the artifact ("prove with the numbers").assemble-trace-graphscript step (container/build_trace_graph.py) does the join, two-way coverage,absent-domain detection, status tallies, and issues feed deterministically in code, emittingtrace_graph.json+manifest.jsonper the traceability-buildergraph-data-schema.md.build-traceabilityis now render-only — it reads the authoritativetrace_graph.jsonand produces the HTML without recomputing anything. Thetraceability-builderskill gained an explicit render vs assemble mode note.F2 — De-risk SDTM staging in
plan-tlfs(reliability)plan-tlfsmixed a deterministic file operation — identify the USDM among the uploads and copy up to 200 SDTM files from/data/into/workspace/sdtm/— into the same LLM step that plans TLFs. An agent hand-copying files can mis-identify the USDM or silently drop datasets.container/stage_inputs.pycontent-detects the USDM (byusdmVersion/study.versions, so a Dataset-JSON SDTM file is never mistaken for it), writes it to/workspace/usdm.json, persists every other upload into/workspace/sdtm/, and fails fast if no USDM or no SDTM is present.plan-tlfsnow runs it as a mandatory STEP 0 (python3 /app/container/stage_inputs.py) before planning; thetlf-plannerskill documents the same. The LLM triggers one reliable tool instead of hand-copying files.plan-tlfsagent container on purpose — uploaded files are mounted at/dataonly on the step that directly follows the upload, and that mount is agent-only.F3 — Make
open-skill-prfail-soft (robustness)open-skill-pris ascriptstep, andcontinueOnErroris honoured only onactionsteps — so a GitHub hiccup or a token missingpull-requests:writewould fail the entire clinical pipeline at its very last step, after all TLFs were generated and human-approved, over a non-critical skill-improvement side effect.open_skill_pr.py'sfail()now records{prCreated:false, reason}and exits 0 instead of 1, so the run advances todone. The reason is still surfaced inresult.json+ stderr for the step log.F7 — Gate the self-learning tail (efficiency)
On a clean first-pass run (no revisions), the ~15-min
propose-skill-updateagent andopen-skill-prstill ran, just to produce a no-op.check-feedbackscript step (container/check_feedback.py) reads/workspace/review_feedback.jsonland emits{hadRevisions}. A conditional branch (output.hadRevisions == true/false, the proven validation-gate pattern) routes intopropose-skill-update→open-skill-pronly when a review actually requested changes; otherwise the run goes straight todone.F8 — README accuracy
The README claimed
fixtures/was baked into the image, but the Dockerfile onlyCOPY container/. Fixed the overclaim, documented all four bakedcontainer/scripts, and rewrote the pipeline table (now 15 steps) + the traceability/self-learning descriptions.Drive-by: fixed pre-existing broken tests
tests/test_open_skill_pr.pyreferencedLESSONS_FILEand a 2-argappend_lessonthat no longer existed after an earlier multi-skill refactor (2 of 3 tests were failing onmain). Updated to the currentlessons_file_for(skill)/ 3-arg API and added a fail-soft assertion for F3.Routing changes (
src/cdisc-case-3.wd.json)Validation
mediforce workflow register --dry-run→ OK (15 steps, 18 transitions).dockerfile: Dockerfileresolves at repo root;COPY container/present; every stepcommandpath lands at/app/container/.py_compileclean.python tests/run_tests.py— new tests forbuild_trace_graph.py(join/coverage/absent-domain/issues/embedded artifacts),stage_inputs.py(USDM detection incl. Dataset-JSON, fail-fast paths),check_feedback.py(hadRevisions gate), plus the repairedopen_skill_pr.pytests. Fixtures persisted undertests/fixtures/.Not runtime-verified here (need an actual run): the Docker image build itself, the render-only agent's HTML output, and end-to-end execution on the platform.
Pinning
Build-context
commitfields (11 in the.wd.json+externalSkillsRepo.commit) are pinned to4163e27, which contains the newcontainer/scripts; HEAD (2aef716) carries the filled.wd.json.4163e27is reachable from HEAD, so the image builds from a commit that has the scripts while the definition registers from the working tree — the standard decoupled two-SHA shape.🤖 Generated with Claude Code