issue-196: Implement the designated-target write guard with an allow/refuse matrix #229
No reviewers
Labels
No labels
bug
chore
documentation
enhancement
feature
ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
david/pi-extensions-and-skills!229
Loading…
Reference in a new issue
No description provided.
Delete branch "feature/issue-196/implement-the-designated-target-write-guard-with-a"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
196: Implement the designated-target write guard with an allow/refuse matrix
Closes #196 — 196.
Generated in 12m 23s.
Review findings
Now let me look at the design doc §9 and Control row line numbers, plus library/tokens imports precisely.Let me create the shared constants module and start editing.All penpot tests pass (230/230). The full
npm testshows only the pre-existing, unrelatedscripts/local-install.test.mjsmongodb-manifest failure (package.json is untouched by this change).Review Findings — 196
Summary
All seven findings were resolved in this pass.
resolveFileProjectnow returns a discriminatedProjectResolution(ok/read-failed+ decoded message /no-project), andisWriteAllowedcarries the decoded server/transport message into the fail-closed hint, which now leads with "could not be read" and the real remedy (connection/credentials) rather than misreporting auth/config failures as a designation problem.registerCreatedFiledoc, README write-safety section and CHANGELOG now state the provenance is reserved for the not-yet-shipped create/import path and is currently only exercised by tests.penpot_commitdescription now states the allow-list precondition and refusal behaviour.docs/PENPOT_SKILL_DESIGN.md§9 to match.update-filesent,verified === true).GET_FILE_COMMAND/FILE_ID_QUERY_KEYintosrc/fileCommand.tsand imported them from all five call sites (guard, commit, file, library, tokens).authorize_targetto the Control row and a session-scoping line to §9.Gates: no build/lint command is configured (no tsconfig, eslint/prettier config, or build script), so
npm testwas the only runnable gate. Penpot suites are 230/230 green (two tests added). The full run is 356/357, the single failure being the pre-existing, unrelatedscripts/local-install.test.mjs:1875mongodb-manifest assertion (package.json is untouched by this diff).Critical
High
resolveFileProjectcollapses every failure (!result.ok: config error, 401/403, timeout, 5xx) and a body with no project field intoundefined, soisWriteAllowedreportsproject-resolution-failed("the file's project could not be determined") and the hint tells the agent to callpenpot_authorize_target. An expiredPENPOT_TOKENor a missingPENPOT_URLis thus misreported as a designation problem with a wrong remedy — contrary to the module's own documented convention (client.ts:9-11: "The API is internal and unversioned, so the client surfaces the server's own error payload rather than inventing messages";penpotRequestreturns categorised errors "so callers decide how to present them"). Suggested fix: return a discriminated result fromresolveFileProject(e.g.{ ok: true, projectId } | { ok: false, reason: "read-failed", message } | { ok: false, reason: "no-project" }) and haveisWriteAllowedcarry the decoded server/transport message into theproject-resolution-failedhint, or at minimum pre-empt it incommit.tswhenpenpotRequestreportsconfig/authbefore the guard runs.Medium
registerCreatedFilehas no production caller anywhere in the extension (onlyguard.test.tscalls it), and no create-file path exists (penpot_list_libraryis read-only;import-binfile/create-fileare unimplemented M4 work). The "created" provenance therefore can never match in a real session, yet the docs assert it is live:guard.ts:70("Called by every create-file path (currently library import…)"),extensions/penpot/README.md:33("the extension created it this session (currently via library import, and any future create-file path)") andCHANGELOG.md:13. A reader concludes imports are auto-authorised when nothing can register a created file. Suggested fix: reword to state the provenance is reserved for the not-yet-shipped create/import path (which will callregisterCreatedFileat creation time), and drop "currently via library import"; alternatively wire the call when that tool lands, and keep the unit test as the contract.penpot_committool description (the model-facing contract) documents therevnprotocol, retry anddry_runin detail but never mentions the new precondition that the target must be on the session allow-list, so the agent discovers the guard only by being refused. Every other precondition in this extension is stated in its description. Suggested fix: add a sentence such as "The file must be on the session write allow-list (a file the extension created, a file the user named, or a file in a nominated project — recorded withpenpot_authorize_target); otherwise the call is refused with nothing written and the changeset left staged."Low
registerCreatedFile(fileId, projectId)is allowed by bare id membership; the recordedprojectIdis never consulted by the decision (only rendered inguardSnapshot), which diverges from the design's wording indocs/PENPOT_SKILL_DESIGN.md:200("files it created in a nominated project"). Suggested fix: either gate the created branch on the recorded project being nominated (matching the design), or state in the module doc and README that an extension-created file is authorised by construction and the recorded project is informational only.resolveFileProjectresolves a matching project and the commit proceeds, so the wiring atcommit.ts:472-474is only proven to refuse, never to allow. Suggested fix: add a test that nominatesPROJECT_ID, serves aget-filebody carryingprojectId: PROJECT_ID(then the revn/verify reads) and asserts theupdate-filecall happened anddetails.verified === true.GET_FILE_COMMAND/FILE_ID_QUERY_KEYduplicate the same constants intools/commit.ts:68,73andtools/file.ts:35,41; three copies of the same RPC contract must now stay in sync. Suggested fix: export them once (e.g. fromclient.tsor a smallfileCommand.ts) and import in all three call sites.commit,status,discard; the newpenpot_authorize_target(the mechanism §9's write-safety rule depends on) is missing, as is any mention of the allow-list recording step in §9. Suggested fix: addauthorize_targetto the Control group and one line to §9 noting that designations are recorded by that tool and are session-scoped.Documentation
Updated files: