Repository navigation
fix: apply the adversarial-review findings for the dev train promoted by #272 - #273
Merged
Merged
Conversation
… 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
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.
fix: apply the adversarial-review findings for the dev train promoted by #272
What changed
An adversarial review of the dev changeset promoted by #272 (five independent reviewers, one
subsystem slice each, findings verified against the source before classification) produced 31
findings; this PR fixes the accepted ones with regression tests, dismisses two with evidence and
defers two with reasons. The full labelled finding set lives in
docs/reviews/2026-10-11-dev-review-272.md; the change fragment for this branch ischanges/fix-dev-review-272.md.devis not the default branch; this PR closes no issue.Bug fixes and changes
resolveDestinationPagerefuses a named or unparsed destination before the reference/index lookup; toggle no longer matches them, export refuses them withbookmark/unsupported-outline-itemnamedDestinationOutlineIsNeverAPageTargetrdf:Description, and never replace attribute-form properties, all while reporting success (2)metadata/xmp-unreadablemetadataReplacesAPropertyCarriedInASecondDescription,metadataDeclaresAppendedPropertyNamespaces,metadataSubstitutesAttributeFormProperties,metadataRefusesAmbiguousPropertyOccurrencesattachment/specification-not-addressableinstead of dropping the entryattachmentInlineSpecificationsAreRefusedNotDroppedPDFFormManager::setFormFieldValuedoes; a widget-bearing match is preferred over a widget-less parentsameNamedFormFieldsAreUpdatedTogether,widgetlessFormFieldMatchFallsBackToTheWritableTwin/Dest nullstate loss) (5)unparsedDestinationOutlineRefusesAnOutlineRewritePDFRevisionIdentity::fromStringaccepted a corrupted hash as a valid identity (6)toString()produces for an in-memory document, and rejecting it broke every post-edit revision round trip)revisionIdentityRoundTripsThroughItsStringForm(corrupted shapes rejected, empty-hash identity round-trips)canvasCompositionDrawsPlacedTilesInViewportUnitsOnHiDpi(ratio 2.0)PdfToolrefuses the same operation (8)extractPagerefuses with the CLI's own sentence; the host gatesactionExtractImageon the copy permission at the UI seamimageExtractionRefusesWhenCopyContentIsDenied(real encrypted fixture, user vs owner authorization)copySelectedTextrefuses before the clipboard;actionCopyTextjoins the export command's permission gateUnitTestsEditorHostselectionSourceRefusesAPageWithNoTextLayerOnEveryCallm_signatureCheckRunning, leaving a signed document "not-signed" and every later check refused (11)refusedSignatureCheckCompletionReleasesTheRunningCheck,supersededSignatureCheckCompletionDoesNotReleaseTheNewerCheckmaxAnnotationPrimitives1000 to 500: findings plus annotations can never consume more than 3500 of 4000 slotsfindingAndAnnotationCapsLeaveHeadroomForSteeringPrimitivescatalogFamilyMatchesTheLiteralCommandListUnitTestsEditorHostroot.host.softProofSummary)Succeededpublishes; the readout is dropped on revision changeinkCoverageRefusesSupersededJobsAndDropsTheReadoutOnRevisionChangecheck_quick_localization.pywas red on its own dev tip:actionExportDocumentused a label form the checker rejects (23)actionPrint; the extraction is factored and coveredtest_check_quick_localization.py(+3 cases, pre-fix replay of the missing set)ci.ymland the whole-tree guards incheck-change.py--accept-new-gaps <reason>records a deliberate gap; the reason is stored beside the idstest_command_catalog.py(growth refused, opt-in recorded, shrink).in/.ui/.qrcare scanned; the Wix installer template names the realLOOPartifactstest_check_loop_identity.py(a legacy token in a.infile is a finding)verify-command-catalog.pyvalidates disposition and target against the schema vocabulary; the Linux twin mirrors ittest_command_catalog.py(typo'd disposition and bad target are RED)bookmarkImportExportRoundTripsAndRefusesAStaleRevisionscreenshotCaptureRefusesToOverwriteAnExistingFileUnitTestsLoopStateVisualgetsLOOP_TRANSLATION_TEST_DIRECTORYfrom the ctest registrationDismissed and deferred (recorded in the review report)
bytes by documented contract; the operator-visible defect was the wedge (11), fixed.
../../include ofshellglossary.hinloopstatevisual.cpp(22). Therelative include is load-bearing:
UnitTestsLoopStateVisualcompiles the file directly withoutLoopLibInteraction/sourceson its include path, so the plain include fails the build(reproduced).
accessible-readout test (32). Both need structure work (the worker pattern; the host
composition extracted), tracked in the review report.
Proof
python scripts/agent/check-change.py --base origin/dev --head-branch fix/dev-review-272 --build-dir C:/.dev/repos/loop-build-fixdevreports pass at the head commit,6d5eebd8: 127/127 checks,modules
build_policy,core,documentation,interaction,quick, riskhigh.LoopLibInteraction, LoopLibQuick, PdfTool, ProductQuickAccessibilitySmoke, UnitTests) and every
mapped suite ran green: 73 tests, including the ten-suite sweep that caught two integration
defects before the gate run (the reported relative-include premise, disproved by the build; and
the
fromStringempty-hash round trip, which broke every post-edit revision and is pinned by anew case).
translation units.
governance registers (and its negative cases), command catalog and its cases, Quick localization
and its cases, help-strings staleness, QML mirror parity, search budget, independent validation
gate, architecture contracts, Windows Qt test runtime, changelog.
UnitTestsEditorHost84/84,UnitTestsDirectEdit20/20.Not run locally: the pwsh/WSL-dependent lanes (Windows installer, Flatpak, release gate) and the
packaging lanes, which run in CI; no release-profile build on this host.
Quality pass
The diff carries no change-narrating comments, no weakened assertions, and no suppressions. Two
comments are kept on purpose: the replaced-XMP-element namespace rationale (the binding the
replaced element carried is why the declaration exists) and the release condition on the
signature-check flag (why a superseded completion must not clear a newer check). Formatting is
clang-format clean on every touched file; whole-file reformat churn is zero.
Security and rollback
The copy-permission refusals close a real gap (extraction and clipboard bypassed
CopyContentwhile the CLI refused the identical operations). No new unsafe construct; the XMP merge parses at
the boundary and refuses shapes it cannot merge. Rollback: revert this branch. No persisted format
changed; the only interface movement is one additive
DirectEditRefusalenum value(
attachment/specification-not-addressable, appended afterWriteRefusedso existing values keeptheir numbers), named with the
LoopLibCore/sources/**andUnitTests/CMakeLists.txtprotected-path edits in the change fragment.
Docs
docs/reviews/2026-10-11-dev-review-272.md(the finding set, dismissals, verified-clean list),changes/fix-dev-review-272.md+.evidence.yaml, and the corrected ADR-014.Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.