Skip to content

[0.2.1] Operator Completion, Product Surface & Trust Leftovers - #275

Open
mberrys wants to merge 67 commits into
stablefrom
unstable
Open

mberrys wants to merge 67 commits into
stablefrom
unstable

Conversation

@mberrys

@mberrys mberrys commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Promotion unstable → stable for the 0.2.1 patch line. Closes the operator, product-surface and trust leftovers from the 0.2.0 line that sat on 0.3.0 before the roadmap realigned, and rules out governed correction execution (that stays on 0.3.0).

Scope: no tag on stable (git describe --tags finds none); base 4b171b07 → tip d809628d — 49 non-merge commits, 8 topic PRs merged into unstable (#262, #263, #264, #266, #267, #268, #271, #273).
Canonical milestone text: docs/github-milestones/0.2.1.md.

Added

Changed

  • Interactive authoring edits are direct Core mutations through the single CommandCatalog path, per ADR-014; governed Fix keeps the planned-operation path (L03-13 — Restore Quick GUI coverage for the retired Widgets surfaces #231).
  • Render options reclassified ADVANCED with rationale; the host routes menus from the manifest's menu_group.
  • Core render diagnostics are read through a private output adapter.

Fixed

Internal

Refs

Issues touched by this promotion whose acceptance criteria were not verified for this body. Closing keywords act only on merge into stable, so each stays open until its criteria are confirmed:

Breaking changes

None. All eight topic fragments on unstable record Breaking-Change: no.

Verification

Not yet established for this promotion. Required before merge: python scripts/agent/check-change.py --base origin/stable --build-dir build-local on head d809628d, with the result quoted here. Topic PRs carry their own check-change results on their PR pages.
🤖 Generated with Claude Code

Complete G00 repository governance and provenance (#108)
#231)

The descriptor availability flag is the handler gate: CommandCatalog refuses
a handler for a declared id and invoke() answers command/not-implemented, so a
KEEP or ABSORB action that is still declared is a menu entry that does nothing.
scripts/verify-command-catalog.py now requires every KEEP/ABSORB action to be
implemented or listed in docs/shell-handler-coverage.json, the baseline that
records today's 70 gaps and may only shrink. --record-ratchet rewrites it.
Negative-case tests prove each rule can fail; ci.yml and check-change.py
report the check as whole-tree evidence.
… the live epic (#231)

The G00 provenance repair re-homed the absorbed plugin rows; these two ADVANCED
rows were left null while their replacement surfaces (signing in loop-fix, the
scanner document workflow) are still pending, so no live issue owned them.
#232)

Text and Measure join the closed InteractionTool vocabulary as their own
InteractionKinds: a text selection is a typed, revision-fenced TextSelection
resolved through Core's text layout in reading order, and a measurement is a
typed revision-fenced Measurement in production units that never becomes a
DragSession. The nine actionSelect*/CopyText/GoTo*Line/Screenshot/ExtractImage
commands are bound; screenshot and image extraction write through the atomic
writer and refuse to overwrite; a page without a text layer reports
text/no-text-layer instead of copying an empty string.
)

Five actionColor* display modes become presentation-only filters applied in
CanvasNodeBuilder::syncTiles over admitted surfaces, converted with Core's own
PDFColorConvertor, marked as simulations in the fidelity indicator and never
reachable as production evidence. Soft proof renders through a local
LittleCMS proofing CMS behind its own surface identity (withSoftProofMarker),
shows the profile identity, and a missing or unreadable output intent is a
visible state. Plates expose Core's separations with visibility and isolation
state that never moves the revision, and the ink-coverage readout measures
named channels at a page point.
One typed QSettings-backed store with confirmed reset-to-factory and visible
repair of invalid persisted values; actionOptions, actionResetToFactorySettings,
actionCollectDiagnostics, the six render-option toggles and the text-block/line
views are bound and developer-gated; the ADR-006 diagnostics bundle comes from
Core's PDFDiagnosticsCollector. The five KEEP render options are reclassified
ADVANCED with rationale and the host routes menus from the manifest's
menu_group, so they are reachable in a developer profile only.
The implemented-descriptor pin is now 51 (26 + display modes + select/copy/
capture + settings/diagnostics), and the handler-coverage baseline is
re-recorded from the merged manifest: 51 KEEP/ABSORB commands remain.
…al (#232, #238)

The shell keyboard test pinned "measure" as an unknown tool; it is a real
vocabulary member now, so the negative case moves to "lasso" and the test
asserts both new tools route to the controller. The new host case proves image
extraction refuses to overwrite an existing file and leaves it byte-identical.
The PR fragment and evidence manifest cover the integrated slices.
…tion publish gate

List annotations from the open revision in a read-only review column and paint them in their own overlay band, bounded so findings and selection primitives keep their budget, with page-region navigation from the list. Verify document signatures off the UI thread and reduce results to five distinct operator-facing states behind a request/generation/revision-fenced token. Disclose save-policy signature invalidation in the correction-plan preview and the approval, and refuse to publish an invalidating plan over a signed document until the exact plan digest is acknowledged.
…ignature-review

L03-11: read-only annotation review and signature status with an invalidation publish gate (#125)
mberrys and others added 29 commits October 11, 2026 03:25
…tory

Present Quick Undo and Redo as governed revision history
Reconcile the EditorHost constructor: settings come first, then the parent and
the recipes directory dev added, so existing dev call sites pass the null
settings argument explicitly. Keep both sides' members in the editor host.
Keep the undo and redo handlers from dev in the command catalog and drop them
from the handler ratchet baseline, which now records 49 gaps. The facade test
expects the 53 implemented commands.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqo8EDq3w2nU1zekEZDoZc
Keep both published-output records side by side. The Action List worker
still records the in-memory published bytes that the export and receipt use,
and now also records the published path that the undo and redo history
tracks. The editor host clears and sets both together. Both sides' tests stay,
the command catalog takes the union of the two branches' implemented commands,
and the facade test expects the 30 implemented commands. The quick PDF parity
note lists print and export and undo and redo as done.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqo8EDq3w2nU1zekEZDoZc
The editor host constructor now takes the settings store first, then the
parent and recipes directory. A test added from dev still used the old
two-argument form, which failed to compile and stopped UnitTestsEditorHost
from being built. Pass a null settings argument at each recipe-path call.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqo8EDq3w2nU1zekEZDoZc
The editor host constructor now takes the settings store first, then the
parent and recipes directory. The merged tests still used the old two-argument
form, which fails to compile. Pass a null settings argument at each
recipe-path call.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqo8EDq3w2nU1zekEZDoZc
The previous commit's regex matched calls that already passed the settings
argument and inserted a second one, which produced four-argument calls that
do not compile and break the clang-format check. Restore one null settings
argument per recipe-path call.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqo8EDq3w2nU1zekEZDoZc
The settings-first constructor exists only on the epic branch. This branch
still builds against dev, where the constructor takes the parent and the
recipes directory, so the test calls I changed here do not compile. Restore
the calls to the dev form.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqo8EDq3w2nU1zekEZDoZc
…export

Add Quick print-to-PDF and exact document exports
Bring in the print and export commands from #263. Keep this branch's finish
steps and availability updates alongside dev's document output step and the
print and export availability. Keep both sides' editor host tests, with the
print and export tests first and the display-mode test after them. Take the
union of both branches' implemented commands and regenerate the handler
ratchet, which now records 48 gaps. The facade test expects the 55 implemented
commands.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqo8EDq3w2nU1zekEZDoZc
feat(quick): handler-coverage gate and read-only surfaces (#231 PR 1)
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.
#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.
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.
feat(quick): direct mutations through the catalog — bookmarks/links, annotations, forms/attachments/metadata (#231 PR 2)
… cannot carry

Adversarial review of the #272 train (docs/reviews/2026-10-11-dev-review-272.md), findings 1-6. A named or unparsed outline destination no longer resolves to page 0, so a toggle cannot delete a hyperref bookmark and export refuses it typed; the outline rewrite guard requires a valid GoTo destination the object factory can write; the XMP merge pre-scans element and attribute occurrences, substitutes attribute-form properties, declares the namespace on replaced and appended properties and refuses an ambiguous duplicate as xmp-unreadable; attachment add/remove refuses an inline specification with the new attachment/specification-not-addressable code instead of unlisting it; form values propagate to same-named fields as PDFFormManager::setFormFieldValue does, preferring a widget-bearing match; fromString validates the hash field (the empty run stays valid: that is what an in-memory document serializes) so a corrupted hash cannot decode.
…ayer cache and the signature release

Findings 7-14. composeCanvasImage draws each tile at its placed rect: the composed image's own device pixel ratio already maps viewport units to device pixels, so the previous *ratio double-counted HiDPI; extractPage refuses a copy-restricted document with PdfTool's own sentence; the cached empty text layout can no longer turn a no-text-layer refusal into an available empty selection; a refused signature-check completion releases the one-check flag when it is still the current check (a superseded completion cannot release a newer one); the annotation cap leaves 3500 of 4000 frame slots so the steering primitives always fit; the unreachable supersede branch is gone; the 18-id annotation command table is pinned by a literal list.
…ge readout

Findings 9, 16-19, 21. The Quick shell answers documentMailRequested with a composed mail-client hand-off and bookmarkImportRequested/bookmarkExportRequested with the file dialogs that answer the host invokables, so no command reports a hand-off nobody performs; the soft-proof state label binds softProofSummary as the property it is; the ink-coverage readout publishes only for a succeeded job and does not outlive its revision; Copy and image extraction join the copy-permission gate actionExportDocument already used, and copySelectedText refuses with the CLI sentence; the annotations tab button gets its siblings' wrapping content item in both QML copies. Tests: the bookmark round trip asserts the custom record and reads the invocation result; screenshot-overwrite refusal and the stale-readout fence are pinned.
…cabulary gaps

Findings 23-28. check_quick_localization.py and the MenuModel label agree again (actionExportDocument gets the ordinary switch arm) and the checker, its unit tests and the help-strings staleness check run whole-tree in ci.yml and check-change.py; the handler-coverage ratchet refuses to grow unless --accept-new-gaps records a deliberate gap with its reason; the identity checker scans .in/.ui/.qrc and the Wix installer template names the real LOOP artifacts; disposition and target are validated against the schema vocabulary in verify-command-catalog.py and the Linux twin; UnitTestsLoopStateVisual loads the compiled catalogs via its ctest environment; ADR-014 pins the shipped counts.
The finding set with per-item dispositions, the dismissed items (signature badge; the relative include, dismissed with build evidence), the deferred items (history re-verification on the interactive thread; the self-referential accessibility test), the verified-clean list and the change fragment with its evidence manifest.
The Quick shell policy forbids Qt.openUrlExternally in QML. The Send by
E-Mail receiver added in this train called it, which failed the quick
shell policy gate in CI. The host now builds the mailto URL (subject and
body carry the file name and revision) and opens it through
QDesktopServices. A client that does not take the message is refused with
mail/client-unavailable instead of reported as handed off.

The QML receivers are removed from both shells. The unit test captures the
mailto URL with a URL handler, so no mail client is launched.

Claude-Session: https://claude.ai/code/session_01WWpvxcKrZtceyLLgMhoZ3k
fix: apply the adversarial-review findings for the dev train promoted by #272
@mberrys mberrys changed the title Merge pull request #273 from studio-berry/fix/dev-review-272 [0.2.1] Operator Completion, Product Surface & Trust Leftovers Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants