Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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