Repository navigation
Add Changesets and GitHub Actions automation - #4
Merged
Merged
Conversation
- Install @changesets/cli for version management - Configure Changesets for public npm publishing - Add GitHub Actions CI workflow for build and type checking - Add GitHub Actions Release workflow for automated publishing - Add changeset npm scripts to package.json - Add repository field to package.json Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
The build step already runs tsc which will fail on errors Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
Set minimal permissions (contents: read) to limit GITHUB_TOKEN scope Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Add necessary automation work including changeset
Add Changesets and GitHub Actions automation
Jan 18, 2026
Regenerate package-lock.json to fix npm ci failure in CI workflow. Updates @types/node from 12.20.55 to 25.0.9 and adds missing undici-types dependency. Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
huangyiirene
marked this pull request as ready for review
January 18, 2026 09:25
os-zhuang
pushed a commit
that referenced
this pull request
May 21, 2026
The real moat of metadata-driven development is not 'low-code UI', it
is that the entire business system is small enough to fit in an AI
agent's context window. Make this an explicit, top-level value across
README and the concept docs.
- README.md
- Add a 'Key Features' bullet on ~100x less code -> AI maintainability
- Add 'Code footprint' and 'AI maintainability' rows to the
Retool/Appsmith comparison table
- Rewrite the 'Why AI-native?' intro to anchor the value on
'fit in an agent's context window'
- content/docs/index.mdx
- Add a second callout under the 'not a low-code UI builder' line
explaining the ~100x code reduction and AI-co-maintenance angle
- Fix stale 'npx @objectstack/cli init' -> 'npx create-objectstack'
quick-start command (matches updated README)
- content/docs/concepts/metadata-driven.mdx
- Rename benefit #4 'Reduced Boilerplate' -> '~100x Less Code -
Sized for AI Agents' and reframe around context-window fit
- Clarify that what gets generated is full CRUD + REST + typed SDK
+ MCP tools + validation + permission scaffolding, not just CRUD
- content/docs/concepts/north-star.mdx
- Add a sixth non-negotiable tenet: 'Compact by Construction'
-> a typical enterprise app fits in ~1% of a hand-written
equivalent, small enough for an AI agent to load and refactor
end-to-end. Explicitly call this out as the real moat.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
xuyushun441-sys
pushed a commit
that referenced
this pull request
May 22, 2026
Introduces an opt-in path in ObjectStackProtocolImplementation.saveMetaItem that writes overlay metadata through SysMetadataRepository.put instead of the raw engine, so writes append to the change-log and emit HMR seq events. Behavioural changes (all behind options.useRepositoryWritePath / OBJECTSTACK_USE_REPOSITORY_WRITE_PATH=1): - saveMetaItem request gained optional parentVersion (If-Match) and actor fields. ConflictError -> 409 metadata_conflict. - Plural type aliases (views, dashboards, ...) normalized to singular before the repo's overlay-allowlist gate (rubber-duck #5). - Object-registry mutation moved AFTER successful put() so a conflict does not leave the in-memory registry stale (rubber-duck #3 invariant test added). Repo/test-fake fixes uncovered by rubber-duck review: - SysMetadataRepository.put/delete now update/delete by row id because the engine's strict .update requires id or multi:true (rubber-duck #1). - sys_metadata.checksum column widened from 64 -> 71 chars to hold the sha256: prefix produced by hashSpec() (rubber-duck #2). - Three test fake engines extended to support both overlay-tuple and id-based where lookups. 333/333 objectql tests pass. Deferred to PR-10d.4: REST plumbing for parentVersion/actor (rubber-duck #6), race-window retry for omitted parentVersion (rubber-duck #4), default flag flip + legacy path removal. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
xuyushun441-sys
pushed a commit
that referenced
this pull request
May 24, 2026
Proposes that every Action opts in to AI exposure via a single `ai:` block
on ActionSchema, and the runtime auto-derives AIToolDefinitions from the
existing ActionRegistry. Eliminates the need to maintain parallel skill /
tool code for every business operation an admin can already perform.
- Adds opt-in `ai: { exposed, description, paramHints, outputSchema, ... }`
block to @objectstack/spec ui/action.zod.ts
- Adds ActionRegistry.toolsForAi(opts) in @objectstack/runtime
- Wires service-ai/agent-runtime to merge action-tools into availableTools
- Routes LLM tool_calls with meta.kind='action' through ActionRegistry so
permissions, validation, hooks, audit, and transactions all apply uniformly
- Confirmation defaults derived from existing confirmText / type='delete'
Authored from HotCRM v1.1 planning. HotCRM will be the first consumer:
delete src/skills/, convert each business skill to defineAction with ai
exposed, ship the 'Operational Parity' story as Wow #4.
Refs ADR-0003, ADR-0008, ADR-0009, ADR-0010.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4 tasks
xuyushun441-sys
added a commit
that referenced
this pull request
Jun 13, 2026
#1821) Small models (e.g. claude-haiku) sometimes answered a "draw a bar chart" request with a markdown TABLE — running query_data/aggregate_data and formatting the numbers — instead of calling visualize_data. This was a tool-selection problem, not a capability gap: the chart preference was buried as guideline #7 and competed with guideline #4 ("format with markdown tables"). - data-explorer-skill.ts: add a prominent "Choosing the right tool" section ABOVE the guidelines — chart intent (incl. CN terms 图表/柱状图/折线图/饼图/画图) → MUST call visualize_data; never substitute a table; reconcile the table-formatting guideline; fix duplicate guideline numbering. - visualize-data.tool.ts: strengthen the tool description to be imperative ("the ONLY tool that draws a chart… you MUST call this, not a table; if you already fetched the numbers, still call visualize_data to render them"). Prompt-only tuning — no behavior/contract change. Raises the likelihood the model reaches for visualize_data on a plain chart request without an explicit "use visualize_data" nudge. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 15, 2026
This was referenced Jun 28, 2026
1 of 6 tasks
This was referenced Oct 1, 2026
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Oct 7, 2026
…lumns are the inline grid column contract, and the showcase Tasks grid binds by name (objectstack-ai#21142) (objectstack-ai#21244) Fixes objectstack-ai#21142 Clause-②: yes Seam: renderer `@object-ui/plugin-form` `LineItemsPanel` → `@object-ui/fields` `GridField` (binds `column.name`) ← producer: objectstack showcase `record:line_items` `columns[].field` The showcase project page's Tasks grid keyed all five of its columns `field`. The line-items grid binds a column by `name`, so every cell rendered empty. Nothing refused it: `record:line_items` had no `ComponentPropsMap` row, so the component-props gate skipped its props bag as unregistered. This PR fixes the producer and adds the row (the claim's call, per triage `5929028089`). From here on a `field`-keyed column is refused at authoring, with the rename to `name`. ## What changed - **`examples/app-showcase/src/ui/pages/project-detail.page.ts`**: the five columns are keyed `name` (`title`, `status`, `priority`, `estimate_hours`, `due_date`). Nothing else on the page moved. - **`packages/spec/src/ui/component.zod.ts`**: new row `ComponentPropsMap['record:line_items']` = `RecordLineItemsProps`, a strict shape of the fifteen keys objectui's `LineItemsPanel` reads (measured below): - `relationshipField` is required, and so is `columns` (at least one). Nothing on this panel derives either one. - `childObject` is optional, because the component-level `dataSource` binding can supply it. - `filter`, `sort` and `limit` take the declarations every sibling door takes: the ViewFilterRule array, the SortItem array and a positive integer. - The keys it shares with an `object-master-detail-form` detail entry take that entry's types and alias table. - The four entry keys this block does not read (`addLabel`, `sortField`, `formFields`, `inlineMode`) are refused with a `guidance` reason. - New types `RecordLineItemsProps` and `RecordLineItemsPropsParsed`. - **`columns` IS `InlineGridColumnSchema`**, by reference and not a copy (Zone 2 objectstack-ai#4: same shape, see below). The retired `field` spelling is refused by name with the prescription naming `name`. One carrier difference is stated in the `describe()`: this panel does not hydrate a column from the child field. For the same reason, `defineStack`'s identity-only check (`collectHydratedInlineColumnErrors`) is deliberately NOT extended to this block, and a control pin holds that. - **`packages/spec/src/ui/component-type-vocabulary.ts`**: `record:line_items` leaves `STRING_ARM_REGISTERED_TYPES`, which is now empty. The export stays, and its docblock records why it is empty. The type stays KNOWN through its row. - **ADR-0087**: new D3 semantic entry `ui-record-line-items-props-closed`, plus a step-18 rationale fragment (order 57). `gen:migration-registry` regenerated the registry. No D2 conversion: page-component `properties` is not parsed on the save or load path, and the census found one producer, respelled here. - **Pins flipped / added**: - `validate-component-props.test.ts`: `record:line_items` leaves the unregistered-skip `it.each`. A new suite asserts the `field`-keyed columns fire `component-props-unknown-key` at `...properties.columns.N.field`, with `component-props-invalid` at `...columns.N.name`. The `name`-keyed control is silent. - `component-type-vocabulary.test.ts`: known through the row, not on the ledger, not an enum member. - `inline-grid-column-carriers.test.ts`: a fourth-carrier section covering identity of the column element, the `field` refusal (code `unrecognized_keys` at path `['columns', 0]`), the currency `scale` refusal, the bogus key, `relationshipField` and `columns` required, `.min(1)`, the alias and guidance refusals, and full-read-set and showcase controls. A last control shows that `defineStack` does not judge an identity-only line-items column, while the same column under `object-master-detail-form` is judged. - New showcase test `examples/app-showcase/test/project-detail-line-items.test.ts`: the five columns are keyed `name`, each names a `showcase_task` field, the block parses against `RecordLineItemsProps` with its keys intact, and no `field`-keyed line-items column exists anywhere in the showcase. - **Prose made false by the change**: `validate-component-props.ts` header (the skip list and its "earlier editions" history), `validate-component-types.test.ts` comment, and the `validate-page-field-bindings.test.ts` test title ("skips a component type its descriptor table does not carry"). Those are comments and a title only; no lint behaviour changed. - **Generated**: - `dropped-refinements.baseline.json`: new site `ui/RecordLineItemsProps` at `columns.element` and `filter.element`, plus its two counts. - Also regenerated: `api-surface/ui.json`, `export-origins/ui.json`, `declaration-map/ui.json`, `authorable-surface/ui.json`, `json-schema.manifest/ui.json`, `content/docs/references/ui/component.mdx` and `index.mdx`, and `docs/audits/...strictness-ledger.counts/ui.md`. - **Changeset** `.changeset/21142-line-items-columns-name.md`: `@objectstack/spec` minor, BREAKING, `Clause-②: yes (narrowing)`, ADR-0087 `registered ui-record-line-items-props-closed`. What reads it is the component-props gate (advisory findings on `objectstack validate` / `build` / `lint`). The stored-page save and load path does not parse `properties`. ## Measurements **Premise**: holds. At `origin/main` `1ecb871beb`, `project-detail.page.ts:76` authors `amountField: 'estimate_hours'` and `:79`–`:104` author five `columns` keyed `field:`. `record:line_items` was the only entry of `STRING_ARM_REGISTERED_TYPES` (`component-type-vocabulary.ts:68`), and its row-lessness was pinned in `validate-component-props.test.ts:495`. **Read set at the `.objectui-sha` pin `31971ff1e28f`** (objectui `packages/plugin-form/src/LineItemsPanel.tsx`; `SchemaRenderer` hoists `properties` onto `schema`). A count of `schema.KEY` reads gives exactly fifteen keys: - `childObject` `:319`, `:327`, `:498`, `:516`, `:638`, `:673`, `:702`, `:778` - `relationshipField` `:515`, `:674` - `columns` `:702` - `parentObject` `:221` - `parentId` and `recordId` `:228` - `amountField` `:669`, `:703` - `totalField` `:667`, `:669`, `:703` - `title` `:722` - `readonly` `:706`, `:707`, `:723`, `:810` - `minRows` `:704` - `maxRows` `:705` - `filter` `:366` - `sort` `:368`, `:377` - `limit` `:341`, `:437` The wrapper adds no key. `ElementDataSourceGate.tsx` reads the node-level `dataSource` plus the same `filter` / `sort` / `limit` (`:421`, `:434`, `:445`). The mapping `RECORD_LINE_ITEMS_DATA_SOURCE` (`plugin-form/src/index.tsx:556`) writes the binding's `object` onto `childObject`. `requiredPermissions` and `aria`, which other record rows declare, have no read here, so they are not declared. **objectui `main` (`d59f11c0d3dc`, pin is an ancestor: `merge-base --is-ancestor` exit 0)**: - Same fifteen keys; the per-key read counts are identical. - One semantic difference. At the pin the grid's footer total appears only when `totalField` is set (`total_field: schema.totalField ? schema.amountField || 'amount' : undefined`). On `main` (objectui `55a12a8e1`, round 8) it appears whenever `amountField` is named. So the showcase's `amountField` with no `totalField` draws a footer only once the console pin moves past that commit. - `GridField` on `main` declares `GridColumn = InlineGridColumn`, which is the spec's type by reference (objectui `75dcc81c3`). - `0a3e5409f` (grid `sort_field`) changes `GridField` and `MasterDetailForm`, not anything this block reads or hands the grid. - No key is read at the pin but retired on `main`. **Column shape (Zone 2 objectstack-ai#4)**: at the pin, `GridField.tsx`'s `GridColumn` interface declares exactly the twenty keys `InlineGridColumnSchema` declares (`name`, `label`, `type`, `options`, `width`, `required`, `prefix`, `step`, `reference`, `displayField`, `idField`, `multiple`, `accept`, `defaultHidden`, `computed`, `expr`, `scale`, `autofill`, `readonlyWhen`, `requiredWhen`). Same shape, so the row references the schema by identity. The one difference belongs to the carrier: `LineItemsPanel` hands `columns` straight to `applyColumnPermissions` and then the grid (`:702`), with no `hydrateColumns` step. An identity-only `{ name }` column therefore draws as a text cell headed by its name, and the `describe()` says so. **Census (Zone 2 objectstack-ai#5)** at `1ecb871beb`, matcher `type: 'record:line_items'`: - `examples/`: 1 producer, the showcase page, 5 `field`-keyed columns, respelled here. - `content/docs/`: 0 blocks. One prose tag-list mention in `ui/react-pages.mdx:38`. - `packages/` non-test: 0. - Lit control: the same matcher shape finds 8 other `record:*` blocks in `examples/` (3 `record:details`, 2 `record:highlights`, 1 each `record:path`, `record:quick_actions`, `record:alert`). - Test fixtures naming the type: the three lint test files above. Each was re-judged: one flipped, one comment moved, and one title made honest. The field-bindings fixture is not a props-gate input. **Served showcase page end to end: NOT MEASURED.** Neither checkout has a console build (`packages/console/dist` is absent in both). `pnpm dev` runs `check:console-sha` first, and producing that build needs a full objectui build at the pin, which this dispatch holds read-only. What is measured instead: - The page's block parses against the row with its five `name` keys intact (showcase test). - The objectstack conversion registry carries no `record:line_items` rewrite (`git grep` in `packages/spec/src/conversions`: 0 hits), so no load-time conversion on this side intervenes. ## Tests (at HEAD `c2b91013`) All through `scripts/pm/os-verify-lock.sh`, exit codes read from its `VERDICT` line. The tree is the merged one: `origin/main` `62b90d74` merged through `os-regen-merge.sh`, plus the regeneration commit. | command | result | |:--|:--| | `pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2` | 596 files, 17466 passed, 1 todo | | `pnpm --filter @objectstack/spec run typecheck` (tsc + scripts + test layer) | exit 0 | | `pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2` | 119 files, 5503 passed | | `pnpm --filter @objectstack/lint run typecheck` | exit 0 | | `pnpm --filter @objectstack/sdui-parser exec vitest run --maxWorkers=2` | 13 files, 217 passed | | `pnpm --filter @objectstack/metadata-protocol exec vitest run --maxWorkers=2` | 200 files passed, 3 skipped; 2973 passed, 19 skipped | | `pnpm --filter @objectstack/example-showcase exec vitest run --maxWorkers=2` | 30 files, 391 passed | | `pnpm --filter @objectstack/example-showcase run typecheck` | exit 0 | | `pnpm --filter @objectstack/spec check:generated` | all 15 artifacts up to date | - Dependency closures were rebuilt first (`pnpm --workspace-concurrency=2 --filter` with the `PKG^...` closure of lint, metadata-protocol, sdui-parser and the showcase, plus `@objectstack/spec build`). - `protocol.meta-types-degenerate-derivation.test.ts` (the served-schema count pins) is green unchanged. The new row moves no count: `page` still serves 24 top-level properties, because `properties` is an open record. - **Pre-merge** (`2479fb67` / `742c6970`): the same suites were green. Six transient failures — module-not-found on `@objectstack/spec/*` and `@objectstack/platform-objects/*` — came from a concurrent dist rebuild by the gate run. They were re-run on stable dists: green (4 files / 30 tests, and 30 files / 391 tests). - **eslint, narrowed (measured)**: `eslint --no-inline-config --format json` over the 12 changed `.ts` files reports 12 results, 0 errors, 0 warnings, and none ignored, so all 12 are in the config's population. `eslint.config.mjs` never enables type-aware linting (its `:327-329`), so this diff cannot move the verdict on any file it did not touch. The repo-wide `pnpm lint` is CI's. ## Reverse verification (ablation) The map row `'record:line_items': RecordLineItemsProps,` was deleted through `scripts/ablation-replace.mjs` (anchor 1 → 0, blob `35459aca2181` → `d052475a42e4`), committed state first. Then `pnpm --filter @objectstack/spec build`. `ablation-dist-preflight.mjs --absent` reported the marker absent from all 230 built files, and the tree carried only the source mutation. - Spec (src): `component-type-vocabulary.test.ts` + `inline-grid-column-carriers.test.ts` → **8 failed** / 55 passed. These are the vocabulary pin and seven of the eight new carrier tests. The eighth, the `defineStack` control, reads no row and stays green as intended. - Lint (spec `dist`): `validate-component-props.test.ts` → **1 failed** / 49 passed. "reports a `field`-keyed column" saw zero findings, which is the pre-fix silence. Its `name`-keyed control stays green (vacuously under the ablation). - Showcase: 4/4 green, as expected, because that test parses with `RecordLineItemsProps` directly rather than through the map. - Restore: `git checkout HEAD`, blob back to `35459aca2181` == HEAD, `git diff HEAD` empty, whole-tree `git status --porcelain` clean. Rebuilt spec, preflight "marker present in 14 built files" and "working tree clean against HEAD". Lint suite back to 50/50. ## Gates - Derived with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` (no paths) at `c2b91013`: **113 commands**, the same list as the pre-merge derivation. - All 113 ran at `c2b91013`, sequentially, each exit code captured before any pipe: **113 × exit 0**. This includes `check:adr-0087-registration` (`registered ui-record-line-items-props-closed (new here)`), `check:changeset-no-major`, `check:nul-bytes`, `check:issue-citations`, `check:doc-authoring`, `check:spec-parsed-alias`, `check:migration-registry`, `check:dual-build-cjs-loads`, and `check:type-check-debt` (re-measure, 336s). - `dispatch-gates --ran` reconciliation: 113 derived, 113 run, 0 NOT-MEASURED, 0 UNRUN. - The pre-merge run (`ffcd210a`), taken alongside a closure build, did not measure seven gates. Five answered PREREQUISITE NOT MET (exit 3). `check:dts-closure` named a package whose declarations were mid-rebuild. `check:type-check-debt` hit a 420s cap. All seven are in the 113 × exit 0 above. - **NOT MEASURED here (CI's)**: the CI jobs that dispatch-gates names as outside the list (Test Core shards, Dogfood, Temporal Conformance, Build Core, the four type-check lanes), and the repo-wide `pnpm lint`. ## Acceptance notes - **`amountField` footer differs between the pin and objectui `main`** (above). This is not a defect here: the console pin bump carries it in. - **Two spellings of one concept, both read**: `parentId` wins over `recordId` (`LineItemsPanel.tsx:228`). Both are declared as measured, on the `object-master-detail-form` `initialValues` / `initialData` precedent, and the `describe()` names the precedence. Retiring one is a separate enforce-or-remove question; not filed (no reach measured). - **The objectui mirror is objectui's**: objectui#10872 waits on this row. Two items there are now stale: objectui `packages/types/src/zod/public-blocks.zod.ts:167`, which says "the spec carries no row", and the registry `inputs` for this block, which declare 5 of the 15 keys. - **`field-no-consumers`** (`validate-field-consumers.ts`) walks child collections by the keys `subforms` / `details`, so it does not credit a `record:line_items` block's column names to the child object. Reach was not measured: the showcase task fields are consumed elsewhere. Noted only. --- _Generated by [Claude Code](https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Oct 7, 2026
…export options object, and a bare format array is refused (objectstack-ai#21229) (objectstack-ai#21287) Fixes objectstack-ai#21229 Clause-②: yes Dispatched by the PM claim `5943166878` (PM loop round 1, `domain:spec` seat 1), on the triage ruling `5939380297`. `ComponentPropsMap['object-grid'].exportOptions` was `z.unknown()`. It now takes `ListViewExportOptionsSchema`, the list view's strict five-member export options object, **by identity**. It does not take the list view's union, whose legacy bare-array arm lifts to `{ formats }`. A bare array is refused with the object form named. The changeset carries the `(narrowing)` arm, the BREAKING banner at `minor`, and the ADR-0087 marker `registered ui-object-grid-export-options-closed`. It is D3 only; the reading is below. ## What changed - **New non-barrel module `packages/spec/src/ui/list-view-export-options.ts`.** It holds the export options block, moved verbatim out of `view.zod.ts`: the retired-`'pdf'` prescription, the `csv` / `xlsx` / `json` format enum, and the strict five-member object. - There is one addition. The object's own error map answers an `invalid_type` on an array input with a prescription naming `{ formats: ['csv', 'xlsx'] }`. - It is the object's map because that is the only map a type failure at this position consults. A wrapper's or the enclosing row's map is never reached, and an object-level refinement never runs once a property has failed its type. - The object is built exactly as `strictObject()` builds one: `closedObject(z.object(shape, { error }).strict())` with the same registered `strictObjectError` declaration. The `prime` handle is forwarded, so `closedObject`'s terminal unknown-key contract is kept. - **New non-barrel module `packages/spec/src/ui/view-history.ts`.** `VIEW_HISTORY` moved here, verbatim, so the moved block keeps the refusal sentence it has always carried. `view.zod.ts` imports both modules, and its 53 `VIEW_HISTORY` uses are unchanged. - **`component.zod.ts`:** `exportOptions: ListViewExportOptionsSchema.optional()`. The "Unvalidated here" describe text is gone. A docblock records the ruling and the door reading. - **D3 entry** `18.ui-object-grid-export-options-closed.ts`, regenerated into `migrations/registry.ts` by `gen:migration-registry`. - **`STEP18_RATIONALE` fragment** `ui-object-grid-export-options-closed`, `order: 59`. `main` holds 57 (PR objectstack-ai#21244) and 58 (PR objectstack-ai#21240) at the merge `b91e40bc89`. It is inserted at its sorted position. - **Pin file `component-object-grid-export-options-members.pin.test.ts`, rewritten.** The objectstack-ai#17166 version asserted the key was still unvalidated, so that this change would red there and be decided deliberately; it did. The new file holds the triage pins: - **identity:** the row's inner schema is the very instance the list view's union holds as its object arm, read from the union rather than imported by name, and it is not the union; - **bare array refused:** `invalid_type` at `exportOptions`, with the object form named. The control is the same array on a list view, which still lifts to `{ formats: ['csv'] }`; - **object form accepted:** all five members, `{}` and absent; - **a format outside the enum refused:** `invalid_value` at `exportOptions.formats.1`, and `pdf` with its retirement text; - **an undeclared key refused:** `unrecognized_keys` at `exportOptions`, naming `this export options block` and the `maxRecord` → `maxRecords` rename. - **Regenerated:** `content/docs/references/ui/component.mdx` (the row's type, and a new nested-shape table) and `docs/audits/2026-07-unknown-key-strictness-ledger.counts/ui.md` (see the strictness-ledger note under Acceptance notes). ## Measured: why a non-public module, not an export (Zone 2 item 1) - `ui/index.ts` is `export * from './view.zod'`, so any export from `view.zod.ts` is public. - **Measured.** I added `export { ListViewExportOptionsSchema }` to `view.zod.ts` through `scripts/ablation-replace.mjs` in wrap mode, then rebuilt spec. The first attempt was refused by the tool as a no-op, because the anchor was inside the replacement; it was re-anchored and re-run. Results: - `check:api-surface` turned red with `./ui + ListViewExportOptionsSchema (const)` ("0 breaking, 1 added"), and `check:export-origins` turned red; - the build also wrote `ui/ListViewExportOptions` into `json-schema.manifest/ui.json` (a new published JSON Schema def) and 5 `ui/ListViewExportOptions:*` rows into `authorable-surface/ui.json`. - The file was restored, blob equal to HEAD and `git diff HEAD` empty. The two dirtied artifacts were restored with `git checkout HEAD`, and after a clean rebuild `check:api-surface` reads "unchanged ✓". - **objectui.** At the pin `31971ff1e28f`, `packages/types/src/__tests__/export-options-spec-parity.test.ts` asserts `expect(specUi.ListViewExportOptionsSchema).toBeUndefined()`. An export would red that test at objectui's next spec bump. - **With the non-barrel module**, `check:api-surface`, `check:export-origins`, `check:declaration-map` and `check:authorable-surface` are unchanged. One declaration; zero public surface. This is the `analytics-column-reference.ts` / `analytics-carrier-filter.ts` / `section-group-reference.ts` precedent. ## Measured: the ADR-0087 disposition is D3 only (Zone 2 item 2) Every pre-PR shape that the pinned renderer handles and the PR refuses was put through every door, before (`f148852752`) and after (`8b2c2bb558`, the schema commit, same `src/ui` as HEAD). The reading uses the built `dist`, `getMetadataTypeSchema('page')` (the save door's per-type parse), `defineStack`, the props gate `validateComponentProps`, and `runtimeAuthoringRulesFor('page')`. | shape (on an `object-grid` node's `properties`) | at the pinned renderer (`ObjectGrid.tsx` at `31971ff1e28f`) | row before → after | save door | `defineStack` | props gate after | |:--|:--|:--|:--|:--|:--| | `['csv']` | `!!exportOptions` is true, so the menu shows the csv/json default; the list is dropped | accept → `invalid_type` | ok | accepted | warning `component-props-invalid` | | `{ formats: ['csv'], foo: 1 }` | renders; `foo` is never read | accept → `unrecognized_keys` | ok | accepted | warning `component-props-unknown-key` | | `{ formats: ['pdf'] }` / `['xml']` | the value is hidden from the menu with a `console.warn` | accept → `invalid_value` | ok | accepted | warning `component-props-invalid` | | `null` | `!!null` is false, so no menu, the same as absent | accept → `invalid_type` | ok | accepted | warning `component-props-invalid` | | `true`, `'csv'`, `{ formats: 'csv' }`, `{ maxRecords: -1 }`, `{ streaming: 'false' }` | renders, with the default or a misread | accept → refused | ok | accepted | warning `component-props-invalid` | - **Lit controls, same run.** An undeclared `object-grid` prop gives the props-gate warning `component-props-unknown-key`. An undeclared page key is REFUSED at the save door and THROWS in `defineStack`. The accepted object forms (`{ formats: ['csv','xlsx'] }`, `{}`, all five, absent) give no finding anywhere. - **The runtime publish gate for `page`** runs one rule, `validatePresetComparands`. The props gate is `tier: 'advisory'`, `surfaces: CLI_ONLY`. - **The renderer.** At the pin, objectui runs its zod mirror only in the `objectui validate`/`check` CLI. `SchemaRenderer` runs a structural `validateSchema` in dev only. - **So nothing on the save or load path refuses any of these shapes.** A stored page saves and loads, and no conversion has a load-path refusal to pre-empt. - **Lossless rewrites.** The undeclared key, `pdf` and `null` have one (delete it), but nothing stops loading. The bare array has none that both keeps today's menu (`{}`) and honours the author's list (`{ formats }`), and that choice is the upgrader's, which the D3 entry states. Triage also ruled out a lift. ## Census (Zone 2 item 3), on `f148852752` - **Zero `object-grid` blocks author `exportOptions`** in `examples/**`, the package fixtures, `content/docs/**` and `skills/**`. - **Control:** the same census finds 10 authored `object-grid` blocks. Nine are TypeScript nodes (two showcase pages and seven package-test fixtures). One is the YAML example in `content/docs/protocol/objectui/layout-dsl.mdx`. The `exportOptions` matcher is lit on the 4 list-view authorings: `app-crm` ×2, the showcase task view, and the lint showcase fixture. - **`skills/**`:** nothing teaches `exportOptions`. `skills/objectstack-ui/rules/pages.md` names `object-grid` in prose only, so no Tier H follow-up is owed. - Nothing needed respelling. ## objectui (Zone 2 item 4, Post-Task Checklist objectstack-ai#4) Nothing the pinned objectui imports moves: - `check:api-surface` is unchanged, and no export was removed or renamed. - `ListViewSchema.shape.exportOptions` is still a two-arm union with one five-key object arm, which is what objectui's `SPEC_EXPORT_OPTIONS_OBJECT_SHAPE` peel reads. - `ListViewExportOptionsSchema` is still not exported, so objectui's floor test holds. - No pinned objectui test parses the row with `exportOptions` and expects a bare array to pass. Six pinned tests mention `exportOptions` near the row; two of them carry only prose that will go stale (see Acceptance notes). ## Changes outside the row, stated - **The list view's nested message.** The list view's `exportOptions` accepts and lifts exactly what it did, and its top-level messages are unchanged. Only when a bare array also fails the array arm (`['docx']`) does the object arm's nested branch message read the new prescription instead of `Invalid input: expected object, received array`. Measured on `dist`. Both carriers read one declaration, and the text is worded to be true on both. The changeset says so. ## Tests and gates: all on `032865c93c` (HEAD, the merge of `origin/main` `b91e40bc89` through `os-regen-merge.sh`) - `pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2`: **597 files passed, 17475 passed, 1 todo**. - `pnpm --filter @objectstack/spec typecheck`: exit 0. `check:test-typecheck` is OK (52 files / 246 errors / 135 pinned signatures, unchanged), so the rewritten pin compiles under `tsconfig.test.json`. - **Contract-face fixture triage.** These are consumers of the spec, the downstream (`...@objectstack/spec`) direction, limited to the three the dispatch names: - `@objectstack/lint` (the props gate): **119 files / 5515 tests passed**; - `@objectstack/metadata-protocol`: **200 passed + 3 skipped files / 2973 passed + 19 skipped tests**; - `@objectstack/spec`: as above. - No fixture in any of them needed a change. - `pnpm --filter @objectstack/spec check:generated`: 15 of 15 up to date after `--fix` regenerated the 2 it proved stale (`check:docs`, `check:strictness-ledger`). - **`dispatch-gates.mjs --commands`** (no paths) derived 112 commands; all 112 ran and exited 0. - Six first exited 3 with PREREQUISITE NOT MET, because `lint`, `client-react` and `objectql` had no `dist`: `check:doc-formula-expressions`, `check:doc-security-posture`, `check:skill-examples`, `check:docs-transcript-drift`, `check:dual-build-cjs-loads` and `check:lean-entry-closure`. Each re-ran green once the dists existed. - `--ran` with `cmd :: exit N`: "112 derived, 112 run, 0 NOT-MEASURED, 0 UNRUN (a DERIVED zero)". - **Reverse verification, cross-package type.** A scratch `probe.ts` typed against the rebuilt `dist/ui/index.d.mts`, using `ObjectGridProps`, was compiled with `tsc`: - `exportOptions: ['csv']` gives `TS2559`; - `{ formats: ['xml'] }` gives `TS2322`; - `{ formats: ['csv'], maxRecord: 1 }` gives `TS2561`; - the control `{ formats: ['csv','xlsx'], maxRecords: 10 }` compiles. - At the base the key was `unknown` (the reference page rendered `any`). - **NOT MEASURED, declared to CI:** the 6 path-scheduled CI jobs (Test Core shards, Temporal Conformance, Dogfood Regression and Verify, Build Core, Build Docs), the 4 workspace type-check lanes, and the 54 artifact-roster families the derivation lists outside its total. ## Acceptance notes - **The strictness ledger's scope.** The ledger (`check:strictness-ledger`) counts `.zod.ts` files only. The moved block is one CLOSED site and now lives in a non-`.zod.ts` module, like the other non-barrel helpers, so the regenerated `ui/` counts read 189 → 188 sites and 179 → 178 strict. The strip count, which is the ratchet's target, is unchanged at 7. Naming the module `.zod.ts` would have kept the site counted, at the price of a hand-written ledger row and of opting a non-public module into the `.zod.ts` generators' discovery. - **Stale prose in objectui.** At the pin, two objectui test files say the spec row is `z.unknown()`: the `ObjectGrid.exportOptionsKeys.test.ts` docblock, and the `object-grid.exportOptions` row in `registry-inputs-spec-parity.test.ts`'s `MEMBER_PINS`. Neither asserts it, so nothing goes red. Once the spec version carrying this lands, both describe a past state. Carrier: objectui's next spec pin-bump PR. Noted, not filed. - **objectui's `properties` bag.** On objectui `origin/main`, the bag arm from objectui#11399 judges the bag by `ComponentPropsMap['object-grid']` by reference. Once objectui installs this spec, its `objectui validate` refuses a bare array in the bag, as its flat mirror has since objectui#7762. No objectui change is owed beyond the pin bump. - **A public export, if objectui later wants one.** objectui's export-options parity test says "When upstream exports the symbol, derive from it and delete both the mirror". Publishing `ListViewExportOptionsSchema` stays possible as its own decision. It moves `api-surface` (+1 const), `json-schema.manifest` (+1 def) and `authorable-surface` (+5 rows), as measured above. It is not done here. --- _Generated by [Claude Code](https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 13, 2026
os-elon-musk
pushed a commit
that referenced
this pull request
Oct 23, 2026
…inator reads a card thread's branch at spawn and the seat claims it (no probe push for card threads), and a direct-session seat runs only when summoned Ruled on the card (batch #4, 1A / 2A): dispatch-runbook 〈卡线程〉 L141–L142 now say the coordinator reads the new thread's branch from its session record and hands it to the seat (the thread reports it only when the coordinator cannot read it), the seat writes assignee, `pm:dispatched` and the `Claim:` naming that branch, and the thread starts once the newest claim names its branch. os-dev.md rule 1 keeps the empty-branch probe push for self-made branches and exempts card threads, whose branch the platform designates and the harness pushes; the two probe lines merge into one so the file stays at its ceiling. SKILL.md's triage-seat line now carries the direct-session rule: summoned only, no self-wake, not resident. Every file keeps its line count; every edited line is at or under 120 bytes. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0178pvZcJjqXL36QzPi1jbSX
This was referenced Oct 23, 2026
This was referenced Nov 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements version management and CI/CD automation infrastructure.
Changes
Changesets Integration
@changesets/cli(v2.29.8) for semantic versioning and changelog automation@objectstack/specchangeset,version,releaseGitHub Actions Workflows
.github/workflows/ci.yml: Build validation on push/PR (Node 20, TypeScript compilation).github/workflows/release.yml: Automated npm publishing via Changesets actioncontents: readfor CI,contents: write+pull-requests: writefor releases)Package Metadata
objectstack-ai/specUsage
Contributors create changesets when making changes:
npm run changeset # Describe changes interactivelyMaintainers merge the auto-generated "Version Packages" PR to trigger npm publish. Requires
NPM_TOKENsecret configured in repository settings.Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.