Every pull_request run of this workflow has ended in startup_failure - ten of
ten across feature/swarm-nfs, feature/svc-net-tor and feature/ai-agent-employees,
including runs that predate the current work. No jobs, no logs, no check runs,
because run-graph validation rejects the workflow before any job is created:
entry-pull-request-change.yml (Line: 130, Col: 3): Error calling workflow
'ci-orchestrator.yml'. The workflow is requesting 'issues: write', but is only
allowed 'issues: none'.
ci-orchestrator.yml requests issues:write at workflow level (:69) and on its
report-main-failures job (:283). The caller declares an explicit permissions
block listing only actions/contents/packages/security-events, and an explicit
block pins every unlisted scope to none. A called workflow may request only a
subset of its caller's declared set, so the graph is invalid. Introduced by
8b58b67121, a three-caller fan-out where only two callers were patched.
The grant goes on the job rather than at workflow level. Job permissions REPLACE
the workflow block instead of merging with it, so the four existing scopes are
restated verbatim alongside the new one.
Do not move this block to workflow level. That block also governs the
pull_request_target half of this file, whose build-ci-images-fork and
images-mirror-missing-fork jobs check out fork-controlled refs with
allow_unsafe_checkout, one of them with secrets: inherit. Widening that token is
the only way this change becomes a security problem.
The scope itself carries no new capability on this path: report-main-failures is
gated on github.ref == refs/heads/main and is unreachable from a PR ref, forks
get a read-only token for pull_request regardless of the declared ceiling, and
same-repo PR authors already hold issue rights. The block already granted
actions:write and packages:write, both broader than issues:write.
Not provable locally - act does not evaluate nested permission escalation, so
this needs a push to confirm.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Workflow Dependency Map
How the workflows under .github/workflows/ trigger and call each other.
Solid arrows: workflow_call (uses:) or job needs. Dotted arrows:
indirect coupling (CLI dispatch, shared concurrency group). Per-workflow
inputs and descriptions: workflows.md.
CI pipeline
flowchart TB
push["push: main, feature/**, hotfix/**, fix/**"] --> epl["entry-push-latest.yml"]
pr["pull_request: opened, synchronize, reopened, ready_for_review"] --> eprc["entry-pull-request-change.yml"]
dispatch["workflow_dispatch"] --> eman["entry-manual.yml"]
epl --> orch["ci-orchestrator.yml"]
epl -->|"version tag on main"| relv["release-version.yml"]
eprc --> orch
eprc -->|"fork PRs: privileged prebuild"| imgbuild["images-build-ci.yml"]
eprc -->|"fork PRs: privileged prebuild"| imgmirror["images-mirror-missing.yml"]
eman --> orch
subgraph orchestrator["ci-orchestrator.yml jobs"]
plan["plan: run-summary table"]
waitfork["wait-fork-prereq-run"] --> forkready["fork-prereqs-ready"]
lintwf["lint.yml: make lint + hadolint"] --> qualitygate["code-quality-gate"]
testwf["test.yml: make test"] --> qualitygate
codeql["security-codeql.yml"] --> qualitygate
qualitygate --> buildci["build-ci-images: images-build-ci.yml"]
buildci --> dns["test-dns.yml"]
mirror["images-mirror-missing.yml"]
dns --> snprio["test-deploy-single-node-priority"]
mirror --> snprio
buildci --> swarmprio["test-deploy-swarm-priority"]
mirror --> swarmprio
snprio -->|"all priority jobs green"| snreg["test-deploy-single-node"]
swarmprio -->|"all priority jobs green"| snreg
snprio -->|"all priority jobs green"| swarmreg["test-deploy-swarm"]
swarmprio -->|"all priority jobs green"| swarmreg
dns --> snreg
mirror --> snreg
buildci --> swarmreg
mirror --> swarmreg
swarmreg --> smoke["test-runner-smoke.yml"]
qualitygate --> instmake["test-install-make.yml"]
qualitygate --> instpkgmgr["test-install-pkgmgr.yml"]
instmake --> instgate["test-install-gate"]
instpkgmgr --> instgate
qualitygate --> devenv["test-workspace: test-workspace.yml"]
mirror --> devenv
buildci --> testguide["test-instructions.yml"]
mirror --> testguide
snprio --> donegate["done"]
swarmprio --> donegate
snreg --> donegate
swarmreg --> donegate
smoke --> donegate
instgate --> donegate
devenv --> donegate
testguide --> donegate
end
snprio --> singlenode["test-deploy-single-node.yml"]
snreg --> singlenode
singlenode --> deploycompose["test-deploy-compose.yml"]
singlenode --> deployhost["test-deploy-host.yml"]
swarmprio --> deployswarm["test-deploy-swarm.yml"]
swarmreg --> deployswarm
test-deploy-single-node-priority and test-deploy-swarm-priority run only
when the orchestrator's priority input is set; with it empty they are
skipped and the regular deploy jobs start directly. The regular jobs receive
the priority ids as blacklist, so each role deploys in exactly one line.
Cancellation
flowchart TB
prclose["pull_request_target: closed, converted_to_draft"] --> eprcancel["entry-pull-request-cancel.yml"]
branchdelete["delete: branch"] --> delbranch["delete-branch.yml"]
eprcancel -.->|"cancels concurrency group"| runningci["running entry + child workflow runs"]
delbranch -.->|"cancels concurrency group"| runningci
Scheduled and standalone
flowchart TB
daily["schedule: daily 00:00 UTC"] --> mirrorall["images-mirror-all.yml"]
daily --> stale["cleanup-stale.yml"]
daily --> relhighest["release-highest.yml"]
weekly["schedule: weekly Sat 00:00 UTC"] --> updatewf["update.yml"]
weekly --> cleanupci["images-cleanup-ci.yml"]
weeklymon["schedule: weekly Mon 00:00 UTC"] --> scorecard["security-scorecard.yml"]
branchprot["branch_protection_rule"] --> scorecard
pushmain["push: main"] --> updatewf
prtarget["pull_request_target: opened, reopened"] --> depclose["dependabot-close.yml"]
relhighest -.->|"gh workflow run"| relver["release-version.yml"]
relver --> imgbuildci["images-build-ci.yml"]
manual["workflow_dispatch"] --> mirrorcleanup["images-mirror-cleanup.yml"]
manual --> deploywf["test-instructions.yml: run a role README Production command"]
Also manually dispatchable: images-mirror-all.yml, images-cleanup-ci.yml,
cleanup-stale.yml, update.yml, release-highest.yml, release-version.yml,
lint.yml, test.yml, test-deploy-swarm.yml, test-dns.yml,
test-workspace.yml, test-runner-smoke.yml.