Skip to content

feat(quick): handler-coverage gate and read-only surfaces (#231 PR 1) - #267

Merged
mberrys merged 16 commits into
devfrom
gh-231-quick-coverage
Oct 11, 2026
Merged

mberrys merged 16 commits into
devfrom
gh-231-quick-coverage

Conversation

@mberrys

@mberrys mberrys commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Epic #231 restores Quick GUI coverage for the retired Widgets surfaces in three PRs. This is PR 1: the contract-level handler-coverage gate plus the read-only and presentation surfaces (measure, select/copy/capture, soft proof and separations, settings and diagnostics, and the display half of the production colour modes). PR 2 brings the direct mutations and PR 3 the governed Fix and publication operations; both stay on this branch as their base.

The gate is docs/shell-handler-coverage.json plus the handler-coverage ratchet in scripts/verify-command-catalog.py. The descriptor availability flag is the handler gate CommandCatalog enforces, so the ratchet reads availability rather than string mentions: every KEEP or ABSORB action must be implemented or recorded, the recorded set must match exactly (a new unbound action fails, and so does a baseline entry kept after its handler lands), and --record-ratchet rewrites the baseline from the contract. The first run recorded 70 gaps on dev at b08e218; this branch ratchets the baseline to 51 and it can only move down. Negative-case tests in scripts/ci/test_command_catalog.py prove each rule can fail; the check runs as its own step in ci.yml's source_integrity job and as whole-tree evidence in scripts/agent/check-change.py, and docs/LOOP_SHELL_CONTRACT.md documents it.

The surfaces on top: Measure and text selection join the closed InteractionTool vocabulary as their own interaction kinds, producing a revision-fenced Measurement in production units that never becomes a DragSession and a revision-fenced TextSelection resolved through Core's text layout in reading order, with nine bound commands, screenshot and image extraction through the atomic writer, and a text/no-text-layer refusal instead of an empty copy (#238, #232). The five actionColor* display modes are presentation-only filters over admitted surfaces, converted with Core's own converter, marked as simulations by the fidelity indicator and never reachable as production evidence; soft proof renders through a local LittleCMS proofing CMS behind its own surface identity with profile-missing and profile-invalid as visible states; plates carry show/hide/isolate state over Core's separations with a real ink-coverage readout (#235 display half, #237). The shell gains one typed settings store with confirmed reset-to-factory, the settings/render-option/diagnostics commands developer-gated, the ADR-006 bundle from Core's collector, the five KEEP render options reclassified ADVANCED with rationale, and manifest-driven menu routing (#239). The absorbed signature-plugin and scanner-plugin product-surface rows keep their reviewed null follow-ups (both have delivered replacement surfaces, which the epic's acceptance permits) so they stay in parity with the G00 source register; re-homing them would amend reviewed governance data and is left to the register owner.

Validation: check-change.py --base origin/dev passed at the head SHA — 112/112 checks across build_policy, documentation, interaction and quick, with the 24 mapped suites built and run in the worktree build, clang-tidy over every changed translation unit, and the catalog/ratchet/mirror-parity/source-integrity/architecture/product-surface checks green. The three surface workstreams each ran their focused suites green before integration and reported their evidence. One EditorHost ctest lane flaked during two earlier gate attempts; both modes were root-caused to the environment rather than the diff — a leftover recipe in the shared QStandardPaths qttest directory written by a concurrently running worktree session (proven by a deterministic repro: clean directory 54/54 pass, one leftover file reproduces the exact failure) and, separately, an empty-output non-zero exit with no crash report, the signature of an external process kill while another session frees build file locks; both are recorded as #265. CI runners are clean, so the hosted gate is unaffected. 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.

Quality review: the slices reuse Core's own text layout, colour conversion, CMS and diagnostics machinery rather than inventing parallel semantics; the display filter is confined to the scene-graph upload path so it cannot leak into a surface identity; and every unfinished piece (isolated-plate pixels and the reference-separation comparison for #237, keyboard-only measurement placement for #238, and actionRendering_Errors/actionMagnifier/actionGetSource staying declared for #239) is reported as a gap rather than dressed up as done.

Advances #231 — the contract gate and the first surface group are in place; the epic closes when the ratchet reaches zero in PR 3.
Advances #232 — text selection, reading-order copy, capture and extraction are bound and tested, including the host-level refusal to overwrite an existing file; the screenshot write shares that same atomic writer and policy.
Advances #238 — the measure tool, production units and revision fencing are tested; keyboard-only placement of a measurement is not implemented.
Advances #237 — soft proof, plate state and the ink readout are real; isolated-plate pixels and the reference-separation comparison remain open.
Advances #239 — all four acceptance bullets are covered by tests; actionRendering_Errors, actionMagnifier and actionGetSource stay declared pending their layers.
Advances #235 — the display colour modes are complete; bleed fixup, bitonal document creation and render-to-images belong to PR 3.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

#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.
Keep both sides of the overlay, editor host, and test additions: the ink
coverage and signature-check finish jobs, the display-mode and soft-proof
properties alongside the signature properties, and the measurement and text
selection tests alongside the annotation tests. The overlay primitive emitter
keeps the label and page-polygon parameters from this branch and takes the
annotation selection flag from dev as a trailing parameter; the annotation
call site now passes empty label and polygon values.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqo8EDq3w2nU1zekEZDoZc
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqo8EDq3w2nU1zekEZDoZc
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
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 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
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
@mberrys
mberrys merged commit 4e58124 into dev Oct 11, 2026
15 checks passed
@mberrys
mberrys deleted the gh-231-quick-coverage branch October 12, 2026 19:01
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