Productionize: real data everywhere (Phase B) + shareable unsigned macOS .dmg (Phase C) #52

Merged
christian merged 5 commits from feat/kill-fixtures into main 2026-07-09 19:23:39 +00:00
Owner

Productionizes CommiTea for team sharing: all screens on real data (Phase B) and a shareable unsigned macOS .dmg (Phase C).

Phase B — real data for the last fixture screens

Every remaining fixture-only surface now renders reconciled gitea data; the fixture stays only as the no-config demo fallback.

  • lib/views/project-data.tsProjectData, the uniform input (backlog + deps + timelines + calibration + workers + today), assembled once in AppShell.
  • Five pure builders (issue-detail, gantt-view, deps-graph, standup-view, inbox-view): ProjectData → the fixture-shaped object each screen already renders. Real signals only, honest degradation where a signal isn't derivable (buffered p80 vs per-issue Monte Carlo; flat "idle in review"; no fabricated inbox mentions/outages).
  • Screens take optional data? and fall back to the fixture; AppShell wires the real view once the backlog is reconciled. Issue "blocks" chips + rail inbox badge resolve from real data too.
  • Dev-only rail surfaces (First run / States / Primitives) gated on import.meta.env.DEV || demo — hidden in a real packaged app. Rail host label reflects the connected instance.

Phase C — package as an unsigned macOS .dmg

  • electron-builder@25 + electron-builder.yml: appId io.stephenmann.commitea, productName CommiTea, logo icon, macOS dmg for arm64 + x64. Ships only out/** (electron-vite already bundles renderer/preload/main with core + react inlined); npmRebuild off; electronVersion pinned (workspace hoisting defeats auto-detect).
  • Unsigned by decision: mac.identity: null, hardenedRuntime off (ad-hoc signed so arm64 runs; not notarized — teammates right-click → Open once).
  • Scripts yarn pack / yarn dist; README.md documents build + the Gatekeeper install step.

Verification

  • typecheck + prod build clean; 14 fixture e2e green.
  • Live onboarding e2e extended to click Board → Gantt → Deps → Issue sidecar → Standup → Inbox on real gitea data with a pageerror guard → zero uncaught render errors.
  • Packaged arm64 app boots from a fresh profile (lands on onboarding); yarn dist produced CommiTea-0.1.0-arm64.dmg (98M) + CommiTea-0.1.0.dmg (x64, 102M), each mounting drag-to-Applications.

Base / stacking

Based on main so merging lands on main (avoids the stacked-merge trap). Carries the Phase A commit (#51) until that merges; merge #51 first and this reduces to Phase B+C, or merge this and #51 closes as included.

Deferred (follow-up)

Real issue comments in the sidecar (not reconciled yet); standup "overnight drift" needs a prior-snapshot diff to be genuinely historical; notarization/Windows are out of scope per the distribution decision.

🤖 Generated with Claude Code

Productionizes CommiTea for team sharing: **all screens on real data** (Phase B) and a **shareable unsigned macOS .dmg** (Phase C). ## Phase B — real data for the last fixture screens Every remaining fixture-only surface now renders reconciled gitea data; the fixture stays only as the no-config demo fallback. - **`lib/views/project-data.ts`** — `ProjectData`, the uniform input (backlog + deps + timelines + calibration + workers + today), assembled once in AppShell. - **Five pure builders** (`issue-detail`, `gantt-view`, `deps-graph`, `standup-view`, `inbox-view`): `ProjectData →` the fixture-shaped object each screen already renders. Real signals only, **honest degradation** where a signal isn't derivable (buffered `p80` vs per-issue Monte Carlo; flat "idle in review"; no fabricated inbox mentions/outages). - Screens take optional `data?` and fall back to the fixture; AppShell wires the real view once the backlog is reconciled. Issue "blocks" chips + rail inbox badge resolve from real data too. - Dev-only rail surfaces (First run / States / Primitives) gated on `import.meta.env.DEV || demo` — hidden in a real packaged app. Rail host label reflects the connected instance. ## Phase C — package as an unsigned macOS .dmg - **electron-builder@25** + `electron-builder.yml`: appId `io.stephenmann.commitea`, productName CommiTea, logo icon, macOS dmg for **arm64 + x64**. Ships only `out/**` (electron-vite already bundles renderer/preload/main with core + react inlined); `npmRebuild` off; `electronVersion` pinned (workspace hoisting defeats auto-detect). - **Unsigned** by decision: `mac.identity: null`, hardenedRuntime off (ad-hoc signed so arm64 runs; not notarized — teammates right-click → Open once). - Scripts `yarn pack` / `yarn dist`; `README.md` documents build + the Gatekeeper install step. ## Verification - typecheck + prod build clean; 14 fixture e2e green. - **Live onboarding e2e** extended to click Board → Gantt → Deps → Issue sidecar → Standup → Inbox on **real gitea data** with a `pageerror` guard → zero uncaught render errors. - Packaged arm64 app boots from a fresh profile (lands on onboarding); `yarn dist` produced `CommiTea-0.1.0-arm64.dmg` (98M) + `CommiTea-0.1.0.dmg` (x64, 102M), each mounting drag-to-Applications. ## Base / stacking Based on `main` so merging lands on main (avoids the stacked-merge trap). Carries the Phase A commit (#51) until that merges; merge **#51 first** and this reduces to Phase B+C, or merge this and #51 closes as included. ## Deferred (follow-up) Real issue comments in the sidecar (not reconciled yet); standup "overnight drift" needs a prior-snapshot diff to be genuinely historical; notarization/Windows are out of scope per the distribution decision. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
christian added 2 commits 2026-07-09 06:40:12 +00:00
The foundation for a shareable team build. Replaces the .env.local-only dev config
with a real, per-teammate connection flow.

main:
- config-store.ts: token encrypted at rest via Electron safeStorage (OS keychain),
  config JSON in userData. Token lives only in main; renderer gets everything but.
- resolveConfig: saved config > .env.local (dev) > null; ignored under COMMITEA_E2E.
  pm-state repo defaults to `${repo}-pm-state`. resetClients() re-reads on change so
  saving config takes effect without a restart. gitea:status gains `demo` (e2e).
- IPC: config:get (no token), config:test (authed read validates token+repo),
  config:set (encrypt+save+reset), config:clear. Model bridge reads config.modelUrl
  and probes reachability — chat is "configured" only if a model actually answers;
  localhost default is dev-only (app.isPackaged gate).

renderer:
- ConnectScreen: real onboarding form (URL/owner/repo/PAT/optional model) → test →
  save. AppShell gates on it: demo → shell (fixtures/e2e); configured → shell (real);
  else → connect. Settings Connection card is real (repo/url/model/sidecar) with
  Reconfigure + Disconnect. Chat cleanly disables with a "no model" state instead of
  the scripted canned reply.

Verified: main + desktop typecheck clean, 14 fixture e2e green (demo mode unchanged),
live onboarding e2e: fresh app → connect form → validated PAT → real board (24 done /
10 open). COMMITEA_NO_ENV_LOCAL + COMMITEA_USERDATA are test hooks for the onboarding path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every remaining fixture-only surface now renders reconciled gitea data,
with the fixture kept only as the no-config demo fallback.

- lib/views/project-data.ts: ProjectData — the uniform input (reconciled
  backlog + deps + timelines + calibration + workers + today) that AppShell
  assembles once and every view builder consumes.
- lib/views/{issue-detail,gantt-view,deps-graph,standup-view,inbox-view}.ts:
  pure builders, ProjectData → the fixture-shaped object each screen already
  renders. Real signals only; honest degradation where a signal isn't derivable
  (buffered p80 vs per-issue Monte Carlo; flat "idle in review"; no fabricated
  inbox mentions/outages).
- Screens take an optional `data?` and fall back to the fixture; AppShell wires
  the real view whenever the backlog is reconciled. Issue "blocks" chips and the
  rail inbox badge now resolve from real data too.
- Dev-only rail surfaces (First run / States / Primitives) gated on
  import.meta.env.DEV || demo — shown in dev + e2e, hidden in a packaged app.
  Rail host label reflects the connected instance.
- Extended the live onboarding e2e to click Board → Gantt → Deps → Issue
  sidecar → Standup → Inbox on real gitea data with a pageerror guard; 14
  fixture e2e stay green, typecheck + prod build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
christian changed title from Phase B: real data for the last fixture screens to Productionize: real data everywhere (Phase B) + shareable unsigned macOS .dmg (Phase C) 2026-07-09 06:47:20 +00:00
christian added 1 commit 2026-07-09 06:47:21 +00:00
Adds shareable desktop packaging so the app can be handed to teammates.

- electron-builder@25 + electron-builder.yml: appId io.stephenmann.commitea,
  productName CommiTea, icon from the logo (build/icon.png, 1024²), macOS dmg
  targets for arm64 + x64. Ships only out/** (electron-vite already bundles
  renderer/preload/main with core + react inlined), npmRebuild off,
  electronVersion pinned to 34.5.8 (workspace hoisting defeats auto-detect).
- Unsigned by decision: mac.identity null, hardenedRuntime off. electron-builder
  ad-hoc signs so arm64 runs; not notarized — teammates right-click → Open once.
- Scripts: `yarn pack` (--dir sanity build), `yarn dist` (both dmgs). package.json
  gains version/author/description (required by electron-builder).
- README.md: dev/build/package commands + the Gatekeeper install step.

Verified: packaged arm64 app boots from a fresh profile (lands on onboarding);
`yarn dist` produced CommiTea-0.1.0-arm64.dmg (98M) and CommiTea-0.1.0.dmg (x64,
102M), each mounting with a drag-to-Applications layout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
christian added 1 commit 2026-07-09 14:41:26 +00:00
christian added 1 commit 2026-07-09 14:53:34 +00:00
scripts/dogfood-report.ts drives the real @commitea/core engine (schedule +
capacity-aware Monte Carlo forecast + per-milestone runway) against the live
christian/commitea backlog and prints the project report each screen derives.
Read-only; `yarn tsx scripts/dogfood-report.ts`. Adds tsx as a devDependency.

First run surfaced two real gaps the fixtures hid: every open issue was
unassigned (so capacity load-balanced work onto the slow half-time lane and the
standup plan-per-person was empty) and no milestone had a due date (so Runway
couldn't judge on-track/at-risk). Both were fixed as PM actions on the repo via
the write path — all open issues assigned, milestone due dates synthesized from
the forecast — so the app's own numbers are now honest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
christian added 1 commit 2026-07-09 15:23:02 +00:00
ganttView ran schedule() — the single-serial-worker layout — while the Monte
Carlo forecast ran scheduleWithCapacity() over the real lanes. So the bars you
saw didn't match what was forecast (serial 1.0/day vs the team's actual lanes).

Switch ganttView to scheduleWithCapacity(open, deps, workers): startDay/endDay
now come from the same lane layout each forecast trial uses, and `who` shows the
lane an issue actually landed on (falling back to assignee, then blank). The
dogfood harness had the same split — updated it to match and to print the lane
per row.

Verified: typecheck clean, 14 fixture e2e green, live-onboarding e2e renders the
real capacity-aware Gantt with no uncaught errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
christian merged commit 86e9a49c08 into main 2026-07-09 19:23:39 +00:00
Sign in to join this conversation.
No description provided.