Skip to content

Session 07: promote package-proof to stable - #527

Merged
mberrys merged 48 commits into
stablefrom
cdx/session-07-package-proof
Sep 4, 2026
Merged

Session 07: promote package-proof to stable#527
mberrys merged 48 commits into
stablefrom
cdx/session-07-package-proof

Conversation

@mberrys

@mberrys mberrys commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes Linux AppImage run 33702376042 (cb2a0a6c): Linux Deploy Qt passed LoopLibQuickplugin staging but failed post-check because libqtquickcontrols2plugin.so was absent under install/usr/lib/qml.

-qmldir (from 5102a008) does not bundle QtQuick.Controls for qrc-resident editor QML. This change explicitly copies the module from the runner Qt install, same pattern as QSQLITE.

Fix

After linuxdeployqt, copy $QT_ROOT_DIR/qml/QtQuick/Controlsinstall/usr/lib/qml/QtQuick/Controls (includes libqtquickcontrols2plugin.so and style submodules). Fail if source module or plugin is missing, or if staged tree still lacks the plugin after copy.

Preserved

LoopLibQuickplugin build target, Linux QSQLITE, authenticated downloads, Windows packaging, identity contract (no loupe-pdf). -qmldir flags remain but are not relied on for Controls.

Verification

  • python3 -m unittest scripts.ci.test_workflow_contracts -v — pass

Next step

Re-dispatch Linux_AppImage with full tip SHA when ready. Merge on hold.

Open in Web Open in Cursor 

mberrys and others added 13 commits September 2, 2026 01:34
vcpkg's gperf port requires autoconf, autoconf-archive, automake, and
libtool (libtoolize) from the system package manager. Without them the
Linux_AppImage workflow fails at 'VCPKG: Install Project Dependencies'
when building fontconfig's gperf dependency.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
The version and capabilities probes run before $pdfTool was assigned,
causing Windows_MSI to fail at Validate staged install tree under
Set-StrictMode. Move the Join-Path assignment above the first use.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
@mberrys
mberrys marked this pull request as ready for review September 2, 2026 17:12
@mberrys
mberrys enabled auto-merge September 2, 2026 17:12
mberrys and others added 4 commits September 2, 2026 10:14
…age-proof

# Conflicts:
#	.github/workflows/LinuxInstall.yml
Combine Linux AppImage workflow deps: keep autoconf/automake/libtool for
gperf/vcpkg (#523) and libfontconfig1-dev from stable. Retain stable's
VCPKG_BINARY_SOURCES cache wiring.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
…dio-berry/loop into cdx/session-07-package-proof

Co-authored-by: michael berry <mberrys@users.noreply.github.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 59d0daa2f8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vcpkg.json Outdated
cursoragent and others added 7 commits September 2, 2026 17:20
Remove continue-on-error from the Sentry debug-file upload steps in
WindowsInstall.yml and reusable-windows.yml. Project targeting already
resolves the numeric id from LOOP_SENTRY_DSN; a 404 must fail the job
instead of producing a green MSI with a skipped upload.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
Install autoconf, autoconf-archive, automake, and libtool on reusable-linux
and fuzz apt steps so vcpkg can build gperf via fontconfig, matching
LinuxInstall.yml (#523).

Fixes Release Gate linux/build and fuzz_regression failures on PR #527.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
Stage libqoffscreen.so (and optional libqminimal.so) under
install/usr/lib/platforms and write install/usr/bin/qt.conf before
linuxdeployqt so PdfTool preflight works when the smoke harness sets
QT_QPA_PLATFORM=offscreen and strips developer Qt paths.

Matches the Windows MSI pattern of a self-describing staged tree and
the prior Session 07 offscreen staging work that did not land on this
branch.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
Copy the built Loop.Canvas QML module into the staged install tree,
bundle qoffscreen.dll for QT_QPA_PLATFORM=offscreen, and set
Qml2Imports=qml;. so LoopEditor --quick-smoke resolves packaged QML
without developer Qt paths.

Also stage Loop.Canvas in Linux_AppImage for coherent cross-platform
packaging.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
Replace Linux heredoc and Windows here-string qt.conf writers with
printf and a PowerShell string array so [Paths] starts at column 0.
Indented workflow bodies were emitting invalid qt.conf and nullifying
offscreen/plugin path staging (#508).

Co-authored-by: michael berry <mberrys@users.noreply.github.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 24ad77d102

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/WindowsInstall.yml Outdated
Comment thread changes/cursor-fix-linux-appimage-gperf-5319.md Outdated
cursoragent and others added 2 commits September 2, 2026 19:14
Stage libqsqlite.so into install/usr/lib/sqldrivers before linuxdeployqt
strips optional SQL drivers from the ephemeral Qt tree. PdfTool preflight
needs QSQLITE for operation-history writes during AppImage smoke.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
Stage qsqlite.dll from the Qt install into install/usr/bin/sqldrivers
before packaged smoke runs with developer Qt env vars stripped. Mirrors
the Linux libqsqlite.so staging added in 88ce56b and fails the job if
the driver is missing.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
cursoragent and others added 22 commits September 2, 2026 20:02
LoopEditor now prepends the executable directory to Qt library paths
before QGuiApplication loads QPA plugins, matching PdfTool's packaged-
smoke fix. WindowsInstall stages LoopEditorQuickplugin and
LoopLibQuickplugin beside the copied Loop.Quick and Loop.Canvas modules,
extends windeployqt QML scan paths to the built modules, and writes
qt.conf with a literal Qml2Imports=qml;. line (hosted PowerShell was
dropping the ';.' suffix from the prior array writer per run 33669439530
evidence).

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
Linux packaging uses download_verified.sh --gh-asset for appimagetool,
type2-runtime, and linuxdeployqt. Hosted runners can return HTTP 403 for
unauthenticated Releases API asset fetches (run 33687722897). Mirror the
existing PowerShell helper: send Authorization: Bearer GITHUB_TOKEN when
the job token is available and retry transient curl failures.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
…ML plugin DLL staging

Linux AppImage smoke failed because linuxdeployqt never scanned LoopEditor
QML imports, so qtquickcontrols2plugin was absent from the packaged tree.
Pass -qmldir for the editor and built Loop modules and fail deploy if the
Controls plugin is still missing.

Windows MSI deploy failed because Copy-BuiltQmlPlugin required
LoopEditorQuickplugin.dll and LoopLibQuickplugin.dll artifacts that the
static LoopEditorQuick build never produces. Remove that staging step;
windeployqt and the existing LoopLibQuick.dll install path already cover
the runtime closure.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
…outputs

LoopEditorQuick is a static qt_add_qml_module linked into LoopEditor, so there
is no LoopEditorQuickplugin.dll to copy. LoopLibQuick is SHARED and emits
LoopLibQuickplugin next to its qmldir under build/LoopLibQuick/Loop/Canvas.

Windows deploy now asserts the built Loop.Quick and Loop.Canvas module trees
include qmldir and qmltypes, then copies LoopLibQuickplugin.dll from the
CMake output directory into qml/Loop/Canvas and the install bin. Linux staging
verifies libLoopLibQuickplugin.so and the module metadata before deploy.

Keeps Linux -qmldir scanning for qtquickcontrols2plugin.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
LoopLibQuickplugin is a separate Qt QML MODULE target that CMake emits at
build/LoopLibQuick/Loop/Canvas/libLoopLibQuickplugin.so, but it is not in the
LoopEditor link closure. Package CI only built LoopEditor and friends, so the
6c7a16e pre-deploy assert failed even though the module metadata existed.

Add LoopLibQuickplugin to the Linux and Windows package build targets and
discover the plugin with find before staging so versioned .so names still
resolve.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
linuxdeployqt -qmldir does not bundle libqtquickcontrols2plugin.so for
qrc-resident LoopEditor QML imports. After deploy, copy the QtQuick/Controls
module tree from QT_ROOT_DIR into install/usr/lib/qml and fail if the plugin
is still missing, matching the manual QSQLITE staging pattern.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
Both packaging jobs failed before reaching packaging, from one change:
moving the Qt closure from an explicit install(DIRECTORY ... REGEX) block
to qt_generate_deploy_qml_app_script left three gates asserting on the old
manifest-tracked layout.

Windows died in cmake --install, before WiX. MSVC_REDISTRIBUTABLES_PATH is
exported as a native Windows path and was embedded verbatim into the
generated cmake_install.cmake, where "C:\Program Files\..." raises
"Invalid character escape '\P'". Normalise it with file(TO_CMAKE_PATH),
matching the get_filename_component() branch that already normalised.

Linux died in verify-loop-surface, before AppImage. Two causes: the scan
root had been narrowed to install/usr/bin, but on Linux the product
libraries install to usr/lib -- only Windows collapses lib onto bin -- so
LoopLibCore and LoopLibQuick could not be found; and the install-manifest
comparison rejected qt.conf, which the deploy script writes outside
install() and which therefore can never appear in install_manifest.txt.

Hold only first-party payload to install_manifest.txt and let the deployer
own the Qt closure. The missing-file direction stays strict, so a
first-party artifact that skips install() still fails closed.

The widgets-free contract no longer has a source regex to inspect. Enforce
it against the real deployed tree instead, via the scan_install_tree path
the verifier already implements, wired into both packaging jobs.

Restore LOOP_INSTALL_QT_DEPENDENCIES as the switch for shipping a Qt
closure: it had become orphaned, so the Flatpak, Docker, fuzz and reusable
Linux builds that configure it OFF were getting a bundled Qt anyway. Make
it explicit in the two packaging workflows now that it is load-bearing.

Also correct the changelog Breaking-Change field to the required yes/no
spelling, and the target count that this change's retirement of
LoopEditorQuick moved from 70 to 69.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UwdQdPY7TZ1fQ1cvpNrpaq
Six test targets still linked LoopEditorQuick, which this change folded
into the LoopEditor executable. CMake treats an unknown target name in
target_link_libraries as a raw -l flag, so configure kept succeeding and
the breakage stayed invisible until the widgets-free gate stopped failing
first and the build actually reached these targets:

  tst_quickdocumentmodeltest.cpp:2:10: fatal error: quickdocumentmodel.h:
  No such file or directory

LoopEditorQuick had carried its source directory as a PUBLIC include, so
retiring it took the LoopEditor headers off every dependent's include path.

Compile the LoopEditor sources each test needs directly into it and add the
LoopEditor include root, matching how ProductQuickAccessibilitySmoke
already reaches the same sources now that no library exports them.

Each target gets only what its test includes, so the deliberately minimal
link lines are preserved: UnitTestsDocumentViewSession keeps Core/Gui/Test
with no QML engine, per architecture invariant I24. UnitTestsQuickAccessibility
also drops LoopEditorQuickplugin and LoopEditorQuickplugin_init, retired
with the library.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UwdQdPY7TZ1fQ1cvpNrpaq
Packaging only ran on workflow_dispatch with a hand-supplied source_sha, so
no pull request ever proved the MSI or the AppImage. A one-character CMake
path bug reached this branch and survived three days of green push CI
because nothing automatic ever ran cmake --install on Windows.

Add a pull_request trigger to both packaging workflows, resolving the
checkout to the dispatch input or the PR head. The exact-SHA verification is
unchanged and still runs on both paths: a PR head SHA is a full 40-character
SHA, so the integrity check holds. Narrowing this with a paths: filter is a
one-line edit if the runner cost bites.

Also defuse three assertions that report ordinary work as a contract breach:

- The packaging build command was pinned as an exact string, -j6 and target
  order included. Pin the target list instead, which is the actual contract.
- counts["targets"] was pinned to a literal. It is the size of an inventory
  that grows with ordinary development, and validate_contract() already
  checks what the evidence means, so drop it. The plugin ledger and the UI
  form ledger are finite, so keep those but assert the set and the ledger
  length rather than a hand-maintained number: a thirteenth plugin now names
  itself instead of arriving as "13 != 12".
- The supply-chain policy required a linuxdeployqt pin while the workflow
  contract forbade the workflows from mentioning the tool. It is no longer
  used, so drop the pin and its required-tools entry.

ubuntu-22.04 stays pinned deliberately: it sets the AppImage glibc floor.
It now carries a comment saying so, so retiring it reads as the decision it
is rather than an assertion to satisfy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UwdQdPY7TZ1fQ1cvpNrpaq
The AppImage job now reaches packaging and installs cleanly, and fails on
the next thing: forty-odd Qt plugins reported as Loop artifacts.

  product-surface: unmanifested first-party artifact:
    usr/lib/qml/QtQuick/Controls/libqtquickcontrols2plugin.so

Qt names its plugins lib<something>plugin.so, and the first-party glob
lib*Plugin* is matched case-insensitively, so it claims every one of them.
What used to stop that is ignored_artifact_paths, but its patterns describe
a flat layout (qml/*, platforms/*) while the deploy script stages modules at
usr/lib/qml/<Module>/... and plugins/<type>/..., which those patterns never
match. Under linuxdeployqt none of this was in the tree at verification time,
so the mismatch could not show.

Name the deployed layout: usr/lib/qml/* and plugins/*. Loop's own plugins
install to usr/lib/loop and usr/lib/pdfplugins, so neither pattern reaches
them, and a regression test pins that in both directions.

This narrows only the first-party classification. Detection of forbidden
installed artifacts runs against every file in the tree and is not gated by
it, so the guarantee that a retired plugin cannot ship is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UwdQdPY7TZ1fQ1cvpNrpaq
Five of the six retargeted tests build. UnitTestsShellKeyboard does not:

  LoopEditor/editorhost.cpp:30:
  LoopLibQuick/sources/loopcanvasitem.h:38:10:
  fatal error: QQuickItem: No such file or directory

editorhost.cpp includes loopcanvasitem.h, which includes <QQuickItem>, and
this target's link line names Qt6::Qml but not Qt6::Quick. It compiled before
because the retired LoopEditorQuick linked Qt6::Quick PUBLIC and passed it
down; LoopLibQuick does not. Naming it restores exactly the environment the
target already had, and matches UnitTestsEditorHost, which compiles the same
sources and builds.

Qt6::Widgets stays absent, which is the invariant this file is protecting:
I21 for the interaction tests and I25 for UnitTestsQuickCanvas both turn on
the missing Widgets link, not on Quick.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UwdQdPY7TZ1fQ1cvpNrpaq
The AppImage job now builds, installs and clears both verification gates,
and fails one step later:

  scripts/smoke-test-install.ps1:80
  (Join-Path $env:ProgramFiles "share\loop\profiles")
  Cannot bind argument to parameter 'Path' because it is null.

This step is new here; stable never ran smoke-test-install.ps1 on Linux.
The script is Windows-shaped throughout -- $env:ProgramFiles, backslash path
literals that become literal filenames on Linux, .exe and .dll suffixes at ten
call sites, and an OCR sidecar block that invokes a .cmd mock with no Linux
equivalent. Making it portable is real work with judgement calls in it, not a
null guard, and it is not what this change is for.

Remove the step and leave Linux where stable had it. Windows keeps its own
relocated-install smoke unchanged, and Linux keeps the AppImage smoke test
that runs against the packaged artifact.

Cross-platform smoke coverage for the relocated Linux tree stays worth having
and is filed as follow-up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UwdQdPY7TZ1fQ1cvpNrpaq
…yout

Three CI failures, two root causes, verified against run 33808874249/33808874196:

1. Windows MSB3191 + Linux QML No-such-file (one root):
   qt_add_qml_module QML_FILES used ../LoopEditor/qml/*.qml. The '..'
   makes Qt derive qmlcache dir ProductQuickAccessibilitySmoke_.._LoopEditor,
   which MSBuild rejects (MSB3191), and misplaces the embedded resource as
   qrc:/qt/qml/Loop/LoopEditor/qml/Main.qml so loadFromModule Loop.Quick/Main
   exits 2 on Linux. Fix: mirror the six QML files under
   ProductQuickAccessibilitySmoke/qml/ (byte-identical, LoopEditor/qml stays
   the single source of truth) and list them without '..'. Proven locally
   with Qt 6.11.1/MinGW: fixed layout configures, builds with a clean
   ProofSmoke_qml qmlcache dir, and loadFromModule resolves LOAD_OK;
   the '..' layout fails Qt file staging on Windows by construction.

2. Windows relocated/staged/MSI smoke Could not locate loop-default.json:
   Resolve-ProfilesDir assumed one depth and its comment named a deleted
   plugin owner. Fix: bounded upward walk (InstallDir + 3 ancestors x
   share/usr-share) covering staged bindir, relocated bindir and MSI
   install-root layouts, keeping the legacy ProgramFiles fallback; comment
   now names PdfTool as the install owner. The smoke also accepts an
   install root for binaries (MSI .../LOOP -> .../LOOP/usr/bin) via a
   resolved binDir used by every binary-relative path.

3. Relocated-tree InstallDir pointed at a phantom level:
   Copy-Item to a nonexistent destination copies contents directly, so the
   tree lands at relocated/usr/bin, not relocated/install/usr/bin.
   The workflow now passes the real path.

Out of scope, intentionally untouched: heat already harvests the whole
build/install tree (profiles reach the MSI); MSIX/ZIP pack only usr/bin
because AppxManifest.xml must sit at the package root and MSIX is not
Session 07 gate evidence per docs/PLATFORM_SUPPORT.md.
@mberrys
mberrys merged commit 0209757 into stable Sep 4, 2026
17 of 19 checks passed
@mberrys
mberrys deleted the cdx/session-07-package-proof branch September 4, 2026 05:56
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.

3 participants