An admin screen listing drafts by state: the photos, the submitter's note, which link it came from, the drafted copy in editable fields, the price, and Publish / Regenerate / Discard. Publish writes the edited values onto the item and calls the existing mark-available. Reuses the existing admin item components where they fit rather than growing a second item editor.
The price field is the point of this screen
Because items are priced on arrival — the model's suggestion, or the 80.00 default — the schema no longer prevents a price nobody chose from reaching the storefront. This screen is where that protection now lives, so the price field is not an ordinary input:
It is labelled with where the number came from, reading price_source — the model, the default, or the admin.
Anything that is not price_source='admin' is marked visibly as unconfirmed.
Editing it sets price_source='admin'.
Publishing something still unconfirmed is allowed, because that is the decision taken deliberately, but it says so plainly at the point of publishing rather than after.
80.00 is a plausible price rather than an obvious sentinel: 0 would render "$0.00" and read as a bug to anyone who saw it, whereas 80.00 reads as a decision, so a default left unnoticed sells the item instead of announcing itself. That is the whole argument for surfacing provenance loudly here.
Also
Discarded drafts are recoverable from this screen, since discard is reachable by one click from an inbox.
E2E assertions scoped to the item under test rather than the whole grid — the dev database never truncates, so a full-catalogue assertion outruns its timeout.
Slice 4 of #220, and the last. Depends on slice 2 for something to review; usable without slice 3.
**Design:** `docs/superpowers/specs/2026-08-29-intake-pipeline-design.md`
## Scope
An admin screen listing drafts by state: the photos, the submitter's note, which link it came from, the drafted copy in editable fields, the price, and Publish / Regenerate / Discard. Publish writes the edited values onto the item and calls the existing `mark-available`. Reuses the existing admin item components where they fit rather than growing a second item editor.
## The price field is the point of this screen
Because items are priced on arrival — the model's suggestion, or the 80.00 default — the schema no longer prevents a price nobody chose from reaching the storefront. This screen is where that protection now lives, so the price field is not an ordinary input:
- It is labelled with where the number came from, reading `price_source` — the model, the default, or the admin.
- Anything that is not `price_source='admin'` is marked visibly as unconfirmed.
- Editing it sets `price_source='admin'`.
- Publishing something still unconfirmed is allowed, because that is the decision taken deliberately, but it says so plainly at the point of publishing rather than after.
80.00 is a plausible price rather than an obvious sentinel: `0` would render "$0.00" and read as a bug to anyone who saw it, whereas 80.00 reads as a decision, so a default left unnoticed sells the item instead of announcing itself. That is the whole argument for surfacing provenance loudly here.
## Also
Discarded drafts are recoverable from this screen, since discard is reachable by one click from an inbox.
E2E assertions scoped to the item under test rather than the whole grid — the dev database never truncates, so a full-catalogue assertion outruns its timeout.
Part of #220
Done and merged to main — it arrived via PR #263, because the #224 plan branch was stacked on top of this one, which is why nothing here closed automatically.
Shipped: GET /api/admin/item-drafts with a state filter, and publish, regenerate, discard and restore. The Review queue tab shows the photos, the sender's note, which link it came from, the drafted copy in editable fields, and the price.
The price behaves as the issue asked, with one thing worth spelling out. Editing the number is the only thing that sets price_source='admin'. Publishing without touching the field deliberately leaves it unconfirmed rather than quietly recording it as chosen — turning "I did not look at this" into "I approved this" is exactly the misrecording this screen exists to prevent. Publishing at an unconfirmed price is still allowed, and names the figure and its origin before it happens. The rule is a pure tested function applied on the server rather than trusted from the browser, with the row locked for the transaction so two admins cannot interleave one's price decision with another's name.
Discard deletes nothing — not the item, not the photographs. Restore returns a draft at the state its own contents justify rather than unconditionally ready, since a submission discarded before it was ever drafted has no copy and must not come back claiming otherwise.
One deviation, recorded rather than buried. The scope says to reuse the existing admin item components rather than grow a second item editor. I built a purpose-made card instead: the fields differ in kind rather than arrangement — this edits a proposal, beside the photographs and note that justify it, with a price input carrying a provenance label and a confirmation the ordinary editor has no concept of. The cost is real and worth naming: two places now render an item's name, description and price, so a change to how items are edited has to be made twice. Category and tag editing was left out for the same reason and is the natural moment to revisit it — the fields are returned by the API, so the screen can show them whenever that is picked up.
Verified: 346 backend unit, 317 backend integration, 157 e2e (up from 155), lint and build clean on both projects, zero warnings.
Done and merged to `main` — it arrived via PR #263, because the #224 plan branch was stacked on top of this one, which is why nothing here closed automatically.
Shipped: `GET /api/admin/item-drafts` with a state filter, and publish, regenerate, discard and restore. The Review queue tab shows the photos, the sender's note, which link it came from, the drafted copy in editable fields, and the price.
**The price behaves as the issue asked, with one thing worth spelling out.** Editing the number is the only thing that sets `price_source='admin'`. Publishing *without* touching the field deliberately leaves it unconfirmed rather than quietly recording it as chosen — turning "I did not look at this" into "I approved this" is exactly the misrecording this screen exists to prevent. Publishing at an unconfirmed price is still allowed, and names the figure and its origin before it happens. The rule is a pure tested function applied on the server rather than trusted from the browser, with the row locked for the transaction so two admins cannot interleave one's price decision with another's name.
Discard deletes nothing — not the item, not the photographs. Restore returns a draft at the state its own contents justify rather than unconditionally `ready`, since a submission discarded before it was ever drafted has no copy and must not come back claiming otherwise.
**One deviation, recorded rather than buried.** The scope says to reuse the existing admin item components rather than grow a second item editor. I built a purpose-made card instead: the fields differ in kind rather than arrangement — this edits a proposal, beside the photographs and note that justify it, with a price input carrying a provenance label and a confirmation the ordinary editor has no concept of. The cost is real and worth naming: two places now render an item's name, description and price, so a change to how items are edited has to be made twice. Category and tag editing was left out for the same reason and is the natural moment to revisit it — the fields are returned by the API, so the screen can show them whenever that is picked up.
Verified: 346 backend unit, 317 backend integration, 157 e2e (up from 155), lint and build clean on both projects, zero warnings.
Closes #225
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Slice 4 of #220, and the last. Depends on slice 2 for something to review; usable without slice 3.
Design:
docs/superpowers/specs/2026-08-29-intake-pipeline-design.mdScope
An admin screen listing drafts by state: the photos, the submitter's note, which link it came from, the drafted copy in editable fields, the price, and Publish / Regenerate / Discard. Publish writes the edited values onto the item and calls the existing
mark-available. Reuses the existing admin item components where they fit rather than growing a second item editor.The price field is the point of this screen
Because items are priced on arrival — the model's suggestion, or the 80.00 default — the schema no longer prevents a price nobody chose from reaching the storefront. This screen is where that protection now lives, so the price field is not an ordinary input:
price_source— the model, the default, or the admin.price_source='admin'is marked visibly as unconfirmed.price_source='admin'.80.00 is a plausible price rather than an obvious sentinel:
0would render "$0.00" and read as a bug to anyone who saw it, whereas 80.00 reads as a decision, so a default left unnoticed sells the item instead of announcing itself. That is the whole argument for surfacing provenance loudly here.Also
Discarded drafts are recoverable from this screen, since discard is reachable by one click from an inbox.
E2E assertions scoped to the item under test rather than the whole grid — the dev database never truncates, so a full-catalogue assertion outruns its timeout.
Part of #220
Done and merged to
main— it arrived via PR #263, because the #224 plan branch was stacked on top of this one, which is why nothing here closed automatically.Shipped:
GET /api/admin/item-draftswith a state filter, and publish, regenerate, discard and restore. The Review queue tab shows the photos, the sender's note, which link it came from, the drafted copy in editable fields, and the price.The price behaves as the issue asked, with one thing worth spelling out. Editing the number is the only thing that sets
price_source='admin'. Publishing without touching the field deliberately leaves it unconfirmed rather than quietly recording it as chosen — turning "I did not look at this" into "I approved this" is exactly the misrecording this screen exists to prevent. Publishing at an unconfirmed price is still allowed, and names the figure and its origin before it happens. The rule is a pure tested function applied on the server rather than trusted from the browser, with the row locked for the transaction so two admins cannot interleave one's price decision with another's name.Discard deletes nothing — not the item, not the photographs. Restore returns a draft at the state its own contents justify rather than unconditionally
ready, since a submission discarded before it was ever drafted has no copy and must not come back claiming otherwise.One deviation, recorded rather than buried. The scope says to reuse the existing admin item components rather than grow a second item editor. I built a purpose-made card instead: the fields differ in kind rather than arrangement — this edits a proposal, beside the photographs and note that justify it, with a price input carrying a provenance label and a confirmation the ordinary editor has no concept of. The cost is real and worth naming: two places now render an item's name, description and price, so a change to how items are edited has to be made twice. Category and tag editing was left out for the same reason and is the natural moment to revisit it — the fields are returned by the API, so the screen can show them whenever that is picked up.
Verified: 346 backend unit, 317 backend integration, 157 e2e (up from 155), lint and build clean on both projects, zero warnings.
Closes #225