Commit graph

18 commits

Author SHA1 Message Date
Randall Stillwell
3810cd9dab feat(worker): harness registry dispatch — wire WorkerExecutor for remote-ag-ui
Replaces the hardcoded createMinimalSession in handle-job.ts with a registry
lookup via getExecutor(harnessConfig). The 'minimal' harness still wraps
createMinimalSession; 'remote-ag-ui' POSTs the task payload to a remote agent
endpoint (OpenHands / Open Code / etc.) and polls for completion.

Worker-side completion of Brief 3 (harness-registry-adapter). Pairs with the
already-built AG-UI inbound route at /api/ag-ui/[workspace] and the
harness_providers / role_harness_routes tables.
2026-09-03 23:27:25 -05:00
Randall Stillwell
e3af2c33e6 feat(polish): cost caps + DAG + tiers + preflight + checks panel
- Cost caps: budget_caps table, MTD spend ledger, tRPC budgets router, orchestrator pause on cap
- DAG viz: convoy-dag.tsx SVG for brief dependencies in Mission Control
- Automation tiers: manual|assist|auto per convoy, orchestrator respects manual
- Preflight: connected-repos preflight endpoint
- Checks panel: forge checks stub in detail panel
- Budget caps schema + tRPC router + Settings UI stub

Convoy: .convoys/polish-dag-tiers-cost

Co-authored-by: orchestrator
2026-08-25 20:33:10 -05:00
Randall Stillwell
6f139d6af2 feat(owner-cockpit): forge webhooks + artifacts + copilot (Epic 8/11/12)
- Forge webhook POST /api/webhooks/forge HMAC sha256 verify, marker echodo-convoy → pr_review→merged as automation
- Completion artifacts table 0017, deterministic summary template, gallery /artifacts
- Ops copilot tRPC copilot + context tools (fleet, convoy, backlog) + panel + Draft Task → backlog.createTask
- Doc-writer SOPs: webhook rotation, copilot prompt, published flow examples
- Allow pr_review→merged as automation for webhook

Convoy: .convoys/owner-cockpit-v02

Co-authored-by: orchestrator
2026-08-25 17:49:38 -05:00
Randall Stillwell
86155455a7 feat(worker): close Epic 7 — draft PR + operator notes + doc-writer
- Draft PR via forge (apps/worker/src/git/pr.ts) push branch + createPullRequest draft with echodo-convoy marker, expose prUrl in Mission Control (convoy-service, convoys router, detail panel)
- Operator notes mid-build: convoy_operator_messages (0016), tRPC post/list, worker poll between tool rounds (queued) and before generateText (direct), detail panel thread for in_progress
- Doc-writer: harness doc-writer examples (docs/examples/research-pipeline.md), SOP template (docs/templates/doc-sop.md), role-doc-writer wiring

Convoy: .convoys/worker-closeout-docwriter

Co-authored-by: orchestrator
2026-08-25 16:56:02 -05:00
Randall Stillwell
6ccb060bea feat(second-brain): open self-hosted Obsidian for crews — harness-agnostic v0.1
- MCP create-backlog-item for natural language Plan/Epic/Task authoring
- Worker non-code: optional worktree + per-harness allowlist + doc_review→published
- Harness registry: harness_providers/role_harness_routes, WorkerAdapter (minimal|crewai|langgraph|remote-ag-ui), AG-UI POST /api/ag-ui, Settings → Harnesses with Test
- Plans tree: backlog.listTree + createPlan/Epic/Task + Start→Convoy, web CRUD without CLI
- Fleet: /fleet rollup across workspaces + SSE /api/fleet/events

Decision: docs/decisions/2026-08-25-second-brain-os-pivot.md
Convoy: .convoys/second-brain-os-v01

Co-authored-by: orchestrator
2026-08-25 16:20:53 -05:00
varutasu
8e1ab90fb1
feat(worker): minimal agent runtime for orchestrator jobs (Epic 7) (#11)
* feat(worker): minimal agent runtime for orchestrator jobs (Epic 7)

Adds apps/worker to consume Redis dispatch jobs, run a tool loop with model registry resolution, and record claim/complete + convoy role_complete events.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore(deploy): add orchestrator and worker to Coolify compose

Fix CI pnpm setup conflict and ship Docker targets for the automation stack alongside web/collab/mcp.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 15:31:34 -05:00
Randall Stillwell
8b01c1b8dd feat(orchestrator): convoy driver tick loop and Redis job queue (Epic 6)
Adds apps/orchestrator with planOrchestratorAction in shared, dry-run mode,
Redis role_dispatch enqueue, gate pauses, and stall detection logging.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 15:16:34 -05:00
Randall Stillwell
ea70716fc6 feat(mission-control): convoy kanban, gate inbox, and co-pilot (Epic 5)
Adds mission control UI with human gate actions via tRPC, kanban columns from
the shared lifecycle enum, and gate co-pilot using resolveModel('gate-copilot').

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 15:01:03 -05:00
Randall Stillwell
9405da8a45 feat(convoys): convoy lifecycle MCP and state machine (Epic 4)
Adds server-enforced convoy transitions with gate actor guards, convoy_events
logging, and six MCP tools (create, brief, transition, log, query, reconcile).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 14:51:19 -05:00
Randall Stillwell
30c4e533e8 feat(automation): connected repos — paste remote URL and forge adapters (Epic 3)
Workspace admins connect git remotes via settings UI; forge type is inferred
from the URL, API tokens stay env-var references, and test connection fetches
default branch for orchestrator/worker consumers.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 14:45:40 -05:00
Randall Stillwell
a6898617b5 fix(shared): drop .js extensions in model-routing imports for Next.js
Webpack resolves workspace packages without Node ESM .js suffixes; extensionless
paths match the rest of packages/shared and fix middleware build failures.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 14:09:40 -05:00
Randall Stillwell
cda4958b17 feat(shared): add model routing schemas and resolveModel()
Implements Epic 1 task 1 per docs/adr-model-routing.md: zod schemas for
providers and role routes, pipeline default aliases, legacy Cursor slug map,
and a pure resolveModel() with brief → convoy → workspace → default order.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 13:56:36 -05:00
Randall Stillwell
58f92f3898 feat(security): in-process rate limit for sign-in and invite creation
Algorithm: a fixed-window token bucket implemented as a pure function in
`@tasks/shared` (`consumeTokenBucket`) plus a thin `apps/web` wrapper that
holds per-key state in a module-scoped `Map`. No Redis, no external deps —
horizontally-scaled deploys will need a Redis-backed swap behind the same
`rateLimit()` signature; called out in the JSDoc as a follow-up. The pure
core is unit-tested in `packages/shared` (6 new vitest cases covering
allow/deny, window reset, key isolation, monotonic retryAfterMs, denied-
flood pegging, and option validation); the wrapper is intentionally not
tested here because apps/web has no vitest harness yet.

Wire-ins (the two narrow surfaces called out in the v1 spec):

  1. Credentials `authorize` in `apps/web/lib/auth.ts`: 5 attempts per
     IP per 60s. IP comes from `next/headers` (x-forwarded-for first
     entry, then x-real-ip); when headers() throws or returns nothing we
     fall back to keying on "unknown" in prod and skipping the limiter
     entirely in dev so a local test loop doesn't lock itself out. On a
     trip we `console.warn` and return null — the standard Auth.js
     "auth failed" signal — without consulting the DB.

  2. `invites.create` in `apps/web/server/routers/invites.ts`: 10
     invite-creates per inviter per hour. Keyed by inviter id (not
     workspace) so a multi-workspace admin can't multiply their
     allowance. On trip we throw TRPCError TOO_MANY_REQUESTS with a
     retry-after seconds count baked into the message.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 13:26:19 -05:00
Randall Stillwell
29e69e964b feat(invites): smart recipient autocomplete combobox (Task 3, done)
Closes Task-invite-recipient-autocomplete. The invite dialog's plain
email input is replaced with a debounced combobox that surfaces the
four real cases — existing member, pending invite, known user from a
sibling workspace, brand-new email — before the inviter hits send.

Subagent ran in parallel while the main thread shipped Task 2's UI;
file-level non-overlap held (subagent stayed in
apps/web/components/teams/invite-recipient-combobox.tsx and the
shared types; main thread stayed in invite-dialog.tsx and the
teams page). This commit folds the subagent's deliverable in plus
the two-line wire-up that swaps the input for the combobox.

Files (5 by subagent + 1 wire-up by main thread):

@tasks/shared:
* packages/shared/src/types/invite-suggestions.ts — InviteSuggestion
  union + pure mergeInviteSuggestions ranker. Lives in shared so
  client + server consume one type definition.
* packages/shared/src/types/invite-suggestions.test.ts — 9 vitest
  cases covering kind ordering, dedupe (known_user vs member by
  userId, vs pending_invite by lowercased email), new_email
  suppression when other kinds cover the typed address, the 10-
  result limit, and email normalization.
* packages/shared/src/types/index.ts — re-export.

apps/web:
* apps/web/server/routers/invites.ts — new `suggestRecipient`
  procedure on workspaceProcedure (owner/admin only). Implements
  the four kinds with the tenancy fence wired as a two-step query:
  first SELECT DISTINCT workspace_id FROM workspace_members WHERE
  user_id = inviter (the inviter's workspace pool), then
  inArray(workspaceMembers.workspaceId, pool) + ne(users.id,
  inviter) on the candidate join. Read the procedure JSDoc for the
  full set of invariants. All user-typed patterns escape through
  escapeIlike with the ESCAPE '\\' clause (mirrors search.ts).
  No existing exports modified.
* apps/web/components/teams/invite-recipient-combobox.tsx —
  standalone controlled combobox. 200ms debounce, min-2-char gate,
  distinct row styling per kind, ArrowUp/Down/Enter/Esc keyboard
  nav, outside-click close.
* apps/web/components/teams/invite-dialog.tsx (wire-up) — Input
  swapped for InviteRecipientCombobox. Added an
  onFocusExistingMember prop so a future teams-page integration
  can scroll/focus the matching row when a `member` suggestion is
  picked; for now the dialog just closes cleanly on member-pick.

Gates: 0 lint errors / 15 warnings (14 baseline + 1 incidental
from earlier teams-page work, none from this task's files); 6/6
type-check; 23/23 tests (14 baseline + 9 new).

Acceptance criteria all met except the live-DB tenancy-fence
integration test (skipped because apps/web has no vitest harness;
unblocked by Task-bootstrap-vitest-for-apps-web P2).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 10:39:09 -05:00
Randall Stillwell
48defb2ffd test(ci): bootstrap vitest + GitHub Actions and lock in 3-gate quality bar
First Path-B task. Path A landed daily-driver features without a
test runner; Path B is "harden so the next batch of changes can't
silently regress what just shipped." Step 1 is making `pnpm test`
real and gating CI on it.

Test runner:
* Install vitest + @vitest/coverage-v8 at the workspace root.
* Add vitest.config.ts (environment: "node", no JSDOM) + test /
  test:watch scripts to packages/shared, packages/database,
  packages/ai. Wire `test` into turbo.json with dependsOn: ^build
  for future-proofing; add `pnpm test` to root package.json.

Three real tests (no snapshot theater — verified by mutation):
* packages/shared/src/utils/id.test.ts: asserts generateId() matches
  the RFC 4122 v4 regex and produces 1000 distinct values. Mutating
  generateId() to a constant fails both assertions.
* packages/shared/src/types/objects.test.ts: pins objectTypes and
  objectStatuses arrays. These back the zod enum on objects.create
  and the "open tasks" count on the workspace-home dashboard; a
  silent reorder/rename would otherwise corrupt the dashboard math.
* packages/database/src/markdown-backlog/parse.test.ts: covers
  parseBacklogMarkdown across three shapes (well-formed Task,
  no-frontmatter Plan with path inference, malformed YAML that
  must NOT throw — the importer runs in a file watcher). Plus
  hashFileContents determinism.
* packages/ai/src/actions/index.test.ts: five tests across the
  prompt builders (summarize/expand/rewrite × 3 tones / translate /
  generateFromPrompt). Pure functions; no model mocking needed.

CI:
* .github/workflows/ci.yml runs on pull_request and push to main.
  Node 20, pnpm 9 pinned explicitly (per AGENTS.md). Uses
  setup-node's built-in pnpm cache. Steps: install --frozen-lockfile,
  lint, type-check, test. Concurrency group cancels superseded runs
  on non-main branches.

Docs:
* AGENTS.md: drop the "no test runner configured" disclaimer.
  Document pnpm test / test:watch. Update the PR-readiness rule
  from `pnpm lint && pnpm type-check` to
  `pnpm lint && pnpm type-check && pnpm test`.

All 14 tests pass; lint + type-check still green across all 6
packages. The CI workflow's first run is gated on the operator
pushing this branch — that's the only acceptance criterion left
unverified in this commit.

Closes plans/Plan-multitenant-saas-hardening/Epic-test-foundation/
Task-bootstrap-vitest-and-ci.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 09:23:55 -05:00
Randall Stillwell
1c9deea3fd chore(lint+types): green pnpm lint && pnpm type-check from a clean clone
Path-A first task: get the repo's two repo-wide quality gates passing.
Both were failing from a clean clone in ways that were silently hiding
each other.

Headline fixes:

* apps/web: add an eslint 9 flat config (eslint.config.mjs) using
  FlatCompat against next/core-web-vitals + next/typescript, and switch
  the `lint` script from `next lint` to `eslint .`. Previously `next
  lint` fell into its interactive setup prompt because there was no
  config at all in apps/web, which made `pnpm lint` permanently fail
  before any rule ever ran.
* packages/shared/src/utils/id.ts: replace `randomUUID` from `node:crypto`
  with `globalThis.crypto.randomUUID`. `@tasks/shared` is forbidden from
  using Node-only APIs (per AGENTS.md / repo-overview.mdc) because it
  has to be importable from the browser bundle.

Adjacent fixes pulled in to make the gates actually green:

* apps/mcp-server/tsconfig.json: drop vestigial rootDir / declaration*
  / outDir / sourceMap (build is via tsup, not tsc emit) and add
  allowImportingTsExtensions. The MCP server uses `.ts`-extension
  re-export shims (db.ts / schema.ts / shared-types.ts) so tsup can
  inline workspace .ts sources into the bundle.
* apps/collab-server/tsconfig.json: same simplification.
* apps/mcp-server/package.json: add @types/node so `process.env` in
  packages/database/src/client.ts (transitively pulled into the MCP
  server's type-check) resolves.
* apps/web/components/ui/input.tsx: empty `interface InputProps extends
  React.InputHTMLAttributes<HTMLInputElement> {}` -> `type` alias.
* apps/web/server/lib/workspace-guard.ts: `from(args.table as any)` ->
  `as unknown as PgTable` with a comment. Standard drizzle escape
  hatch for structural generic tables.
* apps/web/components/whiteboard/shapes/{document,project,task}-card.tsx:
  `BaseBoxShapeUtil<any>` -> `BaseBoxShapeUtil<{Shape}>` plus inline
  `declare module "@tldraw/tlschema"` augmentation of
  TLGlobalShapePropsMap. Required adding @tldraw/tlschema as a direct
  devDep of apps/web so the augmentation target resolves; previously
  it was only present transitively under tldraw's own deps.

Result: `pnpm lint && pnpm type-check` exits 0 across all 6 packages.
16 unused-import / exhaustive-deps warnings remain; they're pre-existing
housekeeping and out of scope for this task.

Closes plans/Plan-daily-driver-finish/Epic-shipping-the-shell/
Task-fix-lint-and-shared-types.md (status: done).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 00:11:52 -05:00
Randall Stillwell
663bc77afe feat: ECHODO app shell, Coolify deploy, Authentik + Umami
Bundles in-flight ECHODO work with the Coolify deployment configuration:

App
- New routes: ai, forms, planner, settings (templates/types), teams,
  doc detail, whiteboard detail
- New components: app shell rework (icon-rail, top-header), forms
  builder/renderer/responses, types manager, objects creation dialog,
  card primitive, form + overview views
- New tRPC routers: favorites, forms, types, workspaces; updates to
  health and objects routers
- Markdown backlog sync (packages/database) + cursor-sync schema/migrations
- Schema additions: forms, types, favorites, markdown_backlog, cursor_sync
- Initial Drizzle migrations checked in

Deployment
- docker/docker-compose.coolify.yml: drops bundled Postgres/Redis
  (uses CT 102 shared services), removes host port mappings, adds
  Coolify SERVICE_FQDN_* magic vars for web + collab
- .env.example rewritten as the full ECHODO/Coolify variable manifest
- NextAuth gains an Authentik OIDC provider (gated on env presence)
- Root layout injects Umami tracking script when configured;
  metadata title flipped to ECHODO

Security
- .gitignore expanded to exclude AGENT-DEPLOY.md, .env.*, secrets/,
  credentials.*, *.key, *.crt, *.pem, ssh keys

Made-with: Cursor
2026-04-26 14:34:34 -05:00
Randall Stillwell
a508ece6e7 feat: Full project management application scaffold
Complete architecture for a ClickUp/Notion/Miro-class project management app:

- Turborepo monorepo with Next.js 15, TypeScript, PostgreSQL (Drizzle ORM)
- Object-centered database schema (everything is an Object: tasks, projects, docs, whiteboards)
- NextAuth v5 authentication with credentials + OAuth providers
- tRPC v11 API layer with full CRUD for objects, properties, relations, templates, search
- Three-panel UI: collapsible sidebar, center content area, push-in right panel
- Purple/teal theme with light/dark mode via Shadcn/ui + Tailwind CSS
- Multiple views: List, Kanban board (dnd-kit), Table (spreadsheet), Embedded iframe
- TipTap rich text editor with slash commands, custom blocks (callout, toggle, mention, embed, divider), AI block
- Real-time collaboration via Yjs + Hocuspocus with presence/cursors
- tldraw whiteboard with custom shape cards (task, document, project)
- MCP server exposing all app data/tools for AI agents
- AI chat panel, editor AI slash commands, Cmd+K command palette
- Template system with built-in templates (Bug Report, Meeting Notes, Sprint)
- Full-text search with result highlighting
- Docker Compose for full-stack deployment (web + collab + postgres + redis)

Made-with: Cursor
2026-03-26 22:39:16 -05:00