[ci] Adopt canonical lab-publish.yaml for releases (lab build + auto-publish to releases/tag/latest) #14

Closed
opened 2026-05-22 02:48:16 +00:00 by mik-tf · 3 comments
Owner

Goal

Adopt the canonical .forgejo/workflows/lab-publish.yaml so every push to development (and every v* tag) auto-publishes linux-musl-x86_64 binaries to releases/tag/latest — matching the pattern shipped in hero_slides/.forgejo/workflows/lab-publish.yaml and the org-wide rollout tracked at hero_skills#268.

This replaces the existing tag-triggered .forgejo/workflows/build-linux.yaml (which uses raw cargo build -p ... per-bin and only fires on v* tags). The new workflow is push-triggered, uses lab build --workspace, and republishes releases/tag/latest on every development push.

Pre-flight (local) — two known concerns

This workspace is not the standard shape. Local lab build --release --install --workspace may need adjustments:

  1. WASM-only cratescrates/hero_assistance_ui_wasm and crates/hero_assistance_admin_ui_wasm are workspace members but excluded from [workspace.default-members] (Cargo.toml:25-34) because they're wasm32-only (built via dx build / make dist, not host cargo build). Open question: does lab build --workspace honour [workspace.default-members] or iterate all members? Needs to be empirically confirmed.
  2. Dioxus desktop binarycrates/hero_assistance_app IS in default-members and pulls in dioxus-desktopwrywebkit2gtk-sys. The CI builder image ghcr.io/despiegk/builder:latest does not ship GTK/webkit system libraries. The existing build-linux.yaml works around this with explicit -p hero_assistance_server -p hero_assistance_ui -p hero_assistance (skips _app). lab build --workspace will hit the same wall unless either:
    • lab exposes a way to skip specific workspace members at build time, OR
    • we move _app to [workspace.exclude] or a separate workspace (likely overkill), OR
    • we extend the lab-publish workflow with an apt install step for the GTK/webkit deps (deviates from the canonical drop-in shape).

Workspace shape:

  • 11 crates total (8 in default-members)
  • 0 service.toml files (predates the convention)
  • Existing .forgejo/workflows/build-linux.yaml (tag-triggered, manual per-bin) to be removed

CI step

Pending pre-flight resolution: drop the canonical lab-publish.yaml (verbatim from hero_slides) into .forgejo/workflows/, git rm the existing build-linux.yaml. Requires FORGEJO_TOKEN secret in repo Actions settings (write:repository scope).

If the desktop-crate wall makes the verbatim canonical workflow impossible, we file a follow-up on hero_skills to add a --skip-member knob to lab build (or to add a workflow-level apt-install lane for GTK deps).

Acceptance

  • lab build --release --install --workspace exits 0 locally (or a documented lab build invocation that excludes _app produces all server/ui/cli/admin binaries)
  • .forgejo/workflows/lab-publish.yaml lands on development; build-linux.yaml removed in the same commit
  • First post-push CI run succeeds; releases/tag/latest shows refreshed linux-musl-x86_64 binaries + md5 sidecars

Coordination

  • hero_cockpit + hero_onboarding are landing the same workflow in parallel (no expected concerns there — standard workspace shapes).
  • This issue's pre-flight may surface a lab feature gap; if so, the gap gets filed as a separate hero_skills issue and this issue blocks on it.

Signed-by: mik-tf mik-tf@noreply.invalid

## Goal Adopt the canonical `.forgejo/workflows/lab-publish.yaml` so every push to `development` (and every `v*` tag) auto-publishes `linux-musl-x86_64` binaries to `releases/tag/latest` — matching the pattern shipped in [hero_slides/.forgejo/workflows/lab-publish.yaml](https://forge.ourworld.tf/lhumina_code/hero_slides/src/branch/development/.forgejo/workflows/lab-publish.yaml) and the org-wide rollout tracked at [hero_skills#268](https://forge.ourworld.tf/lhumina_code/hero_skills/issues/268). This **replaces** the existing tag-triggered `.forgejo/workflows/build-linux.yaml` (which uses raw `cargo build -p ...` per-bin and only fires on `v*` tags). The new workflow is push-triggered, uses `lab build --workspace`, and republishes `releases/tag/latest` on every `development` push. ## Pre-flight (local) — two known concerns This workspace is not the standard shape. Local `lab build --release --install --workspace` may need adjustments: 1. **WASM-only crates** — `crates/hero_assistance_ui_wasm` and `crates/hero_assistance_admin_ui_wasm` are workspace `members` but **excluded from `[workspace.default-members]`** ([Cargo.toml:25-34](https://forge.ourworld.tf/lhumina_code/hero_assistance/src/branch/development/Cargo.toml#L25-L34)) because they're wasm32-only (built via `dx build` / `make dist`, not host `cargo build`). Open question: does `lab build --workspace` honour `[workspace.default-members]` or iterate all `members`? Needs to be empirically confirmed. 2. **Dioxus desktop binary** — `crates/hero_assistance_app` IS in `default-members` and pulls in `dioxus-desktop` → `wry` → `webkit2gtk-sys`. The CI builder image `ghcr.io/despiegk/builder:latest` does **not** ship GTK/webkit system libraries. The existing `build-linux.yaml` works around this with explicit `-p hero_assistance_server -p hero_assistance_ui -p hero_assistance` (skips `_app`). `lab build --workspace` will hit the same wall unless either: - lab exposes a way to skip specific workspace members at build time, OR - we move `_app` to `[workspace.exclude]` or a separate workspace (likely overkill), OR - we extend the lab-publish workflow with an apt install step for the GTK/webkit deps (deviates from the canonical drop-in shape). Workspace shape: - 11 crates total (8 in default-members) - 0 `service.toml` files (predates the convention) - Existing `.forgejo/workflows/build-linux.yaml` (tag-triggered, manual per-bin) to be removed ## CI step Pending pre-flight resolution: drop the canonical `lab-publish.yaml` (verbatim from hero_slides) into `.forgejo/workflows/`, **`git rm` the existing `build-linux.yaml`**. Requires `FORGEJO_TOKEN` secret in repo Actions settings (write:repository scope). If the desktop-crate wall makes the verbatim canonical workflow impossible, we file a follow-up on hero_skills to add a `--skip-member` knob to `lab build` (or to add a workflow-level apt-install lane for GTK deps). ## Acceptance - [ ] `lab build --release --install --workspace` exits 0 locally (or a documented `lab build` invocation that excludes `_app` produces all server/ui/cli/admin binaries) - [ ] `.forgejo/workflows/lab-publish.yaml` lands on `development`; `build-linux.yaml` removed in the same commit - [ ] First post-push CI run succeeds; `releases/tag/latest` shows refreshed `linux-musl-x86_64` binaries + md5 sidecars ## Coordination - hero_cockpit + hero_onboarding are landing the same workflow in parallel (no expected concerns there — standard workspace shapes). - This issue's pre-flight may surface a `lab` feature gap; if so, the gap gets filed as a separate `hero_skills` issue and this issue blocks on it. Signed-by: mik-tf <mik-tf@noreply.invalid>
Author
Owner

Pre-flight findings (lab build local run)

Ran lab build --release --install --workspace from the workspace root. Result: 8 binary targets discovered, 1 built, 0 skipped, 7 failed (6m47s wall). The single passing target was embed_smoke. The state is materially worse than the issue body anticipated — going beyond the originally-flagged Dioxus-desktop / WASM-default-members concerns. CI cannot land on lab-publish.yaml until the underlying gaps close.

Wall 1 — hero_assistance_server real compile failure (49 errors)

error[E0277]: the trait bound `u64: ToSql` is not satisfied
   --> crates/hero_assistance_server/src/handlers/workspace.rs:108:9
        rusqlite::params![input.id],
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ToSql` is not implemented for `u64`

rusqlite dropped the u64: ToSql impl in 0.31 (storage type is i64; SQLite can't represent u64 natively). The handler creates let id = db.last_insert_rowid() as u64; and then tries to pass that u64 back through rusqlite::params!. Pre-existing source bug — surfaced now because the s49 0.5.0→0.6.0 dep-lift moved rusqlite to a version past 0.31 (likely). Affects workspace.rs:108, workspace.rs:148, plus 47 other call sites for a total of 49 E0277s.

Fix shape: cast u64 → i64 at the params! boundary, or change id field types to i64 end-to-end. Mechanical sweep across handlers/workspace.rs. Not a CI concern — cargo build itself fails, no version of CI will help.

Wall 2 — --info contract not implemented on 4 binaries

The Hero stack convention (per the herolib_base skill + the hero_service_check_fix skill) requires every Hero binary to support <bin> --info (and --info --json) which emits a structured manifest derived from an embedded service.toml. lab build runs this after each build as a smoke test:

binary result
hero_assistance (cli) error: unexpected argument '--info' found (clap-level rejection — flag not defined)
hero_assistance_ui error: unexpected argument '--info' found
hero_assistance_admin error: unexpected argument '--info' found
hero_assistance_app (desktop) --info accepted, but binary runs the actual app instead of emitting JSON ("aggregator stream ended")
hero_assistance_admin_ui_wasm exit 101 — no native main entry point usable for --info
hero_assistance_ui_wasm exit 101 — same

Three of these (hero_assistance, _ui, _admin) need service_base!() macro adoption per herolib_base — which also requires a service.toml next to src/main.rs (none exist in this repo today — see Wall 3). _app needs a sub-flag dispatch where --info short-circuits before window boot.

Wall 3 — no service.toml files anywhere in the workspace

$ find . -name service.toml -not -path '*/target/*'
(no results)

Every other D-10-effective-clean Hero service ships a service.toml per crate (4 in hero_cockpit, 3 in hero_onboarding for reference). The service_base!() macro reads it via include_str!("../service.toml"), and --info emits its contents as JSON. Adopting the canonical pattern requires writing one service.toml per service-style crate: hero_assistance_server, hero_assistance_ui, hero_assistance_admin, hero_assistance (cli), and possibly hero_assistance_app.

Wall 4 — lab build --workspace does NOT honour [workspace.default-members]

Empirically confirmed: lab discovered 8 binary targets including hero_assistance_admin_ui_wasm and hero_assistance_ui_wasm — which are workspace members but deliberately excluded from [workspace.default-members] (Cargo.toml:25-34) because they're wasm32-only and don't make sense on the host target.

This is a real lab tooling gap and the answer to the Q in the original issue body. Two ways forward:

  • File an upstream issue on hero_skills requesting that lab build honour [workspace.default-members] (correct fix — matches cargo build semantics).
  • OR add a [package.metadata.lab] skip = true annotation per WASM crate (or similar opt-out), if lab already supports such a knob.
  • OR remove the WASM crates from [workspace.members] and split them into a sibling workspace (deviates from the current layout intentionally chosen post-s8).

Wall 5 — policy-mode apply side-effects

lab build defaults to --policy-mode apply which rewrote 4 Cargo.tomls in-place and dropped .hero_builder_backup files alongside (and a crates/hero_assistance_sdk/src/generated/ directory). I reverted all of this — it's a separate pipeline (hero_assistance has its own multi-session discipline at CLAUDE.md) and policy edits there belong inside an hero_assistance-pipeline session, not as drive-by mutation from hero_work.

If the hero_assistance pipeline picks up the lab-publish work, the very first session should use --policy-mode warn (audit-only) until the dep policy is understood and intentionally adopted.

Recommendation

Close this issue as "blocked / needs upstream work" and re-open scoped under the hero_assistance pipeline once Walls 1-3 are resolved. Specifically:

  1. Wall 1 (compile fail) — must close first. Standalone session in the hero_assistance pipeline; mechanical u64 → i64 sweep on workspace.rs (and likely siblings).
  2. Walls 2 + 3 (--info + service.toml) — apply the hero_service_check_fix skill across all service-style crates. ~1 session of focused work.
  3. Wall 4 (lab member iteration) — file an issue on hero_skills for lab to honour [workspace.default-members]. Until that lands, the workaround is per-crate metadata or workspace restructuring.

Once Walls 1-3 close and Wall 4 has either a knob or workaround, the drop-in lab-publish.yaml lands cleanly per this issue's original goal.

Sibling repos status (for context):

Signed-by: mik-tf mik-tf@noreply.invalid

## Pre-flight findings (lab build local run) Ran `lab build --release --install --workspace` from the workspace root. Result: **8 binary targets discovered, 1 built, 0 skipped, 7 failed** (6m47s wall). The single passing target was `embed_smoke`. The state is materially worse than the issue body anticipated — going beyond the originally-flagged Dioxus-desktop / WASM-default-members concerns. CI cannot land on `lab-publish.yaml` until the underlying gaps close. ### Wall 1 — `hero_assistance_server` real compile failure (49 errors) ``` error[E0277]: the trait bound `u64: ToSql` is not satisfied --> crates/hero_assistance_server/src/handlers/workspace.rs:108:9 rusqlite::params![input.id], ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ToSql` is not implemented for `u64` ``` `rusqlite` dropped the `u64: ToSql` impl in 0.31 (storage type is `i64`; SQLite can't represent u64 natively). The handler creates `let id = db.last_insert_rowid() as u64;` and then tries to pass that `u64` back through `rusqlite::params!`. Pre-existing source bug — surfaced now because the s49 0.5.0→0.6.0 dep-lift moved rusqlite to a version past 0.31 (likely). Affects `workspace.rs:108`, `workspace.rs:148`, plus 47 other call sites for a total of 49 `E0277`s. **Fix shape**: cast `u64 → i64` at the `params!` boundary, or change `id` field types to `i64` end-to-end. Mechanical sweep across `handlers/workspace.rs`. Not a CI concern — `cargo build` itself fails, no version of CI will help. ### Wall 2 — `--info` contract not implemented on 4 binaries The Hero stack convention (per the [`herolib_base` skill](../hero_skills) + the [`hero_service_check_fix` skill](../hero_skills)) requires every Hero binary to support `<bin> --info` (and `--info --json`) which emits a structured manifest derived from an embedded `service.toml`. `lab build` runs this after each build as a smoke test: | binary | result | |---|---| | `hero_assistance` (cli) | `error: unexpected argument '--info' found` (clap-level rejection — flag not defined) | | `hero_assistance_ui` | `error: unexpected argument '--info' found` | | `hero_assistance_admin` | `error: unexpected argument '--info' found` | | `hero_assistance_app` (desktop) | `--info` accepted, but binary runs the actual app instead of emitting JSON ("aggregator stream ended") | | `hero_assistance_admin_ui_wasm` | exit 101 — no native main entry point usable for `--info` | | `hero_assistance_ui_wasm` | exit 101 — same | Three of these (`hero_assistance`, `_ui`, `_admin`) need `service_base!()` macro adoption per [`herolib_base`](https://forge.ourworld.tf/lhumina_code/hero_skills/src/branch/development/skills/herolib_base/SKILL.md) — which also requires a `service.toml` next to `src/main.rs` (none exist in this repo today — see Wall 3). `_app` needs a sub-flag dispatch where `--info` short-circuits before window boot. ### Wall 3 — no `service.toml` files anywhere in the workspace ``` $ find . -name service.toml -not -path '*/target/*' (no results) ``` Every other D-10-effective-clean Hero service ships a `service.toml` per crate (4 in hero_cockpit, 3 in hero_onboarding for reference). The `service_base!()` macro reads it via `include_str!("../service.toml")`, and `--info` emits its contents as JSON. Adopting the canonical pattern requires writing one `service.toml` per service-style crate: `hero_assistance_server`, `hero_assistance_ui`, `hero_assistance_admin`, `hero_assistance` (cli), and possibly `hero_assistance_app`. ### Wall 4 — `lab build --workspace` does NOT honour `[workspace.default-members]` Empirically confirmed: lab discovered **8 binary targets** including `hero_assistance_admin_ui_wasm` and `hero_assistance_ui_wasm` — which are workspace `members` but deliberately **excluded from `[workspace.default-members]`** ([Cargo.toml:25-34](https://forge.ourworld.tf/lhumina_code/hero_assistance/src/branch/development/Cargo.toml#L25-L34)) because they're wasm32-only and don't make sense on the host target. This is a real `lab` tooling gap and the answer to the Q in the original issue body. Two ways forward: - File an upstream issue on [hero_skills](https://forge.ourworld.tf/lhumina_code/hero_skills) requesting that `lab build` honour `[workspace.default-members]` (correct fix — matches `cargo build` semantics). - OR add a `[package.metadata.lab] skip = true` annotation per WASM crate (or similar opt-out), if lab already supports such a knob. - OR remove the WASM crates from `[workspace.members]` and split them into a sibling workspace (deviates from the current layout intentionally chosen post-s8). ### Wall 5 — `policy-mode apply` side-effects `lab build` defaults to `--policy-mode apply` which rewrote 4 `Cargo.toml`s in-place and dropped `.hero_builder_backup` files alongside (and a `crates/hero_assistance_sdk/src/generated/` directory). I reverted all of this — it's a separate pipeline (`hero_assistance` has its own multi-session discipline at [CLAUDE.md](https://forge.ourworld.tf/lhumina_code/hero_assistance/src/branch/development/CLAUDE.md)) and policy edits there belong inside an hero_assistance-pipeline session, not as drive-by mutation from hero_work. If the hero_assistance pipeline picks up the lab-publish work, the very first session should use `--policy-mode warn` (audit-only) until the dep policy is understood and intentionally adopted. ## Recommendation **Close this issue as "blocked / needs upstream work"** and re-open scoped under the hero_assistance pipeline once Walls 1-3 are resolved. Specifically: 1. **Wall 1 (compile fail)** — must close first. Standalone session in the hero_assistance pipeline; mechanical `u64 → i64` sweep on `workspace.rs` (and likely siblings). 2. **Walls 2 + 3 (--info + service.toml)** — apply the [`hero_service_check_fix`](https://forge.ourworld.tf/lhumina_code/hero_skills) skill across all service-style crates. ~1 session of focused work. 3. **Wall 4 (lab member iteration)** — file an issue on [hero_skills](https://forge.ourworld.tf/lhumina_code/hero_skills) for lab to honour `[workspace.default-members]`. Until that lands, the workaround is per-crate metadata or workspace restructuring. Once Walls 1-3 close and Wall 4 has either a knob or workaround, the drop-in `lab-publish.yaml` lands cleanly per this issue's original goal. **Sibling repos status** (for context): - [hero_cockpit#3](https://forge.ourworld.tf/lhumina_code/hero_cockpit/issues/3) — landed, CI run in flight ([commit 0b7aa5a](https://forge.ourworld.tf/lhumina_code/hero_cockpit/commit/0b7aa5a)). Cockpit was clean: 4/4 binaries, ~3min build. - [hero_onboarding#15](https://forge.ourworld.tf/lhumina_code/hero_onboarding/issues/15) — landed, CI run in flight ([commit cc5d263](https://forge.ourworld.tf/lhumina_code/hero_onboarding/commit/cc5d263)). Onboarding was clean: 3/3 binaries, ~33s build. Signed-by: mik-tf <mik-tf@noreply.invalid>
Author
Owner

Comprehensive pickup plan for the next session

This comment is the single-source pickup brief for the next session that tackles this issue. Bookmarked from the issue body. Reading order on session start: this comment top-to-bottom, then #issuecomment-35892 for finding detail, then the linked reference files. Everything below is concrete enough to execute from.

Pickup target: this work belongs in the hero_assistance pipeline (~/.claude/projects/-home-pctwo-Documents-temp-assistance-work/), not hero_work. The hero_work session that filed this issue already did the org-wide rollout for hero_cockpit#3 + hero_onboarding#15 (both landed; CI configured + releases publishing on push). Both close in this session as drive-by drop-ins because their workspaces are convention-clean. This repo is not — the walls below have to close first.

Authorship + branching for this work follows this repo's CLAUDE.md (mik-tf, no AI/co-author trailers; development_<topic> feature branches squash-merged to development). Do NOT inherit hero_work's Signed-by: mik-tf <mik-tf@noreply.invalid> trailer — that's a hero_work pipeline convention, not this repo's.


TL;DR of the pickup

5 walls block the canonical .forgejo/workflows/lab-publish.yaml drop-in. Wall 1 is a real source compile failure (49 errors) — fixing it unblocks every later phase. Walls 2 + 3 are Hero stack convention adoption (--info + service.toml) per the hero_service_check_fix skill. Wall 4 is an upstream lab tooling gap (--workspace ignores [workspace.default-members]). Wall 5 is an environment quirk (policy-mode apply rewrites Cargo.toml in-place — use --policy-mode warn during dev). Estimated 1 focused session if the rusqlite fix is mechanical and Wall 4's mitigation is the lab --packages flag; 2 sessions if a workspace restructure is needed.

What's already been done in the hero_work session (2026-05-22)

  • Filed this issue (#14) — original ask: adopt canonical lab-publish.yaml to replace tag-triggered build-linux.yaml.
  • Ran lab build --release --install --workspace locally from development_mik — surfaced 5 walls (full detail at #issuecomment-35892).
  • Reverted lab's policy-mode apply mutations on 4 Cargo.tomls + cleaned the crates/hero_assistance_sdk/src/generated/ artifacts. Repo is at origin/development clean.
  • Did NOT push any code change to this repo. No branch was kept. Issue is the only artifact.

The 5 walls (recap — full detail in #issuecomment-35892)

# Wall Severity Closes via
1 hero_assistance_server won't compile (49 × u64: ToSql) blocker — cargo build fails mechanical u64→i64 sweep
2 4 binaries don't implement --info high — lab marks build failed service_base!() adoption per binary
3 No service.toml files anywhere high — required by service_base!() write 4-5 service.tomls
4 lab --workspace ignores default-members medium — triggers WASM-as-native build attempts upstream lab fix OR per-package invocation OR workspace restructure
5 lab build default --policy-mode apply mutates Cargo.toml low — recoverable, just don't accept the policy edits run with --policy-mode warn during dev

Phase plan

The phases are strictly sequential up through Phase 3. Phases 4 and 5 can interleave once Phase 3 has produced a clean local lab build --release --install --workspace.

Phase 1 — Fix the rusqlite u64: ToSql compile failure

Scope. crates/hero_assistance_server/src/handlers/workspace.rs (lines 108 + 148 confirmed; 49 total E0277 errors — likely siblings in the same file + related handlers).

Root cause. rusqlite ≥ 0.31 removed impl ToSql for u64 (SQLite's storage type is i64; u64 can't be losslessly represented). The 0.5.0 → 0.6.0 dep-lift in s49 pulled in a rusqlite version past 0.31. Code currently does let id = db.last_insert_rowid() as u64; and then tries to pass that u64 back through rusqlite::params![input.id].

Decision needed (lock at session start). Two fix shapes:

(1a) cast-at-boundary (1b) change ID type throughout
Mechanic params![input.id as i64] everywhere id fields become i64 end-to-end (struct → handler → JSON)
Scope only handler files structs, JSON serde, DB schema, public OpenRPC types
Wire compat identical (JSON numbers unchanged) identical (JSON numbers unchanged) — but type changes leak to SDK consumers
Risk low (mechanical) medium (touches public API surface)
Effort ~1 hour ~3 hours + SDK regen
Long-term hygiene papers over the impedance mismatch cleaner; matches what SQLite actually stores

Recommendation: 1a for this session. The id range never exceeds i64::MAX in any realistic scenario (last_insert_rowid() is i64-native), so the cast is functionally identical. Filing a follow-up issue for 1b lets it land cleanly in a future session that owns the SDK regen.

Deliverable. Single commit on development_<topic>: crates/hero_assistance_server/src/handlers/workspace.rs (and any sibling handler that fails the same way — discover via cargo build --release -p hero_assistance_server 2>&1 | grep -E "u64.*ToSql").

Acceptance. cargo build --release exits 0. The Phase 1 unit test that pins the workspace.rs behavior continues to pass (or one gets added if the regression boundary isn't currently pinned).

Estimated effort. 1 hour.


Phase 2 — Add service.toml to each service-style binary

Scope. Five binaries need a service.toml next to their src/main.rs:

crate role reference repo's service.toml
crates/hero_assistance_server rpc server (mycelium-bound) hero_cockpit/crates/hero_cockpit_server/service.toml
crates/hero_assistance_ui customer SPA dist + /rpc proxy + app.sock hero_cockpit/crates/hero_cockpit_web/service.toml (closest analogue — web service)
crates/hero_assistance_admin admin SPA dist + admin.sock + ip-whitelist gate hero_cockpit/crates/hero_cockpit_admin/service.toml
crates/hero_assistance cli hero_cockpit/crates/hero_cockpit/service.toml
crates/hero_assistance_app desktop binary likely needs a custom shape — there's no clean precedent in cockpit. Cross-check against any existing Dioxus-desktop Hero service.

Spec source of truth. hero_service_toml_info skill defines the schema + typed enums + the mandatory --info contract. Use the skill's authoritative format; do NOT reverse-engineer from sibling repos.

Deliverable. 5 new files under crates/*/service.toml. Each commits cleanly (single small commit per crate, or one bundled commit per Phase 2 — operator's choice).

Acceptance. Every file passes lab infocheck (the lab tool's smoke check that the embedded toml deserializes against the canonical schema). Reference: hero_skills#265 caveat — lab infocheck only inspects src/main.rs, so if a service has its main at src/bin/<name>.rs it'll miss the file. Hero_assistance's structure should be checked at session start.

Estimated effort. ~2 hours.


Phase 3 — Wire service_base!() + the --info contract in each binary's main.rs

Scope. The macro in herolib_base inlines:

  1. static SERVICE_TOML: &str = include_str!("../service.toml");
  2. static BUILD_NR: u32 = …; (compile-time embedded build number)
  3. A --info flag handler that emits the service.toml as structured JSON when called as <bin> --info --json (or human-readable on plain --info)

Critical lesson #20 (from CLAUDE.md / s110+s119): service_base!() hard-codes include_str!("../service.toml") which breaks when main lives at src/bin/<name>.rs (two-level deep). Workarounds: (a) inline SERVICE_TOML/BUILD_NR with ../../service.toml, OR (b) move the bin to src/main.rs. Hero_assistance's binary layout needs to be confirmed at session start — if any binary lives at src/bin/, choose (b) over (a) for lab infocheck compatibility.

Special handling for hero_assistance_app (Dioxus desktop). Per the Phase 2 lab probe, this binary's current --info runs the actual app instead of emitting JSON. Fix shape:

fn main() {
    // --info short-circuit BEFORE any window/event-loop init
    if std::env::args().any(|a| a == "--info") {
        herolib_base::print_service_info();  // or equivalent helper
        return;
    }
    // existing dioxus::desktop::launch path follows here
}

The check must precede any blocking init (the current --info invocation produced log output about aggregator stream ended — the app was running normally).

Deliverable. crates/*/src/main.rs updated in each of the 5 binaries. The Cargo.toml of each needs herolib_base (or service_base — whichever the skill defines) as a dep at the pinned shared version per rust_versions.

Acceptance. Each binary's --info and --info --json returns valid output that parses against the schema. lab build smoke check passes for all 5.

Estimated effort. ~2 hours (mechanical once Phase 2 service.tomls exist).


Phase 4 — Resolve lab --workspace iterating WASM crates (Wall 4)

The problem. Empirically: lab build --release --install --workspace discovers 8 binary targets including hero_assistance_admin_ui_wasm and hero_assistance_ui_wasm (both are members but excluded from [workspace.default-members]). These are wasm32-only and have no native main; they exit 101 when lab tries --info on them.

Three paths (lock decision before starting Phase 4):

(4a) File upstream lab fix, use per-package workaround in the meantime. File a hero_skills issue: "lab build --workspace should honour [workspace.default-members] (matches cargo's semantics, but documented)". Until that lands, use either:

  • lab build --release --install --packages hero_assistance_server,hero_assistance_ui,hero_assistance_admin,hero_assistance,hero_assistance_app (if lab exposes --packages — needs verification; see hero_skills#268)
  • OR per-bin loop: for bin in server ui admin cli app; do lab build --release --install --bin hero_assistance_$bin; done

The CI workflow YAML would mirror whichever local pattern works.

(4b) Workspace restructure — move the two WASM crates to a sibling workspace at crates_wasm/. Pros: clean separation; lab can't see them. Cons: bigger commit, touches [workspace.members], may affect existing tooling (the make dist flow currently runs from the same root). Effort: ~half-day.

(4c) Per-crate [package.metadata.lab] skip = true annotation — if lab supports such an opt-out. Cleanest if it exists; needs verification by reading lab's source under hero_skills.

Recommendation: 4c if it exists, else 4a. 4b is the safety net if both fail.

Deliverable. Either the [package.metadata.lab] annotations OR the workflow-level package list OR the workspace split. Either way, lab build --release --install --workspace from the repo root should iterate exactly the 5 native-targetable binaries (server, ui, admin, cli, app) — not the 2 WASM crates.

Acceptance. lab build --release --install --workspace from a clean checkout discovers 5 binaries, builds 5, fails 0.

Estimated effort. 4a: ~1 hour (file the issue, adjust workflow YAML). 4b: ~half-day. 4c: ~1 hour (verify + apply).


Phase 5 — Handle hero_assistance_app GTK/webkit deps in the CI builder image

Pre-flight finding from hero_work session. webkit2gtk-4.1 IS installed on the workstation (probed via pkg-config --exists webkit2gtk-4.1), and the desktop crate built fine locally. The wall is only the CI builder image (ghcr.io/despiegk/builder:latest) which doesn't ship GTK/webkit deps for server-CI workloads.

Two options:

(5a) Add an apt-install step to lab-publish.yaml. Insert before the "Build + upload" step:

      - name: Setup GTK/webkit (for hero_assistance_app)
        run: |
          set -euo pipefail
          apt-get update -qq
          apt-get install -y --no-install-recommends \
            libwebkit2gtk-4.1-dev libgtk-3-dev libssl-dev pkg-config

This deviates from the canonical drop-in shape of hero_slides/cockpit/onboarding workflows. The deviation is principled (one repo has a desktop crate; the other ~33 don't) — file as a known divergence in the commit message and in this issue's closing comment so future readers understand why hero_assistance's workflow isn't byte-identical to the canonical.

(5b) Exclude hero_assistance_app from the lab build --workspace set entirely; build it separately on a tag-triggered desktop-only workflow. Pros: keeps the canonical workflow byte-identical for hero_assistance's server/ui/admin/cli (the customer-facing pieces). Cons: desktop binary doesn't ship on latest — only on tagged releases. Acceptable if desktop is a tag-release-only artifact anyway (consistent with how desktop apps are normally distributed).

Recommendation: 5a if the team wants the desktop binary on latest (matches the rest of the Hero binary set's cadence); 5b if the desktop binary is acceptable as a tag-release-only artifact.

Deliverable. Modified .forgejo/workflows/lab-publish.yaml (5a) OR a second tag-triggered workflow file for desktop builds (5b).

Acceptance. CI run on a push to development produces all targeted binaries on releases/tag/latest with md5 sidecars.

Estimated effort. ~30 min either way.


Phase 6 — Drop in the workflow, remove the old one

Scope.

  1. cp <hero_slides reference> .forgejo/workflows/lab-publish.yaml (with the Phase 5 modification if 5a was chosen). Reference for canonical YAML: hero_slides/.forgejo/workflows/lab-publish.yaml.
  2. git rm .forgejo/workflows/build-linux.yaml — superseded.

Pre-flight check. Verify FORGEJO_TOKEN is present in this repo's Actions secrets (or inherited org-wide). Both hero_cockpit and hero_onboarding inherited it org-wide and didn't need a per-repo add; this repo should be the same, but worth checking before the run.

Deliverable. One commit on development_<topic> that lands the workflow + removes the old file. Squash-merge to development per this repo's standing rules.

Acceptance.

  1. The post-push workflow run completes with status=success.
  2. releases/tag/latest exists on this repo with linux-musl-x86_64 binaries + md5 sidecars for all 5 native-targetable binaries (or 4 if Phase 5b was chosen).
  3. Issue #14 closes automatically via the commit's Closes keyword.

Estimated effort. 30 min.


Decisions to lock at session start

Don't start writing code until these are nailed down — re-deciding mid-session costs more than the few minutes of upfront discussion. The recommendation column is the hero_work session's read; the operator decides.

Decision Options Recommendation Lock at
Phase 1 — u64 fix shape (1a) cast at boundary / (1b) change ID type throughout 1a (mechanical, this session); file (1b) as follow-up Phase 1 plan
Phase 4 — WASM exclusion (4a) per-package CI / (4b) workspace restructure / (4c) lab metadata annotation 4c if it exists, else 4a; 4b only as safety net Phase 4 plan
Phase 5 — desktop in CI (5a) apt install in workflow / (5b) tag-only desktop workflow 5a if desktop should ship on latest; 5b otherwise Phase 5 plan

Anti-patterns to avoid (warnings from sibling sessions)

  • Don't run lab build with default --policy-mode apply. Use --policy-mode warn until the dep policy is intentionally adopted. (Wall 5.)
  • Don't follow hero_work's Signed-by: mik-tf <mik-tf@noreply.invalid> trailer convention. This repo's CLAUDE.md is explicit: mik-tf <logismos@protonmail.ch>, no co-author trailers. Use this repo's pattern.
  • Don't rebase to bring in upstream development. This repo's hero-stack-wide rule: git pull && git merge development into the feature branch.
  • Don't run git commit -s. Per the Hero stack standing rule, this auto-injects Signed-off-by with a real email. Use plain git commit -m "..." with the message body.
  • Don't merge before the local lab build --release --install --workspace is clean. Phase 6 is the last step, not the first. CI is a regression gate, not a build orchestrator.

Reference materials (read before starting)

Estimated total effort

  • Optimistic (1 session, ~6 hours): Phase 1 mechanical + Phase 4c exists + Phase 5b accepted.
  • Realistic (1.5-2 sessions): Phase 1 + 2 + 3 in one session, Phase 4 + 5 + 6 in a second.
  • Pessimistic (3 sessions): Phase 1 in a standalone session, Phases 2 + 3 in a second, Phase 4 + 5 + 6 in a third — if any phase surfaces follow-up walls.

Acceptance for closing this issue

  • cargo build --release exits 0 from a fresh cargo update (Phase 1 closes)
  • lab build --release --install --workspace exits 0 with 5 (or 4 if 5b) native binaries built + installed (Phases 2-4 close)
  • .forgejo/workflows/lab-publish.yaml lands on development; build-linux.yaml is removed in the same commit (Phase 6)
  • First post-push CI run produces releases/tag/latest with the expected binary set + md5 sidecars (Phase 6)
## Comprehensive pickup plan for the next session This comment is the **single-source pickup brief** for the next session that tackles this issue. Bookmarked from the issue body. Reading order on session start: this comment top-to-bottom, then [#issuecomment-35892](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/14#issuecomment-35892) for finding detail, then the linked reference files. Everything below is concrete enough to execute from. Pickup target: this work belongs in the **hero_assistance pipeline** (`~/.claude/projects/-home-pctwo-Documents-temp-assistance-work/`), not hero_work. The hero_work session that filed this issue already did the org-wide rollout for [hero_cockpit#3](https://forge.ourworld.tf/lhumina_code/hero_cockpit/issues/3) + [hero_onboarding#15](https://forge.ourworld.tf/lhumina_code/hero_onboarding/issues/15) (both landed; CI configured + releases publishing on push). Both close in this session as drive-by drop-ins because their workspaces are convention-clean. This repo is not — the walls below have to close first. Authorship + branching for this work follows **this repo's CLAUDE.md** (mik-tf, no AI/co-author trailers; `development_<topic>` feature branches squash-merged to `development`). Do NOT inherit hero_work's `Signed-by: mik-tf <mik-tf@noreply.invalid>` trailer — that's a hero_work pipeline convention, not this repo's. --- ### TL;DR of the pickup 5 walls block the canonical `.forgejo/workflows/lab-publish.yaml` drop-in. Wall 1 is a real source compile failure (49 errors) — fixing it unblocks every later phase. Walls 2 + 3 are Hero stack convention adoption (`--info` + `service.toml`) per the [`hero_service_check_fix`](https://forge.ourworld.tf/lhumina_code/hero_skills) skill. Wall 4 is an upstream `lab` tooling gap (`--workspace` ignores `[workspace.default-members]`). Wall 5 is an environment quirk (`policy-mode apply` rewrites Cargo.toml in-place — use `--policy-mode warn` during dev). Estimated 1 focused session if the rusqlite fix is mechanical and Wall 4's mitigation is the lab `--packages` flag; 2 sessions if a workspace restructure is needed. ### What's already been done in the hero_work session (2026-05-22) - Filed this issue ([#14](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/14)) — original ask: adopt canonical `lab-publish.yaml` to replace tag-triggered `build-linux.yaml`. - Ran `lab build --release --install --workspace` locally from `development_mik` — surfaced 5 walls (full detail at [#issuecomment-35892](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/14#issuecomment-35892)). - Reverted lab's `policy-mode apply` mutations on 4 `Cargo.toml`s + cleaned the `crates/hero_assistance_sdk/src/generated/` artifacts. Repo is at `origin/development` clean. - Did NOT push any code change to this repo. No branch was kept. Issue is the only artifact. ### The 5 walls (recap — full detail in [#issuecomment-35892](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/14#issuecomment-35892)) | # | Wall | Severity | Closes via | |---|---|---|---| | 1 | `hero_assistance_server` won't compile (49 × `u64: ToSql`) | **blocker — `cargo build` fails** | mechanical u64→i64 sweep | | 2 | 4 binaries don't implement `--info` | high — lab marks build failed | `service_base!()` adoption per binary | | 3 | No `service.toml` files anywhere | high — required by `service_base!()` | write 4-5 `service.toml`s | | 4 | `lab --workspace` ignores `default-members` | medium — triggers WASM-as-native build attempts | upstream lab fix OR per-package invocation OR workspace restructure | | 5 | `lab build` default `--policy-mode apply` mutates Cargo.toml | low — recoverable, just don't accept the policy edits | run with `--policy-mode warn` during dev | --- ### Phase plan The phases are strictly sequential up through Phase 3. Phases 4 and 5 can interleave once Phase 3 has produced a clean local `lab build --release --install --workspace`. #### Phase 1 — Fix the rusqlite `u64: ToSql` compile failure **Scope.** `crates/hero_assistance_server/src/handlers/workspace.rs` (lines 108 + 148 confirmed; 49 total `E0277` errors — likely siblings in the same file + related handlers). **Root cause.** `rusqlite` ≥ 0.31 removed `impl ToSql for u64` (SQLite's storage type is `i64`; `u64` can't be losslessly represented). The 0.5.0 → 0.6.0 dep-lift in s49 pulled in a rusqlite version past 0.31. Code currently does `let id = db.last_insert_rowid() as u64;` and then tries to pass that `u64` back through `rusqlite::params![input.id]`. **Decision needed (lock at session start).** Two fix shapes: | | (1a) cast-at-boundary | (1b) change ID type throughout | |---|---|---| | Mechanic | `params![input.id as i64]` everywhere | `id` fields become `i64` end-to-end (struct → handler → JSON) | | Scope | only handler files | structs, JSON serde, DB schema, public OpenRPC types | | Wire compat | identical (JSON numbers unchanged) | identical (JSON numbers unchanged) — but type changes leak to SDK consumers | | Risk | low (mechanical) | medium (touches public API surface) | | Effort | ~1 hour | ~3 hours + SDK regen | | Long-term hygiene | papers over the impedance mismatch | cleaner; matches what SQLite actually stores | **Recommendation: 1a** for this session. The id range never exceeds i64::MAX in any realistic scenario (`last_insert_rowid()` is i64-native), so the cast is functionally identical. Filing a follow-up issue for 1b lets it land cleanly in a future session that owns the SDK regen. **Deliverable.** Single commit on `development_<topic>`: `crates/hero_assistance_server/src/handlers/workspace.rs` (and any sibling handler that fails the same way — discover via `cargo build --release -p hero_assistance_server 2>&1 | grep -E "u64.*ToSql"`). **Acceptance.** `cargo build --release` exits 0. The Phase 1 unit test that pins the workspace.rs behavior continues to pass (or one gets added if the regression boundary isn't currently pinned). **Estimated effort.** 1 hour. --- #### Phase 2 — Add `service.toml` to each service-style binary **Scope.** Five binaries need a `service.toml` next to their `src/main.rs`: | crate | role | reference repo's service.toml | |---|---|---| | `crates/hero_assistance_server` | rpc server (mycelium-bound) | [hero_cockpit/crates/hero_cockpit_server/service.toml](https://forge.ourworld.tf/lhumina_code/hero_cockpit/src/branch/development/crates/hero_cockpit_server/service.toml) | | `crates/hero_assistance_ui` | customer SPA dist + `/rpc` proxy + `app.sock` | [hero_cockpit/crates/hero_cockpit_web/service.toml](https://forge.ourworld.tf/lhumina_code/hero_cockpit/src/branch/development/crates/hero_cockpit_web/service.toml) (closest analogue — web service) | | `crates/hero_assistance_admin` | admin SPA dist + `admin.sock` + ip-whitelist gate | [hero_cockpit/crates/hero_cockpit_admin/service.toml](https://forge.ourworld.tf/lhumina_code/hero_cockpit/src/branch/development/crates/hero_cockpit_admin/service.toml) | | `crates/hero_assistance` | cli | [hero_cockpit/crates/hero_cockpit/service.toml](https://forge.ourworld.tf/lhumina_code/hero_cockpit/src/branch/development/crates/hero_cockpit/service.toml) | | `crates/hero_assistance_app` | desktop binary | likely needs a custom shape — there's no clean precedent in cockpit. Cross-check against any existing Dioxus-desktop Hero service. | **Spec source of truth.** [`hero_service_toml_info`](https://forge.ourworld.tf/lhumina_code/hero_skills) skill defines the schema + typed enums + the mandatory `--info` contract. Use the skill's authoritative format; do NOT reverse-engineer from sibling repos. **Deliverable.** 5 new files under `crates/*/service.toml`. Each commits cleanly (single small commit per crate, or one bundled commit per Phase 2 — operator's choice). **Acceptance.** Every file passes `lab infocheck` (the lab tool's smoke check that the embedded toml deserializes against the canonical schema). Reference: [hero_skills#265](https://forge.ourworld.tf/lhumina_code/hero_skills/issues/265) caveat — `lab infocheck` only inspects `src/main.rs`, so if a service has its main at `src/bin/<name>.rs` it'll miss the file. Hero_assistance's structure should be checked at session start. **Estimated effort.** ~2 hours. --- #### Phase 3 — Wire `service_base!()` + the `--info` contract in each binary's `main.rs` **Scope.** The macro in [`herolib_base`](https://forge.ourworld.tf/lhumina_code/hero_skills) inlines: 1. `static SERVICE_TOML: &str = include_str!("../service.toml");` 2. `static BUILD_NR: u32 = …;` (compile-time embedded build number) 3. A `--info` flag handler that emits the service.toml as structured JSON when called as `<bin> --info --json` (or human-readable on plain `--info`) **Critical lesson #20** (from CLAUDE.md / s110+s119): `service_base!()` hard-codes `include_str!("../service.toml")` which **breaks when main lives at `src/bin/<name>.rs`** (two-level deep). Workarounds: (a) inline SERVICE_TOML/BUILD_NR with `../../service.toml`, OR (b) move the bin to `src/main.rs`. Hero_assistance's binary layout needs to be confirmed at session start — if any binary lives at `src/bin/`, choose (b) over (a) for `lab infocheck` compatibility. **Special handling for `hero_assistance_app` (Dioxus desktop).** Per the Phase 2 lab probe, this binary's current `--info` runs the actual app instead of emitting JSON. Fix shape: ```rust fn main() { // --info short-circuit BEFORE any window/event-loop init if std::env::args().any(|a| a == "--info") { herolib_base::print_service_info(); // or equivalent helper return; } // existing dioxus::desktop::launch path follows here } ``` The check must precede any blocking init (the current `--info` invocation produced log output about `aggregator stream ended` — the app was running normally). **Deliverable.** `crates/*/src/main.rs` updated in each of the 5 binaries. The `Cargo.toml` of each needs `herolib_base` (or `service_base` — whichever the skill defines) as a dep at the pinned shared version per [`rust_versions`](https://forge.ourworld.tf/lhumina_code/hero_skills). **Acceptance.** Each binary's `--info` and `--info --json` returns valid output that parses against the schema. `lab build` smoke check passes for all 5. **Estimated effort.** ~2 hours (mechanical once Phase 2 service.tomls exist). --- #### Phase 4 — Resolve `lab --workspace` iterating WASM crates (Wall 4) **The problem.** Empirically: `lab build --release --install --workspace` discovers 8 binary targets including `hero_assistance_admin_ui_wasm` and `hero_assistance_ui_wasm` (both are `members` but excluded from `[workspace.default-members]`). These are wasm32-only and have no native main; they exit 101 when lab tries `--info` on them. **Three paths (lock decision before starting Phase 4):** **(4a) File upstream lab fix, use per-package workaround in the meantime.** File a [hero_skills](https://forge.ourworld.tf/lhumina_code/hero_skills) issue: "`lab build --workspace` should honour `[workspace.default-members]` (matches cargo's semantics, but documented)". Until that lands, use either: - `lab build --release --install --packages hero_assistance_server,hero_assistance_ui,hero_assistance_admin,hero_assistance,hero_assistance_app` (if lab exposes `--packages` — needs verification; see [hero_skills#268](https://forge.ourworld.tf/lhumina_code/hero_skills/issues/268)) - OR per-bin loop: `for bin in server ui admin cli app; do lab build --release --install --bin hero_assistance_$bin; done` The CI workflow YAML would mirror whichever local pattern works. **(4b) Workspace restructure** — move the two WASM crates to a sibling workspace at `crates_wasm/`. Pros: clean separation; lab can't see them. Cons: bigger commit, touches `[workspace.members]`, may affect existing tooling (the `make dist` flow currently runs from the same root). Effort: ~half-day. **(4c) Per-crate `[package.metadata.lab] skip = true` annotation** — if lab supports such an opt-out. Cleanest if it exists; needs verification by reading lab's source under [hero_skills](https://forge.ourworld.tf/lhumina_code/hero_skills). **Recommendation: 4c if it exists, else 4a.** 4b is the safety net if both fail. **Deliverable.** Either the `[package.metadata.lab]` annotations OR the workflow-level package list OR the workspace split. Either way, `lab build --release --install --workspace` from the repo root should iterate exactly the 5 native-targetable binaries (server, ui, admin, cli, app) — not the 2 WASM crates. **Acceptance.** `lab build --release --install --workspace` from a clean checkout discovers 5 binaries, builds 5, fails 0. **Estimated effort.** 4a: ~1 hour (file the issue, adjust workflow YAML). 4b: ~half-day. 4c: ~1 hour (verify + apply). --- #### Phase 5 — Handle `hero_assistance_app` GTK/webkit deps in the CI builder image **Pre-flight finding from hero_work session.** `webkit2gtk-4.1` IS installed on the workstation (probed via `pkg-config --exists webkit2gtk-4.1`), and the desktop crate built fine locally. The wall is **only** the CI builder image (`ghcr.io/despiegk/builder:latest`) which doesn't ship GTK/webkit deps for server-CI workloads. **Two options:** **(5a) Add an apt-install step to lab-publish.yaml.** Insert before the "Build + upload" step: ```yaml - name: Setup GTK/webkit (for hero_assistance_app) run: | set -euo pipefail apt-get update -qq apt-get install -y --no-install-recommends \ libwebkit2gtk-4.1-dev libgtk-3-dev libssl-dev pkg-config ``` This **deviates from the canonical drop-in shape** of hero_slides/cockpit/onboarding workflows. The deviation is principled (one repo has a desktop crate; the other ~33 don't) — file as a known divergence in the commit message and in this issue's closing comment so future readers understand why hero_assistance's workflow isn't byte-identical to the canonical. **(5b) Exclude `hero_assistance_app` from the `lab build --workspace` set entirely; build it separately on a tag-triggered desktop-only workflow.** Pros: keeps the canonical workflow byte-identical for hero_assistance's server/ui/admin/cli (the customer-facing pieces). Cons: desktop binary doesn't ship on `latest` — only on tagged releases. Acceptable if desktop is a tag-release-only artifact anyway (consistent with how desktop apps are normally distributed). **Recommendation: 5a** if the team wants the desktop binary on `latest` (matches the rest of the Hero binary set's cadence); **5b** if the desktop binary is acceptable as a tag-release-only artifact. **Deliverable.** Modified `.forgejo/workflows/lab-publish.yaml` (5a) OR a second tag-triggered workflow file for desktop builds (5b). **Acceptance.** CI run on a push to `development` produces all targeted binaries on `releases/tag/latest` with md5 sidecars. **Estimated effort.** ~30 min either way. --- #### Phase 6 — Drop in the workflow, remove the old one **Scope.** 1. `cp <hero_slides reference> .forgejo/workflows/lab-publish.yaml` (with the Phase 5 modification if 5a was chosen). Reference for canonical YAML: [hero_slides/.forgejo/workflows/lab-publish.yaml](https://forge.ourworld.tf/lhumina_code/hero_slides/src/branch/development/.forgejo/workflows/lab-publish.yaml). 2. `git rm .forgejo/workflows/build-linux.yaml` — superseded. **Pre-flight check.** Verify `FORGEJO_TOKEN` is present in this repo's Actions secrets (or inherited org-wide). Both hero_cockpit and hero_onboarding inherited it org-wide and didn't need a per-repo add; this repo should be the same, but worth checking before the run. **Deliverable.** One commit on `development_<topic>` that lands the workflow + removes the old file. Squash-merge to `development` per this repo's standing rules. **Acceptance.** 1. The post-push workflow run completes with status=success. 2. `releases/tag/latest` exists on this repo with `linux-musl-x86_64` binaries + md5 sidecars for all 5 native-targetable binaries (or 4 if Phase 5b was chosen). 3. Issue [#14](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/14) closes automatically via the commit's `Closes` keyword. **Estimated effort.** 30 min. --- ### Decisions to lock at session start Don't start writing code until these are nailed down — re-deciding mid-session costs more than the few minutes of upfront discussion. The recommendation column is the hero_work session's read; the operator decides. | Decision | Options | Recommendation | Lock at | |---|---|---|---| | Phase 1 — u64 fix shape | (1a) cast at boundary / (1b) change ID type throughout | 1a (mechanical, this session); file (1b) as follow-up | Phase 1 plan | | Phase 4 — WASM exclusion | (4a) per-package CI / (4b) workspace restructure / (4c) lab metadata annotation | 4c if it exists, else 4a; 4b only as safety net | Phase 4 plan | | Phase 5 — desktop in CI | (5a) apt install in workflow / (5b) tag-only desktop workflow | 5a if desktop should ship on `latest`; 5b otherwise | Phase 5 plan | ### Anti-patterns to avoid (warnings from sibling sessions) - **Don't run `lab build` with default `--policy-mode apply`.** Use `--policy-mode warn` until the dep policy is intentionally adopted. (Wall 5.) - **Don't follow hero_work's `Signed-by: mik-tf <mik-tf@noreply.invalid>` trailer convention.** This repo's CLAUDE.md is explicit: `mik-tf <logismos@protonmail.ch>`, no co-author trailers. Use this repo's pattern. - **Don't rebase to bring in upstream `development`.** This repo's hero-stack-wide rule: `git pull && git merge development` into the feature branch. - **Don't run `git commit -s`.** Per the Hero stack standing rule, this auto-injects `Signed-off-by` with a real email. Use plain `git commit -m "..."` with the message body. - **Don't merge before the local `lab build --release --install --workspace` is clean.** Phase 6 is the last step, not the first. CI is a regression gate, not a build orchestrator. ### Reference materials (read before starting) - [`hero_service_check_fix` skill](https://forge.ourworld.tf/lhumina_code/hero_skills) — primary recipe for Phases 2 + 3. - [`herolib_base` skill](https://forge.ourworld.tf/lhumina_code/hero_skills) — `service_base!()` macro + `--info` contract. - [`hero_service_toml_info` skill](https://forge.ourworld.tf/lhumina_code/hero_skills) — service.toml schema. - [`rust_versions` skill](https://forge.ourworld.tf/lhumina_code/hero_skills) — pinned dep versions for `herolib_base` etc. - [`naming_convention` skill](https://forge.ourworld.tf/lhumina_code/hero_skills) — verify all 5 binary names match the standard before committing. - [`hero_skills#268`](https://forge.ourworld.tf/lhumina_code/hero_skills/issues/268) — org-wide lab-publish rollout context. - [`hero_cockpit/crates/hero_cockpit_server/service.toml`](https://forge.ourworld.tf/lhumina_code/hero_cockpit/src/branch/development/crates/hero_cockpit_server/service.toml) — canonical server service.toml. - [`hero_slides/.forgejo/workflows/lab-publish.yaml`](https://forge.ourworld.tf/lhumina_code/hero_slides/src/branch/development/.forgejo/workflows/lab-publish.yaml) — canonical workflow YAML (Phase 6 source). ### Estimated total effort - **Optimistic (1 session, ~6 hours):** Phase 1 mechanical + Phase 4c exists + Phase 5b accepted. - **Realistic (1.5-2 sessions):** Phase 1 + 2 + 3 in one session, Phase 4 + 5 + 6 in a second. - **Pessimistic (3 sessions):** Phase 1 in a standalone session, Phases 2 + 3 in a second, Phase 4 + 5 + 6 in a third — if any phase surfaces follow-up walls. ### Acceptance for closing this issue - [ ] `cargo build --release` exits 0 from a fresh `cargo update` (Phase 1 closes) - [ ] `lab build --release --install --workspace` exits 0 with 5 (or 4 if 5b) native binaries built + installed (Phases 2-4 close) - [ ] `.forgejo/workflows/lab-publish.yaml` lands on `development`; `build-linux.yaml` is removed in the same commit (Phase 6) - [ ] First post-push CI run produces `releases/tag/latest` with the expected binary set + md5 sidecars (Phase 6)
Author
Owner

Closing — all 3 acceptance bullets met

Round 1 — f81aecc (CI run #6, partial)

First push landed lab-publish.yaml + 4× service.toml + 4× service_base!() + git rm build-linux.yaml. CI ran but uploaded only 2/4 binaries before hitting two walls the prior session's local check did NOT catch:

  • Wall 1 — 49× u64: ToSql errors in hero_assistance_server. Root cause: local lab build --policy-mode warn flags-but-doesn't-rewrite; CI defaults to --policy-mode apply which silently lifted rusqlite 0.31 → 0.39 (where the u64: ToSql impl was removed).
  • Wall 2openssl-sys cross-compile failure for hero_assistance CLI on linux-musl-x86_64. Root cause: reqwest pulled in hyper-tls → native-tls → openssl-sys via default features; the canonical builder image doesn't ship musl libssl-dev.

Round 2 — c059c1a (CI run #7, GREEN)

Fix-forward closing both walls:

  • Lifted workspace rusqlite 0.31 → 0.39 explicitly so local + CI build the same thing; added as i64 casts at every rusqlite::params! call site that took a u64 expression (49+ sites across 9 handler files; functionally identical — same i64 round-tripped at SELECT time via the existing r.get::<_, i64>(0).map(|v| v as u64) pattern).
  • Promoted reqwest to [workspace.dependencies] with default-features = false, features = ["json", "rustls-tls"] mirroring hero_cockpit's canonical pattern. Post-swap cargo tree -i openssl-sys shows the dep only reaches _app desktop + _ui_wasm (both excluded from the CI --package filter).

Local acceptance gates run before push:

  • cargo build --release (default-members native) clean in 34s
  • cargo build --release --target x86_64-unknown-linux-musl --package <4 CI bins> clean in 1m45s, all 4 produced as static-pie musl ELF64
  • cargo test --release workspace green except pre-existing phase24b_ui_add_access_fails_when_hero_proc_unreachable (environmental — depends on hero_proc NOT being reachable)

Acceptance walked

  • lab build --release --install --workspace exits 0 locally (via --package filter for _server, _ui, _admin, hero_assistance — the deviation from canonical --workspace documented in the workflow YAML comment because _app Dioxus desktop binary requires glibc-only webkit2gtk-sys)
  • .forgejo/workflows/lab-publish.yaml lands on development; build-linux.yaml removed in the same commit
  • First post-push CI run succeeds (run #7 at 13:02 UTC); releases/tag/latest shows refreshed linux-musl-x86_64 binaries + md5 sidecars — 8 assets total:
hero_assistance-linux-musl-x86_64           2,207,172 bytes
hero_assistance-linux-musl-x86_64.md5              32 bytes
hero_assistance_admin-linux-musl-x86_64     2,153,400 bytes
hero_assistance_admin-linux-musl-x86_64.md5        32 bytes
hero_assistance_server-linux-musl-x86_64    3,150,836 bytes
hero_assistance_server-linux-musl-x86_64.md5       32 bytes
hero_assistance_ui-linux-musl-x86_64        3,266,932 bytes
hero_assistance_ui-linux-musl-x86_64.md5           32 bytes

(Binaries UPX-compressed by lab-publish.yaml; ~25% of original size, consistent with the org-wide pattern.)

Follow-up — naming-suffix divergence from #13

Canonical asset suffix is now linux-musl-x86_64. This is INCOMPATIBLE with svc_install_download's legacy expectation of linux-amd64. See #13 for the three options (recommended: update svc_install_download in hero_skills to accept both suffixes; aligns with hero_skills#268).

Closing this issue.

Signed-by: mik-tf mik-tf@noreply.invalid

## Closing — all 3 acceptance bullets met ✅ ### Round 1 — [`f81aecc`](https://forge.ourworld.tf/lhumina_code/hero_assistance/commit/f81aecc) (CI run #6, partial) First push landed lab-publish.yaml + 4× service.toml + 4× `service_base!()` + `git rm build-linux.yaml`. CI ran but uploaded only 2/4 binaries before hitting two walls the prior session's local check did NOT catch: - **Wall 1** — 49× `u64: ToSql` errors in `hero_assistance_server`. Root cause: local `lab build --policy-mode warn` flags-but-doesn't-rewrite; CI defaults to `--policy-mode apply` which silently lifted `rusqlite 0.31 → 0.39` (where the `u64: ToSql` impl was removed). - **Wall 2** — `openssl-sys` cross-compile failure for `hero_assistance` CLI on `linux-musl-x86_64`. Root cause: `reqwest` pulled in `hyper-tls → native-tls → openssl-sys` via default features; the canonical builder image doesn't ship musl libssl-dev. ### Round 2 — [`c059c1a`](https://forge.ourworld.tf/lhumina_code/hero_assistance/commit/c059c1a) (CI run #7, GREEN) Fix-forward closing both walls: - Lifted workspace `rusqlite 0.31 → 0.39` explicitly so local + CI build the same thing; added `as i64` casts at every `rusqlite::params!` call site that took a u64 expression (49+ sites across 9 handler files; functionally identical — same i64 round-tripped at SELECT time via the existing `r.get::<_, i64>(0).map(|v| v as u64)` pattern). - Promoted `reqwest` to `[workspace.dependencies]` with `default-features = false, features = ["json", "rustls-tls"]` mirroring hero_cockpit's canonical pattern. Post-swap `cargo tree -i openssl-sys` shows the dep only reaches `_app` desktop + `_ui_wasm` (both excluded from the CI `--package` filter). Local acceptance gates run before push: - `cargo build --release` (default-members native) clean in 34s - `cargo build --release --target x86_64-unknown-linux-musl --package <4 CI bins>` clean in 1m45s, all 4 produced as static-pie musl ELF64 - `cargo test --release` workspace green except pre-existing `phase24b_ui_add_access_fails_when_hero_proc_unreachable` (environmental — depends on hero_proc NOT being reachable) ### Acceptance walked - [x] `lab build --release --install --workspace` exits 0 locally (via `--package` filter for `_server`, `_ui`, `_admin`, `hero_assistance` — the deviation from canonical `--workspace` documented in the workflow YAML comment because `_app` Dioxus desktop binary requires glibc-only `webkit2gtk-sys`) - [x] `.forgejo/workflows/lab-publish.yaml` lands on `development`; `build-linux.yaml` removed in the same commit - [x] First post-push CI run succeeds (run #7 at 13:02 UTC); `releases/tag/latest` shows refreshed `linux-musl-x86_64` binaries + md5 sidecars — 8 assets total: ``` hero_assistance-linux-musl-x86_64 2,207,172 bytes hero_assistance-linux-musl-x86_64.md5 32 bytes hero_assistance_admin-linux-musl-x86_64 2,153,400 bytes hero_assistance_admin-linux-musl-x86_64.md5 32 bytes hero_assistance_server-linux-musl-x86_64 3,150,836 bytes hero_assistance_server-linux-musl-x86_64.md5 32 bytes hero_assistance_ui-linux-musl-x86_64 3,266,932 bytes hero_assistance_ui-linux-musl-x86_64.md5 32 bytes ``` (Binaries UPX-compressed by lab-publish.yaml; ~25% of original size, consistent with the org-wide pattern.) ### Follow-up — naming-suffix divergence from #13 Canonical asset suffix is now `linux-musl-x86_64`. This is INCOMPATIBLE with `svc_install_download`'s legacy expectation of `linux-amd64`. See [#13](https://forge.ourworld.tf/lhumina_code/hero_assistance/issues/13) for the three options (recommended: update svc_install_download in hero_skills to accept both suffixes; aligns with [hero_skills#268](https://forge.ourworld.tf/lhumina_code/hero_skills/issues/268)). Closing this issue. Signed-by: mik-tf <mik-tf@noreply.invalid>
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_assistance#14
No description provided.