Status: implemented invariant
Introduced: 2026-09-10
Scope: .github/workflows/repository-math-portfolio.yml
The Repository Mathematical Portfolio mixes two classes of observation in one workflow:
pull_request_target; andpush, issues, workflow_dispatch, and schedule.Those classes must not share one cancellation domain.
A global cancel-in-progress: true group lets a new pull-request observation cancel a running observation for an unrelated pull request. GitHub renders that cancellation as a failed/red check even when no step failed. This is the same failure class measured for the Evolution Loop in issue #387.
At the same time, canonical portfolio state is artifact-backed and intentionally linear. Canonical runs restore the latest eligible trusted checkpoint and produce its successor; allowing independent canonical concurrency groups could permit two runs to restore the same parent and publish competing successors.
The portfolio job therefore uses an event-sensitive concurrency key:
concurrency:
group: repository-math-portfolio-${{ github.event_name == 'pull_request_target' && format('advisory-{0}', github.event.pull_request.number) || 'canonical' }}
cancel-in-progress: true
Semantics:
pull_request_target for PR #A -> repository-math-portfolio-advisory-A
pull_request_target for PR #B -> repository-math-portfolio-advisory-B
canonical event -> repository-math-portfolio-canonical
This means:
Concurrency isolation does not make advisory artifacts canonical.
Trusted portfolio parent lookup continues to admit only successful allowlisted canonical events:
issues
push
workflow_dispatch
schedule
pull_request_target remains excluded from trusted future-state selection. The workflow still checks out the trusted default branch for the privileged metadata observation and retains read-only permissions.
The separate verify-pr-head job continues to run on ordinary pull_request, with its own per-PR concurrency key and contents: read permission.
tests/test_evolution_observer_concurrency.py enforces four properties:
pull_request_target;The repository-wide scan intentionally permits an unkeyed group only when it is explicitly recorded and understood. New unkeyed cancelling groups fail the test instead of silently expanding the exception surface.
This change does not alter:
It is a CI correctness and persistent-state serialization fix, not a change to portfolio semantics.
docs/architecture/REPOSITORY_MATHEMATICAL_PORTFOLIO.md — portfolio architecture and trust model.