issue-195: Implement penpot_commit with revn tracking, conflict retry and commit preview #228
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!228
Loading…
Reference in a new issue
No description provided.
Delete branch "feature/issue-195/implement-penpot-commit-with-revn-tracking-conflic"
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?
195: Implement penpot_commit with revn tracking, conflict retry and commit preview
Closes #195 — 195.
Generated in 10m 02s.
Review findings
Now let me apply the fixes. First, the pure helper
src/commit.ts(Low finding at line 80):Now the tool changes. First imports, guard, and conflict predicate:Now update the test file — authorize staged files, adjust the conflict assertion, and add coverage for the new behaviour:I've completed the remediation pass. Here is a summary of the changes:Fixed (all Critical/High/Medium/Low code findings):
tools/commit.ts:252—commitFailurenow branches onerror.category:transport/server/unexpectedget the "may or may not have been applied; re-read before retrying or discarding" wording; only provably-rejected categories claim "Nothing was written".tools/commit.ts:104—assertCommitTargetAllowednow enforcesisTargetAuthorized(fileId), so only a file named/staged by the staging primitives can reachupdate-file.tools/commit.ts:401— bothstate.revn = writeRevnassignments removed, so the staged base revision is never overwritten by the commit.src/commit.ts:80— discrimination is now an explicitswitch (change.type)with adefaultbranch reporting an unverifiable change as missing.tools/commit.ts:418—isRevisionConflicttreatsvern-conflictas retryable alongsiderevn-conflict.tools/commit.ts:462— a verified commit now requiresafter.value.revn > writeRevn, failing withrevisionNotAdvancedFailureotherwise.Added 5 tests (vern-conflict retry, guard refusal, non-advancing revision, 5xx uncertainty wording, base-revn preservation). Penpot suites: 207/207. Full
npm test: 333/334, the single failure pre-existing and unrelated (untouchedpackage.json/scripts/local-install.test.mjs).The
AGENTS.md:23finding concerns the branch name/commit subject/PR title, which cannot be changed here without forbidden git operations (commit/branch). Left unresolved for the pipeline.Review Findings — 195
Summary
All code findings were fixed in a single pass:
commitFailurenow distinguishes provably-rejected categories from possibly-applied ones (transport/server/unexpected); the designated-target guardassertCommitTargetAllowednow enforcesisTargetAuthorized; the commit no longer overwrites the staged baserevn;verifyStagedChangesuses an explicit discriminatedswitchwith adefaultthat reports an unverifiable change as missing;vern-conflictis retried likerevn-conflict; and a verified commit now requires the re-read revision to advance pastwriteRevn. Five focused tests were added (penpot suites 207/207; fullnpm test333/334, the one failure pre-existing on main and untouched by this diff). The remaining unresolved item is the branch/commit-naming finding, which requires forbidden git operations (committing/amending a branch) and is the pipeline's responsibility before the MR is opened.Critical
High
extensions/penpot/src/tools/commit.ts:252—commitFailureis used for every non-revn-conflictfailure, includingtransport(timeout / abort / network error) andserver(5xx) categories. Its text asserts "was rejected by Penpot" and "Nothing was written to Penpot; the staged change remains staged", which the extension cannot know for those categories — the request may have reached the server and been applied, so a caller that retries or discards can leave a duplicate/leftover write. Suggested fix: branch onerror.categoryincommitFailure; fortransport,serverandunexpected, emit the same uncertainty wording already used atextensions/penpot/src/tools/commit.ts:449("the write may or may not have been applied; re-read the file with penpot_get_file before retrying or discarding") instead of the false "Nothing was written" claim.extensions/penpot/src/tools/commit.ts:104— the designated-target guardassertCommitTargetAllowedis a deliberate no-op, so the only write path will write to any file id the token can edit (including files the user never named). This is thePENPOT_SKILL_DESIGN.md§9 / plan-M2 write-safety item, and the store already carries the authorisation (authorizeTarget/isTargetAuthorized, populated by the staging tools atextensions/penpot/src/tools/tokens.ts:288); only the commit call site ignores it. Suggested fix: implement the policy here (at minimumif (!isTargetAuthorized(fileId)) throw new Error(...)) so a file the user did not name/stage can never be written; if it stays deferred, add a test that pins the deferral so the placeholder cannot ship unnoticed.Medium
extensions/penpot/src/tools/commit.ts:401— the commit overwrites the store's base revision (state.revn = writeRevn;, repeated at:439on a conflict refresh), butChangeset.revnis documented as "the base revision observed when staging began" (extensions/penpot/src/changeset.ts:465) and "never overwritten" (extensions/penpot/src/changeset.ts:493). After any failed commit,penpot_statusanddry_runtherefore report a "Base revn" (extensions/penpot/src/changeset.ts:671) that is not the staging base, which is misleading about how far the file has drifted. Suggested fix: keep the live revision in the localwriteRevn/writeVernvariables only (drop bothstate.revn = writeRevnassignments) or add an explicitlastWriteRevnfield so the reported base stays the staging base.Low
extensions/penpot/src/commit.ts:80— the doc comment claims "a change that is not yet verifiable by this function … is treated as missing rather than assumed applied", but the code after theadd-colorbranch unconditionally readschange.typography, so a thirdChangesetChangemember would be a compile error (or, with a widened type, would be mis-verified as a typography), not amissingentry. Suggested fix: make the discrimination explicit (switch (change.type)with adefaultbranch that pushes an unverifiable/missing entry, and aneverexhaustiveness check), or correct the comment to match the actual closed-union behaviour.extensions/penpot/src/tools/commit.ts:418— onlyrevn-conflictis refreshed and retried; avern-conflict(design §7:vern ≠ stored → vern-conflict) is reported as a generic rejection ("fix and retry") even though the retry path already re-readsvernand would very likely succeed on the next attempt. Suggested fix: treatvern-conflictas retryable by the sameisRevnConflict-style predicate so the refresh-and-retry budget covers it too.extensions/penpot/src/tools/commit.ts:462— a verified commit reportsRevision: previousRevn → newRevnwithout checking thatafter.value.revnactually advanced pastwriteRevn; a re-read returning an unchanged revision is still presented as a committed revision transition, and the design's validation criterion ("the file's revision advanced exactly once") is not asserted anywhere. Suggested fix: addafter.value.revn > writeRevnto the verification result (and fail/flag when it does not hold), or word the line so a non-advance is not implied.AGENTS.md:23— the branchfeature/issue-195/implement-penpot-commit-with-revn-tracking-conflichas a truncated kebab slug, and the commit subject is195: Implement penpot_commit…rather than the requiredissue-<N>: <summary>form (AGENTS.md:26). Suggested fix: use the full title-derived slug (…-conflict-retry-and-commit-preview) and ensure the squashed commit / PR title use theissue-195: <title>form before the MR is opened.Documentation
Updated files: