Capability preflight should survive safe read calls or report sequencing requirements clearly #470

Closed
opened 2026-07-07 16:21:57 -05:00 by jcwalker3 · 2 comments
Owner

Problem

Read-only validation calls are part of safe preflight, but they currently appear to reset or invalidate an already-valid capability preflight, forcing a re-resolve immediately before the mutation. The contract for capability-preflight lifetime is undefined, so operators cannot tell whether interleaved reads are safe.

Observed behavior (during closure of superseded PR #414)

  • resolve_task_capability(close_pr) proved gitea.pr.close allowed.
  • Interleaved read-only calls were performed for validation (e.g. whoami, view_pr, view_issue).
  • The capability preflight was no longer accepted for the mutation.
  • Re-resolving capability immediately before close_pr allowed the mutation.
  • Operational workaround: strict sequential resolve_task_capability(close_pr) → close_pr with no interleaved calls.

Why this matters

Read-only whoami/view/read calls are exactly what a careful operator runs to validate live state before a mutation (per cross-session-interference and live-state-revalidation guidance). If those safe reads silently invalidate the preflight, the safe path and the gated path are in conflict: doing the right thing (re-validate live state) breaks the mutation gate with no explicit signal.

Required resolution — one of

  1. Preflight survives safe reads: an already-valid capability preflight for the same profile + session + task is NOT reset by read-only calls (whoami, view_pr, view_issue, list_*, get_runtime_context, eligibility checks). Preserve the proof across them.
  2. Immediate sequencing required (explicit): if capability must be resolved immediately before the mutation, the mutation must fail closed with a clear, explicit message telling the operator to re-run resolve_task_capability(<task>) immediately before the mutation — never a silent/opaque rejection.

Acceptance criteria

  1. Define the intended contract for capability-preflight lifetime (scope: profile / session / task; what preserves vs invalidates it).
  2. If read-only calls should not invalidate preflight: preserve capability proof across whoami/view/read/eligibility calls within the same session/profile/task.
  3. If immediate sequencing is required: return a clear fail-closed error naming the exact required order (resolve_task_capability immediately before mutation).
  4. Add tests for resolve_task_capability(close_pr) → read calls → close_pr (both chosen-contract directions).
  5. Add tests for safe fail-closed behavior when profile/session/task changes (proof must NOT carry across a changed profile/identity/task).
  6. Update reviewer/reconciler workflow docs with the canonical sequencing rule.
  7. No relaxation of exact-capability requirements — this clarifies preflight lifetime only; it must not widen who can mutate.

Evidence

PR #414 closure sequencing:

  • Exact close_pr capability was allowed.
  • Interleaved read validation reset the preflight.
  • Sequential re-resolve immediately before the mutation succeeded.

Related

  • #468 — reconciler close_pr blocked by author branch/worktree guard (distinct: guard membership, not preflight lifetime)
  • #405 — exact per-mutation capability proof in reviewer reports
  • #306 — dedicated reconciler close capability
  • #420 — runtime profile/capability reload behavior
## Problem Read-only validation calls are part of safe preflight, but they currently appear to reset or invalidate an already-valid capability preflight, forcing a re-resolve immediately before the mutation. The contract for capability-preflight lifetime is undefined, so operators cannot tell whether interleaved reads are safe. ## Observed behavior (during closure of superseded PR #414) - `resolve_task_capability(close_pr)` proved `gitea.pr.close` allowed. - Interleaved read-only calls were performed for validation (e.g. `whoami`, `view_pr`, `view_issue`). - The capability preflight was no longer accepted for the mutation. - Re-resolving capability immediately before `close_pr` allowed the mutation. - Operational workaround: strict sequential `resolve_task_capability(close_pr) → close_pr` with no interleaved calls. ## Why this matters Read-only whoami/view/read calls are exactly what a careful operator runs to validate live state before a mutation (per cross-session-interference and live-state-revalidation guidance). If those safe reads silently invalidate the preflight, the safe path and the gated path are in conflict: doing the right thing (re-validate live state) breaks the mutation gate with no explicit signal. ## Required resolution — one of 1. **Preflight survives safe reads:** an already-valid capability preflight for the same `profile` + `session` + `task` is NOT reset by read-only calls (`whoami`, `view_pr`, `view_issue`, `list_*`, `get_runtime_context`, eligibility checks). Preserve the proof across them. 2. **Immediate sequencing required (explicit):** if capability must be resolved immediately before the mutation, the mutation must fail closed with a clear, explicit message telling the operator to re-run `resolve_task_capability(<task>)` immediately before the mutation — never a silent/opaque rejection. ## Acceptance criteria 1. Define the intended contract for capability-preflight lifetime (scope: profile / session / task; what preserves vs invalidates it). 2. If read-only calls should not invalidate preflight: preserve capability proof across `whoami`/`view`/`read`/eligibility calls within the same session/profile/task. 3. If immediate sequencing is required: return a clear fail-closed error naming the exact required order (`resolve_task_capability` immediately before mutation). 4. Add tests for `resolve_task_capability(close_pr) → read calls → close_pr` (both chosen-contract directions). 5. Add tests for safe fail-closed behavior when profile/session/task changes (proof must NOT carry across a changed profile/identity/task). 6. Update reviewer/reconciler workflow docs with the canonical sequencing rule. 7. No relaxation of exact-capability requirements — this clarifies preflight lifetime only; it must not widen who can mutate. ## Evidence PR #414 closure sequencing: - Exact `close_pr` capability was allowed. - Interleaved read validation reset the preflight. - Sequential re-resolve immediately before the mutation succeeded. ## Related - #468 — reconciler `close_pr` blocked by author branch/worktree guard (distinct: guard membership, not preflight lifetime) - #405 — exact per-mutation capability proof in reviewer reports - #306 — dedicated reconciler close capability - #420 — runtime profile/capability reload behavior
jcwalker3 added the status:in-progress label 2026-07-08 01:25:46 -05:00
Author
Owner

Issue claim heartbeat

<!-- gitea-issue-claim-heartbeat:v1 --> **Issue claim heartbeat** - kind: claim - issue: #470 - branch: feat/issue-470-preflight-contract - phase: claimed - profile: prgs-author - pr: none - blocker: none - next_action: create worktree and begin implementation
Author
Owner

Issue claim heartbeat

<!-- gitea-issue-claim-heartbeat:v1 --> **Issue claim heartbeat** - kind: progress - issue: #470 - branch: feat/issue-470-preflight-contract - phase: pr_opened - profile: prgs-author - pr: 490 - blocker: none - next_action: await review
jcwalker3 removed the status:in-progress label 2026-07-08 03:39:13 -05:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#470