Re-verify the Gitea 1.25.4-pinned CI claims after the 1.27.1 upgrade #747
Closed
opened 2026-08-05 23:32:37 +02:00 by timothy
·
6 comments
No Branch/Tag Specified
main
renovate/meziantou.analyzer-3.x
release/v26.15.0-notes
fix/830-add-items-error-surface
renovate/lucene.net
renovate/cliwrap-3.x
issue-806-guard-populations
renovate/dotnet-monorepo
scratch/767b-poisoned
scratch/767b-control
release/v26.14.0-notes
release/v26.14.0
renovate/sqlitepclraw.bundle_e_sqlite3-3.x
docs/510-skill-logo-bug-policy
fix/510-watermark-resolution-policy
fix/629-verdict-classifier-falseopens
fix/609-decisions-edit-token-scope
issue-135-clear-to-none
release/v26.12.0-notes
fix/409b-lastscan-api-parity
fix/401-updatechannel-mirror-422
fix/327-playlist-rename-validation
fix/410-scancancel-log-level
fix/409-447-librariesscreen-neverscanned
fix/338-zap-exit-code
fix/367-plex-budget-message
fix/310-debom-legacy-cs
ci/604-lane-rebalance
feat/388-design-mirror
feat/247-test-ownership
feat/247-primary-action
feat/357-player-owned-playback
feat/357-jellyfin-plugin-poc
fix/289-mcp-hardening
issue58-mcp
feat/244-channels-extract
ci/auto-bump-prod-compose
feat/multi-rerun-collections-api
feat/collections-api
feat/quick-wins
feat/185-docs-part2
feat/140-collections-screen
feat/146-channel-edit
feat/147-classic-ui-link
issue22-renovate-dashboard
feat/91-cutover
feat/63-composite-create
feat/65-library-browse
feat/85-epg
feat/86-schedule-editor
feat/109-dashboard-data
feat/99-session-tracking
fix/dockerfile-node-tag
feat/59-spa-foundation
docs/59-ui-redesign-brief
feat/102-json-guide
feat/111-schedule-durations
feat/104-artwork-upload
feat/103-media-sources-api
feat/playouts-read-api
feat/108-health-api
feat/105-picker-list-endpoints
issue-97-channel-state-api
issue42-jellyfin-musicvideos
issue46-rest-api-error-contract
dependabot/nuget/ErsatzTV.FFmpeg.Tests/multi-d307a2e06f
qsv-improvements
hdr-vulkan-cuda-test
v26.15.0
v26.14.0
v26.13.0
v26.12.0
v26.11.0
v26.10.0
v26.9.0
v26.8.0
v26.7.0
blazor-final
v26.6.0
v26.5.0
v26.4.0
v26.3.1
v26.3.0
v26.2.0
v26.1.1
v26.1.0
v25.9.0
v25.8.0
v25.7.1
v25.7.0
v25.6.0
v25.5.0
v25.4.0
v25.3.1
v25.3.0
v25.2.0
v25.1.0
v0.8.8-beta
v0.8.7-beta
v0.8.6-beta
v0.8.5-beta
v0.8.4-beta
v0.8.3-beta
v0.8.2-beta
v0.8.1-beta
v0.8.0-beta
v0.7.9-beta
v0.7.8-beta
v0.7.7-beta
v0.7.6-beta
v0.7.5-beta
v0.7.4-beta
v0.7.3-beta
v0.7.2-beta
v0.7.1-beta
v0.7.0-beta
v0.6.9-beta
v0.6.8-beta
v0.6.7-beta
v0.6.6-beta
v0.6.5-beta
v0.6.4-beta
v0.6.3-beta
v0.6.2-beta
v0.6.1-beta
v0.6.0-beta
v0.5.8-beta
v0.5.7-beta
v0.5.6-beta
v0.5.5-beta
v0.5.4-beta
v0.5.3-beta
v0.5.2-beta
v0.5.1-beta
v0.5.0-beta
v0.4.5-alpha
v0.4.4-alpha
v0.4.3-alpha
v0.4.2-alpha
v0.4.1-alpha
v0.4.0-alpha
v0.3.8-alpha
v0.3.7-alpha
develop
v0.3.6-alpha
v0.3.5-alpha
v0.3.4-alpha
v0.3.3-alpha
v0.3.2-alpha
v0.3.1-alpha
v0.3.0-alpha
v0.2.5-alpha
v0.2.4-alpha
v0.2.3-alpha
v0.2.2-alpha
v0.2.1-alpha
v0.2.0-alpha
v0.1.5-alpha
v0.1.4-alpha
v0.1.3-alpha
v0.1.2-alpha
v0.1.1-alpha
v0.1.0-alpha
v0.0.62-alpha
v0.0.61-alpha
v0.0.60-alpha
v0.0.59-alpha
v0.0.58-alpha
v0.0.57-alpha
v0.0.56-alpha
v0.0.55-alpha
v0.0.54-alpha
v0.0.53-alpha
v0.0.52-alpha
v0.0.51-alpha
v0.0.50-alpha
v0.0.49-prealpha
v0.0.48-prealpha
v0.0.47-prealpha
v0.0.46-prealpha
v0.0.45-prealpha
v0.0.44-prealpha
v0.0.43-prealpha
v0.0.42-prealpha
v0.0.41-prealpha
v0.0.40-prealpha
v0.0.39-prealpha
v0.0.38-prealpha
v0.0.37-prealpha
v0.0.36-prealpha
v0.0.35-prealpha
v0.0.34-prealpha
v0.0.33-prealpha
v0.0.32-prealpha
v0.0.31-prealpha
v0.0.30-prealpha
v0.0.29-prealpha
v0.0.28-prealpha
v0.0.27-prealpha
v0.0.26-prealpha
v0.0.25-prealpha
v0.0.24-prealpha
v0.0.23-prealpha
v0.0.22-prealpha
v0.0.21-prealpha
v0.0.20-prealpha
v0.0.19-prealpha
v0.0.18-prealpha
v0.0.17-prealpha
v0.0.16-prealpha
v0.0.15-prealpha
v0.0.14-prealpha
v0.0.13-prealpha
v0.0.12-prealpha
v0.0.11-prealpha
v0.0.10-prealpha
v0.0.9-prealpha
v0.0.8-prealpha
v0.0.7-prealpha
v0.0.6-prealpha
v0.0.5-prealpha
v0.0.4-prealpha
v0.0.3-prealpha
v0.0.2-prealpha
v0.0.1-prealpha
Labels
Clear labels
ad-hoc
api
bug
ci-cd
content
dependencies
enhancement
frontend
in-progress
jellyfin
parked
priority: high
priority: low
priority: medium
review
security
One-off / ad-hoc work not tracked by a dedicated issue
REST API / HTTP endpoints
Something isn't working
Build, test, deploy pipeline
Channel content / schedules / playlists
Dependency updates (Renovate)
New feature or improvement
ChicoryTV React SPA frontend
Claimed by an active session — do not pick up
Jellyfin tuner / IPTV integration
Excluded from automatic queue pickup; work only when explicitly selected
Adversarial review finding
Security / vulnerability fix
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: timothy/ersatztv#747
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The Gitea instance was upgraded 1.25.4 -> 1.27.1 mid-session on 2026-08-05 (during #743).
Why this needs a sweep
A lot of this repo's CI knowledge is deliberately recorded as measured on Gitea 1.25.4 rather than as timeless fact — which was the right call, and means nothing is now silently wrong. But it does mean those claims are dated, not current, and several are load-bearing for how we build and gate.
Known 1.25.4-pinned claims:
docs/ci-cd.md:537+ci.docs-only-skip-steps— anif:-skipped job reports commit-statusskipped(a distinct state), verified via probe PR #418.docs/ci-cd.md:896— Gitea auto-cancels supersededpushruns on a branch, but not other run types.ci.batch-pushes-no-cancel-route— no agent-side cancel route (POST .../actions/runs/{id}/cancel404s).ci.actions-credential-scoping— the scope enum has nostatusentry; thereqRepoWritergate; the 403 scope-refusal probe.ci.exemption-provenance—compare/{base}...{head}returns nofiles.ci.shared-pr-file-enumeration— the exactchanged/deletedstatus values Gitea emits.ci.gate-trigger-base-resolved— the four-scratch-PR verification ofpull_requestvspull_request_targetdefinition resolution.ci.jq-version-contract,ci.verdict-write-retarget-fence— version-sensitive behaviours.Also newly relevant at 1.27.1
enable_bypass_allowlist/bypass_allowlist_usernames/bypass_allowlist_teams, which did not exist on 1.25.4. Worth understanding — it may be a better-targeted control than whatrelease.main-direct-push-disabledcurrently uses.permissions:on a job landed in 1.26.0, so the version precondition inci.actions-credential-scopingno longer holds. Whether it actually binds here is unprobed;/api/v1/settings/actionsstill 404s at 1.27.1, so the Actions default token permission (Restricted vs Write) is not readable via API and needs another route (app.ini/ admin UI).Approach
Do not re-verify everything at once. Prioritise the claims that gate merges or that an agent would act on: the
skipped-state contract, the cancel route, and thepermissions:/ default-token question. Update each record's measured-vs-inferred section with the new version and date rather than rewriting history.Done-when
permissions:/ default Actions token permission question answered by probe, andci.actions-credential-scopingupdatedenable_bypass_allowlistsemantics understood andrelease.main-direct-push-disabledrevisited if it is a better fitProbed the
permissions:/ default-token question on 1.27.1 during the upgrade itself (server-management#714). This answers one of the done-when boxes and corrects two details in the issue body.Measured on Gitea 1.27.1, 2026-08-05
permissions:is now parsed and enforced. Theaction_run_jobtable gained atoken_permissions TEXT NULLcolumn — the per-job resolved permission set. On 1.25.4 no such storage existed, which is what made the key inert. The version precondition inci.actions-credential-scopingis now genuinely stale, as this issue predicted.The default token permission is web-UI only — not
app.ini. The issue body guesses "app.ini/ admin UI"; it is neither anapp.inikey nor an admin/instance page. Surfaces checked, each authoritative only for itself:swagger.v1.json, live)/api/v1/settings/actions404 confirmedgitea adminCLIuser,repo-sync-releases,regenerate,auth,sendmail— nothingapp.iniDEFAULT_ACTIONS_URLmatches on a permission-ish grepEndpoints (POST form):
/user/settings/actions/general/{owner}/{repo}/settings/actions/general/token_permissionsFields:
token_permission_mode(permissive|restricted),override_owner_config,enable_max_permissions,max_unit_access_mode_{1,2,3,4,5,8,10}.It IS readable without the UI — as JSON in
user_setting, not a column, which is why a schema grep for a permission column finds nothing:Two traps if you script this, both of which fail silently: the session cookie
i_like_giteaisSecure-flagged, so a login againsthttp://192.168.1.95:3000never persists — usehttps://gitea.tblindustries.be; and these areform-fetch-actionforms with no hidden_csrfinput (CSRF rides on SameSite + Origin), so there is no token to scrape from the HTML.Does it actually bind here? Answered: yes, and it breaks this repo
ci.actions-credential-scoping's core finding is unchanged: there is still nostatusscope, so commit-status write remains inseparable fromwrite:repository. What changed is that the default is now constrainable.Set to
restrictedat owner level fortimothy(covers all 44 repos; no repo setsoverride_owner_config), verified persisted, then reverted topermissive— because it breaks this repo's merge gate:permissions:. Checked all six:ci-image,dependency-scan,docker-build,pr-checks,renovate,review-verdict.review-verdict.ymlwrites the required context with the built-in token —GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}(L152, L168), POSTing/repos/$REPO/statuses/$SHA(L753, L840, L842).docker-build.ymlhas one status usage.Under
restrictedthose POSTs get a read-only token and 403, soreview-verdict/h10becomes unwritable by its own workflow and PRs stop being mergeable. Left atpermissiveso the gate keeps working.The part worth recording as a decision, not just a measurement
The obvious remedy — add
permissions:toreview-verdict.yml— restores the gate by grantingcontents: write, i.e. exactly the broad repo-write that the Restricted default exists to remove. So Restricted does not make the verdict unassertable by workflows; it downgrades the property from every workflow implicitly has status-write to only workflows that visibly opt into repo-write do.That is a real improvement (a newly added workflow no longer inherits status-write for free, and the opt-in is reviewable in the diff) but it is weaker than "closed", and
ci.actions-credential-scopingshould say so explicitly rather than implying the class is shut. It also interacts with #742 and #743: neither Restricted norpermissions:touches the fact that any write-scoped user token can still POST the context directly.Tracking the workflow change needed to actually enable Restricted separately — see the issue linked below.
Not re-probed here, and still dated 1.25.4: the
skipped-state contract, auto-cancel behaviour,comparefilesabsence, and the shared-PR file-enumeration values. The no-cancel-route claim was incidentally re-confirmed on 1.27.1 (live swagger hasrerun,rerun-failed-jobs,jobs/{job_id}/rerun— no cancel).Claiming (same Claude Code session that filed #746/#747 and is finishing #743 + #719).
Hazard worth recording up front: #743 just set
block_admin_merge_override: trueonmain. If apermissions:scope here is wrong andreview-verdict/h10becomes unwritable, PRs stop being mergeable and the adminforce_mergeescape hatch is now closed too. Recovery is still available (PATCH branch protection, or flip the owner default back topermissive) but it is a two-step, so this lands with the default left atpermissiveand the flip verified separately.Releasing the claim — parked, not abandoned. Nothing was changed for this issue beyond what #743 already landed (the
permissions:-below-1.26.0 directive corrected, the 1.25.4 measurements re-dated rather than silently refreshed).Two items were added to this issue's scope by #743 and remain open:
block_admin_merge_override's semantics were never measured (source-attested only), and whether Gitea treats an absent required status context as blocking or as satisfied is asserted by our docs but unproven — PR #749 showed combined statussuccesswithreview-verdict/h10entirely absent. See also #751, which is a concrete instance of that second question mattering.Claiming (fresh Claude Code session, 2026-08-28). The 2026-08-06 claim on this issue was explicitly released ("parked, not abandoned"), so this is a re-pickup, not a duplicate.
Four claim checks run before writing code, all clear: no open PR references #747 (the five open PRs are all Renovate),
git ls-remote --heads origin '*747*'is empty, no claiming comment postdates the release, andorigin/mainis fresh atb16ec15d6.Scope for this pass. Prioritising the read-only probes and the contract corrections, per the issue's own "do not re-verify everything at once" instruction.
block_admin_merge_override's semantics need a destructive probe (attempting aforce_mergepast a red required context onmain), so that one is expected to stay explicitly dated-and-unproven rather than silently refreshed — done-when box 4 is what covers it.Closing record
Outcome: Re-verified the Gitea 1.25.4-pinned CI claims on 1.27.1 and measured the two merge-gate
semantics that had only ever been source-attested. PR #867, merged as
5fb9c8537. The population was derived fromgit ls-filesrather than from this issue's own 9-key list (~21 claim sites across workflows,scripts, tests, docs and records) — auditing exactly the named keys would have repeated the defect
the issue is about.
Re-confirmed unchanged on 1.27.1: the distinct
skippedcommit-status state;compare/{base}...{head}serving no
files; no agent-side cancel route (REST route + swagger only); andreview-verdict.yml'sbranches: [main]suppressing the run off a non-main base.Newly measured on four throwaway scratch bases (
main's rule never PATCHed,updated_atstill2026-08-05; all artifacts deleted and confirmed gone):
block_admin_merge_overrideenable_bypass_allowlistscratch/747-base(#863)false(default)falsesuccessfalsefalsescratch/747a-base(#864)falsefalsescratch/747b-base(#865)truefalsescratch/747c-base(#866)falsetrue, empty listRoot cause: n/a — a dated-knowledge sweep, not a bug fix. The underlying condition is that this
repo deliberately records CI behaviour as measured-on-a-date, so a version upgrade makes a body of
correct claims stale rather than wrong, and nothing detects that automatically.
Decisions/conventions changed: no keys added or superseded. Evidence updated in
release.main-direct-push-disabled(theblock_admin_merge_overridesemantics move fromsource-attested to measured, and
enable_bypass_allowlistis recorded as NOT a substitute),ci.docs-only-skip-steps,ci.batch-pushes-no-cancel-route(retitled — the 1.27.1 re-probe coversthe REST route only),
ci.exemption-provenance,ci.shared-pr-file-enumeration,ci.gate-trigger-base-resolved,ci.actions-credential-scopingandci.verdict-write-retarget-fence.Reusable knowledge:
main. A scratch BASE branch carriesits own rule, so "probing it means merging an unreviewed PR" is false — the whole experiment is
disposable. This is what unblocked a claim #743 left source-attested.
mergeable: truedoes not mean the gate will admit a merge. It wastruewhile the merge wasrefused 405. It reflects conflict-freedom, not required-context state.
block_admin_merge_override; that field governs the FORCE path only.changed/added; 200surfaced
renamedanddeleted.copied/modifiedstill unobserved, which shows nothing.review rounds each introduced a fresh one (a control attached to the wrong arm, an invented
single-field mutation, a new wrong closed set). Replacing the narration with a table — one row per
arm, each self-binding — ended it: round 4 verified every cell and findings fell to 2 Low.
Verification: 1083 script tests ·
decisions-validate: OK· all 8 touched records parse underPyYAML · doc-narrative clean · five independent adversarial rounds (21 → 12 → 9 → 6 → 2). CI on #867.
Caveat: Codex was rate-limited all session, so every round was same-model-family as the
implementer, not the cross-family review this repo prefers.
Deferred:
review-verdict.yml's three 1.25.4-dated comments — untouched because #763 is in-progress on thatfile and its paging work overlaps them.
creator-attribution measurement the H10 allow-list rests on, and the rest of the corpus: thiswas not a sweep, and
ci.actions-credential-scopingnow says so.pull_request_targetoverlap;--depth=1no-merge-base; the scopeenum /
reqRepoWriter/write:package403 — each left dated with its reason.Docs updated:
docs/ci-cd.md,docs/decisions/workflow-process.md, and the eight records above;plus in-code comments in
.gitea/workflows/docker-build.yml,scripts/ci-detect-docs-only.sh,scripts/pr-changed-files.sh,scripts/tests/test_merge_consent_exemption.py.Deferred items from the closing record above are tracked in #869 (the residual 1.25.4-dated claims, including the
creator-attribution measurement the H10 allow-list rests on, andreview-verdict.yml's three comments left alone while #763 held that file).