Repository navigation
feat(quick): direct mutations through the catalog — bookmarks/links, annotations, forms/attachments/metadata (#231 PR 2) - #271
Merged
Merged
Conversation
Records the #234 precondition: annotation, bookmark, link, form, attachment and metadata edits are direct Core mutations through CommandCatalog producing a new revision (fenced, one history event per edit), not planned Fix operations; adds the 013 row missing from the ADR index.
…er-edit history (#234)
#231 PR 2) Cherry-picks the three slice commits and repairs the conflict regions with complete blocks from each slice: one facade gateway per family (unified in follow-up), the 39 implemented ids, expected_action_count 109 -> 113, the handler ratchet re-recorded to 13, and the PR fragment plus evidence manifest.
…uest (#231 PR 2, ADR-014) Collapses the three family gateways into the single DirectEditRequest / DirectEditOutcome / DocumentFacade::applyDirectEdit shape ADR-014 records, dispatching by edit kind to the annotation, outline and document helpers. Refusal codes and fence order per family are unchanged; all call sites and the facade tests are adapted.
… proof lanes (#231 PR 2) Adds UnitTestsAuthoringEdit to the core module (paths + tests) so the catalog target has a proof lane, rewrites the evidence manifest against the touched subsystems' required lanes (core, interaction, quick, build_policy, governed-execution incl. the canonical-json-golden fixture and packaging workflows; the independent-validation stub is listed unresolved), and clang-formats pdfauthoringedit.h.
… test targets (#231 PR 2)
The defaulted operator== makes GCC 13 instantiate the definition while QList's own equality is still forming (has_operator_equal probe), a hard error CI caught; the explicit declaration is portable and MSVC-identical.
…lpers firstLinkOnPage/linkOnPage returned a raw pointer into a temporary or local PDFAnnotationPtr, so the annotation was freed before use; glibc's freed-chunk metadata clobbers the vtable and the next dynamic_cast segfaults (MSVC survived by heap luck). The helpers now return the owning pointer and call sites downcast explicitly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Epic #231 PR 2 — direct mutations through the catalog (bookmarks/links, annotations, forms/attachments/metadata)
Second of three PRs for epic #231, stacked on
dev(PR 1, #267, is merged). The decision the epic required before annotation code is recorded first: ADR-014 (docs/adr/adr-014-direct-revision-authoring-edits.md) settles direct revision versus planned operation for authoring edits — direct Core mutation throughCommandCatalogper edit, fenced, one operation-history event and rollback point per accepted edit; the governed Fix flow stays the only route for publication-affecting operations (PR 3).Thirty-nine commands gain handlers and land as implemented, all through one facade gateway (one
DirectEditRequest/DirectEditOutcome, dispatching by edit kind to the family writer):actionCreateHyperlinkToThisPDF*destination variants — each variant serialized through Core's writer and read back through Core's reader; a stale revision refuses with a typed reason before any write; a document without modify permission refuses the edit. Outline rewrites refuse unsupported structures (bookmark/unsupported-outline-item) instead of dropping them.pdfauthoringhistory), and a provenance write that fails is reported on the completed invocation rather than pretending the edit did not land.actionSetFormFieldValue,actionAddAttachment,actionRemoveAttachment,actionEditDocumentMetadata;expected_action_count109 → 113) plusactionSend_by_E_Mail. Form values round-trip (text, checkbox, choice; signature fields excluded; an XFA-only form is reported unsupported); attachments add/remove through the/Namestree with extraction through the atomic writer that refuses executable auto-open; metadata edits merge Info and XMP without voiding foreign properties (pdfaid:partsurvives) and refuse an unparseable packet rather than replacing it; send-by-email hands the current published revision to the mail client and refuses an unsaved document.The handler-coverage ratchet tightens from 48 to 13 KEEP/ABSORB commands still awaiting a handler; PR 3 takes it to zero and closes the epic.
Validation:
check-change.py --base origin/devpassed at the head SHA — 130/130 checks acrossbuild_policy,core,documentation,interactionandquick(risk: high — real document mutations), with every target built, the 93-suite ctest set run in the worktree build, clang-format over every changed file, and the catalog/ratchet/architecture/governance/mirror-parity checks green. Limits: no native-GPU smoke (headless offscreen + software backend only), no release-profile build locally, and the pwsh/WSL-dependent CI lanes are absent on this host.Gaps, stated rather than dressed up:
actionMoveSelection.direct-edit/history-sidecar-unavailable; the mutation itself is unaffected and the refusal is typed.qsTrstrings await the release-translations workflow (.tsrefresh is its own lane).UnitTestsEditorHostflakes on the sharedqttestdirectory across worktrees) and replaceDocument races in-flight surface renders (use-after-free in PDFPrecompiledPage::draw) #269 (replaceDocumentraces in-flight surface renders — use-after-free; a test-side settle-wait contains it).Quality review: every family enters through the catalog and the facade, so a stale revision, denied permission, XFA-only form or executable attachment is a typed refusal that changes nothing, never a half-write; the XMP writer merges rather than regenerates so an authoring edit cannot silently void a PDF/A or PDF/X claim; the outline writer refuses structures it cannot faithfully rewrite. The one deliberate compromise is the QML affordance gap above — the mutation contract is complete, the interaction surface is not.
Advances #231 — the gate and the first two surface groups are in place; the epic closes when the ratchet reaches zero in PR 3.
Advances #233 — all acceptance bullets evidenced (catalog-routed mutations producing new revisions, import/export round-trip, per-variant read-back, stale-revision refusal); the import/export dialogs remain follow-up.
Advances #234 — all acceptance bullets evidenced (ADR-014 recorded; per-subtype read-back with appearance streams; history model of #124; permission refusal); the gesture bridge remains follow-up.
Advances #242 — all acceptance bullets evidenced (catalog-routed mutations; atomic extraction with executable refusal; published-revision mail hand-off; XFA refusal); the form overlay, attachment panel and metadata editor remain follow-up.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.