carlos / platform Public

plan: task 11 is landed, and what it taught task 12

Ticks the boxes and records the two departures from the plan, both of
which task 12 inherits: the refusal mapping belongs in a table because
its most important arms are unreachable through the door, and the
consent chain is exported rather than re-derived.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ExnZtUYmt2h8QFcymjPaSL
Paul Campbell pushed by paul@keymail.dev 976ff88ea2e57d17e608326ca569ecac85f67443 parent 90adce4
1 file changed, +22 −4
  • docs/superpowers/plans/2026-09-02-scan-on-demand.md +22 −4

CI passed — run details

diff --git a/docs/superpowers/plans/2026-09-02-scan-on-demand.md b/docs/superpowers/plans/2026-09-02-scan-on-demand.md
index a0dd8036a..7019e2d25 100644
--- a/docs/superpowers/plans/2026-09-02-scan-on-demand.md
+++ b/docs/superpowers/plans/2026-09-02-scan-on-demand.md
@@ -381,16 +381,16 @@ Absence is `os.ErrNotExist` (`trash.go:64`), never an error.
**Files:** `internal/console/apiscan.go`, `authcli.go`,
`internal/console/scanhook.go` (new — the path validator only).
-- [ ] One test per refusal: `not_granted`, `not_requested`, `encrypted`,
+- [x] One test per refusal: `not_granted`, `not_requested`, `encrypted`,
`not_enabled`, `disabled`, 429.
-- [ ] `TestEnqueueRefusesWithoutAttestation` — **the consent hole review 3
+- [x] `TestEnqueueRefusesWithoutAttestation` — **the consent hole review 3
found.** The sweep refuses without the member's attestation
(`sweep.go:151-160`) and grant refuses without a request
(`apiscan.go:268-285`); a door checking only the deployment switch went
straight through it.
-- [ ] `TestEnqueueRequiresServiceRole` — `serviceToken`; `Admin` reaches it by
+- [x] `TestEnqueueRequiresServiceRole` — `serviceToken`; `Admin` reaches it by
construction (`svcrole.go:109`), which the test states rather than fights.
-- [ ] **`TestCallbackPathValidation`** — the §6.2 hostile table lands **here**,
+- [x] **`TestCallbackPathValidation`** — the §6.2 hostile table lands **here**,
not in the webhook task: absolute URLs, `//evil/x`, `/\evil/x`,
`@`-authority, traversal, raw and encoded CRLF, fragments, >1KB. Draft 1
accepted the field in this task and validated it four commits later, so
@@ -398,6 +398,24 @@ Absence is `os.ErrNotExist` (`trash.go:64`), never an error.
**Commit:** `console: the scan enqueue door, behind the consent chain`
+**Landed** as `console: the scan enqueue door, behind the consent chain`.
+Two things came out differently from this plan, both worth carrying forward:
+
+- The refusal mapping was moved OUT of the handler into `scanRefusalAnswer`.
+ Two of its arms are unreachable through the door — a trashed app is a 404 at
+ `apiAccountApp` before the chain is consulted, and an undefined refusal name
+ cannot be sent — and both survived mutation testing through HTTP for that
+ reason alone. Task 12's drain has the same pair of unreachable conclusions
+ (§7.5's keep-or-delete) and should be written as a table from the start
+ rather than as a switch inside the goroutine.
+- `objstore.queuePermitted` is now exported as `QueuePermitted`, because the
+ door is in another package and §5.1.1 forbids a second derivation.
+
+`TestCallbackPathValidation` was already landed with the validator; what this
+task added on top is the door-level proof that the door CALLS it, which is a
+different claim and the one that keeps hostile paths out of the bucket.
+
+
---
## Task 12: the drain goroutine and the boot rebuild