Files
PiCloud/HANDOFF.md
MechaCat02 7040f0df83 docs(handoff): machine-switch handoff report 2026-06-05
Session summary, branch inventory, push instructions, v1.1.8
follow-ups, and pickup-on-new-machine smoke commands. Main is at
v1.1.7 (5cbb6ca); seven minor releases shipped this session via
the dispatch-and-review workflow.

Read this first on the new machine.
2026-06-05 07:12:06 +02:00

16 KiB
Raw Blame History

Handoff — 2026-06-05

Machine-switch handoff. This document is the entry point for picking up PiCloud work on a different machine. It captures session state, what shipped, what's queued, and how to continue.


TL;DR

  • main is at v1.1.7 — seven minor releases (v1.1.1 → v1.1.7) shipped this session via the dispatch-and-review workflow.
  • Working tree is clean.
  • Next release is v1.1.8 (User Management). A draft dispatch prompt is sketched in §6 below; ready to send to a dev agent.
  • One dev Postgres container (picloud-postgres-1 on port 15432) is still running on the source machine — tear it down with docker compose down -v before the source machine goes offline.

How to resume on the new machine

git clone https://git.mc02.dev/fabi/PiCloud.git
cd PiCloud
git checkout main
git log --oneline -10        # should show v1.1.7 reviewer commit at HEAD
docker compose up -d          # local Postgres for DB-gated tests
export DATABASE_URL='postgres://picloud:picloud@127.0.0.1:5432/picloud'
cargo test --workspace -- --test-threads=2

If you're starting from this branch (handoff/2026-06-05), it points at the same main HEAD with this HANDOFF.md added; merge or just read it and continue work on main.

For the master encryption key needed by v1.1.7+ secrets:

export PICLOUD_SECRET_KEY="$(openssl rand -base64 32)"
# OR, for dev only:
export PICLOUD_DEV_MODE=true

The dev fallback uses a deterministic key (SHA-256 of a hardcoded string) — fine for local testing, fatal for any real deployment.


Session summary: v1.1.1 → v1.1.7

All seven minor releases completed in one session via the dispatch workflow you set up: I draft a prompt, you dispatch it to a fresh agent in another session, the agent implements and writes HANDBACK.md, you bounce the report back to me, I audit the branch and write REVIEW.md with a verdict, you bounce-back-for-fixes-if-needed, and on approve I fast-forward merge into main.

Release Capability Iterations Status
v1.1.1 Storage & Events (KV + triggers framework + outbox + dispatcher + NATS-style sync HTTP + dead-letter table + dashboard surface) 1 merged
v1.1.2 Documents (docs::* SDK + query DSL + docs:* triggers) 2 merged (iteration 2 fixed a fmt diff)
v1.1.3 Modules (scripts.kind + PicloudModuleResolver + AST caches + script_imports) 1 merged
v1.1.4 Outbound HTTP & Scheduled Tasks (http::* with SSRF deny-list + cron triggers) 1 merged
v1.1.5 Files & Pub/Sub (filesystem-backed blobs + pubsub::publish_durable + first CI workflow) 1 merged
v1.1.6 Realtime Channels & Client Library (SSE + topics + HMAC subscriber tokens + @picloud/client@1.0.0) 1 merged
v1.1.7 Configuration & Email (encrypted secrets + outbound/inbound email + dead-letter handler fix) 1 merged

Versioning state on main:

  • Workspace 1.1.7
  • SDK schema 1.8
  • Dashboard 0.13.0
  • @picloud/client 1.0.0
  • Migrations applied through 0025

Test counts at HEAD: cargo test --workspace --test-threads=2 with DATABASE_URL set → 617 passed / 0 failed. The --test-threads=2 is required on shared dev Postgres (~9 concurrent build_apps otherwise exhaust connections); CI's dedicated Postgres doesn't hit this.


Branches on this machine

v1.1.x feature branches (all merged into main, kept locally for traceability)

Branch HEAD What it contains
feat/v1.1.1-storage-and-events 2796f36 v1.1.1 work + HANDBACK + REVIEW
feat/v1.1.2-documents 5bbbc26 v1.1.2 work (2 iterations) + HANDBACK + REVIEW
feat/v1.1.3-modules 6f17259 v1.1.3 work + HANDBACK + REVIEW
feat/v1.1.4-http-cron 03d03ea v1.1.4 work + HANDBACK + REVIEW
feat/v1.1.5-files-pubsub d064681 v1.1.5 work + HANDBACK + REVIEW
feat/v1.1.6-realtime-client 64ad978 v1.1.6 work + HANDBACK + REVIEW
feat/v1.1.7-secrets-email 5cbb6ca v1.1.7 work + HANDBACK + REVIEW

All seven HEADs are reachable from main (fast-forward merges). Keeping the branches makes it easy to inspect the per-release commit slice without git log filtering.

Older branches predating this session (state uncertain)

These appeared in git branch at session start and weren't touched by v1.1.x work. I don't know which are abandoned, in-flight, or already merged under different names. On the new machine, decide for each:

Branch Last commit Tracking
chore/ui-hardening b42e273 fix(test): admin_is_implicit_app_admin uses force=true on app delete local-only
feat/app-members e6fc6e6 test(picloud): close two app_members test gaps local-only
feat/cli 5d08974 style(cli): re-fmt one stray format! line in the integration test tracks origin/feat/cli (up to date)
feat/multi-app-scoping a393f11 feat(dashboard): auto-slug app names and infer route host kind from input tracks origin/feat/multi-app-scoping (ahead 3)
feat/users-and-keys-ui 6eb32a7 feat(dashboard): adopt ActionMenu for user row actions local-only
feat/users-authz 2aab92a style: cargo fmt across Phase 3.5 changes local-only
test/cli-journeys e4851b3 test(cli): extract shared Fixture into tests/common tracks origin/test/cli-journeys (up to date)
test/frontend-e2e ec3c768 test(dashboard): add full-stack integration specs local-only

Push these if you want them mirrored on the new machine — see §3 below for the push commands. If any are obsolete, delete them locally before resuming.


§3 — Push instructions

Push was denied in this session (sandbox restriction). Run these on the source machine to mirror state to origin:

# 1. The v1.1.x releases on main (55 commits)
git push origin main

# 2. The seven v1.1.x feature branches (preserves per-release history)
git push origin feat/v1.1.1-storage-and-events
git push origin feat/v1.1.2-documents
git push origin feat/v1.1.3-modules
git push origin feat/v1.1.4-http-cron
git push origin feat/v1.1.5-files-pubsub
git push origin feat/v1.1.6-realtime-client
git push origin feat/v1.1.7-secrets-email

# 3. This handoff branch
git push -u origin handoff/2026-06-05

# 4. OPTIONAL — push the older branches you want on the new machine
#    (decide per-branch; some may be abandoned)
git push origin chore/ui-hardening
git push origin feat/app-members
git push origin feat/multi-app-scoping     # ahead 3 of remote
git push origin feat/users-and-keys-ui
git push origin feat/users-authz
git push origin test/frontend-e2e

After pushing, on the new machine: git fetch --all brings everything down. git checkout main puts you at v1.1.7 HEAD.


§4 — Workflow context (read before dispatching v1.1.8)

The dispatch-and-review workflow you've been using:

  1. You ask me to draft the dispatch prompt for the next release.
  2. I draft the prompt based on:
    • The roadmap in docs/v1.1.x-design-notes.md §7
    • Three or so follow-ups identified in the prior release's REVIEW.md
    • Discipline lessons carried forward from prior retros
  3. You dispatch the prompt to a fresh agent in another session — that agent gets no prior conversation context; the prompt + the docs it points at are everything they have.
  4. The agent implements + writes HANDBACK.md at the repo root, then stops.
  5. You bounce the HANDBACK back to me.
  6. I audit the branch and write REVIEW.md with a verdict (APPROVE or NEEDS CHANGES).
  7. If NEEDS CHANGES: you bounce the REVIEW back to the agent; they iterate; back to step 5.
  8. If APPROVE: I fast-forward merge the branch into main and pause for your next instruction.

What's worked well across seven releases:

  • The discipline reminders compound. Each release's retro identifies one small habit the agent dropped (§8 attestation hand-counting, silent prompt-default deviations, brief-internal contradictions silently reinterpreted, clippy run without --all-targets); the next release's prompt explicitly addresses it. By v1.1.7 the agent was catching their own latent findings without prompting.
  • Explicit "deviations beyond the brief" sections in HANDBACK make audits fast — every meaningful judgment call is in one place.
  • The "this is the deferrable piece under scope pressure" clause in big releases (v1.1.6 client lib, v1.1.7 inbound email) gave the agent a clean escape hatch they never actually needed but worked as intended.
  • Latent findings discovered during implementation (v1.1.3 cross-app trigger gap, v1.1.4 SSRF literal-IP bypass, v1.1.6 dead_letter handler never firing, v1.1.7 clippy regression at v1.1.6 HEAD) all surfaced honestly rather than being silently worked around.

What to do differently in v1.1.8:

  • Walk through each code example in the prompt before sending. v1.1.4 brief said (url, opts) but its example was http::post(url, body) — the agent had to fix it during implementation. v1.1.7 brief sketched TriggerEvent::DeadLetter field names that didn't match the actual variant. Both flagged correctly, but pre-resolution saves agent effort.
  • Pin the clippy gate: cargo clean before cargo clippy --all-targets to defeat incremental-cache false-greens. See v1.1.7 REVIEW §3.3 for context.

§5 — Pending follow-ups for v1.1.8

From the v1.1.7 REVIEW.md, three load-bearing items to fold into the v1.1.8 dispatch prompt:

5.1 Drop the plaintext realtime_signing_key column

The v1.1.7 phase-2 commitment. v1.1.7 added NULL-able encrypted columns

  • DROP NOT NULL on the plaintext column; the startup task encrypts existing rows. v1.1.8 drops the plaintext column entirely.

Pre-flight check: scan for any remaining non-NULL rows on the plaintext column. If found, run the encryption migration before the drop. If the v1.1.7 startup task ran on the operator's deploy, all rows should already be encrypted.

CHANGELOG must note that v1.1.8 requires v1.1.7 to have been applied first. No skipping versions.

5.2 Clippy --all-targets discipline refinement

The v1.1.7 audit caught a real regression: four warnings predated v1.1.7 that the v1.1.6 audit reported as clippy-green. Likely cause: cargo's incremental cache leaving test binaries unchecked.

v1.1.8 prompt should require either:

  • cargo clean before cargo clippy --all-targets, OR
  • Explicit verification that the clippy output includes Checking lines for test crates.

CI's .github/workflows/ci.yml (added in v1.1.5) might also benefit from a clippy-cache-check step.

5.3 auth_mode = 'session' for realtime subscriber tokens

v1.1.7's CHECK constraint on topics.auth_mode only allows ('public', 'token'). v1.1.8's users::* work needs to:

  • Extend the CHECK to include 'session'.
  • Add a session-token validator alongside the existing HMAC validator behind the unchanged RealtimeAuthority trait.

The trait shape from v1.1.6 already supports this — natural extension.


§6 — Draft v1.1.8 dispatch prompt outline

Not the full prompt — just the scope sketch so you can ask me to expand it on the new machine.

v1.1.8 — User Management (users::*)

Core scope:

  • users::create / get / find / update / delete / list SDK
  • Password hashing (argon2id)
  • users table per-app
  • Sessions: users::login(email, password) → returns a session token; users::verify(session_token) returns the user or ()
  • Sessions table with TTL + revocation
  • Email verification flow (uses v1.1.7 email::send)
  • Password reset flow (uses v1.1.7 email::send + tokens)
  • Invitations (admin creates an invite → email link → user accepts + sets password)
  • Roles: per-app role assignments on users
  • Capability::AppUsersRead/Write/Admin mapped to existing scopes
  • Dashboard: Users tab on app detail page (list, invite, role-edit)

Follow-ups from v1.1.7 retro (fold in):

  • Drop plaintext realtime_signing_key column (phase-2)
  • Clippy --all-targets discipline refinement
  • auth_mode = 'session' for realtime subscriber tokens (uses v1.1.8 sessions)

Out of scope:

  • OAuth providers (defer to v1.2+)
  • 2FA / MFA (defer to v1.2+)
  • SSO / SAML (defer)
  • Password policy customization (defer; ship with sensible default)
  • User-to-user messaging (defer; userland)

Ask me to expand this into a full prompt when you're ready.


§7 — Environmental notes

  • Dev Postgres container picloud-postgres-1 (port 15432) was running at session end on the source machine. The v1.1.5/v1.1.6/ v1.1.7 agents started it for live DB-gated tests. Tear down with docker compose down -v before the source machine goes offline if you want a clean state.
  • PICLOUD_SECRET_KEY is required for v1.1.7+ to start. Pick one with openssl rand -base64 32 for production; use PICLOUD_DEV_MODE=true (no master key needed) for local development. The dev key is deterministic so secrets persist across restarts in dev.
  • CI workflow lives at .github/workflows/ci.yml (added in v1.1.5). Runs fmt + clippy + cargo test --workspace against a postgres:15 service, plus dashboard npm run check. When you push to main or open a PR, CI will run. First push after this handoff will exercise it for the first time on real workload — watch the run.

§8 — Key documents for orientation

  • CLAUDE.md — project conventions. Read first.
  • serverless_cloud_blueprint.md — the authoritative architecture document.
  • docs/sdk-shape.md — SDK conventions every v1.1.x service follows.
  • docs/v1.1.x-design-notes.md — the in-flight-decisions document. Sections §1§6 contain the "Decided 2026-06-01" annotations from the design conversation that preceded this session; §7 holds the v1.1.x roadmap; §14 are candidates for pruning (their decisions shipped in v1.1.1).
  • docs/versioning.md — patch-bump policy under the post-1.0 expansion-phase carve-out.
  • docs/git-workflow.md — trunk-based workflow conventions.
  • CHANGELOG.md — release notes for v1.1.1 onward. v1.1.7's entry includes the retroactive dead_letter security note.

Per-release artifacts on main:

  • HANDBACK.md at repo root — currently holds the v1.1.7 agent's handback. Overwritten each release.
  • REVIEW.md at repo root — currently holds the v1.1.7 reviewer's audit. Overwritten each release.

If you want the full per-release HANDBACK + REVIEW history, the seven feat/v1.1.x-* branches preserve them (each branch's HEAD~1 contains the HANDBACK and HEAD contains the REVIEW for that release).


§9 — Quick smoke after resuming

After cloning + setting up the new machine:

# Basic gates
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --workspace

# DB-gated (needs Postgres)
docker compose up -d
export DATABASE_URL='postgres://picloud:picloud@127.0.0.1:5432/picloud'
export PICLOUD_DEV_MODE=true
cargo test --workspace --test-threads=2

# Dashboard
cd dashboard && npm install && npm run check

# Client library
cd clients/typescript && npm install && npm run lint && npm run test && npm run build

If all green: machine is ready. Resume v1.1.8 work by asking me for the full dispatch prompt.


Handoff written 2026-06-05. Main HEAD: 5cbb6ca (v1.1.7 reviewer APPROVE).