ci: plan affected validation instead of running every native suite - #18
Merged
Merged
Conversation
`scripts/validate.py --stages LIST` runs only the named stages, always in the fixed order whitespace, source-contract, documentation, protocol-analysis, dependency-boundary, format, clippy, contracts. The Rust toolchain is probed only when a Cargo stage is selected, so the documentation and source-contract checks run without Rust. A stage left out is recorded as not_selected_by_plan, never as passed, and a run of only some stages does not claim the DG-0 milestone. The default run is unchanged: every stage except whitespace, the same commands in the same order. test_measure.py is its own stage (protocol-analysis) instead of part of documentation. The new whitespace stage is `git diff --check` of `--diff-base`..HEAD, or of the whole tree at HEAD when no base is given. The whole tree exempts only files whose bytes match the SHA-256 the CI policy pins. The workspace graph moves to module level (WORKSPACE_GRAPH) so the CI planner can derive the affected functional suites from it.
scripts/ci-policy.json classifies every changed path, first match in this order: - full: .github, scripts, Cargo manifests and locks, toolchain files, Git ignore/attribute files and .devguard.toml; - historical: docs/handoff records, which run whitespace and documentation checks only; - normative: design, contract, operations, planning, translation and ledger inputs, AGENTS.md, README.md, LICENSE and NOTICE, which run whitespace and documentation, plus source-contract for the approved design, its source record and milestones.json; - a workspace crate, which runs the complete validator on both platforms and the functional suites whose packages, or the binaries they build first (qualify.py PREBUILD), depend on it. A path in no class, a missing or untrusted base, an empty diff and any change to a planning input make the plan full. scripts/ci_plan.py binds the plan to GITHUB_SHA, the event, the policy digest, the run id and the attempt. pull_request plans the merge against its first parent and push plans main's before..after; schedule and workflow_dispatch are full; any other event, merge_group included, has no plan. PR #17's one-file diff (a docs/handoff record) is a regression case: it selects whitespace and documentation only, no Rust and no suite. Drift tests tie the policy to the workspace members, the suite map to the dependency closure recomputed from qualify.py, the native set to qualify.NATIVE, the source-contract inputs to what source_contract() reads, and every tracked path to a class.
scripts/ci_run.py runs one job's share of the plan after refusing any plan not made for this commit, event, policy, run and attempt, and records that binding beside the job's evidence: - repository: whitespace and the plan's other non-Rust validator stages; - bind contracts / contracts --os OS: the functional suites the plan selects for OS, in policy order, continuing after a failure and never starting an unplanned suite. The hosted-runner exceptions moved into the policy: a suite listed there runs with --allow-incomplete, and each case its report records as not run must match an allowance's suite, stage, case, path and reason exactly; - summary --os OS: the job summary, with today's statement of scope. scripts/check_ci_results.py derives the plan again and requires the plan job's to equal it, the jobs to have the planned results, and evidence only from artifacts of the current attempt whose bindings name this run and attempt. An earlier attempt's artifact never satisfies the current one, so re-running only the failed jobs cannot pass.
The workflow keeps its name, the contracts matrix (contracts (macos-14), contracts (ubuntu-24.04)), the toolchain step, the exact validator step and the dg0-<os>-<attempt> artifacts, and adds: - Plan: runs the CI policy tests, then scripts/ci_plan.py; - Repository checks: whitespace (git diff --check, now a real CI check) and the planned non-Rust stages, without Rust; - contracts: only when the plan selects Rust; the suites and the hosted-runner exceptions come from the policy; - Required checks: always runs and passes only when this run and attempt did what the plan requires. Pull requests and pushes to main are planned; branch pushes no longer duplicate pull-request runs. A daily scheduled run and every manual run are full: the scheduled run is the compensating control that keeps qualifying the whole repository while pull requests and main pushes run only affected checks. A newer push to a pull request cancels its older run. No expression is expanded inside a script, and every checkout drops its credentials.
The verification plan's commands, status list, suite paragraph and evidence rules now describe the affected-check model: path classes, full-coverage triggers, the daily scheduled full run as its compensating control, not_selected_by_plan, policy-listed hosted exceptions and the current-attempt gate. The CS-RG sections are unchanged. The sentence that CodeSpace's existing CI checks still run for documentation PRs is out of date since CodeSpace #70; it now says what CodeSpace main (ab0341b) runs for a documentation-only change, as its workflow, policy and docs-only run 36336288225 show. The Korean counterpart was reviewed and only the planning-verification pair's hashes were recorded.
Require changed-paths.json, both leg records and every current-run artifact name to match the current attempt's plan. Earlier attempts remain ignored and can never satisfy it; unexpected unsuffixed artifacts now fail instead of being mislabeled as earlier evidence. Validate incomplete suites stage by stage: each allowed not-run case must appear in its policy-named incomplete stage, every other stage must pass, and duplicate or unexplained cases fail.
13 tasks
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
Select the minimum sufficient CI/qualification coverage for a change while failing closed for unknown, CI-sensitive or untrusted changes.
This is an independent CI-hardening change. It is not CS-RG implementation, changes no DevGuard resource semantics, and does not modify or amend #17.
Base:
9e21cc8f707f16b7da98490293b5ea7e4548916dHead:
094b0b7b46c7ebe6f1a37ccdaaa08edb5f7c9ca0Motivation
#17 changes only
docs/handoff/2026-09-28-w3-decision-packet.md. Its pull-request macOS run 36408760157 nevertheless rancargo test --workspaceand failed the unrelated native upgrade test only at its< 10 stiming assertion, after the expectedResourceUnavailable/did not finishresult. Its push run 36408700221 passed on the same macOS image. The PR merge tree and branch head have the same Git tree.That is not treated as a product regression. It shows that running all native qualification for every documentation record produces a poor merge signal.
What changes
scripts/ci-policy.jsonis the reviewable source of path classes, component-to-suite dependencies, platforms, full-coverage triggers, whole-tree whitespace pins and exact hosted-runner incomplete allowances.scripts/ci_plan.pybinds a plan to the source SHA, event, policy digest, run ID and attempt.scripts/ci_run.pyruns only one job's planned stages/suites and records the binding and leg result.scripts/check_ci_results.pyis the final gate. Required checks always runs, derives the plan again and accepts only the exact current-attempt plan, changed paths, leg records and reports.scripts/validate.pycan run selected stages without probing Rust. Its default invocation preserves the existing command order and scope; the protocol-analysis tests are only split into their own recorded stage.git diff --checkis now an actual CI stage.docs/planning/verification.mdand its reviewed Korean counterpart describe the affected-check model. The stale CodeSpace documentation-CI sentence now matches CodeSpacemainatab0341band observed docs-only run 36336288225.Profiles
docs/handoff/**historical recordThe suite map is recomputed in tests from
qualify.py's package selectors, the explicit workspace dependency graph and its non-CargoPREBUILDlaunch-helper edges.The #17 one-file diff is a policy regression case: it selects
whitespaceanddocumentation, no Rust and no functional suite, including nodg1-upgrade.Events and full-coverage control
pull_request: affected plan against the checked-out merge's trusted first parent.push:mainonly, affected plan from trustedbeforetoGITHUB_SHA.schedule(daily 18:17 UTC): full.workflow_dispatch: full.merge_group, has no plan and fails. Read-only repository queries found no classic branch protection, no rulesets/effective rules and no merge queue or pastmerge_grouprun.The new daily full run is the compensating control for affected PR/main checks: it continues qualification of the full repository on both platforms, all native suites included. This PR does not activate that schedule unless merged.
Evidence integrity
github.run_attempt; every report is bound to the exact source/event/policy/run/attempt.changed-paths.json, mismatched leg records, unplanned suites or a stage reported as passed when not selected fail the gate.Validation
At exact head
094b0b7b46c7ebe6f1a37ccdaaa08edb5f7c9ca0:python3.12 -B -m unittest discover -s scripts -p 'test_ci_*.py': 95 passed (also exercised on Python 3.10 before the final evidence-tightening commit).python3 scripts/check_docs.py: 17 reviewed pairs, 48 work units, 25 logical groups.git diff --check origin/main HEAD: passed.python3 scripts/ci_plan.py --base origin/main --head HEAD: full, 14 paths, zero unclassified, 12 macOS suites and 2 Ubuntu suites; reasonsplanning-changed,.github/**,scripts/**.python3.12 -B scripts/validate.py --offline --output target/qualification/ci-hardening-task8-full-094b0b7: passed, including source contract, docs, 62 protocol-analysis tests, dependency boundary, fmt, workspace Clippy and 318 workspace test results.Pull-request run 36443987640 passed at the exact branch head. GitHub tested merge source
d770796edb3f28b6bdd35d219d5481fc5ab25b8d, whose parents are the base and094b0b7…and whose tree exactly equals the branch head's tree.f81f54c…, run 36443987640 attempt 1.not_selected_by_plan.contracts (ubuntu-24.04): passed; complete validator (198 test results),dg1-authorityanddg1-authpassed.contracts (macos-14): passed; complete validator (318 test results), all 12 suites ran. Nine passed;dg1-scopes,dg1-launchanddg1-cargowere accepted as incomplete only for their exact policy-listed hosted-runner cases.Repository settings and limitations
Read-only preflight found no required checks and no merge queue. No repository setting changes are in this task. If this is later approved for merge, making Required checks required is a separate owner action.
This first version deliberately does not narrow workspace Clippy or workspace tests by package. Rust source still gets those complete checks. Package-level narrowing requires a separately proven model for test-only, feature, platform and runtime-binary edges.
Policy self-tests reject a newly tracked unclassified path before planning, so a new path class must be reviewed in the same PR. The planner itself also treats unclassified changed paths as full.
Rollback
Revert this PR. It changes only CI/planning code and verification documentation; no runtime state, service, credential, journal, release or host setting changes.
Evidence
Local ignored evidence:
evidence/ci-hardening-2026-09-28/(Task 0 settings/merge-queue queries, #17 baseline runs, CodeSpace readback and Task 8 hashes). No private payload or credential scan hit was found.