Skip to content

jpdev check --changed follow-ups: the vendored check.sh, CI on pull requests, the small-fix prompt, the version message (#43) - #45

Merged
kikashy merged 2 commits into
mainfrom
changed-followups-43
Oct 26, 2026
Merged

kikashy merged 2 commits into
mainfrom
changed-followups-43

Conversation

@kikashy

@kikashy kikashy commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Closes #43: the follow-ups of jpdev check --changed (#40). One pull request, four decisions.

What changes

  1. The vendored check.sh --changed [BASE]. scripts/dev/check.sh and templates/check.sh (byte-identical, as before) take --changed [BASE] with the kit's rules: the whole list when a path is unmapped, when jpdev.json, the manifest or the runner changed, when nothing changed, or with no mapping. Decision: import, not embed. The runner calls scripts/dev/changed_checks.py, which is lib/changed_checks.py with the release on line 2. That is the same vendoring the handover script uses. The selection code exists once. A test holds the copy to the library line for line, and the release workflow refuses a copy that drifted. The template run from the kit falls back to lib/changed_checks.py. check.sh --changed refuses with a named message when the copy is absent, when it is given with --only or --setup, and when the selection code refuses (no output, exit 2). The vendored selection code is a selection input, so a change to it runs the whole list (both runners). changed_checks.py --label names the runner in the report and in a refusal.
    • Tests: every fixture of tests/changed_checks_test.py runs a second time through the clone's vendored runner (VendoredChangedCheckTests). One added test compares the two runners' selection blocks on the same changes, which is the input test the kit has for its own runner. Others cover the template in the kit, the missing copy, --setup, and a change to the vendored selection code.
  2. CI on pull requests. Decision: pull requests run check.sh --changed origin/<base branch>; a push to the default branch and a dispatch run the whole list. This is the policy's split (docs/policy.md, "Scoped runs"). CI owns the deterministic checks, the scoped run is allowed on pull requests, and the full list is required on the default branch and before a release. A repository with no changed mapping still runs the whole list on every pull request. The change applies to templates/ci-check.yml and to the kit's own ci.yml. In the kit's own CI each job keeps its steps: on a pull request lint runs --changed … --skip tests and tests runs --changed … --skip <the lint steps>. Main and dispatch runs are unchanged (--only per job). Checkouts use fetch-depth: 0 so that the merge base is found. The base ref is passed through env, never inlined into the script. The release evidence is unchanged: .github/ci-evidence.json still names push and workflow_dispatch only, and the same per-job check lists. Pull-request runs record no evidence. scripts/dev/changed_checks.py is added to the evidence inputs, as part of the runner. The choice is written up in docs/adopting.md §4, docs/ci-evidence.md and docs/policy.md.
  3. The small-fix prompt. jpdev codex run --small now says to run jpdev check --changed and not to choose the steps, instead of jpdev check --only <steps>. The test asserts the new line and that --only is absent.
  4. The version message. jpdev --version keeps printing jpdev X.Y.Z alone, because the release gate compares it exactly. The new jpdev version adds the kit checkout's git describe (for example jpdev 0.7.0 (v0.7.0-4-g1f02688)), which tells a kit at a release from one past it. It adds nothing for an unpacked tarball or for a kit that only sits inside another repository's clone. An unknown jpdev check option and a refused --changed selection name that version. An unknown check.sh option names the runner's line-2 release. A kit installed before this change cannot be changed after the fact. From this change on, a newer option fails on an older kit with the kit's version in the message.

Public surface

  • New option scripts/dev/check.sh --changed [BASE], plus a new vendored file scripts/dev/changed_checks.py (docs/adopting.md §2, README).
  • New command jpdev version; jpdev --version is unchanged.
  • Changed messages: the unknown-option messages of jpdev check and check.sh, and an added line on a refused --changed.
  • Selection inputs now include scripts/dev/changed_checks.py.
  • templates/ci-check.yml runs --changed on pull requests.
  • The release prep (docs/releasing.md, release.yml) sets and checks line 2 of the new vendored file.

Checks

jpdev check --changed --all on this head (installed kit, jpdev 0.7.0):

== jpdev check --changed: 18 changed paths since origin/main (merge base 1f02688) ==
selection input changed: scripts/dev/check.sh: whole list
== whole list: 8 checks ==
== check summary (dev.json) ==
PASS     diff-check               0s
PASS     local-paths              0s
PASS     agent-docs               0s
PASS     bash-n                   0s
PASS     duplication              0s
PASS     json                     0s
PASS     shellcheck               3s
PASS     tests                    80s
    436 passed, 0 failed
== 8 passed, 0 failed, 0 skipped, 0 not run ==

tests/changed_checks_test.py: 49 tests OK, 23 of them the vendored-runner pass.

Each new safeguard was broken once in a scratch copy, and its test failed at the intended assertion:

  • Dropping scripts/dev/changed_checks.py from the selection inputs fails the selection-code test in both classes.
  • Ignoring the selection in check.sh fails 7 vendored tests.
  • Dropping the version from jpdev's refusal and unknown-option messages fails ChangedCheckTests.test_failures_name_the_kit_version.
  • Dropping the release line from check.sh's unknown-argument message fails the vendored variant.
  • Dropping the top-level check from kit_version made jpdev version print the outer repository's commit, which the run.sh case refuses.

A note for whoever runs tests/run.sh locally: tests/clean_test.py (already on main, not touched here) calls git mktree with stdin inherited. Under a harness whose stdin is an open socket it waits forever. Running with < /dev/null passes. That is a separate issue.

Fix round

At 993b75a, in response to the review:

  • MEDIUM 1 (the no-merge-base refusal had no test): test_a_shallow_clone_without_the_merge_base_is_refused now runs through both runners. The fixture is a --depth 1 --no-single-branch clone whose branch tips share no commit, plus a working-tree change. With and without BASE, the run must refuse with exit 2 and "no merge base of origin/main and HEAD (a shallow clone needs the history)", with no report and no check run. test_an_unresolved_base_is_refused now also covers an empty base branch name (origin/). Mutant: falling back to HEAD in both copies of resolve_base fails 4 subtests (2 per runner); restored, 51 OK.
  • LOW 2: tests/run.sh checks that .github/ci-evidence.json inputs hold scripts/dev/check.sh and scripts/dev/changed_checks.py.
  • LOW 3 (one line): the kit's lint job now runs --all --skip tests on a push as well. That is the same selection as on a pull request and the same seven steps today. A step added to dev.json later fails evidence capture until the evidence file lists it.
  • LOW 4: docs/adopting.md §4 now says a pull-request job fails when the base cannot be resolved, and that a push to the default branch runs the whole list.
  • LOW 5: kit_version runs git on the kit with GIT_DIR, GIT_WORK_TREE and the other repository-locating variables unset. tests/run.sh covers it for a kit checkout and for a kit inside another clone. Mutant: plain git -C printed the other repository's commit for the kit checkout, which the new case refuses.

jpdev check --changed --all at 993b75a: whole list (selection input scripts/dev/check.sh changed), == 8 passed, 0 failed, 0 skipped, 0 not run ==, tests 439 passed, 0 failed.

Sign-off

Every commit carries a Developer Certificate of Origin sign-off that matches its author.

🤖 Generated with Claude Code

…equests, the small-fix prompt, the version message (#43)

- scripts/dev/check.sh --changed [BASE] (and templates/check.sh): the same selection, rules and
  report as jpdev check --changed, through scripts/dev/changed_checks.py, a versioned copy of
  lib/changed_checks.py vendored beside it (the template run from the kit falls back to
  lib/changed_checks.py). The vendored selection code is a selection input. Every fixture of
  tests/changed_checks_test.py runs through both runners, and a test compares their reports.
- CI: a pull request runs check.sh --changed against its base branch; a push to the default
  branch and a dispatch run the whole list, and only they record release evidence. Applied to
  templates/ci-check.yml and the kit's own ci.yml (each job keeps its steps with --skip).
- codex run --small tells the builder to run jpdev check --changed instead of --only <steps>.
- jpdev version adds the kit checkout's git describe; an unknown jpdev check option and a refused
  --changed selection name it; an unknown check.sh option names the runner's release line.

Closes #43

Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kikashy

kikashy commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Review record: devkit PR #45 (issue #43, jpdev check --changed follow-ups), c23ad39 on base 1f02688

The release-evidence boundary holds. A pull-request --changed run cannot be captured, selected or admitted as release evidence, and the push and dispatch commands still cover exactly the lists in the evidence file. No HIGH. One MEDIUM: an untested refusal from #40 that the CI path does not reach. Four LOWs.
Same-vendor review, not a cross-vendor claim. One round, read from source, with probes and mutants in scratch copies only.

# Finding Sev Evidence What to do
1 The "no merge base" refusal in resolve_base (code from #40, used by this PR's CI path) has no test. MEDIUM (from #40) Mutant M2d (fall back to HEAD in both copies) survives: 49/49 OK. In CI (fetch-depth: 0, clean tree) the mutant would still run the whole list. Locally, with uncommitted edits, it would select too few checks. Add a shallow-clone fixture to ChangedCheckTests that asserts exit 2 and the message, so it runs through both runners. Pre-release batch.
2 No test pins the new scripts/dev/changed_checks.py entry in .github/ci-evidence.json inputs. LOW M3 survives in ci_evidence_test and run.sh, as expected: capture and verify both hash the inputs from git show at the same SHA, so removing the entry changes the record but no admit/refuse decision. Optional: a test that the inputs cover scripts/dev/*.
3 The kit's lint job selects steps differently on PRs (--skip tests) than on push (--only plus 7 names). LOW A step added to dev.json later would run in PR lint but in neither job on main, and the explicit evidence list would not require it. The --only on push is older; this PR makes the two paths diverge. Use --skip tests on push too. Capture then refuses with check-coverage until the evidence file lists the new step.
4 When the base cannot be resolved (missing ref, empty BASE_REF, shallow clone), the run refuses (exit 2, the required job fails) instead of running the whole list. LOW (docs) Probed: all three exit 2 before any check runs, and none is read as "nothing changed". This fails closed, and the README lists the refusal. Optional: adopting.md §4 can say the PR job fails rather than falls back.
5 jpdev version with GIT_DIR in the environment (for example, under a git hook) describes the other repository's commit as the kit's. LOW Probe: GIT_DIR=<fixture>/.git jpdev version printed jpdev 0.7.0 (b0529e7), the fixture's commit. Clear GIT_DIR/GIT_WORK_TREE for the two git calls in kit_version.

What held:

  • A (boundary): Two guards keep PR runs out of capture: the step if (refs/heads/main and not pull_request) and capture itself (GITHUB_REF main, event in {push, workflow_dispatch}). verify admits only runs with head_branch main, an allowed event, no PR association, head_sha equal to the tag commit, and this repository as head repository. Coverage must equal the configured list, all PASS. The push and dispatch commands are unchanged (lint 7 + tests 1 = dev.json's 8, equal to the evidence lists). Across the two jobs, the PR split (--skip tests / --skip 7) runs the whole list.

  • B: Probed on a simulated PR merge commit with the vendored runner:

    • docs-only: lint 3, tests none
    • AGENTS.md: 4 checks plus tests
    • templates/*.md: 2 checks plus tests
    • whole list across both jobs: unmapped, mixed, rename to unmapped, scripts/dev/check.sh, scripts/dev/changed_checks.py, jpdev.json, dev.json, empty PR

    BASE_REF reaches the script through env and is quoted.

  • C: run.sh compares the whole library with the vendored copy minus line 2, and checks line 2 against jpdev --version. The release.yml snippet, extracted and run: the clean copy passes; body drift, line-1 drift, wrong line 2, an appended line and a missing file are all refused. All 18 ChangedCheckTests also run through the vendored runner (18 + 5 = 23), and the report comparison covers 3 cases.

  • D: The messages carry no path. An unpacked (non-checkout) kit and a PATH without git both print the plain version with exit 0. --version is unchanged.

  • E: 13 mutants killed: unmapped fallback (lib only; vendored only), unresolved base to HEAD, runner refusal to whole, runner drops --base, empty selection, vendored body drift, vendored line 2, library not re-copied, evidence coverage subset, capture on any event, capture on any ref, select on any event. Survived: M2d (jpdev v0.1.0: setup, check, mutants, record; the manifest, the public runner, the agent docs and the policy #1), M3 (The go toolchain takes a version in dev.json, so a workflow's Go pin has one source #2), and E3, where run_identity's event clause is redundant with select()'s filter (E4 killed; code from Reuse trusted exact-commit CI evidence at release through a shared tested gate #37, no gap).

  • F: No machine-local paths, personal names or organizations in the added lines of docs or workflows.

What ran:

  • Setup: a shared clone at c23ad39, Python 3.12.11, ulimit -v 4000000, timeout and < /dev/null on every run.
  • Baseline: tests/run.sh 436 passed; tests/changed_checks_test.py 49 OK (23 through VendoredChangedCheckTests).
  • Mutants: 16, each applied with an exact-once substitution and reset after. Target: changed_checks_test, ci_evidence_test, or the full run.sh for the copy and inputs mutants.
  • Probes: CI selection on a fixture origin with a GitHub-style merge commit; base and shallow cases; jpdev version in a checkout, an unpacked kit, a PATH without git and with GIT_DIR set; the release.yml copy gate.
  • Cleanup: every probe and scratch copy was deleted afterwards. The review checkout has no changes.

merge: yes (no HIGH). Disposition per the kit policy: the MEDIUM and the cheap LOWs are fixed in this PR in one round by the builder and confirmed by this reviewer.

…by jpdev version, evidence inputs pinned

- tests/changed_checks_test.py: a shallow clone whose branch tips share no commit is refused
  with exit 2 and "no merge base of origin/main and HEAD", through both runners, with a working
  tree change that a fallback would have selected; an empty base branch name (`origin/`) is
  refused as an unresolved BASE.
- bin/jpdev: kit_version runs git on the kit with GIT_DIR, GIT_WORK_TREE and the other
  repository-locating variables unset; tests/run.sh sets GIT_DIR to another repository.
- tests/run.sh: .github/ci-evidence.json inputs hold scripts/dev/check.sh and
  scripts/dev/changed_checks.py.
- ci.yml: the lint job runs `--all --skip tests` on a push too, the same selection as on a pull
  request (the same seven steps today; a new step then fails capture until the evidence lists it).
- docs/adopting.md: a pull-request job fails when the base cannot be resolved; a push to the
  default branch runs the whole list.

Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kikashy

kikashy commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Confirmation: 993b75a

All five findings are fixed: one commit on top of c23ad39, 5 files, +55/−5. Read from the diff, and re-run in a scratch copy of 993b75a. No new finding and no new HIGH.

# Disposition Evidence
1 MEDIUM fixed A new shallow-clone fixture runs through both runners and checks exit 2, the named message, no report and no check run. The surviving mutant M2d (no merge base falls back to HEAD) is now killed in all three forms: in both copies (4 subtests fail), in the library only (2) and in the vendored copy only (2).
2 LOW fixed tests/run.sh pins scripts/dev/check.sh and scripts/dev/changed_checks.py in the evidence inputs. Dropping the selection-code entry fails that test (438/439).
3 LOW fixed On push, lint now runs --all --skip tests. I ran that command at 993b75a: 7 PASS. The log goes through ci_evidence.coverage() with no refusal, and its coverage equals the evidence file's lint list. dev.json minus tests equals that list, and there is no parity step. The tests job and the evidence events (push, workflow_dispatch) are unchanged.
4 LOW fixed adopting.md §4 now says a PR job fails with the refusal when the base cannot be resolved, and that a push runs the whole list. The unresolved-base test now also covers an empty base name (origin/).
5 LOW fixed kit_git clears GIT_DIR, GIT_WORK_TREE and the other repository-locating variables for both git calls. Putting plain git -C back fails both new GIT_DIR tests (437/439).

What ran:

  • Baseline: tests/changed_checks_test.py 51 OK; tests/run.sh 439 passed.
  • Mutants: 5, all killed (M2d in three forms, the inputs entry, kit_git).
  • The push lint command, run once, with its log fed to the evidence coverage validator.
  • Limits as before. Scratch copies are deleted, and no tracked file changed.

merge: yes (993b75a)

@kikashy
kikashy merged commit 3557adf into main Oct 26, 2026
3 checks passed
@kikashy
kikashy deleted the changed-followups-43 branch October 26, 2026 04:28
@github-actions

Copy link
Copy Markdown

🎉 Thank you for your contribution to Judgment Pack!

If you’d like another task, choose a path that interests you:

@kikashy kikashy mentioned this pull request Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

jpdev check --changed follow-ups: the vendored check.sh, the CI templates and the small-fix prompt

1 participant