Run-identity supersession (#113230) resolves the scenario this heuristic
softened: superseded runs and rerun-attempt leftovers are filtered before
classification, so aggregate-FAILURE-with-green-duplicates now classifies
as GREEN or PENDING on evidence. The only remaining reachable case —
identity-less app check runs posting duplicate same-name results — is
genuinely ambiguous and now honestly exits FAILING (15) instead of the
special verify-manually exit 17.
* fix(scripts): ignore superseded workflow runs in watch-pr-ci rollup classification
Draft->ready re-triggers leave cancelled superseded runs on the head SHA
forever, and GitHub's aggregate rollup state counts them, so the watcher
emitted terminal FAILURE while the replacement run was still in progress
and could never reach GREEN (observed on PR #113150, head bd1b9a0e).
- fetch run identity per check and resolve same-name checks to the newest
run/check id (GitHub latest-name-wins); drop cancelled checks from
replaced runs; keep older runs' unique jobs visible
- paginate statusCheckRollup contexts (bounded, 10 pages) so >100-context
rollups are not truncation-blind; changed or lost snapshots throw into
the bounded retry
- classify GREEN when aggregate FAILURE is explained solely by superseded
contexts with full visibility; truncation still fails conservatively
* ci: scope GitHub App token permissions in stale, labeler, auto-response, and pr-ci-sweeper
Fixes red main: #112963 bumped zizmor v1.22.0 -> v1.28.0, whose github-app
audit flags create-github-app-token mints without permission-* inputs (14
high findings, Workflow Sanity red on main since 3b7b2a2a1f). Most
workflows already migrated to scoped tokens; these four were stragglers.
Scopes follow each consumer's actual API surface: stale needs issues/PR
write plus actions read for its state-cache check; labeler needs label
CRUD (issues write), PR write, members read for maintainer gates, and
contents read where actions/labeler reads its config; auto-response needs
issues/PR write plus members read; pr-ci-sweeper needs actions write to
re-fire runs, checks read, and PR write. Verified locally with the exact
CI invocation (zizmor 1.28.0, repo config, regular persona, medium
severity/confidence): no findings, ignore/suppress counts match CI.
* feat(ui): merge creator avatar into sidebar leading slot and move creator filter into Threads menu
The per-row creator chip previously rendered next to the leading state
indicator, so rows with a known creator started their titles at a
different x-offset than rows without one. The avatar now occupies the
single fixed leading slot: unread renders as a corner badge on it,
running as a spinner ring, and open/merged PR state as a colored corner
badge, while attention and pinned icons keep the slot. Child rows keep
their status badges and no longer render owner chips.
The standalone 'Filter by creator' select above the session list is
gone; the Threads funnel menu gains a People radio section with owner
chips, and the funnel trigger shows an accent dot while a creator
filter is active. The empty Threads header stays visible when ownership
chrome is active so the filter can always be cleared. Mock dev fixtures
gain two creator identities so the ownership chrome is demonstrable.
* test(ui): guard parent row spread in child-avatar ownership case
* feat(gateway): add web-only incognito sessions held in process memory
* feat(ui): add incognito toggle and badges to the web new-session flow
* fix(sessions): classify incognito by key shape, fail closed on stale keys, and gate memory writes
* fix(codex): start harness threads ephemeral for incognito sessions
* fix(sessions): reshape internal-effects incognito keys and add doctor repair for reserved key collisions
* refactor(plugin-sdk): export canonical incognito key classifier and guard the sentinel path
* fix(state): classify incognito DB handles from the recorded open-time set
* fix(gateway): isolate incognito sessions from durable lineage and allocation on read-only misses
* docs(sessions): pin the reserved incognito namespace ownership decision
* feat(gateway): admin-scope incognito visibility and incognito-blind cross-session surfaces
* fix(ci): repair kysely guardrails, dead export, docs map, protocol bindings, and ACP reset rotation
* fix(gateway): remove non-admin observability side channels for incognito sessions
* fix(gateway): enforce admin-scope incognito access and cover all parent-reference creation paths
* feat(sessions): enforce visibility and membership
* feat(ui): add session sharing controls
* docs: add session sharing implementation report
* refactor(sessions): use canonical creator identity
* fix(sessions): adopt creator ownership contract
* docs: refresh session sharing rebase report
* docs: record final creator integration proof
* docs: record final main rebase
* chore: drop worktree report artifact
* fix(sessions): keep drafts owner-only
* fix(ui): preserve redacted session restrictions
* fix(sessions): preserve scoped sharing authorization
* fix(sessions): re-verify session instance inside sharing mutation queue
* test(sessions): cover stale sharing mutation
* fix(sessions): bind membership to session instance, gate absence blocking on sharing
* fix(sessions): preserve entry normalization on rebase
* fix(sessions): atomic visibility instance guard, reset visibility on recreate
* docs(ui): name the absence-heuristic tradeoff and link follow-up
* feat(protocol): expose session sharing row state
* docs: note generated creator identity type
* fix(sessions): bind member writes and visibility rollback to session instance
* fix(ui): discard stale-connection sharing loads; drop worktree scratch files
* fix(ui): block composer only on observed sharing state, never on list absence
* fix(gateway): authorize agent runs against the resolved session (close keyless bypass)
* chore(protocol): allowlist Control UI-only session.sharing event for mobile
* test(config): record session.sharing keys in common-tier snapshot
* refactor(sessions): unexport internal sharing helpers
* test(gateway): update sessions changed routing assertions
* fix(sessions): align sharing identity with created actor
* fix(sessions): align membership identities and storage keys
* fix(gateway): re-filter drafts against fresh sharing state in sessions.list
* fix(gateway): drafts stay owner+admin only in the sessions.list fresh filter
* fix(ui): re-export sharing protocol types for the Control UI
* fix(ui): keep SessionSharingRole internal to satisfy deadcode gate
* fix(gateway): read runtime config lazily in session-mutation authorization
authorizeSessionMutation ran on every gateway request but eagerly called
context.getRuntimeConfig() — a non-trivial config resolve — for methods that
are never session mutations. Read config only once a real session-mutation
target is resolved. Also register the four session sharing methods in the
2026.7 release-train inventory test.
* fix(gateway): share one config snapshot across session-mutation authorization
Group rename/delete discovery and the authorization loop were each resolving
runtime config separately after the lazy-read change. Memoize the resolve so
non-session requests still pay nothing, while any session mutation resolves
config at most once and both discovery and authorization use that single
snapshot (no double reload, no mid-request config-change split).
* fix(gateway): resolve session-sharing CI gates
- isGatewayAdmin: null-safe connect access so internal/plugin-runtime runs
(which reach authorization with a connect-less client) do not crash.
- emitSessionsChanged: scope only to a concrete session key; a [undefined]
sessionKeys scope filters nothing correctly and would strip draft gating.
- session stores: mark the sync TOCTOU re-read and the sqlite_master
existence probe as narrowly-justified raw SQLite primitives.
- tests: provide getRuntimeConfig to the session-action contract context,
drop a shadowed 'call' binding, use structuredClone, and assert the
agent-scoped sessions.changed broadcast shape.
* docs(gateway): note best-effort participation gate + refresh native i18n baseline
Session ownership/visibility are usability features, not a security boundary
(docs/concepts/multi-user.md, SECURITY.md); document that the pre-dispatch
authorization is intentionally not commit-bound to the resolved instance.
Also refresh apps/.i18n/native-source.json after the session-sharing protocol
codegen shifted line numbers of existing native strings (position-only).
* test(gateway): reset session sharing snapshots
* style(gateway): format sharing reset import