Skip to content

Bump actions/upload-artifact from 7.0.1 to 7.0.2 - #34

Merged
kikashy merged 7 commits into
mainfrom
dependabot/github_actions/actions/upload-artifact-7.0.2
Oct 27, 2026
Merged

kikashy merged 7 commits into
mainfrom
dependabot/github_actions/actions/upload-artifact-7.0.2

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Oct 21, 2026

Copy link
Copy Markdown
Contributor

Bumps actions/upload-artifact from 7.0.1 to 7.0.2.

Release notes

Sourced from actions/upload-artifact's releases.

v7.0.2

What's Changed

  • Improves artifact download retries when the service returns HTTP 429 (rate limiting), including honoring valid Retry-After headers.
  • Updates @​actions/artifact to v6.3.1.

New Contributors

Full Changelog: actions/upload-artifact@v7.0.1...v7.0.2

Commits
  • cf430e0 Merge pull request #822 from actions/tunc-d-prepare-upload-v7-0-2
  • c2acded chore: prepare v7.0.2 release
  • 4687c1b Merge pull request #821 from actions/tunc-d-upload-artifact-package-bump
  • 8cbe1be Bump @​actions/artifact to 6.3.1
  • See full diff in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 7.0.1 to 7.0.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@043fb46...cf430e0)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file github_actions Pull requests that update GitHub Actions code labels Oct 21, 2026
kikashy added a commit that referenced this pull request Oct 26, 2026
…ndent checks, keep the tag-context run for the rest, fail closed (#38) (#44)

Closes #38. Its "Required evidence gate" list is the acceptance
contract. Milestone v0.3.0 (tooling). One review round: release
admission is a trust boundary.

## Summary

A release no longer runs the whole check list twice more at the tag
(once from the tag push's own CI run, once from the release's call). It
now does two things in order:

1. **Reuses main's evidence for the checks that read the tree.** The
release takes the evidence main's push CI recorded at the **exact**
release commit for `local-paths`, `agent-docs` and `diff-check`. It does
this through the development kit's release evidence gate (devkit
v0.10.0, vendored as `scripts/dev/release_evidence.py`, configured by
`.github/ci-evidence.json`) in a new job, `CI evidence at the tag`. Any
refusal fails that job, names the code and stops the release.
2. **Runs the `entries` tests again in the tag's context.** These tests
read the tags and the release history (`release_being_made()` and the
tag listing in `tests/test_entries.py`), and the release's tag changes
both. So the release still runs them at the tag, after the gate: `CI at
the tag` calls the commit's `ci.yml` with `tag-context: true`. The
checkout still has the whole history and tags, the specification is
still at the pin, and no verifier test is skipped. That call is now the
only run in the tag's context. `ci.yml`'s `push` trigger is limited to
branches, so a tag push no longer starts a duplicate run that gates
nothing.

Every release-specific check stays exactly as it was and in the same
order: tag grammar (shell and `release_tag_ok`), tag names the commit,
commit on main, notes, environment reviewer, manifest blob bytes,
checksums, attestation, no existing release, draft identity, notes and
asset digests, published digests. The required `entries` status keeps
its name (the job now says `name: entries` explicitly, which gives the
same check name). Branch protection, review and publication requirements
are unchanged.

### Public surface changes

- `ci.yml` gains a `workflow_call` input `tag-context` (boolean, default
`false`). A push now runs its list in two parts: the tree checks, whose
summary block is the evidence log, then `HISTORY_CHECKS` (`entries`). A
failure in the first part does not stop the second. On a push to main it
then runs `release_evidence.py capture` and uploads
`ci-evidence-ci-entries-<attempt>` (7 days).
- `ci.yml` no longer runs on a tag push (`push: branches: ["**"]`).
Branch pushes and pull requests run as before.
- `release.yml` gains the `evidence` job (`CI evidence at the tag`,
`contents: read`, `actions: read`). `verify` (`CI at the tag`) needs it,
and `package` needs both. The decision record is in the step log and in
the `release-evidence-<attempt>` artifact, uploaded on reuse and on
refusal (`!cancelled()`).
- **New release precondition:** main's CI run at the commit must have
completed (`run-unfinished` otherwise), must be under 48 h old, and must
have run on the runner image the release job resolves. The tag must be
annotated; `git tag -s`, which `docs/releasing.md` already prescribes,
makes an annotated tag, and v0.1.0 to v0.2.1 are all annotated.

## Inventory: three executions at a release commit

| Check | Main push CI (before the tag) | Tag-push CI (before) |
Release-called CI (before) | After this PR |
| --- | --- | --- | --- | --- |
| `local-paths`, `agent-docs`, `diff-check` (read the commit's tree
only) | run | run | run | main: run **and recorded**. Release:
**reused** from main's evidence at the exact commit |
| `entries` (the unittest suite: reads `GITHUB_REF`'s release tag, the
tag listing below it, the earlier releases' blobs, and the specification
at the pin) | run with no release being made and the tags present then |
run at the tag | run at the tag | main: run, not recorded as evidence.
Release: **run once, fresh, at the tag** (`CI at the tag`, `tag-context:
true`) |
| setup (`python-deps`, an empty requirements file) | run | run | run |
main: run. Release: run before `entries` |
| tag, notes, environment reviewer, manifest bytes, checksums,
attestation, draft/published identity | not run | not run | release.yml,
run | release.yml, run, unchanged |

Main's evidence and the release's tag-context run do not overlap. A test
holds the configuration's reused checks plus `HISTORY_CHECKS` to exactly
`dev.json`'s checks, with no check in both. The tag-push run was
equivalent to the release's call: same commit, same `refs/tags/<tag>`
ref (so the same `release_being_made()`), same full-history checkout,
same pinned specification, started by the same push. Its result gated
nothing. The release's call now covers every check it ran, which is the
issue's condition for suppressing tag CI.

**Branch CI is not changed.** A branch push (whole list, at the head
commit) and a pull request run (the `--changed` selection, at the
synthetic merge commit) have different commits and selections, so they
are not equivalent executions. A branch without a pull request has only
the push run.

**Toolchain and inputs.** There is one profile, `linux`, under `compare:
release-runner`. The release job resolves the image (`ImageOS`,
`ImageVersion`, `/etc/os-release`) and `bash`, `git`, `cmp` and
`python3` itself, and they must equal main's. CI runs the checks with
setup-python's 3.12 (3.12.15 from the runner's tool cache, not the
image's own `python3`), so the evidence job sets Python up with the same
two steps as `ci.yml`, and a test holds the two to the same text.
Recorded inputs: `dev.json`, the configuration, the gate, `ci.yml`
(which also holds the specification pin), `requirements-dev.txt`,
`scripts/dev/check.sh` and `scripts/sync-agent-docs.sh`. Permitted
skips: none (no conditional job, no optional check skip). Trusted event:
`push` to main only; this repository does not dispatch CI.

## Fresh route (documented in `docs/releasing.md`)

A refusal stops the run, and nothing runs CI again on its own. The
operator picks one route, once:

1. Re-run all jobs of main's CI run at the commit. This is also the
route for `run-age` and for a hosted image that changed
(`runtime-identity`). For `run-unfinished`, wait for main's run instead.
2. When that run has completed, re-run the release run's failed jobs.
3. If the gate refuses again: fix on main and release a new version.

## Live probe (read-only API)

`probe.py` walks real runs through the vendored gate's `decide()` with
this configuration, using GET calls only. Three things are stubbed:
- the release tag, since no tag exists at the probed commits: an
annotated tag object naming the commit;
- the runner identity, with this machine standing in for both runners;
- in the first walk only, the evidence artifact. Main's `ci.yml` does
not capture until this PR merges, so the probe builds the artifact with
the gate's own `evidence()`. It uses the real run's ids, the real job
interval, and the real `PASS` rows of that job's log for the three tree
checks.

| Walk | Commit | Live reads | Decision |
| --- | --- | --- | --- |
| Latest main CI run 40395182182 (push, attempt 1), evidence artifact
synthesized | `6398ed8` | repository and default branch; workflow
`ci.yml` resolved (id 384594220, name `CI`, active); runs at the commit:
1, applicable 1; run identity; attempt-1 jobs (`entries`, success,
`ubuntu-latest`, hosted group 0); re-read of the run; re-selection twice
| **`reuse`** (run 40395182182 attempt 1, age 0.92 h) |
| The same run, no stub for the artifact | `6398ed8` | the same, then
the real artifact listing | `refuse: artifact-coverage` (0 artifacts
named `ci-evidence-ci-entries-1`: main records no evidence before this
merges) |
| PR #43 head (pull_request run 40390714808, branch push run
40390706439) | `88cbf53` | runs at the commit: 2, applicable 0 |
**`refuse: run-missing`** |
| The same two runs handed directly to the gate's run identity check |
`88cbf53` | each run read by id | `run-trigger` for both |

The first live `capture` is the first main push after merge. A capture
refusal there fails main's `entries` job visibly. The first live
`verify` is the next release. Locally, `capture` with this
configuration, given the real `check.sh --all --skip entries` summary of
this head, records `{agent-docs, diff-check, local-paths: PASS}` and the
seven input ids.

## Tests and mutants

`tests/test_release_evidence.py` adds 15 tests. They run inside the
`entries` check, both at main and at the tag:
- The configuration passes the gate's own `check_config`.
- Every input is a file of the tree.
- The workflow is `ci.yml` by path and `name:`, with `push` only and no
skipped jobs.
- Every required job is a job of `ci.yml`, on its labels, with no matrix
and no job-level `if`.
- The reused checks plus `HISTORY_CHECKS` equal `dev.json`'s checks,
with none in both.
- A handwritten `check.sh` summary block is read by the gate's own
`coverage()`. The three rows are accepted. Adding `entries`, dropping a
check, or a `SKIP` each refuses.
- `ci.yml`'s capture and upload run on a push to main after the checks,
from the tee'd log.
- `release.yml`'s gate needs `admit`, has `actions: read`, has no
`continue-on-error`, and keeps its record on refusal.
- The gate job sets up Python as `ci.yml` does.
- `CI at the tag` needs the gate and passes `tag-context: true`, and
`package` needs both.
- `ci.yml` has no tag trigger.
- The vendored copy: line 2 names v0.10.0, the rest has the git blob id
of the kit's `lib/release_evidence.py` at v0.10.0 (`d00e4d48…`, checked
against the kit's tag), and the four vendored scripts name one kit
release.

The kit's 85 gate tests are not copied: the copy is held byte for byte
instead.

| Mutant (scratch copy of the head, one safeguard broken) | Caught by |
| --- | --- |
| M01 the configuration's job name drifts from `ci.yml` |
`test_every_required_job_is_a_job_of_ci_yml_on_its_labels` |
| M02 an input path that does not exist |
`test_every_input_path_is_a_file_of_the_tree` |
| M03 `entries` added to the reused checks | `…_dev_json_split_in_two`,
`test_the_evidence_log_is_read_as_the_gate_reads_it` |
| M04 reused checks `"all"` | the same two |
| M05 the vendored gate changed after line 2 |
`test_the_rest_is_the_kits_file_byte_for_byte` |
| M06 line 2 names another release |
`test_line_two_names_the_kit_release`,
`test_the_vendored_scripts_name_one_kit_release` |
| M07 `ci.yml` runs on a tag push again |
`test_a_tag_push_starts_no_second_run_at_the_tag` |
| M08 `ci.yml`'s job display name drifts |
`test_every_required_job_is_a_job_of_ci_yml_on_its_labels` |
| M09 the push path drops the history checks |
`test_ci_records_the_evidence_on_a_push_to_main_after_the_checks` |
| M10 capture on every push, not only main | the same |
| M11 `HISTORY_CHECKS` emptied | `…_dev_json_split_in_two` |
| M12 the release calls `ci.yml` for the whole list |
`test_the_tag_context_run_follows_the_gate_and_the_package_waits_for_both`
|
| M13 no decision record on a refusal |
`test_the_gate_runs_after_admission_and_keeps_its_record` |
| M14 `package` no longer needs the gate |
`test_the_tag_context_run_follows_…` |
| M15 the tag-context run no longer needs the gate | the same |
| M16 `continue-on-error` on the gate step |
`test_the_gate_runs_after_admission_and_keeps_its_record` |
| M17 the gate job without CI's Python |
`test_the_gate_sets_up_the_python_ci_yml_ran_with` |
| M18 the evidence log no longer the checks' output |
`test_ci_records_the_evidence_on_a_push_to_main_after_the_checks` |

All 18 were caught, and the unmutated head passes. actionlint 1.7.12 on
both workflows reports one finding, SC2034 (`attempt` unused) in the
unchanged draft step, which is the same on main.

## Measured before, expected after

Durations are from the API, job start to completion.

| Release | Main push CI `entries` | Tag-push CI `entries` | `CI at the
tag / entries` | Admit end to package start |
| --- | --- | --- | --- | --- |
| v0.2.1 (run 39175705155) | 12 s (run 39175678865) | 14 s (run
39175705135) | 13 s | 34 s |
| v0.2.0 (run 39070293785) | 17 s (run 39070265083) | 14 s (run
39070293773) | 12 s | 17 s |

On the latest main run (40395182182), the three tree checks report `0s`
each, `entries` 3 s, and the job takes 13 s, mostly checkout and setup.

After this PR, a release has one fewer full CI run (the tag-push run,
about 14 s), and the release's call drops three checks that each take
under a second. In exchange it adds the gate job, which runs before the
tag-context run. The kit measured its earlier gate job at 14–15 s
(v0.8.0, v0.9.0). This gate reads one more repository object and sets up
Python, so a similar cost is expected, but that is not measured here.

**No saving is claimed.** Total runner time at a release is expected to
stay about the same. The release's own critical path is expected to grow
by about the gate job's duration, because the gate runs first, as the
brief orders. What changes is that two tag-context runs become one, and
an exact-commit decision record now exists. The first real numbers come
at the next release. A refusal adds the fresh route's re-run.

## Not changed / not verifiable before merge

- This PR's CI does not exercise `capture`, because it runs only on a
push to main. The first push after merge will.
- `verify` is first live at the next release.
- `update-branches.yml` is not changed.
- Dependabot #34 (upload-artifact 7.0.2) will need a rebase to cover the
two new uses of the 7.0.1 pin.

## Checks

Head `134100a`, `jpdev check --changed` against `origin/main` (merge
base `6398ed8`; `jpdev.json` changed, so the whole list ran), with
`.spec/` at the pin:

```text
== check summary (dev.json) ==
handover: boundary 1 paths, shared 2 paths; see AGENTS.md "Before you hand over"
PASS     local-paths              0s
PASS     agent-docs               0s
    sync-agent-docs: CLAUDE.md matches AGENTS.md
PASS     diff-check               0s
PASS     entries                  6s, python .venv/bin/python3 (Python 3.12.11)
    Ran 153 tests in 6.538s
    OK (skipped=3)
== 4 passed, 0 failed, 0 skipped, 0 not run ==
```

The handover self-check against `origin/main` passed: the boundary path
`scripts` gains only a new file.

## Fix round

This round answers the [review
record](#44 (comment))
at `134100a` (0 HIGH, 2 MEDIUM, 2 LOW) in one commit, `15ff65e`.

| Finding | Fix | Safeguard removed in a scratch copy, then caught by |
| --- | --- | --- |
| MEDIUM 1: nothing held the three reused checks to the tree |
`ReusedChecksReadTheTree`: the `dev.json` commands of `local-paths`,
`agent-docs` and `diff-check` are pinned in the test (shell toolchain,
no `cwd`/`requires_env`), so editing one means classifying it again.
Each command, and the repository script it runs
(`scripts/sync-agent-docs.sh`, which runs no other), is scanned for a
read of a tag, the run's ref or the history or `.spec` (`git
tag`/`describe`/`log`/`rev-list`/`for-each-ref`/`ls-remote`/`show-ref`/`cat-file`/`show`/`merge-base`/`fetch`...,
`GITHUB_`, `refs/tags/`, `HEAD~`). The scan has its own handwritten
vectors | R1 (diff-check reads `git tag --points-at`), R1b (local-paths
branches on `$GITHUB_REF`), R1c (pin and command both moved to `git
describe`), R1d (`git log` in `sync-agent-docs.sh`), R1e (the script
runs another repository script): all caught |
| MEDIUM 2: fail-closed was held only through `needs:` |
`test_no_job_after_the_gate_can_run_past_a_refusal`: the release's jobs
are exactly admit, evidence, verify, package, attest-and-draft and
publish. None has a job-level `if:` or `continue-on-error`, and each
after the gate needs `evidence` directly or through the jobs it needs |
R3 (`package` `if: ${{ always() }}`), R3b (`verify` `if: ${{
!cancelled() }}`), R3c (`publish` `continue-on-error`), R3d
(`attest-and-draft` needs `admit` only): all caught |
| LOW 3: `release_being_made` docstring named a tag push | now
"release.yml calling ci.yml at the tag" | |
| LOW 4: stale "image python 3.12.3" in this body | corrected above:
both jobs set up 3.12.15 from the runner's tool cache | |

Mutants this round: 9 of 9 caught, and the unmutated head passes.
`tests/test_release_evidence.py` now has 19 tests.

Head `15ff65e`, `jpdev check --changed` against `origin/main`, which ran
the whole list because `jpdev.json` changed:

```text
== check summary (dev.json) ==
handover: boundary 1 paths, shared 2 paths; see AGENTS.md "Before you hand over"
PASS     local-paths              0s
PASS     agent-docs               0s
    sync-agent-docs: CLAUDE.md matches AGENTS.md
PASS     diff-check               0s
PASS     entries                  7s, python .venv/bin/python3 (Python 3.12.11)
    Ran 157 tests in 7.000s
    OK (skipped=3)
== 4 passed, 0 failed, 0 skipped, 0 not run ==
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@kikashy
kikashy merged commit e61b231 into main Oct 27, 2026
4 checks passed
@kikashy
kikashy deleted the dependabot/github_actions/actions/upload-artifact-7.0.2 branch October 27, 2026 11:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file github_actions Pull requests that update GitHub Actions code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant