live: heartbeat sync 2026-10-20T15:15:41-07:00 (Refs #11)

This commit is contained in:
Frank
2026-10-20 15:15:41 -07:00
parent 495e2229f5
commit 6af3714bd0
10 changed files with 193 additions and 5 deletions
@@ -0,0 +1,55 @@
---
name: franksign-worktree-db-cred-trap
description: "Diagnose franksign e2e seam 5 / health 503 db:down in a fresh worktree: setup-dev.sh minted a password that mismatches the shared franksign-db container."
---
# franksign fresh-worktree DB credential trap
## Symptom
`tests/run.sh` in a fresh franksign worktree fails seam 5 ("e2e server did not
become healthy") while the server log says `franksign-api listening on :3100`.
Manual probe shows the server IS up:
```bash
PORT=3100 node --env-file=.env apps/api/dist/cli.js >/tmp/manual-e2e.log 2>&1 &
sleep 2; curl -s http://127.0.0.1:3100/health
# -> 503 {"status":"degraded","db":"down",...}
```
Not a regression, not a :3100 port collision (check `ss -tlnp | grep 3100` to rule that out).
## Root cause
`infra/setup-dev.sh` mints a fresh `POSTGRES_PASSWORD` per checkout
(`if [ ! -f infra/postgres.env ]`), but `franksign-db` + `franksign-pgdata`
are shared box state owned by the main checkout
(`~/Projects/franksign`). The new worktree's `.env` DATABASE_URL then fails
TCP auth against the initialized volume.
The setup script masks it: its readiness gate is
`podman exec franksign-db pg_isready`, which uses the in-container local
socket and passes regardless of password — so setup prints "postgres ready"
while the checkout is dead on arrival.
## Fix (git-ignored files, no commit needed)
Copy the shared credentials from the owning checkout:
```bash
grep DATABASE_URL /home/blank/Projects/franksign/.env # source of truth
# PUT that exact DATABASE_URL line into this worktree's .env
# PUT the matching POSTGRES_PASSWORD into this worktree's infra/postgres.env
```
Gotcha: the shared password may contain literal `$$`. Keep it byte-for-byte —
systemd `EnvironmentFile` and `node --env-file` both read values literally,
so `$$` in the file IS `$$` in the password. No escaping, no de-escaping.
## Verify
```bash
PORT=3100 node --env-file=.env apps/api/dist/cli.js & # then
curl -fsS http://127.0.0.1:3100/health # -> {"status":"ok","db":"up"}
tests/run.sh # 14/14
```
## Permanent fix (proposed, not yet landed)
`setup-dev.sh` should reuse `~/.config/containers/systemd/postgres.env`
(symlink to the owning checkout) when present instead of minting new creds.
@@ -0,0 +1,45 @@
---
name: land-branch-without-checkout
description: "Open a PR from a shared worktree when workspace policy blocks git checkout/restore: push a named branch ref from HEAD without switching branches."
---
# Landing a branch when `git checkout` is policy-blocked
Shared-workspace policy blocks `git checkout` and `git restore` (working-tree
switches could strand concurrent agents' uncommitted work). You still must ship
the commit on the current worktree branch as a named `chore|feat|fix/<issue>-<slug>`
branch. Never work around the block — go around it via refs.
## Sequence
```bash
# 1. commit only your files (atomic); leave other dirty paths alone
git add <your-files> && git commit -m "..."
# 2. branch ref at HEAD — no working-tree change, stays on wt/<...>
git branch <type>/<issue>-<slug> HEAD
git push origin <type>/<issue>-<slug> # pre-push hooks still run
# 3. PR via tracker API with head = pushed ref (see gitea-pr-api-cycle / gh)
```
- The worktree stays checked out on `wt/<timestamp>`; the new branch exists
only as the pushed ref (plus a local ref that merges cleanly later).
- `-u` upstream tracking on the wt branch is misleading — skip it; set
`git config branch.<name>.remote origin` + `.merge` only if you will
later check the branch out.
- Dirty regenerated fixtures left behind (e.g. `gen:sample` PDFs rewritten
by setup/gate runs): `git restore` is also blocked — do NOT edit-forward
to revert them; report them. They are regenerated on every bootstrap, so
dirt is expected and harmless.
## Verify
- `git ls-remote --heads origin <branch>` prints the same sha as `git rev-parse HEAD`.
- PR API returns 201 with `head.sha` equal to that sha.
## Do NOT
- `git checkout -b`, `git stash`, `git restore`, `git reset --hard` here —
all working-tree mutations the policy can block, and stashes in a shared
checkout are a cross-agent footgun.
@@ -0,0 +1,72 @@
---
name: typescript-fleet-repo-bootstrap
description: "Initialize a TypeScript monorepo (Bun workspaces + Turborepo + Biome + Express/Vite + quadlet dev DB + Storybook/Playwright gates) on Frank's fleet, with the traps that cost hours at franksign init."
---
# TypeScript fleet repo bootstrap
Bootstrap a fleet-style TS monorepo the way franksign was initialized (2026-10-20):
Bun workspaces + Turborepo + Biome + Express 5 on Node + Vite/React SPA + quadlet
dev Postgres + Storybook-as-component-test + prod-shaped Playwright e2e, gated by a
single `tests/run.sh` seam runner (serverAppConfigs pattern).
## Shape
```
apps/ homepage (zero-JS), web (Vite+React SPA), api (Express, single origin: /, /app, /api, /health)
packages/ domain libs (each: src/*.ts, tests colocated, tsc -p tsconfig.build.json)
templates/ data the API serves
tests/ run.sh + seams/*.bats + e2e/*.spec.ts + lib/ guards (.mjs)
infra/ quadlet/*.container + setup-dev.sh (idempotent bootstrap)
<repo>-docs/ OKF v0.2 KB: index.md, log.md, read-only/ (agent-off-limits)
AGENTS.md git law (trunk-based, --no-ff, explicit merge OK) + TDD tiers + docs law
HANDOFF.md at repo base for worktree-based future sessions
```
## Non-obvious traps (all measured on Frank's box, Node 26 / bun 1.4.3 / playwright 1.64)
- **Playwright `testDir` resolves against process CWD, not the config file.**
Pin the seam to invoke playwright with `cd` into the spec dir.
- **A `testMatch` regex with a leading `^` silently matches nothing** — Playwright
tests it against prefixed paths; "No tests found", zero diagnostics. Anchor only
the trailing `$`. And keep raw `/` out of the pattern if a guard script parses
the config literals.
- **`tsc` with `noEmit: false` but no `rootDir`/`outDir` emits `.js` strays into
`src/`** while package `main` points at `dist/` — the strays resurface on every
build. Pin `rootDir: "src", outDir: "dist", declaration: true` in every
tsconfig.build.json.
- **`bunx <tool>` ignores the workspace install** and pulls its own version (observed
with biome and playwright). Git hooks MUST call `./node_modules/.bin/<tool>`.
- **Node 26 rejects `--env-file-if-set`** (no "if-set" variant on that build);
use `--env-file=<file>` — a shell-set env var still wins over file values, so
`PORT=3100 node --env-file=.env dist/cli.js` overrides cleanly.
- **Quadlet resolves `EnvironmentFile=` relative to the UNIT directory**, not the
repo: `setup-dev.sh` must link the env file beside the unit in
`~/.config/containers/systemd/`. Generated quadlet units cannot be
`systemctl --user enable --now`'d — the generator's `default.target.wants`
symlink IS the enablement; plain `start` is enough.
- **`gitignore` rule `.env.*` swallows `.env.example`** — add the `!.env.example`
negation when you write the first ignore file.
- **Gitea 1.27.3 API create-repo with a JSON body can answer 422 `[Name]: Required`**
even when the field is present; probe `/api/v1/repos/<owner>/<name>` first — it
may already exist (empty = ready for `git push -u origin main`).
- **Biome 2.x**: `biome migrate` needs `--write` (without it, it only prints the
plan) and migrates `rules.recommended` → `rules.preset`; `check --write` skips
`useTemplate`/`noNonNullAssertion` (unsafe) — prefer rewriting `!` assertions to
named lookups so `--unsafe` never touches tests.
- **Grafana-grade gate law** (from serverAppConfigs): every e2e spec must be routed
by a `projects[].testMatch` or it silently never runs — ship a scaffold-guard
script that fails the seam on an orphan spec.
## Gate wiring
`tests/run.sh` (bats seams: build, unit, docs-lint, homepage budget, quadlet,
storybook build, e2e) ← pre-push hook; pre-commit = secret-regex scan on staged
diff + `./node_modules/.bin/biome check` on staged code. e2e seam boots the
prod-shaped stack (built assets + Express + quadlet DB on a dedicated port,
health-gated with `curl` before Playwright starts).
## Session close-out
log.md entry with `(cwd · session)` footer; HANDOFF.md updated; origin on Gitea
owner `dank`; PR bodies carry the attribution footer.
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1792532762570
}
@@ -0,0 +1,2 @@
## Handoff Location
- put it at ~/Projects
@@ -1,4 +1,10 @@
{
"v": 1,
"lastConsolidatedAt": 1792530736757
"lastConsolidatedAt": 1792534456791,
"lastResult": {
"at": 1792532921161,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
}
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1792534385325
}
@@ -275,13 +275,13 @@ transition = [ "fade" ]
interval_seconds = 1200
[wallpaper.default]
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven_1kdgpg.jpg"
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven_nzz3gy.jpg"
[wallpaper.last]
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven_1kdgpg.jpg"
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven_nzz3gy.jpg"
[wallpaper.monitors.DP-1]
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven_1kdgpg.jpg"
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven_nzz3gy.jpg"
[wallpaper.monitors.DP-2]
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven_oxex69.jpg"
@@ -290,7 +290,7 @@ transition = [ "fade" ]
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven_v9e2w3.jpg"
[wallpaper.monitors.eDP-1]
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven_1kdgpg.jpg"
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven_nzz3gy.jpg"
[weather]
unit = "imperial"