Skip to content

spec(report): a report declares a dataset-dimension date range and export options #26548

Closed

Description

@objectstack-fleet

Ruling-ref: 6408567749
Clause-②: yes (widening)
Metadata-protocol: added

Filing gate ③, a task the maintainer set: ruling 6408567749 on #25873 (letter B, maintainer 「同意」 2026-10-26T04:56Z). The ruling's execution order files this spec card first; #25873 carries Blocked-by: this card and then becomes the ui/ renderer work. ⛔ Not a claim. Grade p2 inherited from #25873.

维护者速读

  • 事由: 报表页只有「编辑」:没有时间范围,没有导出。裁决 B(6408567749)要求报表复用仪表盘的 dateRange 词汇与预设(数据集有时间维度时生效),并通过现有的列表导出权限导出报表表格(CSV / XLSX),不做订阅。今天 ReportSchema 两样都没有声明,渲染器也没有地方可读。
  • 要做的事: 在 ReportSchema 上新增两个可选键。① dateRange: { dimension?, defaultRange, allowCustomRange }:defaultRange / allowCustomRange 与仪表盘逐字相同(同一份预设表、同样的默认值);dimension 指数据集里一个 type: 'date' 的维度(ADR-0021 的语法:报表按名字选维度,不写原始字段),省略时取数据集唯一的日期维度。② exportOptions: { formats?, includeHeaders?, fileNamePrefix? }:成员取自列表的导出块,格式只留 csv / xlsx;谁能导出由现有的 allowExport 权限轴决定,不新增权限位。另加别名、表单、生成的 schema、liveness 行、lint 规则和文档。
  • 风险: (1) 仪表盘的 dateRange.field 今天按原始对象字段生效(默认 created_at,见 analytics: a dashboard global filter whose field shares a dataset dimension's name is lowered to that dimension — the spec says it resolves against the dataset object #25814),而 ADR-0021 第 392 行计划的是维度名。本卡按 ADR 走 dimension,field 只作为改名提示的别名;如果维护者要求报表与仪表盘同一个键名 field,评审时改名,形状不变。(2) 列表的 XLSX 只走服务端流(GET /data/:object/export),而那条门导出的是记录,不是聚合行。所以报表的 XLSX 要在浏览器端生成,权限只能管住按钮,管不住数据(看得到的人本来就能读这些行)。这一点在 ui 卡里说明,不在这里新开服务端门。(3) 新键在 ui 卡落地前没有读取方,liveness 记 planned,并给作者警告。

What was measured

All readings at origin/main f486e9c.

What a report declares today: no date range, no export.

  • packages/spec/src/ui/report.zod.ts:347-591 — ReportSchema is a strictObject with name, label, description, type, dataset (:436), rows (:441), columns (:448), values (:453), runtimeFilter (:459), order (:484), drilldown (:490), chart (:500), blocks (:515), protection (:525) and the protection fields. git grep -n -iE 'dateRange|timeRange|export' -- packages/spec/src/ui/report.zod.ts finds only TS export statements. No time-range key, no export key.
  • Alias table :375-404 (dataset / values / rows / runtimeFilter / order targets) and guidance :405-417 (drillDown). The comment at :351-368 requires the report and block tables to stay parallel: report.test.ts derives that parity, and alias-integrity.test.ts proves that every alias targets a declared key and that no alias is filed under a declared key.
  • JoinedReportBlockSchema :238-329 has its own alias and guidance tables. Its guidance uses wrong-layer pointers (blocks / drilldown / protection) and one retired key (chart, [finding] a joined report's chart — container and blocks[].chart — parses and is never drawn, while the liveness ledger names the joined branch as its reader #20161).
  • The superRefine at :532-591 already refuses one presentation key on a joined container (chart, :585-587), with no pointer onto blocks[]. That is the precedent for a container key a joined report cannot honour.
  • packages/spec/src/ui/report.form.ts:18-117 has four sections (Basics / Dataset binding / Joined blocks / Filter & chart). None of them offers a period or export control.
  • packages/spec/liveness/report.json has rows for every current key. A new key needs a row, or check:liveness reports it as UNCLASSIFIED.

The dashboard's vocabulary.

  • packages/spec/src/ui/dashboard.zod.ts:1829-1837 — dashboard.dateRange is strictObject { field?: string, defaultRange: enum(DATE_RANGE_DEFAULT_RANGES).default('this_month'), allowCustomRange: boolean.default(true) }, with aliases dateField/fieldName→field, preset/range/default→defaultRange, allowCustom/custom→allowCustomRange. There is no presets key. The chip offers the whole preset list.
  • :1778 — dashboard top-level aliases dateFilter/timeRange → dateRange.
  • :1523 re-exports DATE_RANGE_PRESETS from packages/spec/src/data/date-range-presets.ts:42-49: today, yesterday, this/last_week, this/last_month, this/last_quarter, this/last_year, last_7/30/90_days. :1539 DATE_RANGE_DEFAULT_RANGES = [...DATE_RANGE_PRESETS, 'custom'] (custom = open the picker, no window).
  • :1408-1418 — filterBindings: a dashboard date range binds a widget field, defaulting to dateRange.field ?? 'created_at'. In the shipped dashboard, field is a raw object field merged into the widget's runtimeFilter. That is consistent with analytics: a dashboard global filter whose field shares a dataset dimension's name is lowered to that dimension — the spec says it resolves against the dataset object #25814 (closed 2026-10-26), where global-filter field resolves against the dataset object.
  • :1241-1268 — compareTo.dimension is the dashboard's own dataset-dimension key for "which date column". The executor resolves an omitted one as "the selection's time dimensions that carry a dateRange; exactly one is used; zero or several is a loud error naming what it found". Its aliases are field/dateField/timeDimension → dimension.

What a time dimension is.

  • packages/spec/src/ui/dataset.zod.ts:190 — DatasetDimension.type: enum(['string','number','date','boolean','lookup']).optional(); :192 dateGranularity (default bucket for date dimensions).
  • packages/services/service-analytics/src/dataset-compiler.ts:538-546 — dimensionType: date → cube 'time'; absent type → 'string'. So a dimension over a date column that does not declare type: 'date' is not a time dimension. "The dataset has a time dimension" therefore means "declares a dimension with type: 'date'".
  • Example: examples/app-todo/src/datasets/task.dataset.ts:30-31 declares two (due_date, completed_date). A report on it has to name which one is meant.

What the server accepts today (measured by reading, not executed).

The list-export surface this reuses.

  • packages/spec/src/ui/view.zod.ts:3176-3182 — view.exportOptions is the union of a legacy bare format array and ListViewExportOptionsSchema.
  • packages/spec/src/ui/list-view-export-options.ts:70 — ListViewExportFormatSchema = enum(['csv','xlsx','json']) (pdf retired). :92-103 — members formats, maxRecords, includeHeaders, fileNamePrefix, streaming. :94 — "XLSX is delivered by the server stream only".
  • The permission is packages/spec/src/security/permission.zod.ts:514, ObjectPermission.allowExport (opt-in, read ∧ grant, :270). The server resolves it into apiOperations on /me/permissions and enforces it at GET /data/:object/export → 403 EXPORT_NOT_PERMITTED (:510-513).
  • The list affordance gate is ui/packages/plugin-list/src/ListView.tsx:2092-2095: exportPermitted = schema.allowExport !== false && schema.operations?.export !== false && (effectiveApiOps ? effectiveApiOps.includes('export') : true). The same predicate is in ui/packages/plugin-grid/src/ObjectGrid.tsx:5730.
  • ui/packages/plugin-report/src/ReportExportEngine.ts / LiveReportExporter.ts already hold a client-side CSV writer, for the legacy ReportComponentSchema. exceljs is already a ui dependency (ui/packages/plugin-grid/package.json:37).

What ADR-0021 plans.

  • docs/adr/0021-analytics-dataset-semantic-layer.md:392 (WS1): "dashboard.zod.ts (…; globalFilters/dateRange → dimension names)". :273: "Dashboard-level globalFilters and dateRange bind to dimension names, not raw fields."
  • :44: a report "renders a pivot grid … with subtotals, drill-to-record, export".
  • :207-232 (D2 ReportSchema sketch): no date-range key. The report's only scope key is runtimeFilter.
  • So the ADR plans dimension-named date ranges for the dashboard and names export as a report capability. It plans no report date key. This card applies the dimension-name grammar to the report.

Proposed shape

Both keys are optional and top-level on ReportSchema. An existing report parses and renders exactly as today.

1. dateRange

dateRange: strictObject({
  surface: 'this report date range',
  history: <ReportSchema history>,
  aliases: {
    // the dashboard's own rename hints, verbatim (dashboard.zod.ts:1832)
    preset: 'defaultRange', range: 'defaultRange', default: 'defaultRange',
    allowCustom: 'allowCustomRange', custom: 'allowCustomRange',
    // the dashboard compareTo's "which date column" hints (dashboard.zod.ts:1263-1268)
    field: 'dimension', dateField: 'dimension', fieldName: 'dimension', timeDimension: 'dimension',
  },
}, {
  dimension: z.string().optional()
    .describe('Dataset dimension (declared `type: \'date\'`) the window applies to; omitted = the dataset\'s one date dimension'),
  defaultRange: z.enum(DATE_RANGE_DEFAULT_RANGES).default('this_month')
    .describe('Default date range preset'),
  allowCustomRange: z.boolean().default(true)
    .describe('Allow users to pick a custom date range'),
}).optional()
  • defaultRange and allowCustomRange are the dashboard's declarations: the same enum (DATE_RANGE_DEFAULT_RANGES), the same defaults and the same describes. If review wants one declaration and not two, factor a shared DateRangeChoiceShape beside DATE_RANGE_DEFAULT_RANGES and spread it into both. The dashboard's JSON Schema must not change.
  • dimension names a dataset dimension, not an object field (ADR-0021:273/392). A dimension carries its own relationship path, so account.created_at works through include with no new grammar.
  • Lowering (contract stated in the describe and TSDoc; implemented by the ui card): the active window rides selection.timeDimensions: [{ dimension, dateRange }] with no granularity. That makes it a filter window under the analytics: 只用来限定「日期区间」的 timeDimension 被补上默认 dateGranularity,于是网格被静默按月拆分 —— 「按 Owner 统计」加个日期筛选就变成「按 Owner × 月」 #5688 rule, ANDed with runtimeFilter and the dataset's intrinsic filter. A preset goes on the wire by name. A custom pick goes as [start, end]. Applying it needs no server change.
  • Resolution, one rule with the dashboard executor's compareTo: with dimension omitted, the dataset's dimensions declaring type: 'date' are the candidates. Exactly one is used. Zero or several is an authoring error that names what it found.
  • Where that is judged: ReportSchema parses without the dataset, so the parse checks only the shape. The dataset-aware half is a new @objectstack/lint rule beside validate-chart-bindings.ts, which already resolves a report against its dataset. It answers three cases: report-date-range-dimension-unknown (the name is not a dimension of the dataset), report-date-range-dimension-not-date (the dimension is not type: 'date'), and report-date-range-dimension-ambiguous / -none when dimension is omitted. Each gets an entry in rule-explanations.ts. "Applied when the dataset has a time dimension" becomes something the build checks, not something the renderer guesses (ruling facet ③).
  • joined: refused on a joined container in this card. Blocks may bind different datasets, so a single dimension cannot name a column in all of them. This uses the same superRefine arm and the same no-pointer refusal style as the container chart (:585-587). JoinedReportBlockSchema gets a guidance entry for dateRange saying it is a non-joined report key. A per-block or conformed-dimension range is a later widening (ADR-0021 Q3), not this card.
  • Top-level ReportSchema aliases: add dateFilter: 'dateRange' and timeRange: 'dateRange' (the dashboard's, :1778).

2. exportOptions

exportOptions: ReportExportOptionsSchema.optional()
// declared in packages/spec/src/ui/list-view-export-options.ts beside its sibling:
ReportExportOptionsSchema = closed object {
  formats?: Array<ListViewExportFormatSchema.exclude(['json'])>,  // 'csv' | 'xlsx'
  includeHeaders?: <the list block's member, by identity>,
  fileNamePrefix?: <the list block's member, by identity>,
}
  • Key name, members and format values are the list's (view.exportOptions). No new vocabulary is minted. formats is the list enum narrowed with .exclude(['json']), so csv / xlsx as ruled. 'json' is refused with a message naming the two report formats, and 'pdf' keeps the list's retirement prescription.
  • Default on: with exportOptions absent, a non-empty report offers ['csv', 'xlsx'], the same as the list's default-allow. formats: [] turns the control off. That empty list is the "export flag" the ruling names, and it needs no second key. (If review prefers an explicit boolean, the alternative is exportOptions: false. That would be a union, and union 分支里的 unknown-key 处方永远到不了作者:zodIssuesToFields 只映射顶层 issue,失败的 union 只剩 Invalid input #5014 shows a union buries its errors, so this card does not propose it.)
  • Who may export: the existing axis, no new bit. The control is offered when the viewer's effective apiOperations for the dataset's base object (dataset.object, echoed as object on the query answer) includes 'export'. That is the ObjectPermission.allowExport axis, through the same predicate as ListView.tsx:2092-2095. The describe states this. The spec declares no permission key of its own.
  • What is exported (contract in the TSDoc; built by the ui card): the report table as rendered, within the active date range and runtimeFilter. That covers the selected rows × values, the matrix pivot and the server totals, with headers from the dimension and measure labels. The file name defaults to the report label plus the period, and fileNamePrefix replaces the label.
  • Guidance entries on ReportSchema:

Not added

  • No subscription or scheduled delivery (ruling: C waits; scheduled work is off on Free plans, service-automation: a hosted environment has no way to enable schedule / time_relative flows for an installed app (OS_AUTOMATION_SCHEDULED_WORK_ENABLED is deployment-wide and off on cloud) #25442).
  • No new permission key and no new server route. There is no dataset-rows export door, and XLSX is written in the browser (risk 2 above).
  • No json or pdf report format. No maxRecords or streaming: those are record-stream members, and report rows are already aggregated and bounded by the selection.
  • No presets list key (the dashboard has none) and no new preset. In particular, no all_time default. An author who wants "all time" by default leaves dateRange undeclared. Making "All time" a declarable default would widen DATE_RANGE_DEFAULT_RANGES for dashboards too, which needs its own decision.
  • No granularity inside dateRange. Bucketing stays on the dimension's dateGranularity and the report's rows / columns.
  • No dateRange on blocks or on a joined container (see above). No change to dashboard.dateRange.

Acceptance

Spec tests (packages/spec/src/ui/report.test.ts, plus a focused report-date-range-export.test.ts):

  • A report with neither key parses byte-identically to today, and its parsed output has no dateRange or exportOptions.
  • dateRange: {} parses to { defaultRange: 'this_month', allowCustomRange: true }. Every member of DATE_RANGE_DEFAULT_RANGES is accepted. 'last_6_months' is refused, with the enum message naming the presets. An unknown member is refused, and dateField / preset / allowCustom each earn their rename hint.
  • dateRange on a type: 'joined' report is refused at ['dateRange']. blocks[i].dateRange is refused with its guidance text.
  • exportOptions: { formats: ['csv','xlsx'] } and { formats: [] } parse. 'json' and 'pdf' are refused with their messages, and maxRecords / streaming are refused as unknown keys.
  • The top-level aliases dateFilter / timeRange and the guidance allowExport / schedule answer as specified.
  • The block↔report alias parity test and alias-integrity.test.ts stay green.

Lint (packages/lint): the new date-range rule has tests for all four cases: unknown, not a date dimension, two date dimensions with dimension omitted (using task.dataset.ts's due_date / completed_date shape), and none. Each case has an os explain entry.

Registration surfaces:

Generated artifacts: pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec check:generated --fix (gen:schema for the JSON Schema and the authorable surface, gen:docs for content/docs/references/ui/report.mdx, gen:api-surface for the new exported schema(s)). check:liveness stays green.

Docs:

  • content/docs/ui/reports.mdx gets a "Period and export" section: the dimension rule, the window lowering, the export permission axis and what is not offered.
  • skills/objectstack-ui/rules/dashboards.md § Report Configuration gets one example with dateRange: { dimension: 'close_date', defaultRange: 'this_quarter' }.

Changeset: minor on @objectstack/spec (and @objectstack/lint). The body carries Clause-②: yes (widening) and Metadata-protocol: added. No ADR-0087 disposition is needed, because nothing is removed or narrowed.

After this lands

  • [Decision] The report page offers only "Edit": should a shipped report get a time-range filter, export and subscription, and in which order #25873 drops its Blocked-by: and becomes the ui/ renderer card: a Console report page date-range chip that lowers to selection.timeDimensions[] in DatasetReportRenderer's useDatasetRows, and an export control gated on apiOperations 'export' for the dataset's base object, writing CSV and XLSX client-side. The liveness rows flip to live there.
  • HotCRM declares dateRange on its ten reports once a release carries both halves. Not measured here: whether each HotCRM report's dataset declares exactly one type: 'date' dimension. The lint rule above will say which ones need dimension named or a dimension typed.
  • C (subscription) stays deferred under the ruling. If a customer asks for it, it becomes a card of its own.

Dedupe: MCP search_issues on this repo with "report dateRange time range filter" (hit #1752, closed, drill range: different), "report export CSV XLSX table export permission" (hits #3544 and #3710, closed: the allowExport axis this card reuses, and the retired scheduled-report CSV door: different), "ReportSchema new key spec report period date window dataset time dimension" (#21702, #16185, #14933, #1752: all closed, different) and "dashboard dateRange field bind to dataset dimension name instead of raw field" (#25814, closed: the field-vs-dimension namespace this card sides on; #5688, closed: the window-only rule this lowering relies on). area:reports open issues: only #25873. git grep -n -iE 'dateRange|exportOptions' origin/main -- packages/spec/src/ui/report.zod.ts → 0 key hits. New.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — enhancement · priority:p1 (from p2) · domain:spec · area:reports · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T07:56Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · 2026-10-27T08:40Z
    Session: session_01WGufzfhobWEgUUrjz4rrbh
    Account: os-support-ai
    Branch: claude/project-thread-xsyisg (card thread session_01WGufzhMJrMdCkQS33DYtmu. The platform assigns the thread's branch and the thread can push only that one, so the branch is written as measured instead of claude/issue-26548-<slug>.)
    Worktree: objectstack-issue-26548
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: packages/spec/src/ui/report.zod.ts (dateRange, exportOptions, their aliases), report.form.ts, list-view-export-options.ts (a report export schema beside its list sibling), report.test.ts and one focused test, packages/spec/liveness/report.json, the generated spec artifacts, one new packages/lint rule beside validate-chart-bindings.ts with its explanations and tests, the translation bundles via check-i18n-bundles --write, content/docs/ui/reports.mdx, skills/objectstack-ui/rules/dashboards.md (Skills-docs), one changeset (@objectstack/spec + @objectstack/lint minor). ⛔ No ui/ renderer (that is #25873) and no server export door. Stop on a breach and explain it in the report.
    Container & model: M, mode:thread, model: TIER_DEFAULT, effort: xhigh
    Clause-②: yes (widening)
    Metadata-protocol: added
    Responsibility: n/a. Filing gate ③ of ruling 6408567749 on #25873; #25873 stays Blocked-by: this card for its renderer half.
    Thread-read: 6436657773
    Premise: holds (read at origin/main 1c632311: report.zod.ts declares no dateRange and no exportOptions; DATE_RANGE_DEFAULT_RANGES is re-exported from dashboard.zod.ts)
    Serial constraints cleared: yes. No open PR or claim names report.zod.ts. A skills/** touch makes the landing Tier H.
    Ruling-ref: 6408567749

  3. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26548,
      "status": "done",
      "branch": "claude/project-thread-xsyisg",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26628",
      "session": "session_01WGufzhMJrMdCkQS33DYtmu",
      "premise_still_valid": true,
      "summary": "ReportSchema gains two optional top-level keys in the card's proposed shape: dateRange { dimension?, defaultRange, allowCustomRange } (the dashboard's DATE_RANGE_DEFAULT_RANGES, defaults and rename hints, held equal to the dashboard declaration by test; dimension names a type: 'date' dataset dimension) and exportOptions { formats?, includeHeaders?, fileNamePrefix? } (ReportExportOptionsSchema beside the list block, csv / xlsx only, no permission key, allowExport stays the axis). A joined report refuses both, on the container and on a block; dateFilter / timeRange are aliases, allowExport / subscription / schedule are guidance. The dataset-aware half is a new @objectstack/lint reference-integrity rule validateReportDateRange (four error verdicts with os explain entries, also on the report publish door); liveness rows are planned with an author warning and drilled, the form gains a translated Period & export section, and the docs page and skills example are written. ui/ renderer work stays on #25873; no server export door.",
      "tests": "pnpm --filter @objectstack/spec test: Test Files 729 passed | 1 skipped (730), Tests 22095 passed | 1 skipped | 1 todo. pnpm --filter @objectstack/lint test: Test Files 167 passed (167), Tests 7411 passed (7411), after moving the predicate census pins 88 → 89 and 63 → 64 for the new form gate. @objectstack/platform-objects vitest run: Test Files 96 passed (96), Tests 1361 passed (1361). New focused files: report-date-range-export.test.ts 48 passed; validate-report-date-range.test.ts plus runtime-gate.inert-type-writes, lint-liveness-properties, reference-integrity-suite, rule-explanations 191 passed. Typecheck spec / lint / platform-objects: exit 0 each. No ablation: no tested behaviour changed, only added.",
      "gates": "local subset: node scripts/pm/dispatch-gates.mjs --commands named 113 families at 4dca57f78, all run on a full workspace build (turbo 118/118) with exit codes recorded before any pipe; 112 exit 0, check:empty-state exit 1 (the dateRange TSDoc's absent-window sentence was unclassified). Fixed in 8c03abecb (sentence beside the key, dateRange registered as a scope empty state); that commit derived one more family. Re-run after it, all exit 0: check:empty-state, check:pnpm-flag-spellings, check:spec-docblock-symbol-anchors, spec check:liveness, spec check:docs, spec check:api-surface, check:nul-bytes. dispatch-gates --ran: 114 derived, 114 run, 0 NOT-MEASURED, 0 UNRUN. After the final main merge (0c548c59a): spec check:generated --fix rebuilt spec, 0 of 14 stale; check:empty-state and check:liveness exit 0. Local-only: check:dev-prereqs exit 0, check:skill-frame-freshness exit 0. pnpm check:i18n OK (10 packages in sync). eslint on 15 changed source files exit 0. CI: check-runs not yet reported when the PR was opened, in_progress.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "read-only only: mcp__github__issue_read (the card, its comments, the ruling thread on #25873, #26475) and mcp__github__pull_request_read get (#26628 read-back). No write tool.",
      "api_writes": "3 relay writes, each a POST /repos/objectstack-ai/objectstack/dispatches through scripts/pm/fleet-write: pr_create with assignees (#26628, body read back identical, 13039 bytes), pr_request_reviewers os-support-ai (ordered best-effort), and this os-dev-report comment. Plus git push of the branch.",
      "open_questions": [
        {
          "question": "Should a joined report refuse exportOptions on the container? The card decides the joined case for dateRange only; this PR refuses exportOptions there too.",
          "options": [
            "A keep the refusal: a joined report draws one table per block, so there is no single table to export; widening later is non-breaking",
            "B accept a container exportOptions that applies to every block's table, and spell what the download holds (one file per block, or one workbook with a sheet per block)"
          ],
          "recommendation": "A, because B needs a decision on the file shape the renderer card has not made, and refusing now leaves B open as a pure widening."
        }
      ],
      "out_of_scope_findings": []
    }

    Deviations (also in the PR body, Acceptance notes): exportOptions refused on a joined container (the open question above); json and pdf carry report-side messages instead of the list's pdf prescription; the subscription / schedule guidance cites no ruling because runtime strings carry no tracker numbers; validateReportDateRange also runs at the report publish door; the skills example uses this_year, not this_quarter (the example matrix buckets by quarter); the liveness lint now walks reports (packages/lint); both liveness containers are drilled; translations landed in @objectstack/platform-objects (patch in the changeset). Skills-docs: follow-up owed — skills/objectstack-ui/evals/analytics-inline-vs-dataset.json (both ui eval files are at their token ceiling, headroom 0).


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:spec seat 1 (#6017) · session session_01WGufzfhobWEgUUrjz4rrbh · 2026-10-27T14:16Z

    PR #26628 merged at bb91ae86 (merged 2026-10-27T14:16Z, maintainer-merged as Tier H). bb91ae86 is an ancestor of origin/main. The PR touched 30 paths. On 29 of them the merge tree matches the accepted head be4a0204 byte for byte. The 30th, packages/lint/src/rule-explanations.ts, differs only by main's own hunk from #26658 (the controlledByParent owner arm), not by this PR's content.

    A report now declares dateRange and exportOptions in the published schema, the form and the lint, and a joined container refuses both (ruling A). @objectstack/spec and @objectstack/lint minor, @objectstack/platform-objects patch, Metadata-protocol: added. The card closed through Fixes #26548, and this seat removes pm:dispatched.

    Not done here: the eval prompt is #26633. The renderer is #25873.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:reportsBusiness reporting — dashboards, reports, the numbers a manager readsdomain:specenhancementNew feature or requestpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions