Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion greenfield/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ lerna-debug.log*
node_modules
dist
release-manifest.json
data/
/data/
!/src/browser/data/
!/src/browser/data/**
.test-openclaw/
.test-data
dist-ssr
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ once. Reusable procedure builders are limited to:

Expected errors use a small stable code set such as `UNAUTHORIZED`, `FORBIDDEN`, `CONFLICT`,
`NOT_FOUND`, `PRECONDITION_FAILED`, `TOO_MANY_REQUESTS`, and `SERVICE_UNAVAILABLE` with safe
structured details. The `ContractErrorCode` union, all 36 actual router paths, the server-owned
structured details. The `ContractErrorCode` union, all 55 actual router paths, the server-owned
runtime allowlist, and generated contract metadata must match exactly. The base procedure
middleware enforces that allowlist for immediate and deferred subscription failures; an
implemented procedure missing from the policy or an undeclared code becomes a redacted internal
Expand Down Expand Up @@ -602,6 +602,11 @@ cache key. A server snapshot always wins over conflicting speculative collection
- Feature modules own their query option factories, mutation option factories, collection
adapter, components, and tests.
- Shared UI contains presentation primitives, not domain-specific orchestration.
- The reviewed Dashboard agent directory is code-owned configuration. Gateway discovery may
enrich future live availability, but cannot add identities or grant agent capabilities.
- Agent current-task writes require an `agents:write` automation principal and retain durable
actor attribution. Browser sessions consume the read projection and history; they cannot
impersonate the task-tracking caller.
- React Compiler remains enabled. Manual memoization is used only where stable identity is an
external contract and a profiler or test justifies it.
- Lists with unbounded rows use TanStack Virtual; tables use TanStack Table; neither becomes a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,25 @@ Moltbook remain external systems. Dashboard persists only configuration, bounded
audit/history, job state, or recovery state that it owns. It does not mirror entire external
databases.

### Task and agent ownership

The reviewed agent directory is application configuration rather than database or Gateway
discovery state. `agent_task_runs` persists only current-task intervals owned by Dashboard:

- one partial unique index permits at most one active interval per configured agent;
- run identity, agent, task, start time, and originating actor are immutable;
- completed intervals are append-only history and cannot be reopened or rewritten;
- user actors use UUIDv7 identities, automation actors use canonical scoped-principal IDs;
- start, activity, and completion timestamps are monotonic and bounded; and
- newest-first global and per-agent indexes support strict `(started_at, id)` keyset pages.

An `agents:write` caller can target only an identity in the reviewed directory. It never creates
an agent. Start, heartbeat, replace, and clear transitions run inside an admitted immediate
transaction. State-changing start, replace, and clear transitions append the matching realtime
event atomically; a same-task heartbeat only advances durable activity. The production
task-tracking credential must receive this capability during the delivery/provisioning slice; no
browser session can invoke the mutation.

### Incident and notification lifecycle

Heartbeat and other monitors can report many simultaneous problems across tasks, jobs, system
Expand Down Expand Up @@ -173,6 +192,7 @@ queryable lifecycle.
| Task board | `tasks(status, priority, updated_at_ms DESC)` |
| Task label filter | `task_labels(label, task_id)` |
| Task timeline | `task_updates(task_id, created_at_ms, id)` and equivalent event index |
| Agent task history | unique active-agent partial index plus `(agent_id, started_at_ms, id)` |
| Latest reports | `reports(kind, occurred_at_ms DESC, id DESC)` |
| Heartbeat stream | `reports(source, source_job_id, occurred_at_ms DESC, id DESC)` |
| Active incidents | partial `incidents(monitor_key, last_seen_at_ms DESC) WHERE state = 'active'` |
Expand Down Expand Up @@ -201,9 +221,10 @@ Drizzle Kit v1 stores the migration graph as timestamped directories containing
one evolving `*_dashboard-foundation` baseline generated from the complete current Drizzle schema.
The generated SQL includes the security identity objects, SQLite `STRICT` table options, canonical
NUL-free constraints, bounded migration-ledger identity fields, and deliberate
`audit_events WITHOUT ROWID` hardening. The custom audit metadata, append-only audit/migration
ledger, monitoring-JSON, and automation replacement-integrity triggers are reviewed additions
because Drizzle does not model them.
`audit_events WITHOUT ROWID` and `agent_task_runs WITHOUT ROWID` hardening. The custom audit
metadata, append-only audit/migration ledger, immutable completed agent-run history,
monitoring-JSON, and automation replacement-integrity triggers are reviewed additions because
Drizzle does not model them.
There is no compatibility preflight or upgrade path for an intermediate rewrite database: every
test and the final cutover start empty and apply this one baseline. Each schema slice regenerates
the baseline, reviews the complete SQL/snapshot diff, and updates the explicit manifest checksums.
Expand Down
24 changes: 23 additions & 1 deletion greenfield/docs/architecture/greenfield-rewrite/progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ closes a phase; dated entries below provide the evidence, not a second status so
| 0 — Evidence and qualification | Complete | All eight mandatory spikes pass on exact Bun revision `17d6843606d76620cb55d31424d7fb0aed51c367`: build, transport, cross-process SQLite/outbox, Drizzle/Bun SQLite, browser data, chat batching, shutdown, and capped resources. Source-derived parity and the OpenClaw source audit pass as additional evidence. |
| 1 — Foundation | Complete | The self-contained future root builds immutable browser/web/worker artifacts, protects project-local production state, installs exact Bun and systemd artifacts, migrates a database copy, atomically promotes the release/database pair, serves readiness/browser assets, writes project-local logs, and proves crash-safe rollback and shutdown in a disposable lifecycle. |
| 2 — Trust and transport | Complete for the stated server scope | Authentication, MFA, WebAuthn, automation credentials, audit, authenticated renewable SSE, one-shot native Gateway bootstrap verification, and the consolidated [threat model](../../security/greenfield-phase-two-threat-model.md) have executable evidence. Browser UI and production cutover remain later gates. |
| 3 — Core operator domains | Started | The task domain and `/tasks` parity slice are implemented with durable history, realtime invalidation, and browser workflows. Agent, report, incident, notification, schedule/job, cache/metrics, overview, and worker-domain parity remain open. |
| 3 — Core operator domains | Started | Task and agent-directory parity are implemented with durable history, realtime invalidation, and browser workflows. Report, incident, notification, schedule/job, cache/metrics, overview, and worker-domain parity remain open. |
| 4 — Gateway and chat | Not started | The Phase 2 verifier is one-shot only. Persistent native Gateway lifecycle, current-protocol re-audit, sessions, chat journal/recovery, attachments, and frontend remain open. |
| 5 — Privileged and external domains | Not started | Worker-owned file/media, Docker, database, OpenClaw, GitHub, deployment, backup, and other privileged adapters remain open. |
| 6 — Parity, hardening, and cutover | Not started | Full UI parity, generated `/docs`, load/resource/restore evidence, cutover rehearsal, fresh production database, and legacy removal remain open. |
Expand Down Expand Up @@ -819,3 +819,25 @@ full-browser parity, production rehearsal, cutover, and legacy deletion remain o
- The reviewed parity inventory now marks the 11 task operations and `/tasks` route implemented.
This closes only the task portion of Phase 3. Agent, report, incident, notification, job,
monitoring API, overview, cache/metrics, and real worker execution remain explicit gates.

### 2026-08-07 — Phase 3 agent status and task-history slice

- A reviewed, code-owned directory defines the five Dashboard automation agents independently of
Gateway connection state. Typed `agents:read` and `agents:write` capabilities expose exact
configuration, one/all current statuses, keyset-paginated task history, and scoped metadata
updates without treating mutable Gateway discovery as application authorization.
- `agent_task_runs` retains one active interval per configured agent and immutable completed
history in a strict `WITHOUT ROWID` table. Every transition revalidates persisted rows, records
the user or automation actor, and runs behind immediate-write admission. State changes append a
durable `agents.status` realtime invalidation in the same transaction and wake delivery only
after commit; same-task heartbeats update activity without unbounded realtime-event growth.
- `/agents` uses the shared Dashboard shell and presentation primitives, query-backed TanStack DB
collections for normalized definitions and live statuses, TanStack Query for keyset-paginated
history, TanStack Table, and the shared virtualizer. Durable realtime events invalidate the
relevant collection/query roots; a 30-second fallback begins only after the terminal event
stream closes. Current-task mutation remains an authenticated automation boundary rather than a
browser editing control.
- The parity inventory now marks the five agent operations and `/agents` route implemented.
Persistent OpenClaw/Gateway availability and session state remain Phase 4 work; reports,
incidents, notifications, schedules/jobs, overview, cache/metrics, and the real worker remain
open Phase 3 gates.
7 changes: 6 additions & 1 deletion greenfield/docs/generated/procedures.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
| `accountSecurity.stepUpTotp` | mutation | account-security | Authenticated browser session | [input](./schemas/accountSecurity.stepUpTotp.input.schema.json) | [output](./schemas/accountSecurity.stepUpTotp.output.schema.json) | `CONFLICT`, `FORBIDDEN`, `SERVICE_UNAVAILABLE`, `TOO_MANY_REQUESTS`, `UNAUTHORIZED` | `mfa_enrollment_required` | Rotates the session after a fresh TOTP proof. |
| `accountSecurity.stepUpWebAuthn` | mutation | account-security | Authenticated browser session | [input](./schemas/accountSecurity.stepUpWebAuthn.input.schema.json) | [output](./schemas/accountSecurity.stepUpWebAuthn.output.schema.json) | `CONFLICT`, `FORBIDDEN`, `SERVICE_UNAVAILABLE`, `TOO_MANY_REQUESTS`, `UNAUTHORIZED` | `mfa_enrollment_required` | Consumes a WebAuthn challenge and rotates the verified session. |
| `accountSecurity.summary` | query | account-security | Authenticated browser session | [input](./schemas/accountSecurity.summary.input.schema.json) | [output](./schemas/accountSecurity.summary.output.schema.json) | `FORBIDDEN`, `UNAUTHORIZED` | None | Returns MFA inventory and server-relative recent-auth state. |
| `agents.getConfiguration` | query | agents | Authenticated: agents:read | [input](./schemas/agents.getConfiguration.input.schema.json) | [output](./schemas/agents.getConfiguration.output.schema.json) | `FORBIDDEN`, `UNAUTHORIZED` | None | Returns the reviewed Dashboard-owned agent directory. |
| `agents.getStatus` | query | agents | Authenticated: agents:read | [input](./schemas/agents.getStatus.input.schema.json) | [output](./schemas/agents.getStatus.output.schema.json) | `FORBIDDEN`, `NOT_FOUND`, `UNAUTHORIZED` | None | Returns the current task projection for one configured agent. |
| `agents.listStatuses` | query | agents | Authenticated: agents:read | [input](./schemas/agents.listStatuses.input.schema.json) | [output](./schemas/agents.listStatuses.output.schema.json) | `FORBIDDEN`, `UNAUTHORIZED` | None | Returns current task projections for all configured agents. |
| `agents.listTaskHistory` | query | agents | Authenticated: agents:read | [input](./schemas/agents.listTaskHistory.input.schema.json) | [output](./schemas/agents.listTaskHistory.output.schema.json) | `FORBIDDEN`, `NOT_FOUND`, `UNAUTHORIZED` | None | Lists durable newest-first agent current-task history. |
| `agents.updateMetadata` | mutation | agents | Authenticated automation principal: agents:write | [input](./schemas/agents.updateMetadata.input.schema.json) | [output](./schemas/agents.updateMetadata.output.schema.json) | `FORBIDDEN`, `NOT_FOUND`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | None | Atomically starts, touches, replaces, or clears one agent current task. |
| `auth.beginWebAuthnLogin` | mutation | auth | Pending MFA login | [input](./schemas/auth.beginWebAuthnLogin.input.schema.json) | [output](./schemas/auth.beginWebAuthnLogin.output.schema.json) | `CONFLICT`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | None | Creates one pending-login-bound WebAuthn assertion challenge. |
| `auth.bootstrap` | mutation | auth | Public | [input](./schemas/auth.bootstrap.input.schema.json) | [output](./schemas/auth.bootstrap.output.schema.json) | `CONFLICT`, `SERVICE_UNAVAILABLE`, `TOO_MANY_REQUESTS`, `UNAUTHORIZED` | None | Verifies the Gateway credential and creates the sole first user. |
| `auth.changePassword` | mutation | auth | Browser session when MFA is disabled; recent MFA when enabled | [input](./schemas/auth.changePassword.input.schema.json) | [output](./schemas/auth.changePassword.output.schema.json) | `CONFLICT`, `FORBIDDEN`, `SERVICE_UNAVAILABLE`, `TOO_MANY_REQUESTS`, `UNAUTHORIZED` | `step_up_required` | Changes the password, rotates the current session, and revokes the rest. |
Expand All @@ -40,7 +45,7 @@
| `automationSecurity.replaceCapabilities` | mutation | automation-security | MFA enrollment required; recent MFA when enabled | [input](./schemas/automationSecurity.replaceCapabilities.input.schema.json) | [output](./schemas/automationSecurity.replaceCapabilities.output.schema.json) | `CONFLICT`, `FORBIDDEN`, `NOT_FOUND`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | `mfa_enrollment_required`, `step_up_required` | Atomically replaces a principal's least-privilege capability set. |
| `automationSecurity.revokeCredential` | mutation | automation-security | MFA enrollment required; recent MFA when enabled | [input](./schemas/automationSecurity.revokeCredential.input.schema.json) | [output](./schemas/automationSecurity.revokeCredential.output.schema.json) | `CONFLICT`, `FORBIDDEN`, `NOT_FOUND`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | `mfa_enrollment_required`, `step_up_required` | Explicitly revokes one automation credential after client cutover. |
| `automationSecurity.rotateCredential` | mutation | automation-security | MFA enrollment required; recent MFA when enabled | [input](./schemas/automationSecurity.rotateCredential.input.schema.json) | [output](./schemas/automationSecurity.rotateCredential.output.schema.json) | `CONFLICT`, `FORBIDDEN`, `NOT_FOUND`, `PRECONDITION_FAILED`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | `mfa_enrollment_required`, `step_up_required` | Stages a linked replacement credential without revoking its predecessor. |
| `events.stream` | subscription | events | Authenticated; per-topic: notifications:read, reports:read, tasks:read | [input](./schemas/events.stream.input.schema.json) | [output](./schemas/events.stream.output.schema.json) | `BAD_REQUEST`, `FORBIDDEN`, `SERVICE_UNAVAILABLE`, `TOO_MANY_REQUESTS`, `UNAUTHORIZED` | None | Streams authorized durable changes with tracked resume cursors. |
| `events.stream` | subscription | events | Authenticated; per-topic: agents:read, notifications:read, reports:read, tasks:read | [input](./schemas/events.stream.input.schema.json) | [output](./schemas/events.stream.output.schema.json) | `BAD_REQUEST`, `FORBIDDEN`, `SERVICE_UNAVAILABLE`, `TOO_MANY_REQUESTS`, `UNAUTHORIZED` | None | Streams authorized durable changes with tracked resume cursors. |
| `securityAudit.listEvents` | query | securityAudit | Authenticated browser session | [input](./schemas/securityAudit.listEvents.input.schema.json) | [output](./schemas/securityAudit.listEvents.output.schema.json) | `FORBIDDEN`, `UNAUTHORIZED` | None | Lists redacted immutable security events in stable newest-first order. |
| `system.runtimeIdentity` | query | system | Public | [input](./schemas/system.runtimeIdentity.input.schema.json) | [output](./schemas/system.runtimeIdentity.output.schema.json) | None | None | Returns the Bun runtime identity of the serving process. |
| `tasks.addUpdate` | mutation | tasks | Authenticated: tasks:write | [input](./schemas/tasks.addUpdate.input.schema.json) | [output](./schemas/tasks.addUpdate.output.schema.json) | `CONFLICT`, `FORBIDDEN`, `NOT_FOUND`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | None | Appends one authenticated progress update to a task. |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$id": "urn:mira-dashboard:agents.getConfiguration.input",
"type": "object",
"properties": {},
"required": [],
"additionalProperties": false,
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
Loading