From 3cca9d05c146eac559016877cbbe88ab609eeec4 Mon Sep 17 00:00:00 2001 From: charles chen Date: Sat, 30 May 2026 22:28:45 +0300 Subject: [PATCH 1/3] chore(toolchain): upgrade medharness to 0.10.0 and migrate dhfkit commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bump medharness[full] pin from 0.9.0 → 0.10.0 in requirements.txt - Migrate all DHF data-layer CLI calls (item, validate, doc, report) from `medharness dhf …` to the new `dhfkit` binary (split introduced in v0.8.0) - Update `medharness ci cr-status` → `medharness change status` (v0.8.0 rename) - Document dhfkit as bundled inside the medharness wheel (one install, both CLIs) - Add v0.9/0.10 test-point annotation guidance (@testing:T1 / @pytest.mark.dhf_testing) to Agent Rule #3 and the for-stage stage name hint to Quick Reference - Fix autocontour:service script to use .venv/bin/python -m uvicorn for unambiguous venv resolution Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 52 +++++++++++++++++++++++++++++------------------- package.json | 2 +- requirements.txt | 2 +- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3e67631..0a11cca 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -40,13 +40,14 @@ pnpm --filter @contourlab/client typecheck # typecheck frontend pnpm -r typecheck # typecheck all workspaces pnpm -r build # build all workspaces -# DHF operations (Python) +# DHF operations (Python) — two CLIs: medharness (AI/CI harness) + dhfkit (data layer) +# dhfkit is bundled inside the medharness wheel; one install gets both binaries pip install -r requirements.txt # one-time setup -medharness --dhf DHF dhf item list --type cr # list all CRs -medharness --dhf DHF dhf item get CR-NNN # get CR details -medharness --dhf DHF dhf item transition CR-NNN --by "Author" -medharness --dhf DHF dhf validate schema # validate all item YAMLs -medharness --dhf DHF dhf doc generate ALL # regenerate spec documents +dhfkit --dhf DHF item list --type cr # list all CRs +dhfkit --dhf DHF item get CR-NNN # get CR details +dhfkit --dhf DHF item transition CR-NNN --by "Author" +dhfkit --dhf DHF validate schema # validate all item YAMLs +dhfkit --dhf DHF doc generate ALL # regenerate spec documents ``` ## Key Conventions @@ -56,35 +57,41 @@ medharness --dhf DHF dhf doc generate ALL # regenerate spec document - **Proxy**: Vite proxies `/api` and `/ws` → `localhost:4000`; `/dicom-web` → Orthanc `localhost:8042` - **TypeScript**: strict mode throughout, no `any` - **Styling**: Tailwind only, no inline styles, dark clinical theme (see `/ux-design`) -- **DHF**: DHF items live at `DHF/items/`. Use `medharness --dhf DHF dhf ...` commands. The CR - design plan lives in the `implementation_notes` field of `DHF/items/09_cr/CR-NNN.yaml`. Do not - scatter direct DHF file reads across automation — use the `medharness` CLI facade. +- **DHF**: DHF items live at `DHF/items/`. Use `dhfkit --dhf DHF ...` for data operations (CRUD, + validate, doc generate) and `medharness --dhf DHF ...` for AI/CI harness operations (context, + change status, approval). The CR design plan lives in the `implementation_notes` field of + `DHF/items/09_cr/CR-NNN.yaml`. Do not scatter direct DHF file reads across automation — use + the CLI facade. ### DHF Facade API Quick Reference ```bash +# --- medharness: AI/CI harness commands --- + # Get CR implementation context (spec + DHF overview) for AI/CI consumption medharness --dhf DHF dhf context implementation --cr CR-034 --out-dir /tmp/cr-context -# Get scoped context for a specific workflow stage (analyze / design / develop) +# Get scoped context for a specific workflow stage (stage: analyze | design | develop) medharness --dhf DHF dhf context for-stage develop --cr CR-034 +# Check CR stage and approval status (machine-readable JSON) +medharness --dhf DHF change status --cr CR-034 --pr 42 + +# --- dhfkit: DHF data-layer commands --- + # Print human-readable traceability coverage report -medharness --dhf DHF dhf report +dhfkit --dhf DHF report # Validate DHF schema and traceability locally -medharness --dhf DHF dhf validate schema -medharness --dhf DHF dhf validate traceability - -# Check CR stage and approval status (machine-readable JSON) -medharness --dhf DHF ci cr-status --cr CR-034 --pr 42 +dhfkit --dhf DHF validate schema +dhfkit --dhf DHF validate traceability # Transition a CR -medharness --dhf DHF dhf item transition CR-034 completed --by "agent" +dhfkit --dhf DHF item transition CR-034 completed --by "agent" # List and inspect items -medharness --dhf DHF dhf item get SRS-001 -medharness --dhf DHF dhf item list --type SRS +dhfkit --dhf DHF item get SRS-001 +dhfkit --dhf DHF item list --type SRS ``` ## Sources of Truth @@ -139,7 +146,10 @@ Design and implementation live on the same branch, each committed separately. 3. **Tests** — write alongside every functional change: unit tests for pure logic, component tests for React components, regression tests for bug fixes. Colocate at `*.test.ts(x)`. - Add `@links:SRS-xxx` or `@links:SYS-xxx` annotations to tests that verify DHF requirements. + - Add `@links:SRS-xxx` or `@links:SYS-xxx` annotations to tests that verify DHF requirements. + - For numbered test points (`T1:`, `T2:` on a requirement): embed `@testing:T1` in the test + name (JS/TS) or use `@pytest.mark.dhf_testing("T1")` (Python) so the CI gate + `medharness verify tests` can confirm every test point has passing coverage. 4. **Design** — `generate-dhf` writes the implementation plan into the CR item's `implementation_notes`; treat that as the primary input for `develop-cr`. Invoke `/ux-design` before any UI work. @@ -150,7 +160,7 @@ Design and implementation live on the same branch, each committed separately. pnpm --filter @contourlab/client lint && pnpm --filter @contourlab/client typecheck dotnet build apps/api/api.csproj --configuration Release # API changes pnpm -r typecheck # data model changes - medharness --dhf DHF dhf validate schema # DHF item changes + dhfkit --dhf DHF validate schema # DHF item changes ``` 7. **Handoff** — run `/post-implement`; open PR with CR ID in title, change summary, DHF files updated, validation run, manual test plan. diff --git a/package.json b/package.json index 924cd98..d4682a5 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "scripts": { "dev": "pnpm --filter @contourlab/client dev", "api": "cd apps/api && dotnet run --launch-profile http", - "autocontour:service": "cd apps/autocontour-service && uvicorn main:app --host 127.0.0.1 --port 4010 --reload", + "autocontour:service": "cd apps/autocontour-service && .venv/bin/python -m uvicorn main:app --host 127.0.0.1 --port 4010 --reload", "repo:up": "docker compose up -d dicom-repo", "repo:down": "docker compose down", "repo:logs": "docker compose logs -f dicom-repo", diff --git a/requirements.txt b/requirements.txt index d30ed17..1f3ae88 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -medharness[full]==0.9.0 +medharness[full]==0.10.0 From 4b974f4ca544d83d60620dc80f801952795c3dec Mon Sep 17 00:00:00 2001 From: charles chen Date: Sun, 31 May 2026 12:00:02 +0300 Subject: [PATCH 2/3] chore(toolchain): migrate CI workflows from medharness ci/* to 0.10.0 command API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit medharness 0.8.0 reorganised the CLI — the flat `ci` subgroup was replaced by focused groups. Update all workflow files and the contract check script to use the new API: ci generate-dhf → change plan ci develop-cr → change implement ci cr-status → change status ci advance-stage → change advance ci approve-gate → approval check ci github-event → automation github-event ci validate-branch → verify branch ci validate-code → verify code ci dhf-validate → verify dhf ci test-coverage → verify tests ci test-points → verify tests ci validate-verif. → verify verification ci evidence bundle → evidence bundle Also bump medharness-setup action pin from 0.9.0 → 0.10.0 to match requirements.txt (fixes MedHarness Contract CI gate). Co-Authored-By: Claude Sonnet 4.6 --- .github/actions/medharness-setup/action.yml | 2 +- .github/workflows/ci-pipeline.yml | 14 ++-- .github/workflows/cr-lifecycle.yml | 20 +++--- .github/workflows/issue-to-cr.yml | 2 +- scripts/ci/check_medharness_contract.py | 76 +++++++++++++-------- 5 files changed, 66 insertions(+), 48 deletions(-) diff --git a/.github/actions/medharness-setup/action.yml b/.github/actions/medharness-setup/action.yml index cea3b9e..8feb6a8 100644 --- a/.github/actions/medharness-setup/action.yml +++ b/.github/actions/medharness-setup/action.yml @@ -21,4 +21,4 @@ runs: - name: Install medharness shell: bash - run: pip install "medharness${{ inputs.extras }}==0.9.0" + run: pip install "medharness${{ inputs.extras }}==0.10.0" diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 3b2d36b..fe8b26f 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -205,7 +205,7 @@ jobs: - name: Run DHF validation run: | - medharness ci dhf-validate \ + medharness verify dhf \ --dhf DHF \ --run-schema \ --run-traceability \ @@ -252,7 +252,7 @@ jobs: PR_NUMBER: ${{ github.event.pull_request.number }} run: | CR_ID=$(echo "${{ github.event.pull_request.head.ref }}" | grep -oE 'CR-[0-9]+') - if ! medharness --dhf DHF ci validate-branch \ + if ! medharness --dhf DHF verify branch \ --cr "$CR_ID" \ --code-path apps/ \ --code-path packages/; then @@ -267,7 +267,7 @@ jobs: PR_NUMBER: ${{ github.event.pull_request.number }} run: | CR_ID=$(echo "${{ github.event.pull_request.head.ref }}" | grep -oE 'CR-[0-9]+') - if ! medharness --dhf DHF ci validate-code \ + if ! medharness --dhf DHF verify code \ --cr "$CR_ID"; then gh pr comment "$PR_NUMBER" --body \ "⚠️ **Test annotation check failed** for $CR_ID — one or more items in \`test_plan.needs_new_tc\` are missing \`@links:\` annotations. See CI logs for details." @@ -535,7 +535,7 @@ jobs: - name: Run requirement-test coverage gate run: | - medharness ci test-coverage \ + medharness verify tests \ --dhf DHF \ --junit-dir test-results/srs \ --junit-dir test-results/srs-api \ @@ -547,7 +547,7 @@ jobs: - name: Run test-point coverage gate (TDD) run: | - medharness ci test-points \ + medharness verify tests \ --dhf DHF \ --junit-dir test-results/srs \ --junit-dir test-results/srs-api \ @@ -559,7 +559,7 @@ jobs: - name: Run verification completeness gate run: | - medharness ci validate-verification \ + medharness verify verification \ --dhf DHF \ --junit-dir test-results/srs \ --junit-dir test-results/srs-api \ @@ -625,7 +625,7 @@ jobs: - name: Generate evidence bundle run: | - medharness --dhf DHF ci evidence bundle \ + medharness --dhf DHF evidence bundle \ --out-dir dhf-artifacts \ --traceability-type UC \ --traceability-type CRS \ diff --git a/.github/workflows/cr-lifecycle.yml b/.github/workflows/cr-lifecycle.yml index e87ff4f..9290ec9 100644 --- a/.github/workflows/cr-lifecycle.yml +++ b/.github/workflows/cr-lifecycle.yml @@ -62,7 +62,7 @@ jobs: - name: Plan lifecycle action id: plan run: | - python -m medharness ci github-event \ + python -m medharness automation github-event \ --stage-label-prefix "cr:stage/" \ --dispatch-action design=gen-code \ --dispatch-action code=code-approved \ @@ -115,7 +115,7 @@ jobs: run: | CR_ID=$(jq -r '.cr_id // ""' /tmp/event-plan.json) PR_NUMBER="${{ github.event.pull_request.number }}" - [ -n "$CR_ID" ] && medharness --dhf DHF ci cr-status \ + [ -n "$CR_ID" ] && medharness --dhf DHF change status \ --cr "$CR_ID" ${PR_NUMBER:+--pr "$PR_NUMBER"} \ --branch "${{ github.head_ref || github.ref_name }}" @@ -180,7 +180,7 @@ jobs: PR_NUMBER: ${{ needs.detect.outputs.pr_number }} run: | set -o pipefail - python -m medharness --dhf DHF ci generate-dhf --cr "$CR_ID" ${PR_NUMBER:+--pr "$PR_NUMBER"} | tee /tmp/generate-dhf-out.json + python -m medharness --dhf DHF change plan --cr "$CR_ID" ${PR_NUMBER:+--pr "$PR_NUMBER"} | tee /tmp/generate-dhf-out.json - name: Upload design payload if: always() @@ -244,7 +244,7 @@ jobs: CR_ID: ${{ needs.detect.outputs.cr_id }} PR_NUMBER: ${{ needs.detect.outputs.pr_number }} run: | - if ! python -m medharness ci dhf-validate \ + if ! python -m medharness verify dhf \ --dhf DHF \ --run-schema \ --run-traceability \ @@ -265,7 +265,7 @@ jobs: PR_NUMBER: ${{ needs.detect.outputs.pr_number }} ISSUE_NUMBER: ${{ needs.detect.outputs.issue_number }} run: | - medharness ci advance-stage \ + medharness change advance \ --pr "$PR_NUMBER" \ --from-stage cr \ --to-stage design \ @@ -300,7 +300,7 @@ jobs: PR_NUMBER: ${{ needs.detect.outputs.pr_number }} run: | set -o pipefail - python -m medharness --dhf DHF ci generate-dhf --cr "$CR_ID" --pr "$PR_NUMBER" | tee /tmp/revise-generate-dhf-out.json + python -m medharness --dhf DHF change plan --cr "$CR_ID" --pr "$PR_NUMBER" | tee /tmp/revise-generate-dhf-out.json - name: Upload revise-design payload if: always() @@ -368,7 +368,7 @@ jobs: CR_ID: ${{ needs.detect.outputs.cr_id }} PR_NUMBER: ${{ needs.detect.outputs.pr_number }} run: | - medharness ci approve-gate --cr "$CR_ID" --stage design --pr "$PR_NUMBER" + medharness approval check --cr "$CR_ID" --stage design --pr "$PR_NUMBER" - name: Generate implementation env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} @@ -378,7 +378,7 @@ jobs: PR_NUMBER: ${{ needs.detect.outputs.pr_number }} run: | set -o pipefail - python -m medharness --dhf DHF ci develop-cr --cr "$CR_ID" ${PR_NUMBER:+--pr "$PR_NUMBER"} | tee /tmp/develop-out.json + python -m medharness --dhf DHF change implement --cr "$CR_ID" ${PR_NUMBER:+--pr "$PR_NUMBER"} | tee /tmp/develop-out.json - name: Upload develop payload if: always() @@ -436,7 +436,7 @@ jobs: PR_NUMBER: ${{ needs.detect.outputs.pr_number }} ISSUE_NUMBER: ${{ needs.detect.outputs.issue_number }} run: | - medharness ci advance-stage \ + medharness change advance \ --pr "$PR_NUMBER" \ --from-stage design \ --to-stage code \ @@ -526,7 +526,7 @@ jobs: PR_NUMBER: ${{ needs.detect.outputs.pr_number }} run: | set -o pipefail - python -m medharness --dhf DHF ci develop-cr --cr "$CR_ID" --pr "$PR_NUMBER" | tee /tmp/revise-code-out.json + python -m medharness --dhf DHF change implement --cr "$CR_ID" --pr "$PR_NUMBER" | tee /tmp/revise-code-out.json - name: Upload revise-code payload if: always() diff --git a/.github/workflows/issue-to-cr.yml b/.github/workflows/issue-to-cr.yml index 31b3a68..36f417c 100644 --- a/.github/workflows/issue-to-cr.yml +++ b/.github/workflows/issue-to-cr.yml @@ -124,7 +124,7 @@ jobs: # No --pr here: passing --pr switches generate-dhf into revision mode # (reads PR review feedback), which is wrong on a brand-new draft PR. # Revision mode is only used by cr-lifecycle.yml revise-design. - python -m medharness --dhf DHF ci generate-dhf --cr "$CR_ID" | tee /tmp/generate-dhf-out.json + python -m medharness --dhf DHF change plan --cr "$CR_ID" | tee /tmp/generate-dhf-out.json - name: Post design failure comment if: always() && steps.open_pr.outcome == 'success' && steps.generate_design.outcome != 'success' diff --git a/scripts/ci/check_medharness_contract.py b/scripts/ci/check_medharness_contract.py index c585a22..224c100 100644 --- a/scripts/ci/check_medharness_contract.py +++ b/scripts/ci/check_medharness_contract.py @@ -91,21 +91,24 @@ def main() -> int: errors: list[str] = [] help_commands = { - "generate-dhf": ("python", "-m", "medharness", "ci", "generate-dhf", "--help"), - "develop-cr": ("python", "-m", "medharness", "ci", "develop-cr", "--help"), - "validate-code": ("python", "-m", "medharness", "ci", "validate-code", "--help"), - "validate-branch": ("python", "-m", "medharness", "ci", "validate-branch", "--help"), - # Smoke-check session helpers even though workflows rely on the built-in - # threading that generate-dhf and develop-cr perform when --pr is supplied. - "claude-session-get": ("python", "-m", "medharness", "ci", "claude-session", "get", "--help"), - "claude-session-put": ("python", "-m", "medharness", "ci", "claude-session", "put", "--help"), + # change group (formerly ci generate-dhf / develop-cr / cr-status / advance-stage) + "change-plan": ("python", "-m", "medharness", "change", "plan", "--help"), + "change-implement": ("python", "-m", "medharness", "change", "implement", "--help"), + "change-status": ("python", "-m", "medharness", "change", "status", "--help"), + "change-advance": ("python", "-m", "medharness", "change", "advance", "--help"), + # verify group (formerly ci validate-branch / validate-code) + "verify-branch": ("python", "-m", "medharness", "verify", "branch", "--help"), + "verify-code": ("python", "-m", "medharness", "verify", "code", "--help"), + # approval group (formerly ci approve-gate) + "approval-check": ("python", "-m", "medharness", "approval", "check", "--help"), + # automation group (formerly ci claude-session get/put) + "session-get": ("python", "-m", "medharness", "automation", "session", "get", "--help"), + "session-put": ("python", "-m", "medharness", "automation", "session", "put", "--help"), + # dhfkit data-layer commands (unchanged) "dhf-report": ("dhfkit", "--dhf", ".", "report", "--help"), "dhf-context-implementation": ("python", "-m", "medharness", "dhf", "context", "implementation", "--help"), "dhfkit-soup-sync": ("dhfkit", "--dhf", ".", "soup-sync", "--help"), "dhfkit-release-baseline": ("dhfkit", "--dhf", ".", "release-baseline", "--help"), - "ci-approve-gate": ("python", "-m", "medharness", "ci", "approve-gate", "--help"), - "ci-cr-status": ("python", "-m", "medharness", "ci", "cr-status", "--help"), - "ci-advance-stage": ("python", "-m", "medharness", "ci", "advance-stage", "--help"), } help_output: dict[str, str] = {} @@ -136,8 +139,18 @@ def main() -> int: cr_complete_text = CR_COMPLETE.read_text(encoding="utf-8") require( - "python -m medharness --dhf DHF ci generate-dhf" in cr_text, - "cr-lifecycle.yml must call generate-dhf with global --dhf", + "python -m medharness --dhf DHF change plan" in cr_text, + "cr-lifecycle.yml must call change plan with global --dhf", + errors, + ) + require( + "python -m medharness --dhf DHF change implement" in cr_text, + "cr-lifecycle.yml must call change implement with global --dhf", + errors, + ) + require( + "python -m medharness --dhf DHF ci generate-dhf" not in cr_text, + "cr-lifecycle.yml still contains old ci generate-dhf call — use change plan", errors, ) require( @@ -156,23 +169,23 @@ def main() -> int: errors, ) require( - "medharness --dhf DHF ci validate-branch" in ci_text, - "ci-pipeline.yml must call validate-branch with global --dhf", + "medharness --dhf DHF verify branch" in ci_text, + "ci-pipeline.yml must call verify branch with global --dhf", errors, ) require( - "medharness --dhf DHF ci validate-code" in ci_text, - "ci-pipeline.yml must call validate-code with global --dhf", + "medharness --dhf DHF verify code" in ci_text, + "ci-pipeline.yml must call verify code with global --dhf", errors, ) require( - "medharness ci validate-branch" not in ci_text, - "ci-pipeline.yml still contains a local validate-branch invocation", + "medharness --dhf DHF ci validate-branch" not in ci_text, + "ci-pipeline.yml still contains old ci validate-branch — use verify branch", errors, ) require( - "medharness ci validate-code" not in ci_text, - "ci-pipeline.yml still contains a local validate-code invocation", + "medharness --dhf DHF ci validate-code" not in ci_text, + "ci-pipeline.yml still contains old ci validate-code — use verify code", errors, ) require( @@ -207,8 +220,13 @@ def main() -> int: errors, ) require( - "python -m medharness --dhf DHF ci generate-dhf" in issue_to_cr_text, - "issue-to-cr.yml must call generate-dhf inline at intake", + "python -m medharness --dhf DHF change plan" in issue_to_cr_text, + "issue-to-cr.yml must call change plan inline at intake", + errors, + ) + require( + "python -m medharness --dhf DHF ci generate-dhf" not in issue_to_cr_text, + "issue-to-cr.yml still contains old ci generate-dhf — use change plan", errors, ) @@ -229,19 +247,19 @@ def main() -> int: ) require( - "medharness ci approve-gate" in cr_text, - "cr-lifecycle.yml must call approve-gate before develop-cr to guard against event misclassification", + "medharness approval check" in cr_text, + "cr-lifecycle.yml must call approval check before change implement to guard against event misclassification", errors, ) require( - "medharness --dhf DHF ci cr-status" in cr_text, - "cr-lifecycle.yml must emit a cr-status step for observability in the detect job", + "medharness --dhf DHF change status" in cr_text, + "cr-lifecycle.yml must emit a change status step for observability in the detect job", errors, ) require( - "medharness ci advance-stage" in cr_text, - "cr-lifecycle.yml must use ci advance-stage for label management — no raw gh api label calls", + "medharness change advance" in cr_text, + "cr-lifecycle.yml must use change advance for label management — no raw gh api label calls", errors, ) From c32491c367c30035f734725c765eb8b76b3eecc1 Mon Sep 17 00:00:00 2001 From: charles chen Date: Sun, 31 May 2026 12:04:21 +0300 Subject: [PATCH 3/3] =?UTF-8?q?fix(ci):=20rename=20--req-type=20=E2=86=92?= =?UTF-8?q?=20--requirement-type=20for=20medharness=20verify=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old ci test-points command accepted --req-type; the new verify tests command uses the full --requirement-type flag. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci-pipeline.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index fe8b26f..b9826b7 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -553,9 +553,9 @@ jobs: --junit-dir test-results/srs-api \ --junit-dir test-results/sys \ --junit-dir test-results/crs \ - --req-type SRS \ - --req-type SYS \ - --req-type CRS + --requirement-type SRS \ + --requirement-type SYS \ + --requirement-type CRS - name: Run verification completeness gate run: |