Skip to content

Commit 8136eb7

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-21289-untyped-subschema-refusal
2 parents 2ef6e13 + bcd68a2 commit 8136eb7

48 files changed

Lines changed: 572 additions & 334 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
'@objectstack/connector-mcp': patch
3+
'@objectstack/plugin-email': patch
4+
'@objectstack/service-knowledge': patch
5+
'@objectstack/service-queue': patch
6+
'@objectstack/service-sms': patch
7+
'@objectstack/service-storage': patch
8+
'@objectstack/trigger-record-change': patch
9+
---
10+
11+
MCP stdio, email, knowledge, queue, SMS, storage and record-trigger refusals, warnings and template descriptions no longer cite tracker numbers; each one states the decision behind it in words
12+
13+
Clause-②: no
14+
15+
Some strings these seven packages show to operators, administrators and flow authors pointed at an issue-tracker number for the reason behind them. The number goes; where the sentence did not already say what was decided, it now does.
16+
17+
- `@objectstack/connector-mcp`: the declarative stdio refusals say a stdio transport launches a local process, so stack metadata may only name a command the host's own code allows, and that an http transport is not gated by this policy.
18+
- `@objectstack/plugin-email`: the built-in change-email notice template's description, in all four locales, says the notice goes to the previous address so a hijacked session cannot move the account identity unannounced; the internal-headers refusal says a missing header does not announce itself, so the send would succeed while silently deviating from what was authored; the over-limit attachments line says the storage capability holds large content outside the row while the row keeps a reference and the attachment's audit metadata.
19+
- `@objectstack/service-knowledge`: the no-identity retrieval warning says a missing identity is not a grant of authority, so retrieval fails closed rather than searching the whole corpus unscoped; the predicate-write warning says the lifecycle reap guard de-indexes retention-swept rows before they are deleted.
20+
- `@objectstack/service-queue`: the missing-retention refusal says the one platform reaper sweeps completed rows by that declaration, so the adapter does not sweep the table itself; the rejected-floor error says the floor is what makes the lifecycle service refuse an override below the idempotency window.
21+
- `@objectstack/service-sms`: the unreadable-counter warning says a quota the platform cannot count must not refuse the one-time codes users sign in with; the counter store's lines name the daily SMS send quota without a number.
22+
- `@objectstack/service-storage`: the reclamation-gate line says deleting bytes cannot be undone, so it waits for a verified migration with no deviation on record, while reversible work carries on.
23+
- `@objectstack/trigger-record-change`: the array-trigger warning says multi-event arrays are deferred until two independent projects need a combination other than created-or-updated.
24+
25+
Text only: no status, error code, field, route or control flow moves. A client or log filter that matches the old text (for example a tracker-number suffix) needs the new spelling.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@objectstack/cli': minor
3+
---
4+
5+
feat(cli): `objectstack generate picklist NAME` scaffolds a shared option list, and the metadata summary counts picklists
6+
7+
Clause-②: yes (widening)
8+
9+
- **`objectstack generate picklist NAME`** (alias `os g picklist`) writes `src/picklists/NAME.picklist.ts`, a list declared with `definePicklist({ name, label, options })`, and adds its export line to `src/picklists/index.ts`. The list is collected under the `picklists` stack key. A select field takes its options from the list by naming it, `Field.select({ picklist: 'NAME' })`, in place of options of its own. The server serves that field with the list's options resolved onto it, together with any options other packages add through `picklistExtensions`, and judges writes against them. `objectstack validate` and `objectstack build` refuse a field whose `picklist` names no list the stack declares, and so does the boot.
10+
- **`objectstack init`** wires the new `src/picklists` barrel in the `app` and `plugin` templates, the same way it wires every other directory `objectstack generate` writes into: an empty `src/picklists/index.ts` and a `picklists: exportsOf(picklists)` key in `objectstack.config.ts`. A project scaffolded by an earlier release keeps its config. `objectstack generate picklist` then reports the list as not wired and prints the import line and the `defineStack` key to add.
11+
- **The metadata summary** that `objectstack validate`, `objectstack build` and `objectstack info` print counts the picklists a stack declares, in the `Data:` row: `Data: 1 Objects 3 Fields 1 Picklists`. A stack that declares none prints the row it printed before. The `stats` object in the `--json` output of the same three commands gains a `picklists` count. A `picklistExtensions` entry is not counted as a list.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'create-objectstack': minor
3+
---
4+
5+
feat(create-objectstack): the blank starter wires a `src/picklists` barrel for `objectstack generate picklist`
6+
7+
Clause-②: yes (widening)
8+
9+
A new blank project ships an empty `src/picklists/index.ts`, and its `objectstack.config.ts` imports it and hands its exports to `defineStack` under `picklists`, as it already does for every other directory `objectstack generate` writes into. `objectstack generate picklist NAME` then writes `src/picklists/NAME.picklist.ts` and its export line, and the list is part of the stack with no edit to the config. A select field takes its options from the list with `Field.select({ picklist: 'NAME' })`, and the server serves that field with the list's options.
10+
11+
A project scaffolded by an earlier release keeps its config. There, `objectstack generate picklist NAME` writes the list, reports that it does not reach the stack, and prints the import line and the `defineStack` key that wire `src/picklists`.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
fix(spec): the strict blueprint nav item's `label` describe says `null` inherits the target's current label
6+
7+
Clause-②: no
8+
9+
`SolutionBlueprintStrictSchema` is the output contract the AI design step generates against, and
10+
strict mode makes every nav entry's `label` a required decision. Its describe read only "Nav entry
11+
label, or null", so nothing the model reads said which of the two choices follows a rename of the
12+
target, and the model was steered toward writing one. The describe now states the lenient
13+
`BlueprintNavItemSchema.label` rule in the strict spelling: `null` ⇒ the entry inherits the CURRENT
14+
label of what it opens at render time (a renamed target shows its new name); a string ⇒ rendered
15+
verbatim, never a copy of the target's label. Write a label only when the entry must read
16+
differently from what it opens.
17+
18+
Describe text only: the key stays `z.string().nullable()`, so the schema accepts and refuses the
19+
same blueprints. A pin holds the lenient and strict `label` describes to one rule.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
docs(spec): an `object-master-detail-form` detail entry's `inlineMode` and `formFields` describes say what happens when the key is omitted on both of the renderer's paths (#21284)
6+
7+
Clause-②: no
8+
9+
- **Derived entry** (any entry that does not name both `relationshipField` and at least one column): an omitted `inlineMode` is resolved from the relationship field's `inlineEdit`, else from the child object's shape, and an omitted `formFields` is derived from the child object's fields. This is unchanged.
10+
- **Entry kept as authored** (one that names both `relationshipField` and at least one column): the renderer resolves and derives nothing. An omitted `inlineMode` renders the collection as a grid, which offers the per-row form only when `formFields` lists more fields than `columns`. An omitted `formFields` means the per-row form is offered only when `inlineMode` is `form`, and it then draws the child object's full field list.
11+
- The `inlineMode` describe used to say only "resolved from the relationship field's `inlineEdit` when omitted", and the `formFields` describe only "derived from the child object's editable fields when omitted". Neither holds for an entry kept as authored. The `formFields` describe also no longer says "editable": the derived list keeps `readonly` fields, as `deriveInlineRowFormFields` (`@objectstack/spec/data`) does.
12+
- No schema accepts or refuses anything new. Only the two describes, the reference page that lifts them, and one source comment change.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@objectstack/cli': patch
3+
---
4+
5+
The published `package.json` no longer declares `oclif.plugins`, and the package no longer lists `@oclif/plugin-help` or `@oclif/plugin-plugins` as devDependencies. The array named both plugins, but they were only devDependencies, and oclif loads an `oclif.plugins` entry only when the same name is in `dependencies`. Neither plugin ever loaded.
6+
7+
Clause-②: no
8+
9+
**What changes for an operator.** Nothing. `os --help`, every command and topic, and the output of `os help` and `os plugins` read byte-identical before and after the change. `os help` and `os plugins …` were never commands, and each still exits 2 with `command … not found`. Use `os --help` or `os <command> --help` for help.
10+
11+
**What the README now says.** It said `os plugins install`, `uninstall` and `update` came from `@oclif/plugin-plugins` and installed CLI extensions. That was never true. This CLI ships no plugin manager. To add commands to it, build an `os` distribution: a package whose own `package.json` lists the extension in both `oclif.plugins` and `dependencies`.

‎content/docs/deployment/cli.mdx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,7 @@ os g flow customer # Generate a flow that runs when a Customer changes
14831483
os g dashboard sales # Generate a dashboard
14841484
os g app customer # Generate an app whose navigation opens Customer
14851485
os g skill lead_qual # Generate an AI skill
1486+
os g picklist industry # Generate a shared option list select fields name
14861487
14871488
os g object task -d lib/ # Override target directory
14881489
os g object task --dry-run # Preview without writing
@@ -1499,6 +1500,7 @@ os g object task --dry-run # Preview without writing
14991500
| `dashboard` | `src/dashboards/` | `NAME.dashboard.ts` | `dashboards` | Analytics dashboard |
15001501
| `app` | `src/apps/` | `NAME.app.ts` | `apps` | Application navigation |
15011502
| `skill` | `src/skills/` | `NAME.skill.ts` | `skills` | AI skill — the ADR-0063 extension primitive |
1503+
| `picklist` | `src/picklists/` | `NAME.picklist.ts` | `picklists` | Shared option list that select fields reference by name |
15021504
15031505
<Callout type="info" title="Why generated files carry a type infix">
15041506
Every scaffold is written as `NAME.TYPE.ts`, and the infix is read from the
@@ -1555,7 +1557,7 @@ the rules a JSON Schema can express and names the rest under
15551557
15561558
**What it does:**
15571559
1. For a type that names an object (`object`, `view`, `action`, `flow`, `app`), reads `manifest.namespace` from the project config (`objectstack.config.ts`, `.js` or `.mjs`) and prefixes the object name with it (see above)
1558-
2. Creates the TypeScript file — an `object` declared with `ObjectSchema.create({ … })`, the same shape the `os init` templates write; a `skill` declared with `defineSkill({ … })`; the other types as typed literals (`UI.View`, `UI.Action`, `Automation.Flow`, `UI.Dashboard`, `UI.App`)
1560+
2. Creates the TypeScript file — an `object` declared with `ObjectSchema.create({ … })`, the same shape the `os init` templates write; a `skill` declared with `defineSkill({ … })`; a `picklist` declared with `definePicklist({ … })`, which a select field names with `Field.select({ picklist: 'NAME' })` in place of its own `options`, and which the server resolves into that field's `options`; the other types as typed literals (`UI.View`, `UI.Action`, `Automation.Flow`, `UI.Dashboard`, `UI.App`)
15591561
3. Creates or updates the barrel `index.ts` in the target directory
15601562
4. Shows a hint to run `objectstack validate`
15611563

‎content/docs/plugins/index.mdx‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -399,13 +399,15 @@ export default class MarketplaceSearch extends Command {
399399
#### Step 3: Load the Plugin into the CLI
400400

401401
<Callout type="warn">
402-
**`os plugins install` is not available today.** `@objectstack/cli`'s `package.json`
403-
lists `@oclif/plugin-plugins` under `oclif.plugins`, but the package sits in
404-
`devDependencies` — and oclif's core-plugin loader only matches names that appear in
405-
`dependencies`. The result is that neither `os plugins …` nor `os help` is a
406-
registered command; `os --help` shows no `plugins` topic. Until that is fixed, load a
407-
CLI extension by building an `os` distribution that lists the package in **both**
408-
`oclif.plugins` and `dependencies`.
402+
**`@objectstack/cli` ships no plugin manager.** Its `package.json` declares no
403+
`oclif.plugins`, and it does not depend on `@oclif/plugin-plugins` or
404+
`@oclif/plugin-help`. So `os plugins …` (`install`, `link`, `update`, …) and `os help`
405+
are not commands: each exits 2 with `command … not found`. `os --help` is the help
406+
entry, and it shows no `plugins` topic. To load a CLI extension, build an `os`
407+
distribution: a package whose own `package.json` lists the extension in **both**
408+
`oclif.plugins` and `dependencies`. oclif's core-plugin loader matches `oclif.plugins`
409+
names only against `dependencies`, so a name listed under `devDependencies` alone never
410+
loads.
409411
</Callout>
410412

411413
#### Using the Extended CLI

‎content/docs/references/ui/component.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -976,8 +976,8 @@ Sort field and direction pair
976976
| **childObject** | `string` | ✅ | Child object whose records are entered inline |
977977
| **relationshipField** | `string` | optional | FK on the child pointing back to the parent (auto-detected from the child's master_detail/lookup field when omitted) |
978978
| **columns** | `{ name: string; label?: string; type?: Enum<'text' \| 'number' \| 'currency' \| 'date' \| 'datetime' \| 'time' \| 'select' \| 'lookup' \| 'file'>; width?: number; … }[]` | optional | Editable grid columns (derived from the child object when omitted). Each entry is the strict, name-keyed inline grid column a relationship field's `inlineColumns` takes (`{ name, label?, type?, … }` — objectui GridColumn); identity-only entries (`{ name }`) hydrate everything else from the child object's fields. Unknown keys and the retired `field` spelling are refused at parse. |
979-
| **formFields** | `string[]` | optional | Child field names for the per-row expand form (derived from the child object's editable fields when omitted) |
980-
| **inlineMode** | `Enum<'grid' \| 'form'>` | optional | Inline-edit form factor: 'grid' = editable cells; 'form' = read-only list + per-row full form. Resolved from the relationship field's `inlineEdit` when omitted |
979+
| **formFields** | `string[]` | optional | Child field names for the per-row expand form. When omitted they are derived from the child object's fields — except on an entry that names both `relationshipField` and at least one column, which is kept as authored: nothing is derived, and the per-row form is offered only when `inlineMode` is 'form', where it draws the child object's full field list |
980+
| **inlineMode** | `Enum<'grid' \| 'form'>` | optional | Inline-edit form factor: 'grid' = editable cells; 'form' = read-only list + per-row full form. When omitted it is resolved from the relationship field's `inlineEdit`, else from the child object's shape — except on an entry that names both `relationshipField` and at least one column, which is kept as authored: nothing is resolved, the collection renders as a grid, and the per-row form is offered only when `formFields` lists more fields than `columns` |
981981
| **amountField** | `string` | optional | Numeric child column summed for the running total |
982982
| **sortField** | `string` | optional | Child field holding the line sort position, stamped on drag-reorder (derived from a `position` / `sort_order` / … field when omitted) |
983983
| **totalField** | `string` | optional | Parent field to receive the rolled-up sum |

‎docs/qa/platform-checklist/areas/cli.json‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@
574574
"title": "Wrong flags and unknown commands error with usage and a nonzero exit — never silently ignored, never executed anyway",
575575
"since": "v16",
576576
"status": "active",
577-
"revision": 1,
577+
"revision": 2,
578578
"priority": "P2",
579579
"surface": "cli",
580580
"personas": ["operator (local shell)"],
@@ -655,11 +655,12 @@
655655
],
656656
"source": [
657657
"packages/cli/src/commands/ (the 20 top-level commands + 10 topics enumerated as variants — oclif pattern discovery per package.json oclif.commands)",
658-
"packages/cli/package.json (oclif.plugins = help + plugins only — no not-found plugin, so unknown commands hard-error)",
658+
"packages/cli/package.json (no oclif.plugins at all — no plugin manager, no help command and no not-found plugin, so unknown commands hard-error)",
659659
"@oclif/core parse contract (Nonexistent flag / enum FailedFlagValidation / command-not-found, exit 2)"
660660
],
661661
"history": [
662-
{ "revision": 1, "date": "2026-08-07", "change": "new item: flag/command error UX with the full registered command surface enumerated from src/commands/ as variants, and never-executed-anyway as the load-bearing negative", "ref": "claude/platform-test-checklist-ocwugl" }
662+
{ "revision": 1, "date": "2026-08-07", "change": "new item: flag/command error UX with the full registered command surface enumerated from src/commands/ as variants, and never-executed-anyway as the load-bearing negative", "ref": "claude/platform-test-checklist-ocwugl" },
663+
{ "revision": 2, "date": "2026-10-02", "change": "source: package.json no longer declares oclif.plugins — the two listed plugins sat in devDependencies and never loaded, so the array was dropped; the registered surface and the unknown-command hard-error are unchanged (os --help and the full command table read byte-identical before and after)", "ref": "claude/issue-21285-drop-dead-oclif-plugins" }
663664
]
664665
},
665666
{

0 commit comments

Comments
 (0)