Intent: the direction says a human approves every message; today nothing records that anyone did.
Done looks like:
[ ] Each asset (MLS, three social posts, flyer, email) has an Approve button on the listing page beside Copy; approval stores who and when on the asset row (forward-only migration in src/db.ts).
[ ] Editing an asset's text, or Rewrite all, clears its approval; an asset with open Fair Housing findings can still be approved but the button says "Approve with 2 flags".
[ ] The listing header shows "4 of 6 approved" and turns to "Ready to send" at 6 of 6.
[ ] One action in src/actions.ts (approveAsset), permitted as listing:update through permit, reached by the page form, the JSON API (src/routes.ts, contract/openapi.json re-emitted), the MCP tool list and the terminal.
[ ] Approve and un-approve are audit rows.
[ ] Tests: approve sets it, edit clears it, a non-author is 403, the MCP tool and the API route exist.
Files: src/db.ts, src/actions.ts, src/routes.ts, src/mcp.ts, src/tty.ts, src/pages/listing.ts, static/listing.js, static/app.css, contract/openapi.json, README.md (step 3).
Risks: contract cells must stay as they are (update author). Keep the page working without JS.
Brief: builder.
Intent: the direction says a human approves every message; today nothing records that anyone did.
Done looks like:
assetrow (forward-only migration insrc/db.ts).src/actions.ts(approveAsset), permitted aslisting:updatethroughpermit, reached by the page form, the JSON API (src/routes.ts,contract/openapi.jsonre-emitted), the MCP tool list and the terminal.Files: src/db.ts, src/actions.ts, src/routes.ts, src/mcp.ts, src/tty.ts, src/pages/listing.ts, static/listing.js, static/app.css, contract/openapi.json, README.md (step 3). Risks: contract cells must stay as they are (update author). Keep the page working without JS. Brief: builder.