From f15ff91307f6af14c630223ab818013efbf76266 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Sun, 10 May 2026 23:40:02 -0400 Subject: [PATCH] chore: plans --- .../2026-05-10-auth-sessions-frontend.md | 152 ++++++++++++++++++ .../2026-05-11-automations-end-to-end.md | 123 ++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-10-auth-sessions-frontend.md create mode 100644 docs/superpowers/plans/2026-05-11-automations-end-to-end.md diff --git a/docs/superpowers/plans/2026-05-10-auth-sessions-frontend.md b/docs/superpowers/plans/2026-05-10-auth-sessions-frontend.md new file mode 100644 index 000000000..c085b429a --- /dev/null +++ b/docs/superpowers/plans/2026-05-10-auth-sessions-frontend.md @@ -0,0 +1,152 @@ +# Unified Auth Sessions Frontend Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Fill `/profile/sessions` with a unified sessions view backed by the new auth sessions API. + +**Architecture:** The frontend will consume the normalized `GET /api/v1/auth/sessions` response and render all session kinds in one list. The page follows the existing Profile UI patterns and does not encode backend storage details beyond the `kind`, `current`, and `revocable` fields. + +**Tech Stack:** React 19, React Router, SWR, generated TypeScript Axios client, Tailwind CSS, Bun tests. + +**Backend dependency:** This plan depends on [2026-05-10-auth-sessions-backend.md](2026-05-10-auth-sessions-backend.md). Implement the backend plan and regenerate the TypeScript API client before starting this plan. + +--- + +## Contract Consumed + +Use the generated client for: + +- `GET /api/v1/auth/sessions` +- `DELETE /api/v1/auth/sessions/{id}` + +Expected response shape: + +```ts +type AuthSessionsResponse = { + sessions: AuthSession[]; +}; + +type AuthSession = { + id: string; + kind: "browser" | "cli"; + current: boolean; + provider: string; + login: string; + label: string; + userAgent?: string | null; + createdAt: string; + lastSeenAt: string; + expiresAt: string; + revocable: boolean; +}; +``` + +## Tasks + +### Task 1: Regenerate The TypeScript API Client + +**Files:** +- Generated: `lib/packages/fabro-api-client/src/api/auth-api.ts` +- Generated: `lib/packages/fabro-api-client/src/models/*` + +- [ ] Confirm the backend OpenAPI changes from the backend plan are present. +- [ ] Run: + +```bash +cd lib/packages/fabro-api-client && bun run generate +``` + +Expected: generated client includes auth sessions list and delete methods plus `AuthSession` / `AuthSessionsResponse` models. + +### Task 2: Add Query Keys And Data Hook + +**Files:** +- Modify: `apps/fabro-web/app/lib/query-keys.ts` +- Modify: `apps/fabro-web/app/lib/queries.ts` + +- [ ] Add `queryKeys.auth.sessions()`. +- [ ] Add `useAuthSessions()` that calls the generated auth API method and returns `AuthSessionsResponse`. +- [ ] Keep the hook behavior consistent with `useAuthMe()`: authenticated, cookie-backed, SWR-managed. + +### Task 3: Build The Sessions Page + +**Files:** +- Modify: `apps/fabro-web/app/routes/profile-sessions.tsx` + +- [ ] Replace the current empty component. +- [ ] Use existing `Panel`, `PanelSkeleton`, `Badge`, `Muted`, and `Mono` patterns from `apps/fabro-web/app/components/settings-panel.tsx`. +- [ ] Render skeletons while `useAuthSessions()` is loading. +- [ ] Sort sessions with `current === true` first, then descending `lastSeenAt`. +- [ ] Render one row per session with: + - session label + - kind badge (`browser` or `cli`) + - provider + - login + - last active timestamp + - expiry timestamp + - user agent when present +- [ ] Show no action for `revocable === false`. +- [ ] Show a compact revoke button for `revocable === true`. + +### Task 4: Wire Revocation + +**Files:** +- Modify: `apps/fabro-web/app/routes/profile-sessions.tsx` + +- [ ] On revoke click, call the generated delete sessions API method with the session ID. +- [ ] Disable the button while that session is being revoked. +- [ ] After successful revocation, revalidate `queryKeys.auth.sessions()`. +- [ ] If revocation fails, keep the session visible and render a small inline error near the list. + +### Task 5: Add Frontend Tests + +**Files:** +- Add: `apps/fabro-web/app/routes/profile-sessions.test.tsx` + +- [ ] Test loading state renders a profile-style skeleton. +- [ ] Test browser and CLI sessions render from a mocked unified response. +- [ ] Test non-revocable browser sessions do not show a revoke button. +- [ ] Test revocable CLI sessions show a revoke button. +- [ ] Test clicking revoke calls the delete endpoint and refreshes the sessions query. +- [ ] Run: + +```bash +cd apps/fabro-web && bun test +``` + +Expected: `PASS`. + +### Task 6: Typecheck And Visual Smoke + +**Files:** +- Modify only if tests/typecheck reveal a real issue in the files above. + +- [ ] Run: + +```bash +cd apps/fabro-web && bun run typecheck +``` + +Expected: `PASS`. + +- [ ] Start the app stack using the repository's normal dev server flow and visit `/profile/sessions`. +- [ ] Confirm the page shows the current browser session and any authenticated CLI sessions returned by the backend. +- [ ] Confirm the layout matches the existing Profile section and remains usable at narrow widths. + +## Final Validation + +Run: + +```bash +cd lib/packages/fabro-api-client && bun run generate +cd apps/fabro-web && bun test +cd apps/fabro-web && bun run typecheck +``` + +Expected: all commands pass. + +## Assumptions + +- The generated API client exposes methods for listing and deleting auth sessions after the backend OpenAPI update. +- The frontend does not invent local session kinds; it renders the normalized backend response. +- Revocation is action-gated entirely by the backend-provided `revocable` field. diff --git a/docs/superpowers/plans/2026-05-11-automations-end-to-end.md b/docs/superpowers/plans/2026-05-11-automations-end-to-end.md new file mode 100644 index 000000000..fde6d29fd --- /dev/null +++ b/docs/superpowers/plans/2026-05-11-automations-end-to-end.md @@ -0,0 +1,123 @@ +# Automations End-to-End Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build Automations as server-configured, API-manageable scheduled workflow definitions that persist to `settings.toml` and eventually create Fabro runs. + +**Architecture:** Add a new `fabro-automation` crate for domain types, compact TOML parsing, schedule parsing, and structure-preserving TOML edits. Wire the crate through `fabro-config`, expose CRUD through `fabro-server`, store runtime execution state in SlateDB, and add a separate automation scheduler that creates regular Fabro runs. + +**Tech Stack:** Rust, serde, toml/toml_edit, chrono, croner, Axum, OpenAPI/progenitor, SlateDB via `fabro-store`. + +--- + +## Summary + +Automations are defined in top-level TOML as `[automations.]` and can be managed through REST endpoints. `settings.toml` remains the source of truth for automation definitions. Runtime execution state, including one-time consumed markers, lives in SlateDB so one-time automation definitions can remain enabled without firing repeatedly. + +Definitions use compact TOML: + +```toml +[automations.nightly-deps] +name = "Nightly dependency update" +repository = "fabro-sh/fabro" +ref = "main" +workflow = "dependency-update" + +[automations.nightly-deps.trigger] +schedule = "0 3 * * *" +``` + +## Stage 1: `fabro-automation` Domain And TOML Config + +- [ ] Create `lib/crates/fabro-automation` and add it to the workspace. +- [ ] Define strict domain types: `Automation`, `AutomationId`, `AutomationTarget`, `RepositorySlug`, `GitRefSelector`, `WorkflowSlug`, `AutomationTrigger`, and `ScheduleTrigger`. +- [ ] Add compact TOML-facing config types for `[automations.]`. +- [ ] Default `enabled` to `true`. +- [ ] Parse `schedule = "now"` and RFC3339 timestamps as one-time schedules. +- [ ] Parse five-field cron strings as recurring schedules. +- [ ] Use UTC for cron evaluation in v1. Do not add timezone TOML yet. +- [ ] Reject event-based triggers explicitly for now. +- [ ] Use `croner` for cron parsing and next-occurrence calculation. +- [ ] Add tests for valid config, invalid IDs/slugs/repositories/refs, schedule classification, `now` normalization, and cron validation. + +## Stage 2: Settings Integration + +- [ ] Add top-level `automations` to `fabro-config`'s sparse settings layer using plural TOML: `[automations.]`. +- [ ] Resolve automations into `ServerRuntimeSettings` so the server can load them alongside server settings. +- [ ] Keep `fabro-automation` independent of `fabro-server`; allow `fabro-config` to depend on `fabro-automation`. +- [ ] Add config tests proving `[automations.]` parses from `settings.toml`, rejects malformed entries, and does not affect existing `[server]`, `[run]`, `[workflow]`, or `[cli]` behavior. +- [ ] Update server configuration docs with compact TOML examples. + +## Stage 3: Structure-Preserving TOML Persistence + +- [ ] Add a TOML edit module in `fabro-automation` built on `toml_edit::DocumentMut`. +- [ ] Provide operations for `list`, `get`, `create`, `replace`, `patch`, and `delete` under `automations.`. +- [ ] Preserve unrelated comments, whitespace, table ordering, and non-automation sections. +- [ ] Update fields minimally when possible. Deleting an automation removes that automation table and its attached comments. +- [ ] Add config revision support using a stable hash of the current `settings.toml` contents. +- [ ] Implement server write flow: read file, parse with `toml_edit`, apply automation patch, validate full settings through `fabro-config`, write atomically under a file lock, then refresh in-memory runtime settings. +- [ ] Add golden tests with comments and odd spacing proving unrelated TOML is unchanged byte-for-byte where possible. + +## Stage 4: REST API And OpenAPI + +- [ ] Add REST endpoints under `/api/v1/automations`: + +```http +GET /api/v1/automations +POST /api/v1/automations +GET /api/v1/automations/{id} +PUT /api/v1/automations/{id} +PATCH /api/v1/automations/{id} +DELETE /api/v1/automations/{id} +``` + +- [ ] Make JSON create bodies include `id`. +- [ ] Make path-based `PUT` and `PATCH` bodies omit `id`, or reject bodies whose supplied `id` does not match the path. +- [ ] Require `If-Match` on mutating requests using the settings revision returned by GET/list responses. +- [ ] Return `409` for revision mismatch, `409` for duplicate create, `404` for missing automation, and `422` for domain validation errors. +- [ ] Add OpenAPI schemas and regenerate Rust and TypeScript API clients through the existing API workflow. +- [ ] Add server route tests for CRUD, conflict handling, validation errors, TOML persistence, and runtime settings reload. + +## Stage 5: Automation Runtime State + +- [ ] Add a SlateDB-backed automation state store in `fabro-store`. +- [ ] Track per automation: last attempted fire time, last successful fire time, last created run ID, last error, and one-time consumed marker. +- [ ] Keep one-time automation definitions enabled in `settings.toml`; use the consumed marker to prevent repeat execution. +- [ ] Expose status fields in `GET /api/v1/automations` and `GET /api/v1/automations/{id}` without writing status back to TOML. +- [ ] Add store tests for insert/update, one-time consumed behavior, status retrieval, and restart-safe persistence. + +## Stage 6: Scheduler And Run Creation + +- [ ] Add an automation scheduler service in `fabro-server` separate from the existing queued-run scheduler. +- [ ] On startup and settings reload, evaluate enabled automations, compute due schedules, and create runs for due entries. +- [ ] Add server-side materialization for automation targets: clone or fetch the configured GitHub repo/ref into a temporary workspace, resolve the workflow slug with existing project workflow discovery rules, build a `RunManifest`, then reuse the existing run creation/start path. +- [ ] Set run provenance to identify the automation ID and system actor. +- [ ] Queue created runs immediately so the existing run scheduler executes them. +- [ ] Record automation success/failure in the automation state store. +- [ ] Add scheduler tests with frozen time for recurring schedules, `now`, fixed timestamps, disabled automations, one-time consumed behavior, failed materialization, and restart behavior. + +## Stage 7: Docs And Rollout + +- [ ] Document `[automations.]` TOML, REST CRUD, optimistic concurrency, and runtime status. +- [ ] Document that v1 supports GitHub `owner/repo`, `ref` as a branch/tag/SHA selector, schedule triggers only, and UTC cron. +- [ ] Add an end-to-end integration test that creates an automation through the API, verifies `settings.toml` was minimally updated, reloads settings, fires the schedule, and observes a created run. + +## Test Plan + +- Config parsing tests in `fabro-automation` and `fabro-config`. +- TOML edit golden tests preserving comments, whitespace, and unrelated sections. +- API tests for all CRUD endpoints, validation failures, revision conflicts, and settings reload. +- Store tests for automation execution state and one-time consumed markers. +- Scheduler tests with controlled time for due/not-due schedules, disabled automations, one-time repeat prevention, and failed run materialization. +- OpenAPI conformance and generated-client checks after API schema updates. + +## Assumptions And Defaults + +- TOML root is plural: `[automations.]`. +- `settings.toml` remains the source of truth for automation definitions. +- Runtime status and one-time consumed state live in SlateDB, not TOML. +- `ref = "main"` is accepted as a selector and resolved at run materialization time. +- Event triggers are out of scope for v1, but the type model should leave room for them. +- Cron schedules are UTC-only in v1. +- Writes require optimistic concurrency via `If-Match`. +- Before implementation, read `docs/internal/testing-strategy.md`, `docs/internal/error-handling-strategy.md`, and `docs/internal/logging-strategy.md` for the stages that add tests, API errors, and scheduler logging.