Captures the owner's requirements and decisions from the planning conversation, current branch/worktree state, open tasks 17-19, pending owner decisions and the controller's rulings so a new agent can resume. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.0 KiB
Task 16: controller rulings (binding, in addition to the brief)
The core API already exists. Read the real signatures in packages/core/src/** (auth, works, matching, entries, engagements, lists, copies, custom-fields, attachments, crypto, export, backup, jobs, registry, connections, sync/{run,changes}) and in packages/plugin-sdk/src/types.ts. Several signatures differ from the original plan text. The code is authoritative.
-
Placeholder web. If
apps/web/index.htmldoesn't exist, create a minimal placeholder (<!doctype html><title>STTracker</title><div id="root">STTracker is starting…</div>). Task 17 replaces it. Serve it at/*via Bun HTML imports, with/api/*and/ingest/*handled first. -
Secrets and connections in API responses:
- Never return secrets,
credentials_enc, token hashes or password hashes. GET sourcesreturnslistConnectionsoutput and must hide deleted connections, i.e. those withstatus "paused"and no credentials (Task 9 note).POST sourcesandPATCH sources/:idpass the plugin manifest from the registry intocreateConnection/updateConnection.
- Never return secrets,
-
Running and importing:
POST sources/:id/run→enqueue(db, "sync", { connectionId, mode? }, { lockKey: "sync:<id>" }). A null result → 409conflictwith the message "A sync for this source is already queued or running."POST sources/:id/importtakes a multipart file and enqueues a sync job whose payload carries{ connectionId, file: { name, base64 } }.- Size limit: 50 MB → 413 with error code
validation. - Make sure the sync handler (Task 9's
syncHandler) supports file mode. If it doesn't, extend it minimally inpackages/core/src/connections.ts(allowed).
-
Ownership of everything by id. Every
:idroute verifies ownership through the core function, which throwsNotFoundError→ 404. For attachments, beforestoreAttachmentverify thatentity/entityIdbelongs to the user (Task 7 deferred this to the API layer).GET attachments/:idserves withContent-Type= the stored mime, plusX-Content-Type-Options: nosniffandContent-Disposition: inline; filename="…"(sanitized). -
Error mapping:
ValidationError→ 400validation, with field and reason in the message.AuthError→ 401 forinvalid_credentials/unauthenticated, 403 forforbidden, 400 forinvite_required/username_taken/invalid_username.NotFoundError→ 404not_found.ImportError→ 400 with its code (409 foraccount_not_emptyandconflict).- Anything else → 500
internal, with a generic message; log the detail server-side, never return it.
-
Security basics:
- Session cookie per the brief.
- CSRF: for cookie-authenticated non-GET requests, require the header
X-Requested-With: sttracker(the web app sends it; Bearer-token requests are exempt). - Login and register are rate-limited in-process: 10 per minute per IP, then 429.
- All JSON bodies are capped at 1 MB.
-
Boot (
main.ts):createDb(DATABASE_URL)→migrateToLatest→loadPlugins(PLUGIN_DIRS);startWorker(db, { sync: syncHandler(...), backup: backupHandler(...) })andstartScheduler;- enqueue a daily
backupjob (lockKey "backup", runAt the next 03:00 local) and re-enqueue it after each run; - SIGTERM/SIGINT → stop the worker and scheduler, then close the DB.
- The
DATA_DIRdefault./data, created if missing.
-
OpenAPI is generated from the route table with zod v4
z.toJSONSchema. Every route appears with method, path and summary, plus the request schema where there is one. -
AGENTS.md Commands: after verifying, set Dev to
bun --hot apps/server/src/main.ts(needsSTTRACKER_SECRET) and a Start command. Updatedocs/PERMISSIONS.mdand the tool permission files (.claude/settings.json,.codex/rules/project.rules,.gemini/settings.json) only with commands you actually ran successfully. Update ARCHITECTURE (apps/server real) and DECISIONS (top: CSRF header, error mapping).