Captures session state at the natural pause point: design spec approved, no implementation started. Supersedes the scaffold-only handoff (which described an empty-dir state). Validated 80/100 by validate_handoff.py. The 4 file-not-found warnings reference scripts/* that are intentionally unwritten — they are the next session's deliverables per the spec. Resume next session with writing-plans skill against the spec at sethlabels-docs/specs/2026-04-29-packaging-design.md.
19 KiB
Handoff: Packaging design spec approved — implementation plan pending
Session Metadata
- Created: 2026-04-29 09:55:34 UTC
- Project: /home/claude/bin/sethLabels
- Branch: main
- Session duration: ~1 hour
- Live URL: https://git.sethpc.xyz/Seth/sethLabels
Recent Commits (for context)
d3e14c6docs: refresh CLAUDE.md + IDEA.md for design-approved phase8e272c0docs: add packaging design spec + decision log9dc6776chore: add sethLabels deployment-fork scaffoldd1ee78eFix incorrect font sizes (#321) ← upstream HEAD06675f8Create template style guide and minor updates… ← upstream
The first three commits are sethLabels'; everything from d1ee78e back is upstream's history (preserved untouched per strict-zero policy — see Important Context below).
Handoff Chain
- Continues from: 2026-04-29-125823-scaffold-only.md
- Previous title: sethLabels scaffolded — no code yet
- Supersedes: None. The scaffold-only handoff documented an empty-dir state; this one documents the post-design-approval state.
Review the previous handoff for full context before filling this one. The "scaffold-only" predecessor's "Suggested Next Steps" #1 (decide upstream-tracking strategy) and #5 (initialize git + push to Gitea) are now complete; #2 (verify upstream alive) and #3 (confirm Qt version) are also resolved during this session.
Current State Summary
This session moved sethLabels from "scaffold-only, no upstream pulled" to "design-approved, ready for implementation plan." Three things happened in order: (1) upstream j-evins/glabels-qt was cloned in-place and pushed to Gitea as main, with all 24 upstream contributors' commit authorship preserved end-to-end; (2) the brainstorming skill ran a 6-question multiple-choice round to lock all packaging-strategy decisions; (3) the resulting design was written into a 398-line spec at sethlabels-docs/specs/2026-04-29-packaging-design.md and committed. No scripts/ or packaging/ code was written — that's the next session's job. The spec is the load-bearing artifact; everything else (CLAUDE.md, IDEA.md, DECISIONS.md) was updated to point at it.
The user explicitly chose option (3) pause here at session close, declining to invoke writing-plans this session. The spec is sufficient to resume cleanly; there are no in-progress edits or half-implemented states.
Architecture Overview
sethLabels is a deployment fork, not a real fork. The discipline (locked as Invariant I1 in the spec) is strict zero source patches — no upstream-tracked file is ever edited. The only exception is .gitignore (allowlisted as a one-time scaffold-time touch). Every sethLabels addition lives in NEW files in NEW top-level directories:
- Already created:
CLAUDE.md,IDEA.md,DECISIONS.md,GITEA_API.md(gitignored symlink),.claude/handoffs/,sethlabels-docs/specs/ - Planned per spec:
scripts/(build-deb, build-appimages, compute-version, check-no-upstream-edits, lib/),packaging/(deb-metadata.env, appimage-recipe.env, changelog.md),README.sethlabels.md
The "fork" is therefore best thought of as a packaging repo with upstream's history embedded — anyone can audit it instantly and confirm we changed nothing in the application itself.
Upstream details discovered during the session:
- Qt version: Qt6 6.2 (find_package at
CMakeLists.txt:119) - CPack metadata: pre-wired at
CMakeLists.txt:84-104; generators NOT pinned →cpack -G DEBandcpack -G DragNDropselectable at command line with zero source change - Install rules: clean and FHS-compliant across all subdirs (
bin,share/icons/hicolor,share/applications,share/mime,share/metainfo,share/man/man1,share/glabels-qt/{templates,translations}) - Desktop integration:
.desktopfile, AppStream metainfo, MIME XML, hicolor icon set already provided by upstream - Two binaries shipped:
glabels-qt(GUI) +glabels-batch-qt(CLI for scripted/mail-merge use) - CI matrix exists for ubuntu-latest, ubuntu-22.04, windows-latest, macos-latest — but upstream doesn't publish binary releases ("Currently there are no self-hosted binary snapshot releases available" — upstream README). sethLabels exists to fill this gap.
Critical Files
| File | Purpose | Relevance |
|---|---|---|
sethlabels-docs/specs/2026-04-29-packaging-design.md |
THE design spec — 13 sections covering invariants, decisions, repo layout, build pipeline, release flow, brew tap, failure modes, smoke tests, glossary | Must read before any implementation. All design questions are answered here; do not re-derive. |
DECISIONS.md |
Short-form decision log (6 settled choices + 13 rejected/deferred items) | Quick scan to check what's already been decided/rejected |
CLAUDE.md |
Durable project instructions (refreshed this session) | Loaded on every session start; points at this handoff + the spec |
IDEA.md |
Plain-language project brief (refreshed this session) | Read if scope feels unclear |
CMakeLists.txt (upstream root) |
Defines Qt6 dep, CPack metadata, install rules | Read-only — strict-zero forbids edits. Reference for understanding what cpack and linuxdeploy will see. |
.github/workflows/build-tests.yml (upstream) |
Upstream CI multi-platform build matrix | Reference for valid apt install deps + cmake invocation patterns when writing scripts/lib/deps-debian.sh |
docs/BUILD-INSTRUCTIONS-LINUX.md (upstream) |
Upstream's manual build instructions | Reference for the apt install list + cmake flags; our scripts/build-deb.sh automates this |
Key Patterns Discovered
- Authorship preservation: the gitea repo holds all 24 upstream contributors' commit authorship intact; the only Seth-authored commits are the sethLabels-specific additions (scaffold + spec + context-refresh). Verifiable via
git log --format='%an' origin/main | sort -u | wc -l→ 25 (24 upstream + Seth). - Convention from sibling Seth projects (
blind_chess,sethmux,kitty-web,mcp-gemma4,seth-orchestrator):GITEA_API.mdis gitignored (it's a local symlink to~/bin/GITEA_API.md);.backup/is gitignored; project-tracked files areCLAUDE.md,IDEA.md,DECISIONS.md,.claude/handoffs/*.md. sethLabels follows this convention but adds the strict-zero-policy gitignore section. - Versioning convention:
<upstream-tag>-seth<N>(e.g.,3.99-master618-seth1). Tag isgit describe --tags --abbrev=0 upstream/master;<N>increments on re-package of the same upstream commit. See spec §D4 + §5.4.
Tasks Finished
- Cloned upstream
j-evins/glabels-qtin-place; preserved all upstream commit authorship - Created Gitea repo
git.sethpc.xyz/Seth/sethLabels(default branch:main); pushed full history + scaffold commit - Configured
upstreamremote →github.com/j-evins/glabels-qtfor periodic rebases - Added scaffold commit (
9dc6776) with authorSeth Freiberg <seth@sethfreiberg.com>containing CLAUDE.md, IDEA.md, DECISIONS.md, .claude/handoffs/, .gitignore additions - Brainstormed all 6 packaging-strategy decisions: Linux format, macOS format, build infra, versioning, upstream-touch policy, package name
- Wrote design spec at
sethlabels-docs/specs/2026-04-29-packaging-design.md(398 lines, 13 sections); self-reviewed and fixed 5 inline issues (FF/rebase wording, --peek removal, tag-fetch caller responsibility, T3 brittleness, working-tree drift in check-no-upstream-edits) - Updated
DECISIONS.mdwith 6 settled + 13 rejected/deferred decisions (all dated 2026-04-29) - Refreshed
CLAUDE.md"Current State" + "Conventions" sections; refreshedIDEA.md"Constraints / preferences" - All 3 sethLabels commits pushed to Gitea immediately per the gitea-workflow convention
Files Modified
| File | Changes | Rationale |
|---|---|---|
.gitignore |
Appended sethLabels section (.backup/, GITEA_API.md, .env*, .claude/handoffs/*.draft.md) |
Allowlisted exception to strict-zero (one-time scaffold touch) |
CLAUDE.md |
NEW — project instructions | Created at scaffold time; refreshed this session for design-approved phase |
IDEA.md |
NEW — project brief | Created at scaffold time; refreshed this session |
DECISIONS.md |
NEW — populated with 6 settled + 13 rejected/deferred decisions | Tracks what was decided vs. explicitly rejected (per Seth's global persistence convention) |
sethlabels-docs/specs/2026-04-29-packaging-design.md |
NEW — design spec | The design artifact this session produced |
.claude/handoffs/2026-04-29-095534-spec-approved-pre-implementation.md |
NEW — this handoff | Session close artifact |
No upstream files were modified (verified by git diff --name-only upstream/master..HEAD — only allowlist entries appear).
Decisions Made
| Decision | Options Considered | Rationale |
|---|---|---|
Linux: .deb + AppImage |
AppImage-only, .deb-only, both, Flatpak | .deb for Debian-family install ergonomics + AppImage for any-Linux portability. Flatpak too heavyweight for use case. See spec §D1. |
| macOS: Homebrew tap, build-from-source | Unsigned .dmg, signed+notarized .dmg ($99/yr), brew tap, brew tap with cask | Brew tap eliminates macOS CI/signing/Apple Dev ID entirely. User's Mac builds from source on brew install. See spec §D2. |
| Build infrastructure: manual local builds | Manual, Gitea Actions runner, GitHub Actions public, GitHub Actions private | Local feedback loop beats CI loop during iteration. Scripts in scripts/ are canonical recipe; CI YAML at the public-flip will call them unmodified. See spec §D3. |
Versioning: <upstream-tag>-seth<N> |
This vs. plain upstream tag, independent semver, date-based | Lineage-preserving + rebuild counter survives packaging-only fixes + sorts correctly under dpkg --compare-versions. See spec §D4. |
| Upstream-touch policy: strict zero | Strict zero, permissive small patches, strict-zero-with-CMakeLists-carveout | CPack -D flags cover all needed metadata at build time → no edit required to package. See spec §D5. |
Package name: glabels-qt |
This vs. sethlabels |
Strict-zero forbids renaming the binary, so package name should match command name. sethLabels identity lives in repo name + version-string -seth<N> marker. See spec §D6. |
Spec/docs location: sethlabels-docs/ (top-level new dir) |
This vs. docs/superpowers/specs/ (inside upstream's docs/) |
Strict-zero spirit forbids polluting upstream namespaces, even with new files. Top-level new dir = cleanest fork boundary. |
Immediate Next Steps
- Invoke
writing-plansskill with the spec atsethlabels-docs/specs/2026-04-29-packaging-design.mdas input. The plan should produce an ordered, dependency-aware implementation sequence — likely something like: (a) scripts/lib/deps-debian.sh, (b) scripts/check-no-upstream-edits.sh, (c) scripts/compute-version.sh, (d) scripts/build-deb.sh + smoke test on a clean Debian 13 box, (e) scripts/build-appimages.sh + smoke tests, (f) packaging/ env files, (g) README.sethlabels.md, (h) homebrew tap repo + initial formula, (i) first end-to-end release dry run. - Address the 4 user-flagged review items before or during plan execution:
- §5.1 build dependency list — Seth may know Debian 13 quirks (e.g.,
qt6-tools-dev-toolsvsqt6-tools-devpackage naming) - §5.5 allowlist pattern — anything Seth wants to add that doesn't fit
scripts//packaging//sethlabels-docs/? - §7.2 brew formula
:recommendeddeps —qrencodeandzintas recommended (default-on) vs required vs optional - §10 smoke test T5 — gate every release on a fresh-Debian-13-VM install test, or only on upstream-tag bumps?
- §5.1 build dependency list — Seth may know Debian 13 quirks (e.g.,
- Consider whether to spike Path A (Qt for WebAssembly) later — Seth raised this as a hypothetical and we concluded it's "possible but not easy" because Qt's
QPrintSupportdoesn't work in WASM (would need to render-to-PDF and let user download). Not blocking, just flagged for future.
Blockers / Open Questions
- None blocking. The 4 review items in step 2 above are open but optional — they can be addressed during plan execution as small fixups, not gating questions.
Deferred Items
- Windows packaging — deferred per project brief. Upstream's NSIS support is intact and works for anyone who wants to build their own.
- Custom default templates baked into the package — strict-zero forbids; user-specific templates can live in
~/.config/glabels-qt/templates/or a future separate repo. - Branding, icon, splash, or string changes — strict-zero forbids. sethLabels is a packaging fork, not a rebrand.
- CI infrastructure (Gitea Actions / GitHub Actions runner) — battle-test phase is manual local builds; CI is added at the eventual public-flip on GitHub.
- Distribution to Debian backports / PPA / Ubuntu universe — requires Debian Developer mentorship + ongoing policy compliance work; not justified for current scope.
- Headless print-server CT — mentioned in
IDEA.mdas "optional/later"; not in current spec scope.
Important Context
The strict-zero source-patch policy is not a guideline, it is the project's defining discipline. Violating it means the project drifts from "deployment fork" toward "real fork," which breaks the trivial-rebase property that makes the whole approach work. The spec's Invariant I1 is enforced by scripts/check-no-upstream-edits.sh (to be implemented per spec §5.5). Any temptation to "just edit one upstream file to fix a packaging issue" should be resisted — the right move is either (a) pass via cpack -D flags or linuxdeploy config, or (b) upstream the fix as a PR to glabels-qt.
The eventually-public-on-GitHub framing matters for build-host neutrality. During battle-test, builds happen on steel141 (Seth's primary dev machine, Debian 13). At public-flip, builds move to GitHub Actions ubuntu-latest runners. The build scripts MUST work on a clean Debian 13 / Ubuntu LTS VM with nothing pre-installed beyond what scripts/lib/deps-debian.sh declares. Any steel141-specific path or tool dependency is a bug.
Steel141 is a build host, NOT an install target. Earlier session draft framed it as the deploy target — that was wrong, corrected during Q1. The artifacts ship to anyone running Debian-family Linux or macOS-with-brew.
Authorship in git history is load-bearing. All 24 upstream contributors' commits appear in git log with their original author/committer fields intact. Only sethLabels-specific commits (currently 3) carry Seth's authorship. This makes the fork's relationship to upstream provable end-to-end and is critical for the eventual public-fork narrative.
The "scaffold commit" is the only sethLabels commit on top of upstream/master. Currently 9dc6776 chore: add sethLabels deployment-fork scaffold. Two more commits exist (8e272c0 spec + d3e14c6 CLAUDE.md/IDEA.md refresh) but they only touch sethLabels-namespaced files. The number of commits on top of upstream will grow; what matters is that all of them honor strict-zero.
Assumptions Made
- The user picked
glabels-qtas the package name (Q6 → α) intending the binary, brew formula, and.debpackage to all share the name. If a future Debian officialglabels-qtpackage emerges, the version-string-seth<N>marker will dominate the sort, but a name conflict could be revisited. - The user has Homebrew on the macOS machines they intend to install on. The brew-tap approach assumes a technical user; if a non-technical macOS user enters scope, signed-DMG would have to be reconsidered.
- The user's scaffold commit (Author: Seth Freiberg seth@sethfreiberg.com) was set explicitly via
git config user.email seth@sethfreiberg.comfor this repo only. The global git identity on steel141 isMortdecai(a bot identity). Future commits in this repo from Claude Code should use the same Seth identity to maintain authorship consistency. The per-repo.git/configis already set; new agents inherit it automatically.
Potential Gotchas
- Don't run a non-rebase pull from upstream.
git pull upstream master(without--rebase) would create a merge commit, breaking linear history and complicating future strict-zero enforcement. The release flow's step 2 (spec §6) saysgit rebase upstream/master, which is correct. compute-version.shreads local tag database. If invoked outside the release flow without a freshgit fetch --all --tags, it can produce a stale<N>value. Spec §5.4 calls this out, but it's easy to miss when running scripts manually during development.- AppImage builds need
linuxdeployandlinuxdeploy-plugin-qtfrom GitHub releases — neither is apt-installable.scripts/lib/linuxdeploy.sh(to be written) handles bootstrap to a script-local cache. The cache directory must be added to.gitignore. dpkg-shlibdeps(CPack'sSHLIBDEPS=ON) sometimes mis-detects runtime deps — particularly with optional Qt6 plugins. Mitigation in spec §F8: smoke-test install on a clean Debian 13 VM (T5).brew tapfrom non-GitHub URLs requires the explicit URL form. Initial install isbrew tap seth/tap https://git.sethpc.xyz/Seth/homebrew-tap.git, notbrew tap seth/tap. This is documented in spec §7.3 but easy to forget when writing the brew tap README.- Don't add
superpowers/paths underdocs/. The brainstorming skill's default spec location isdocs/superpowers/specs/, butdocs/is an upstream directory, so we usesethlabels-docs/specs/instead. This was caught and corrected mid-session.
Environment State
Tools/Services Used
- gitea CLI (
~/bin/gitea) — used forgitea create sethLabels,gitea remote sethLabels. Token loaded from~/.config/gitea/token. Documented in~/bin/GITEA_API.md(symlinked into project but gitignored). - git — local tooling for clone, fetch, commit, push, tag. Per-repo identity set to
Seth Freiberg <seth@sethfreiberg.com>to override the globalMortdecaibot identity. - curl + python3 — used for Gitea API calls to verify
default_branch=mainpost-push.
Active Processes
- None. No background services were started or left running. No long-running shells.
Environment Variables
HOMELAB_PASSWORD— referenced by~/bin/CLAUDE.mdfor SSH access, NOT used in this session.- No other env vars set or required for the current state.
Related Resources
- Gitea repo: https://git.sethpc.xyz/Seth/sethLabels (default branch
main) - Spec:
sethlabels-docs/specs/2026-04-29-packaging-design.md— read this before any implementation - Decision log:
DECISIONS.md— short-form - Predecessor handoff:
2026-04-29-125823-scaffold-only.md— the empty-scaffold state we resumed from - Upstream: https://github.com/j-evins/glabels-qt (j-evins is Jaye Evins of glabels.org)
- Upstream README packaging gap: the line we're filling — "Currently there are no self-hosted binary snapshot releases available… I encourage you to try building the code yourself."
- Sibling Seth projects with similar conventions:
~/bin/blind_chess/(handoff structure reference),~/bin/sethmux/(gitignore convention)
Security Reminder: Validated via validate_handoff.py post-write. No secrets present.