Editions: editions/<id>/ holds edition.json (names, accent, asset prefix), app-icon.svg, app.ico and an optional logo.png. ./build/Build-Installer.ps1 -Edition <id> builds a fully branded companion, service and CLI (product, company and file description metadata; the client's mark on splash and About; the client's icon on window, taskbar, executable and Start menu; logo in Settings) and an MSI named <assetPrefix>-<version>-x64.msi whose product name, manufacturer and service display name are the client's and are forced over any stored labels.
Updates stay on the client's line: the companion's updater looks for <assetPrefix>-v<version>-x64.msi, so a client install is never offered the generic MSI (or another client's).
Every customer, every release: CI builds each edition's unsigned installer; the stable release workflow enumerates editions/, runs the sign chain once per edition and once for the generic build (matrix), re-verifies every signature in a new assemble job, and publishes all installers with one SHA256SUMS. Package-Release.ps1 includes every edition and refuses to drop one.
Shared across editions: application id, data paths, executable file names, install directory, protocol handler, MSI upgrade identity and signing identity, so editions and the generic build upgrade each other in place. Deployment branding (Install-Branded.ps1) still applies on top.
First edition: gg-forge (GG Forge Print Agent, Greater Goods, #424242, mark rendered from the server's advertised icon). Version 1.2.0. Docs: editions/README.md, BRANDING.md, RELEASING.md, CHANGELOG.
Verification
NativeCI solution: zero warnings. Qqq.PrintAgent.Cli.Tests: 352 passed. New EditionTests (Core) run on hosted CI.
Build-Installer.ps1 -Edition gg-forge built GgForgePrintAgent-1.1.1-x64.msi and GgForgePrintAgent-1.2.0-x64.msi locally; the Property table carries the forced GG Forge name and company, all three executables report GG Forge product/company/description, and the descriptor, mark, icon and logo ship in the payload. The script's own checks compare the published artwork hashes to the edition folder.
Gates: PSScriptAnalyzer (16 scripts, no findings), actionlint and zizmor on the workflows, source headers (300 files), packaging-input and branded-installer dry runs.
The release-workflow matrix and assemble job can only be exercised by a stable tag; the first v1.2.0 run is the acceptance for that path.
Substantial AI assistance was used for this change.
## Summary
- **Editions**: `editions/<id>/` holds `edition.json` (names, accent, asset prefix), `app-icon.svg`, `app.ico` and an optional `logo.png`. `./build/Build-Installer.ps1 -Edition <id>` builds a fully branded companion, service and CLI (product, company and file description metadata; the client's mark on splash and About; the client's icon on window, taskbar, executable and Start menu; logo in Settings) and an MSI named `<assetPrefix>-<version>-x64.msi` whose product name, manufacturer and service display name are the client's and are forced over any stored labels.
- **Updates stay on the client's line**: the companion's updater looks for `<assetPrefix>-v<version>-x64.msi`, so a client install is never offered the generic MSI (or another client's).
- **Every customer, every release**: CI builds each edition's unsigned installer; the stable release workflow enumerates `editions/`, runs the sign chain once per edition and once for the generic build (matrix), re-verifies every signature in a new `assemble` job, and publishes all installers with one `SHA256SUMS`. `Package-Release.ps1` includes every edition and refuses to drop one.
- Shared across editions: application id, data paths, executable file names, install directory, protocol handler, MSI upgrade identity and signing identity, so editions and the generic build upgrade each other in place. Deployment branding (`Install-Branded.ps1`) still applies on top.
- First edition: `gg-forge` (GG Forge Print Agent, Greater Goods, #424242, mark rendered from the server's advertised icon). Version 1.2.0. Docs: `editions/README.md`, BRANDING.md, RELEASING.md, CHANGELOG.
## Verification
- NativeCI solution: zero warnings. `Qqq.PrintAgent.Cli.Tests`: 352 passed. New `EditionTests` (Core) run on hosted CI.
- `Build-Installer.ps1 -Edition gg-forge` built `GgForgePrintAgent-1.1.1-x64.msi` and `GgForgePrintAgent-1.2.0-x64.msi` locally; the Property table carries the forced GG Forge name and company, all three executables report GG Forge product/company/description, and the descriptor, mark, icon and logo ship in the payload. The script's own checks compare the published artwork hashes to the edition folder.
- Gates: PSScriptAnalyzer (16 scripts, no findings), actionlint and zizmor on the workflows, source headers (300 files), packaging-input and branded-installer dry runs.
- The release-workflow matrix and assemble job can only be exercised by a stable tag; the first `v1.2.0` run is the acceptance for that path.
Substantial AI assistance was used for this change.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
A folder under editions/<id> (descriptor, SVG mark, ICO icon, optional logo) and
Build-Installer.ps1 -Edition <id> produce a fully branded build and installer: the
client's mark and icon on the splash, About, window, taskbar and Start menu; the
client's product, company and file description in every executable; the client's
product name, manufacturer and service display name in the MSI, forced over any
stored labels; the MSI and release asset named after the edition's asset prefix;
and the installed updater following that asset on the shared feed, so a client
install only ever sees its own edition.
CI builds every edition's unsigned installer. The stable release workflow lists
the editions, signs and publishes one installer per edition beside the generic
one through a matrix sign job and an assemble job that re-verifies every
signature and writes a single SHA256SUMS. Package-Release.ps1 includes every
edition. First edition: gg-forge (GG Forge Print Agent by Greater Goods).
Version 1.2.0. No wire change.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
RuntimeBranding.LoadMachine takes includeInstallerLabels; an edition build skips the
DisplayName/CompanyName the installer persisted so labels from an earlier generic install
cannot relabel the edition, while the machine branding file still applies on top. The
companion's log is typed through a new IAgentLog interface so the layout harness can
supply its per-user log; the harness answers get-server-trust and, with
QQQ_LAYOUT_CAPTURE_EDITION=1, captures the splash, window, Settings page and About
dialog with the build's real identity as evidence.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
An edition descriptor may set serverName (what user-facing text calls the server;
the generic build keeps QQQ), and aboutMascot/aboutLicense (default true) to leave
the Query mascot and License sections out of the About dialog while the header and
Open source components stay. The generic build is unchanged. About composition moves
to AboutContent so the window and the layout harness render the same dialog. The
gg-forge edition sets serverName to GG Forge and turns both sections off.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
The exchange carries the attempt's own nonce as expectedNonce when the grant policy
is REQUIRED or OPTIONAL, or when discovery advertises nativeSessionNonceValidation
and the grant states no policy; legacy servers keep the unchanged body and other
policy values are still refused before a browser opens. The decision is logged
without the nonce. build/Build-CiRelease.ps1 delegates to Package-Release.ps1 so
the shared Gitea .NET CI packages the generic and every edition installer through
the one packaging path. Docs, CHANGELOG and TODO updated.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
The content dialog's default width clipped the sign-in form. The dialog now allows
760 px, disables horizontal overflow, wraps the certificate checkbox label, folds the
long certificate explanation into a one-line caption with the detail on hover, groups
the form into Server, Sign in and New pairing steps with caption-style guidance, and
puts the sign-in actions on one row. The layout harness captures the dialog in
edition-capture mode.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
The device routes never report approval, so the service reads the server's
operator printer catalog after each accepted inventory and about once a minute
while connected, keeps the per-device approved set in memory (reset with the
connection), and the snapshot carries APPROVED / AWAITING_APPROVAL / NOT_SHARED
/ NOT_REPORTED (with the reason) / NOT_CHECKED per printer plus the last reading.
The Printers page shows it in a new Server column, the summary line and the
expanded details, named for the connected server. The session client admits the
catalog read (GET operator/printers) and nothing else from the operator API.
Also: a Clear logs button on the shared activity log viewer, the connection
dialog's result banner pinned above the scrolling form with a waiting state
during browser and password sign-in, and a service sign-in reply that no longer
repeats itself.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
Printing extension 2.4 (QBit 1.1.0, protocol 1.1.0): when discovery advertises a
stable 2.4-or-newer printing extension the HELLO carries printer-approval-v1 and
the server's PRINTERS frame (every printer in the latest accepted inventory with
its approval, pushed after each inventory and on every approval change) becomes
the authoritative per-printer server state; the operator catalog is not read on
such a server. Older servers keep the 2.3 catalog fallback and an unchanged wire.
The capability is re-evaluated on every socket, the reading is reset with the
connection, and the companion's tooltip says whether approval is pushed or polled.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
editions/<id>/holdsedition.json(names, accent, asset prefix),app-icon.svg,app.icoand an optionallogo.png../build/Build-Installer.ps1 -Edition <id>builds a fully branded companion, service and CLI (product, company and file description metadata; the client's mark on splash and About; the client's icon on window, taskbar, executable and Start menu; logo in Settings) and an MSI named<assetPrefix>-<version>-x64.msiwhose product name, manufacturer and service display name are the client's and are forced over any stored labels.<assetPrefix>-v<version>-x64.msi, so a client install is never offered the generic MSI (or another client's).editions/, runs the sign chain once per edition and once for the generic build (matrix), re-verifies every signature in a newassemblejob, and publishes all installers with oneSHA256SUMS.Package-Release.ps1includes every edition and refuses to drop one.Install-Branded.ps1) still applies on top.gg-forge(GG Forge Print Agent, Greater Goods, #424242, mark rendered from the server's advertised icon). Version 1.2.0. Docs:editions/README.md, BRANDING.md, RELEASING.md, CHANGELOG.Verification
Qqq.PrintAgent.Cli.Tests: 352 passed. NewEditionTests(Core) run on hosted CI.Build-Installer.ps1 -Edition gg-forgebuiltGgForgePrintAgent-1.1.1-x64.msiandGgForgePrintAgent-1.2.0-x64.msilocally; the Property table carries the forced GG Forge name and company, all three executables report GG Forge product/company/description, and the descriptor, mark, icon and logo ship in the payload. The script's own checks compare the published artwork hashes to the edition folder.v1.2.0run is the acceptance for that path.Substantial AI assistance was used for this change.
🤖 Generated with Claude Code