Skip to content

docs(adr): 0010 — Automation Engine with split-evaluator design#51

Merged
Musiker15 merged 1 commit into
mainfrom
docs/adr-0010-automation
May 25, 2026
Merged

docs(adr): 0010 — Automation Engine with split-evaluator design#51
Musiker15 merged 1 commit into
mainfrom
docs/adr-0010-automation

Conversation

@Musiker15
Copy link
Copy Markdown
Member

Summary

Pre-implementation ADR for issue #44 (Automation-Engine). Captures the why behind the four design decisions that shape the next 6–10 h of work:

  1. Split engine: client-side evaluator is canonical; a server-side per-minute scheduler only fires ticks for time-triggers, the actual rule body still decrypts client-side
  2. Trigger envelope: small plaintext sibling fields (trigger_type, trigger_meta) on AutomationRule — only carries data the server already sees, whitelisted server-side at write time
  3. Idempotency: idempotent repository actions need no coordination; observable actions (post_comment, emit_webhook) claim a Redis SETNX key with 60 s TTL
  4. v1 scope cap: short trigger/action lists (5 / 6), cascade depth 1, bulk-import skips automation

Also fixes a pre-existing gap: the ADR index was missing 0009. Both 0009 and 0010 are added there; CLAUDE.md §14 ADR-Übersicht is updated the same way.

Why ship the ADR separately

Per CLAUDE.md §16 #2: "Vor dem Schreiben neuer Features prüfen, ob ADR nötig — falls ja, ADR-Entwurf als ersten Schritt vorschlagen." This is exactly that step. The trigger-envelope decision in particular has cryptographic implications (it's a plaintext sibling to enc_rule); having it reviewed before code lands is much cheaper than re-doing the schema migration.

Files

  • docs/architecture/0010-automation-engine.md (new)
  • docs/architecture/README.md (index — adds 0009 + 0010)
  • CLAUDE.md (§14 ADR-Übersicht — adds 0010)

Test plan

  • Local markdown renders cleanly
  • CI doesn't need to do anything special — docs-only

Follow-up

After merge, implement the engine in a separate PR per issue #44.

🤖 Generated with Claude Code

Captures the why behind the upcoming automation rule implementation
(closes the open-question line on issue #44):

- Client-side evaluator is the canonical path (rule body lives in
  enc_rule, decrypted under the BoardKey, evaluated and dispatched
  through the same HTTP endpoints a human would call — RBAC stays
  uniform, no privileged automation code path).
- A small server-visible trigger envelope (trigger_type +
  trigger_meta) lets a per-minute BullMQ scheduler emit board
  ticks for card_due_reached; the next online client of any board
  member then runs the evaluator. Time-triggers survive author
  going offline.
- trigger_meta is whitelisted server-side to only allow data the
  server already sees (IDs, dates, enums) so the "metadata" field
  cannot become a back channel for plaintext leakage.
- Conflict resolution for multi-client races on the same rule:
  idempotent actions (attach_label / assign_member / move_to_column
  / append_checklist) need no coordination because the underlying
  repository calls are themselves idempotent; observable actions
  (post_comment, emit_webhook) claim a Redis SETNX key with 60s
  TTL before running.
- v1 trigger/action lists deliberately kept short — every new
  entry is a security review item. Cascade depth capped at 1.

Also patches the ADR index (was missing 0009 too) and the CLAUDE.md
ADR-Übersicht table.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Musiker15 <info@musiker15.de>
@Musiker15 Musiker15 merged commit 9024fff into main May 25, 2026
8 checks passed
@Musiker15 Musiker15 deleted the docs/adr-0010-automation branch May 25, 2026 14:58
Musiker15 added a commit that referenced this pull request May 29, 2026
…API, correct overstated docs

Phase A of the gaps backlog (#43, #44, #45, #51): additive cleanup + doc truth,
no runtime behaviour change.

- Remove unused deps never imported in src/: @tanstack/react-query, bullmq,
  iron-session, socket.io(+client). Delete the never-wired tests/msw skeleton
  and drop msw from devDependencies.
- CLAUDE.md sections 2/6: correct tech-stack to reality (self-written auth not
  Auth.js, native ws not socket.io, in-process schedulers not BullMQ, libsodium
  crypto_pwhash not argon2-browser, fetch+Zustand not TanStack Query).
- OpenAPI -> v0.6.0: document the previously-missing Milestones, Automation and
  account/auth-settings routes; fix the wrong route count (~70 handlers).
- Clarify /api/auth/2fa/verify is not a standalone endpoint (verification runs
  inside /api/auth/login).
- threat-model.md + CLAUDE.md truth pass: exponential backoff is prepared but
  unwired (#27); safe-regex (#30) / HIBP (#29) / login-notification emails (#32)
  are planned-not-built; Markdown is escaped plaintext (no render -> no DOMPurify
  sink yet, #33); offline is read-only + description drafts (no Workbox/write
  sync-queue, #38); live cursors sit behind a default-off flag (ADR 0011);
  style-src still uses unsafe-inline; Apache must set no Content-Security-Policy.

Typecheck, ESLint and the 193-test suite all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Musiker15 added a commit that referenced this pull request May 30, 2026
Two halves (ADR 0021), no Workbox dependency:

A) Cold-start offline. `scripts/gen-precache-manifest.mjs` (run in `pnpm
   build`) emits public/precache-manifest.json listing the build's hashed
   JS/CSS + shell, versioned by the Next build id. The hand-rolled SW
   precaches it on install → installed PWA boots fully offline. Runtime:
   cache-first for immutable /_next/static/, network-first for navigations,
   /api/ never cached. Manifest is gitignored (regenerated per build).

B) Offline write outbox. `queuedFetch` (src/lib/offline/sync.ts) is a fetch
   drop-in: online it fetches; offline (or on a network throw) it persists
   the intent to an IndexedDB outbox (src/lib/idb/outbox.ts) and reports
   `queued`, so the caller keeps its optimistic UI. `flushOutbox` replays
   oldest-first on reconnect + on mount, driven by the new OfflineSync
   header pill (+ conflict toast). Server stays authority: permanent 4xx
   drops the change (LWW + toast); transient error keeps the ordered tail.

   v1 queues only naturally-idempotent, no-return ops — card move/reorder,
   metadata + archive (PATCH /api/cards/[id]), checklist-item toggle — so a
   double replay is harmless and NO server route / idempotency key changed.
   Wired in board-client (moveCard) + card-drawer (saveMeta/archive/
   toggleItemDone). Offline create + comment-edit deferred (need client ids /
   temp-id reconciliation), documented in ADR 0021.

Zero-knowledge preserved: queued bodies are already enc_* envelopes, so the
outbox holds ciphertext + structural metadata only — never plaintext. Wiped
on logout (key-bundle reset), like the read cache + Yjs drafts.

Shared IndexedDB handle extracted to src/lib/idb/db.ts (DB v2: boards +
outbox stores); board-cache refactored onto it. OnlineStatus' "will sync on
reconnect" promise is now honest.

Tests: tests/unit/outbox.test.ts (+7) — classifyReplay + flushOutbox
(success / drop-and-continue / transient-stop / network-throw). typecheck/
lint/tests green (294).

Docs: ADR 0021 (new), CLAUDE.md (§5.1, header, ADR table — full #27#51
backlog now complete), CHANGELOG.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Musiker15 added a commit that referenced this pull request May 30, 2026
Roll the Unreleased section into [0.6.0-beta] — 2026-05-30 (the full
#27#51 gap backlog: auth hardening, transactional email, Markdown,
custom fields, templates, importers, attachments, GDPR, admin, public
REST API, mind-map, a11y tests, virtual scroll, live CRDT checklists/
comments [ADR 0020], offline-first [ADR 0021]). Update compare links +
the CLAUDE.md status line. The `v0.6.0-beta` tag triggers release.yml
(GHCR image + cosign keyless + provenance, CycloneDX/SPDX SBOM, GitHub
prerelease with this CHANGELOG section).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant