Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
6203a12
feat(missions): replace Mission model with spec mission blob + s256 i…
dasiths Jun 5, 2026
530f802
feat(missions): bind mission through token chain and HTTP signature
dasiths Jun 5, 2026
475fdac
feat(missions): add PS token-request params, clarification chat, and …
dasiths Jun 5, 2026
b9c69ea
feat(missions): add PS governance clients, server seams, and mission log
dasiths Jun 5, 2026
e2a9749
feat(missions): unify deferred transport and add governance facade
dasiths Jun 5, 2026
8760f8a
feat(missions): add MissionAgent CLI and mission-aware resource gover…
dasiths Jun 5, 2026
ee5ff13
feat(missions): add --pre-approve scope arg to MissionAgent
dasiths Jun 5, 2026
a31a09a
feat(missions): enhance Makefile and README for agent-mission with PR…
dasiths Jun 5, 2026
5fe6809
feat(missions): add out-of-mission scope gate, e2e validation, fix ag…
dasiths Jun 6, 2026
c313090
docs(plan): restore accidentally dropped Phase 7 heading
dasiths Jun 6, 2026
937932c
docs(missions): rewrite missions doc and add PS-governance docs
dasiths Jun 6, 2026
47ce1ef
docs(samples): document mission governance in sample READMEs
dasiths Jun 6, 2026
663c547
refactor(missions): remove non-spec capability value; Phase 8 review
dasiths Jun 6, 2026
ec741c2
feat(missions): add DI-friendly governance API surface (Phase 1)
dasiths Jun 6, 2026
29f1fec
feat(missions): bind governance clients and map mission creation + de…
dasiths Jun 6, 2026
d8466d9
feat(missions): refactor mission governance API and add call-chain demo
dasiths Jun 7, 2026
de8ca74
docs(missions): ground governance docs against SDK (Phase 11 review)
dasiths Jun 7, 2026
71e0b1e
fix(missions): align denial wire code with spec (access_denied → denied)
dasiths Jun 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
797 changes: 797 additions & 0 deletions .agent/plans/2026-06-05-missions-ps-governance/implementation-plan.md

Large diffs are not rendered by default.

876 changes: 876 additions & 0 deletions .agent/plans/2026-06-05-missions-ps-governance/research.md

Large diffs are not rendered by default.

601 changes: 601 additions & 0 deletions .agent/plans/2026-06-06-mission-api-refactor/implementation-plan.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Mission API Refactor — Issues & Deviations Log

Significant issues, spec deviations, and decisions surfaced while implementing the
[implementation-plan.md](implementation-plan.md). Research findings stay in
[research.md](research.md); this file is the running ledger of **problems and
deviations** (not routine progress).

## How to use this log

- Add an entry whenever a phase surfaces a real issue, a deviation from the AAuth
spec, or a design call that the user may want to revisit.
- Keep each entry short: what, where, why, and the disposition.
- `Status` values: `open`, `fixed`, `deferred`, `intentional`, `needs-decision`.
- Cite the governing spec section for anything spec-related.

## Spec references

- AAuth Protocol — `aauth-spec/draft-hardt-oauth-aauth-protocol.md`
- Upcoming changes — `aauth-spec/upcoming-changes-02.md`

## Open decisions for the user

These judgment calls were made during Phase 1. **All confirmed by the user on
2026-06-06**; the fixes are folded into the Phase 2 consistency pass.

- **D1 — Additive first pass, then remove (DECIDED).** Phase 1 adds the new surface
*alongside* the existing per-call-PS methods so the solution keeps building 0/0
and no flow breaks (DC6). **Phase 2 removes** the per-call `personServer` methods
(sample migration completes in Phase 4), reaching DC1's "no dual surface" end
state.
- **D2 — Flat `MissionSession` methods (DECIDED).** Keep the flat methods
(`RequestPermissionAsync`, `RecordAuditAsync`, `AskQuestionAsync`,
`ProposeCompletionAsync`); no nested facades.
- **D3 — Promote PS mission machinery into the SDK (DECIDED, Phase 2).** Move the
approval-blob builder into the SDK + add an `IMissionApprover` seam so
`MapAAuthGovernance` can map mission creation; add a pending/deferred-consent
abstraction so a `Prompt` outcome returns a 202 deferred response. Closes DEV-1
and DEV-2.

## Deviation entries

| ID | Phase | Area | Summary | Spec § | Status |
|----|-------|------|---------|--------|--------|
| DEV-1 | 1 | Resource mapper | `MapAAuthGovernance` resolved `PermissionOutcome.Prompt` as a denial — no built-in deferred (202) user-consent channel. | §Permission Endpoint (deferred consent) | resolved (Phase 2, D3 — `IDeferredConsentStore` seam + `AddAAuthDeferredConsent()`; mapper parks `Prompt` and answers 202 + poll route. Store is opt-in so the existing `Prompt`→denied default is preserved. Interactive browser page stays a sample concern.) |
| DEV-2 | 1 | Resource mapper | Mission-creation endpoint not mapped by `MapAAuthGovernance`; approval-blob building + proposal approval stayed PS-side (`MissionApproval` was sample-local). | §Mission Creation, §Mission Approval | resolved (Phase 2, D3 — `MissionApprovalBuilder` + `IMissionApprover`/`DefaultMissionApprover` promoted into the SDK; `MapAAuthGovernance` maps the mission endpoint, persists the `StoredMission`, and emits the `AAuth-Mission` header. MockPersonServer now uses `MissionApprovalBuilder`.) |
| DEV-3 | 1 | Default seams | `DefaultInteractionRelay` has no user channel: questions get an empty answer and completion is treated as not-accepted. A real PS must override it. Documented behavior, not a spec violation. | §Interaction Endpoint | intentional |
| DEV-4 | 4 | MockPersonServer | Phase 4 listed "swap MockPersonServer's hand-wired `/mission` `/permission` `/audit` `/mission-interaction` + pending routes for `MapAAuthGovernance()`." Kept the hand-wired endpoints instead. They are bound to the deterministic `MissionConsentScript` (`/admin/*` scripting the e2e suite drives), the `MissionPolicyStore` that decides silent-vs-prompt **token exchange** at `/token`, and the interactive browser `/interaction` consent page. `MapAAuthGovernance()` targets the *non-interactive default* PS; reproducing this rich behavior through `IMissionApprover` + a custom `IDeferredConsentStore` is a large rewrite of a spec-conformant, e2e-green file for **zero spec/behavior gain**, against DC6 (no regressions). Consistent with **DEV-1**'s decision that the interactive browser page "stays a sample concern." The agent-facing call-sites (MissionAgent, Mission.razor, GuidedTour) ARE migrated to the new session API; server-side request parsing still uses `GovernanceEndpoints` + `MissionApprovalBuilder`. | §Mission Creation, §Permission Endpoint, §Audit Endpoint (deferred consent) | intentional |
| DEV-5 | 4 | WhoAmI | Phase 4 listed "WhoAmI — resource governance builder for mission-aware challenge." No such builder exists or was introduced (Phase 3 built the **agent** session surface, not a resource-side one). `ChallengeOptions { MissionAware = true }` IS the canonical, spec-correct resource-side seam (§Terminology: a *mission-aware resource* includes the mission object from the `AAuth-Mission` header in the resource tokens it issues), already used by `WhoAmI`. No agent-style manual `MissionClaim` threading exists on the resource side. No change required. | §Mission Context at Resources, §Terminology (mission-aware resource) | intentional |
| DEV-6 | 5 | SDK deferred exchange | Building the Phase 5 combined page surfaced two real SDK bugs in the clarification→interaction escalation path (`DeferredExchange.cs`). **Bug 1:** after a clarification round, the poller did not stop on a subsequent interaction `202` (the `stopOnInteraction` flag was not threaded through `PollAsync`/`ComposePollerOptions`), so the SDK kept polling instead of surfacing the user-approval gate. **Bug 2:** when a polled interaction `202` omitted the `Location` header, `ResolveLocation` returned null instead of falling back to the last pending URL, dropping the interaction URL the UI needs. Both fixed in the SDK and covered by `ChallengeClarificationSeamTests` (4/4). | §Clarification Chat, §Interaction Endpoint | fixed (Phase 5; conformance 4/4, build 0/0) |
| DEV-7 | 5 | SampleApp Blazor page | `MissionCallChain.razor` originally spawned a per-second `PeriodicTimer`/`Task.Run` poll-counter that called `InvokeAsync(StateHasChanged)` while parked on a prompt. Because the approval popup leaves the main tab backgrounded, Chromium throttles it and stops ACKing SignalR render batches; the timer filled the circuit's unacked-batch buffer (default max 10) and **paused all rendering ~120 s** until it drained — an e2e timeout. Root cause is a Blazor Server anti-pattern (background-timer `StateHasChanged` on a backgroundable tab), not the protocol. **Fix:** removed the cosmetic timer entirely; the polling banner is surfaced by a single `StateHasChanged` with a static spinner. (`Mission.razor` keeps its `ct`-bound timer and passes; left untouched to avoid regression risk.) | (sample/UI only) | fixed (Phase 5) |
| DEV-8 | 5 | e2e spec timing | `mission-call-chain.spec.ts`'s `approvePrompt` asserted an **exact** transient `toHaveCount(2)` after the step-2 approval. Unlike `mission.spec.ts` — where every gate parks on the next prompt so the count settles — step 3 here is **silent and final**: it advances with no gate and appends its card immediately, and Blazor **coalesces** the step-2 and step-3 `StateHasChanged` into one render batch (the DOM jumps 1→3, never showing 2). The exact count was therefore racy by construction; the page behaviour is correct (forcing artificial render flushes into product code to satisfy a test would be the anti-pattern). **Fix:** the helper now waits for the just-approved step's card via `expect(stepCard(page, expectedCards)).toBeVisible()` (i.e. ≥ expectedCards), matching the helper's own documented intent ("reach expectedCards"). The strict final `toHaveCount(3)` and all per-card content assertions are unchanged; `mission.spec.ts` is untouched. | (e2e test only) | fixed (Phase 5; two consecutive clean full-suite runs) |
| DEV-9 | 10 | SDK governance mapper | The Phase 10 independent spec-compliance review found a genuine non-compliance (NC-1): in `MapAAuthGovernance`'s interaction handler, the `interaction`/`payment` branch returned `200 {status:"ok"}` unconditionally and never read `InteractionRelayResult.Pending`, so a relay that signalled `Pending = true` could not drive the spec-mandated `202` + poll loop. §Interaction Response: *"For `interaction` and `payment` types, the PS relays the interaction to the user and **returns a deferred response**… The agent polls until the user completes the interaction."* **Fix:** the handler now, when the relay returns `Pending` **and** an `IDeferredConsentStore` is registered, parks the interaction (new `DeferredConsentKind.Interaction`) and answers `202` with a poll `Location` (mirroring the permission/mission prompt path); the poll resolves to `200 {status:"ok"}` once the user completes. Without a store it degrades to a synchronous `200` (no user channel), consistent with the permission `Prompt`-without-store fallback. The agent side already polled `202`s correctly (`DeferredExchange`), so no client change was needed. Covered by 4 new `GovernanceDeferredConsentMapperTests`. | §Interaction Response, §Deferred Responses | fixed (Phase 10; conformance 12/12, build 0/0) |
| DEV-10 | 10 | SDK governance mapper | Secondary observation from the Phase 10 review: the `completion` interaction branch resolves synchronously via the blocking relay (`InteractionRelayResult.Accepted`) rather than returning a deferred response while the user reviews (§Interaction Response: *"The PS returns a deferred response while the user reviews."*). Unlike NC-1 there is **no `Pending`-style field being dropped** — the relay contract for completion is intentionally synchronous (the relay blocks until the user accepts/declines), so this is a design simplification rather than an unwired contract. A real PS whose completion review is asynchronous can model it on its own relay; the default mapper's blocking model is spec-tolerable. Left as-is to avoid a relay-contract change that would risk the completion flow (DC6). | §Interaction Response | intentional |
| DEV-11 | 11 | Docs & code snippets | The Phase 11 docs-vs-SDK grounding review (subagent) found doc snippets that would not compile against the current SDK: (a) `mission-governance-clients.md`, `mission-governed-access.md` passed **bare strings** to `RequestPermissionAsync`/`RecordAuditAsync`, which take a `MissionAction` (no implicit `string` conversion exists) — CS1503; (b) `server/mission-governance.md`'s `MyPermissionDecider` compared `t.Name == context.Request.Action` (string vs `MissionAction`) — CS0019; (c) `dependency-injection.md` + `configuration.md` documented `AAuthAgentOptions` members that do not exist (`UseHwk()`, `InteractionHandling`, `InteractionHandlingOptions`, `BaseAddress`, `ChallengeHandling*`, `RefreshThreshold`, `Capabilities`, `InnerHandler`, `CallChainProvider`, `SignatureKeyProvider`) and omitted the real ones (`OnResourceInteraction`, `OnApprovalPending`, `PollingTimeout`); (d) `error-handling.md`'s `TokenErrorCode` listing omitted `MissionTerminated`; (e) `server/mission-governance.md` comment said the interaction route is `/interaction` (actual default `/mission-interaction`); (f) `dependency-injection.md` claimed the PS-side seams are "always supplied by the PS" whereas `AddAAuthGovernance` registers no-op defaults via `TryAdd`. **Fix:** all corrected against `src/AAuth/` (the source of truth — samples already used `new MissionAction(...)`/`tool.ToAction()` and build 0/0). | (docs only — grounded against SDK) | fixed (Phase 11) |
| DEV-12 | 11 | SDK denial wire code | The Phase 11 SDK-vs-spec review (subagent, S-1) found that the deferred-poll **denial** code was emitted/recognized as `access_denied` (OAuth/RFC 6749 vocabulary), but §Polling Error Codes defines the explicit-denial code as **`denied`** (`` `denied` | 403 | User or approver explicitly denied the request ``) — `access_denied` appears nowhere in the AAuth error tables. **Fix (user-approved full rename):** every site was renamed to `denied` with **no** backward-compat alias, since every emitter and classifier in this repo is ours and now round-trips `denied` end-to-end, keeping the system internally consistent and 100% spec-aligned. Scope covered: the SDK PS governance emits (`AAuthGovernanceApplicationBuilderExtensions`), the four-party AS path (`AAuthAccessServerEndpoints` 3 emits, `AccessServerClient.IsDeniedAsync` classifier — confirmed in-scope as those `403`/`AccessDecisionKind.Deny` responses are AAuth polling denials), the agent classifiers (`TokenExchangeClient.IsDeniedAsync`, `DeferredExchange` comments), interface/exception doc comments (`IAccessPolicy`, `IAccessPendingStore`, `AAuthInteractionExceptions`), all sample mocks (`MockPersonServer`, `Orchestrator`, `MockAccessServer`, `MissionAgent`), the SampleApp `Mission.razor` payload+narration, `GuidedTour` narration + classifier, all conformance/integration test asserts (incl. method `Pending_Returns403Denied_AfterDeny` and the Keycloak test emit), the Playwright specs, and `docs/advanced/interaction-chaining.md`. Spec is unaffected (`denied` is already the only denial code there). | §Polling Error Codes (L2023) | fixed (build 0/0) |
| DEV-13 | 11 | SDK mission endpoint | Phase 11 review (S-2): `HandleMissionAsync`'s carrier-type guard returns `401 {error:"invalid_carrier_token"}` (JSON) when a non-agent token is presented. §Error Responses states *"A `401` response from any AAuth endpoint uses the `Signature-Error` header."* This is a **semantic** token-type check (the signature itself already verified), not a signature-authentication failure, so the spec's 401/`Signature-Error` rule is a poor fit; the behavior is pinned by two existing conformance/integration tests (`GovernanceDeferredConsentMapperTests`, `MockPersonServerTests`). Left as-is (LOW); changing the status/shape would churn our own just-written conformance tests for no protocol-observable gain. | §Error Responses | intentional |
| DEV-14 | 11 | SDK token error code | Phase 11 review (S-3): `TokenErrorCode.UserUnreachable` (`user_unreachable`, 400) is **forward-looking** — it is defined in `upcoming-changes-02.md` (F5), not yet in the authoritative `draft-hardt-oauth-aauth-protocol.md`. The code comment cites draft-02, which is the agreed direction. Acceptable; tracked until draft-02 lands. | §Token Endpoint Error Codes; upcoming-changes-02 (F5) | intentional (forward-looking) |

## Notes

- Known spec-alignment findings from research (F1–F6) are tracked in
[research.md](research.md) Part F; only deviations discovered **during
implementation** are logged here.
Loading
Loading