Context
The reporting domain, modeled entirely in app/Models/ (schema via buddy generate:migrations, #5). Keep the shapes boring and explicit; the query engine (#10), builder (#12), and templates (#14) all consume these.
Scope
Report: project-scoped, name, slug, description,status(draft|published),origin(user|template),template_key(nullable, for #14 idempotency), default time range, layout version.useTimestamps,useSoftDeletes.ReportBlock: belongs to Report;kind(line|area|bar|donut|big_number|table|funnel|heatmap|text); grid placement{x, y, w, h}on a 12-column grid;queryJSON (measure(s), event name(s), dimension, filters, time grain, comparison range) validated with@stacksjs/validationschemas - the same schema the builder UI and query engine share;vizJSON (axes, palette slot, number format).ReportRevision: autosave snapshots of layout + blocks (bounded count per report; builder undo/restore in #12).ReportSchedule: belongs to Report; cadence (daily|weekly|monthly), hour + timezone, recipients, format (link|csv|xlsx); consumed by #15.ReportShare: revocable public token, optional expiry; consumed by #16.ReportExport: requested export bookkeeping (status, file path via@stacksjs/storage, expiry); consumed by #15.- Relationships, factories, and seeders for all of the above - the seeded dev database must contain a convincing demo project with events (#6 factories) and two seeded reports, because every UI issue develops against the seed.
- Owner/member authorization helpers consistent with #4 (
app/Support/access.tspattern). - Feature tests: model CRUD through actions, block query-JSON validation rejects malformed configs, revision pruning.
Acceptance
buddy migrate:fresh --seedyields a demo project with seeded reports renderable once #10/#11 land; zero hand-written SQL.
Depends on #4, #5. Blocks #10, #12, #13, #14, #15, #16. Part of #1 (M3 Reporting core). Ground rules: see #1.