akif/arclet-boards

Arclet Boards — private Kanban workspaces with AppID, DataK3, shared web and MCP actions.

A remix template · 1 remixes · Remix akif/arclet-boards

DESIGN_DECISIONS.md

Design decisions

  1. One app and one origin. FastAPI serves static assets, the JSON API and /mcp. AppID's HttpOnly host-only gateway cookie reaches all browser APIs. There is no separate frontend host, token storage in JavaScript, or custom login.

  2. Build on the starter's integrations. Keep its service-account OAuth flow, DataK3 pg-wire approach, Dodil Models envelope handling, MCP transport pattern, and Git CI/CD manifests. The original checkout and history were preserved before changes. The local six-test baseline passes with the updated dependencies. The starter's own remixed CI and deployment also passed.

  3. Permissions are use cases. Every action, read included, authorizes the caller inside application/boards.py. Web and MCP cannot diverge. Workspace membership permits workspace operations; private boards have separate access. A workspace owner cannot enumerate another person's private board merely by owning the workspace. Only the board owner changes its member grants.

  4. Stable identity. Owners use AppID sub, not mutable email. Pending member invitations use email, then bind to the first authenticated subject. Pool email verification is a required post-remix setup step because the current strict remix schema does not expose pool settings. A platform or service-client principal is never an end user. amr is checked against current AppID docs.

  5. DataK3 transactions. PostgreSQL wire only for runtime persistence: atomic commits across content, order, activity and command receipts. Current SQL docs explicitly support read-your-writes, foreign keys on insert, guarded reads, and whole-transaction retry on 40001. No SERIAL, unsupported trigger, foreign database, or raw SQL API is introduced. IDs are UUID hex strings.

  6. Concurrency is visible. Cards, lists and boards carry versions. Edits require the version the client saw. Moves also require the board version so stale drop positions cannot silently overwrite another person's ordering. All board mutations guard and bump the board row, closing phantom races in ordering and membership. A conflict becomes an actionable 409.

  7. Replay protection is durable. A command receipt is a plain INSERT in the same transaction. Request IDs are bound to the acting subject and payload hash. A replay returns the original response after rechecking current access; a different payload with the same key is refused. No lost response creates duplicate cards/history. Receipts currently have no retention cleanup.

  8. Explicit ordered lists and cards. Integer positions are contiguous and updated atomically. This costs writes proportional to shifted cards, but is simple to verify and deterministic. Every card position changed by a move receives an activity record, including indirectly shifted cards.

  9. No destructive starter migration. Arclet tables use a namespace prefix. The real starter remix can receive the schema without deleting or changing legacy tables. New remixes create only the complete Arclet schema. Future schema changes must be explicit additive migrations; IF NOT EXISTS alone does not migrate existing columns.

  10. Small, honest UI. Vanilla JavaScript and CSS avoid a second build system. Views derive from real backend results. Drag and drop waits for persistence; keyboard/touch users get a move control and list-order buttons. Forms retain errors. Dialogs use native focus trapping; user content is escaped and inline event handlers are absent. No Trello artwork or name is used in the product.

  11. Core before AI. Extras were added only after 33 real deployed API/MCP checks and 15 browser checks passed. Model calls run outside DB transactions; results are re-authorized and version-checked before exposure or persistence. Checklist drafts require human review. Summaries use real counts and titles, with AI permitted only to select existing focus-card IDs. Semantic embeddings live in DataK3 VECTOR(512) and are excluded when their content hash is stale. Explicit indexing is bounded at 100 cards/board; no model is called during ordinary card edits. Dependency edges guard the board row, prevent cycles, and enforce safe completion/reopening. No fabricated cards enter summaries.

    A live API check found that the embedding task enum requires the full EMBED_TASK_TYPE_SEMANTIC_SIMILARITY value, despite the current guide's short example. Kimi's reasoning also exhausted a 512-token budget before finishing JSON, so the tested bound is 2048. Both findings have regression tests.

  12. Evidence has levels. In-memory tests are test-only and prove application rules. Separate tests exercise real DataK3 across new connections. Real pool sessions on the deployed gateway are necessary to prove header stripping, service-principal rejection and cross-user isolation. A green health probe or public URL is not sufficient evidence.