DashForge is a dashboards and analytics application built on UIForge (github.com/plexusone/uiforge), the specification-driven UI composition platform extracted from this repo. UISpec types, the component registry, and the runtime engines are consumed from uiforge; this repo owns the dashboard IR, analytics engine, server, and builder UI.
Design decisions are documented in docs/specs/ — read before implementing:
docs/specs/PRD.md— problem, goals, non-goals, experience profilesdocs/specs/TRD.md— architecture, UISpec type system, component namespaces, repository structuredocs/specs/PLAN.md— phased build order with exit criteria and risksdocs/specs/ROADMAP.md— RMI-level breakdown by themed phase
- Go module:
github.com/plexusone/dashforge— renamed back fromuiforgein v0.6.0 (the project wasdashforgethrough v0.3.0,uiforgefor v0.4.0–v0.5.0). Old versions stay importable under the path they were tagged with. Initiative/RMI IDs are permanent and keep their era's slug:INIT-UIFORGE-*/RMI-UIFORGE-*andINIT-DASHFORGE-*/RMI-DASHFORGE-*both remain valid — never rewrite one into the other. - ORM: Ent (
entgo.io/ent) for any persistence needs - CLI: Cobra (
github.com/spf13/cobra) - Frontend: React + TypeScript (in
builder/); PageSpec rendering via the npm packages@plexusone/uiforge-renderer/@plexusone/uiforge-renderer-lit - JSON Schema: generated from Go types via
invopop/jsonschema, linted withschemakit(formerly schemago/schemalint) - Query safety: GrokifyQL for saved Questions and analytics queries
- Authorization: SystemForge
authz.Authorizer, with SpiceDB mode for fine-grained permissions
- Saved Questions are read-only GrokifyQL artifacts persisted by the DashForge backend. Dashboard widgets reference Questions and choose their own visualization.
- The Question builder intentionally separates SQL display and editing: existing saved Questions open in formatted, syntax-highlighted read mode; new Questions and query mutations switch to edit mode. Result tables should scroll horizontally inside the middle column for wide result sets.
- The analytics catalog is the source of truth for queryable sources, datasets,
and fields. Use
dashboardir.AnalyticsCatalogand the publicanalyticspackage instead of hard-coding application-specific assumptions into UI code. - Persist analytics source connection metadata in DashForge's metadata DB, but
store only secret references (
env://...,keyring://...,aws-sm://..., etc.). Resolve them at runtime with OmniVault; use VaultGuard when posture and provider-selection policy are needed. Never return resolved DSNs/tokens to the browser. - If a self-contained encrypted-in-DB fallback is needed, use OmniVault's SQL
store provider (
sql://...) with a DashForge-owned table and an encryption key sourced outside that database. Do not create a parallel DashForge-only secret interface. - Agent marketplace concepts live in
github.com/plexusone/omniagent, not in DashForge. DashForge should consume OmniAgent marketplace providers and publish app-owned capability names such asdashforge.query.run,dashforge.question.write, anddashforge.field_values.read. - GrokifyQL policy is enforced both when saving Questions and when executing
ad-hoc
/api/v1/analytics/queryrequests. - In SpiceDB mode, DashForge uses SystemForge as the bridge and maps catalog
datasets/fields to
analytics_datasetandanalytics_fieldresources. Keep resource ID generation stable when changing query names.
cube/has no lint/typecheck/format tooling, by design. It's a thin Cube.js semantic-layer service directory (@cubejs-backend/*deps only,buildscript is a no-op echo) — it was never meant to be a typechecked/linted TS app likebuilder/.atrelease release's validation auto-detects it as a TS project anyway (it has.tsfiles) and fails on missing eslint config / notsc. This is a false positive, not real debt — don't add tooling tocube/to silence it; runatrelease release <version> --skip-checksinstead (after manually verifyinggo build/go test/golangci-lint/builder/are actually clean).- CI can't run a newer Go patch than what it cached. The shared
plexusone/.githubreusablego-ci.yaml(actions/setup-go@v7) resolvesgo-version: "1.26.x"to whatever patch it last cached and pinsGOTOOLCHAIN=local— it does not auto-upgrade just becausego.mod'sgodirective asks for a newer patch. Bumpinggo.modpast CI's cached version breaks the build withgo: go.mod requires go >= X (running go Y; GOTOOLCHAIN=local). Check CI's actual resolved version before bumping (or fix the shared workflow tocheck-latest: true) — don't just bump-and-push. See the same note in theplexusoneorg CLAUDE.md.
This repo's roadmap items are tracked in prism-control. The initiative is INIT-UIFORGE-001 with 37 RMIs across 6 phases.
Every Claude Code session that implements work follows this check-in/check-out protocol:
prismctl work ready --initiative INIT-UIFORGE-001Claim an RMI before starting. This creates a lease-based assignment:
prismctl work claim RMI-UIFORGE-001 --worker "session-$(date +%s)" --lease-hours 4Transition the RMI to in-progress:
prismctl rmi update RMI-UIFORGE-001 --status in_progressRenew the lease if work takes longer:
prismctl work renew <assignment-id> --lease-hours 4Work in this repo. Every commit carries the RMI trailer:
feat(uispec): define core PageSpec types
Refs: RMI-UIFORGE-001
Trailer rules:
- Git trailer with key
Refs:— value is one RMI ID - RMI only, never the initiative ID
- Subject line stays clean; trailer goes in the footer
- Conventional Commits format for the message itself
# Add evidence and handoff notes
prismctl work update <assignment-id> \
--handoff '{"completed":["module rename"],"remaining":[],"decisions":[],"next_action":"none"}'
# Mark complete and auto-transition RMI to completed
prismctl work complete <assignment-id> --transitionOr release if handing off to another session:
prismctl work release <assignment-id> \
--handoff '{"completed":["core logic"],"remaining":["tests"],"next_action":"add unit tests"}'Some RMIs land in sibling repos (plexusone/agentos, plexusone/agentos-web). The RMI ID reflects where the code lands, not the initiative home:
RMI-UIFORGE-*— work in this repo during the uiforge era (INIT-UIFORGE-001); new work here usesRMI-DASHFORGE-*RMI-AGENTOS-*— work in agentos or agentos-web