mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-10-25 14:00:40 +00:00
82c45f24cd
* feat(apps): app item actions and context codes (TASK-3414, SPEC-6 U11) [wip] Item actions are written as app_item_actions rows at provisioning, kept in step by a reviewed upgrade (a changed action gets a new revision, a removed one is retired), deleted at uninstall and backfilled at start. A viewer's click mints a single-use, two-minute context code; the app redeems it with its service token. Mint and redeem take the install, item and action rows in that order (FOR SHARE on Postgres), re-check the install, the item's current companion collection, the action's revision and the viewer's current visibility (the human item read's rule), and refuse with one 404 body. Redeem consumes the code first. The frozen TS type ItemAppAction and api.items.appActions / mintAppAction ship for U9. Migrations 125 / pg 099. Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * test(apps): U11 mint, redeem, refusal and revocation tests; redeem in the read census; NUL baseline (TASK-3414) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * test(apps): invalidating mutations wait for a redeem holding the section 6 locks (TASK-3414) Deterministic: a hook pauses the redeem with its locks held while disable, an upgrade's action change, a move or a delete is started; each stays blocked until the redeem commits. On Postgres, dropping the install lock lets disable through, dropping the item lock lets move and delete through. Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * test(apps): U11 mutant kills: epoch, deactivation, companion moves, release, admin non-member, backfill and workspace guards (TASK-3414) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * fix(apps): redeem at the token's workspace, service tokens only; no pool reads under the context locks; normalized base URL (codex r1 on U11, TASK-3414) Redeem moves to POST /api/app/v1/context/redeem (§6's path), outside /workspaces/{ws}: the workspace is the token's, injected before the same per-route stack, so no workspace-path refusal can answer before the code is consumed. It refuses a delegated token with the same 404. The viewer re-admission reads the user, role, membership and grants on the mint or redeem transaction (GetUserQ, ViewerRoleQ, UserHasGrantsInWorkspaceQ). A one-connection pool runs mint and redeem for a full member, a restricted member and a guest without hanging; switching any of those reads back to the pool deadlocks it. The link-out URL is built from the validated origin, refused before a code is written when base_url does not normalize. Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * fix(apps): a delegated redeem attempt consumes the code before it is refused (codex r2 on U11, TASK-3414) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * fix(apps): build the context code expiry from its constant (lint, TASK-3414) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS