Skip to content

Releases: objectstack-ai/objectstack

create-objectstack@18.3.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 15:17
83d7e4a

Sections follow what the reader must do; each entry is placed by its own declarations (Metadata-protocol:, the Clause-②: arm, the BREAKING banner, the ADR-0087 disposition). The package CHANGELOG keeps the level grouping.

需手动处理 — action required: narrowed, with no ADR-0087 conversion

  • ad224b7: fix(create-objectstack)!: a scaffold into the current directory refuses, writing nothing, when a template file would overwrite one already there (#29134)

    Clause-②: no (narrowing) — create-objectstack with no name (or an empty one) in a current directory holding a file the template writes (README.md, package.json, …) refuses with exit 1 and leaves the directory untouched, where it overwrote those files and exited 0 before. Nothing is widened.
    Metadata-protocol: none

    BREAKING for a script that runs create-objectstack with no name inside a directory that already holds files the template writes.

    • Before. A named directory that was not empty was refused (Directory already exists and is not empty, exit 1), but the current directory was not checked at all: the template was copied over whatever was there, so an existing project's README.md and package.json were replaced without a word and the run exited 0.
    • Now. Before anything is written, the run compares the template's files with what the current directory holds. If any template file would replace an existing file (or a file stands where the template needs a directory), it refuses with exit 1, names each of those paths, and writes nothing.
    • Unchanged. A current directory that holds only files the template does not write (a .git/, a notes file) still scaffolds, and those files are left as they were; the closing summary still says the directory already had contents. An empty current directory and a named directory behave as before.

    The remedy. Move the named files aside and re-run, or scaffold into a new directory: create-objectstack my-app.

其他修复 — other fixes

  • 0f0b9f5: fix(create-objectstack): a project name npm would refuse is refused before anything is written, with the rule and a suggested name

    Clause-②: no
    Metadata-protocol: none

    create-objectstack "My App!" (or MyApp, or no argument inside a directory called My App) used to write that name verbatim into package.json and objectstack.manifest.json. validate and build passed, and the first refusal came from npm at npm publish.

    The resolved project name (the argument, or the current directory's name when there is none) is now checked against npm's rules for a new package name, using validate-npm-package-name (the module npm itself uses), before the first file is written. A refused name exits 1 and prints npm's reasons, the rule, and a suggested name (My App! and MyApp both suggest my-app). The name is never rewritten for you: re-run with the suggested name or one of your own. Names npm accepts, such as my-app and @scope/my-app, scaffold as before.

    create-objectstack gains one runtime dependency, validate-npm-package-name.

  • a1ae469: fix(scaffold): a scaffolded namespace is at most 13 characters, so a fresh project from a long name validates with no namespace-no-customization-pair warning (#28992)

    Clause-②: no
    Metadata-protocol: none

    npx create-objectstack and os init derive manifest.namespace from the project name. They truncated it at 20 characters, the manifest grammar's limit, while a namespace longer than 13 has no paired customization namespace (ADR-0146 D3). So qa-console-first-paint scaffolded the namespace qa_console_first_pai, and the project's first os validate warned about it and about the object names it prefixes, none of which the author wrote.

    Both scaffolders now cap the namespace at MAX_CUSTOMIZATION_BASE_NAMESPACE_LENGTH (13): qa-console-first-paint gets qa_console_fi, and every object name and file the scaffold derives from the namespace follows it. A truncated namespace never ends in _. os init reads the constant from @objectstack/spec/kernel; create-objectstack, which does not depend on the spec at runtime, keeps its own copy, pinned equal to the spec constant by a test. Project names whose namespace already fit are scaffolded exactly as before.

  • 0a46a26: fix(create-objectstack): the scaffolded AGENTS.md lists only the src/ directories the project's objectstack.config.ts loads

    Clause-②: no
    Metadata-protocol: none

    The "Project Structure" block of the AGENTS.md a new project gets named src/reports/, src/agents/, src/i18n/ and src/handlers/, which the scaffolded objectstack.config.ts never imports, so metadata a coding agent wrote there was never loaded and npm run validate still passed. It left out src/skills/ and src/picklists/, which the config does load. The block now lists exactly the eight wired directories (objects, views, actions, flows, dashboards, apps, skills, picklists) and says that a new src/ directory is never loaded or validated until its barrel is imported in objectstack.config.ts. What the config loads is unchanged.

    An existing project keeps the AGENTS.md it was created with; to adopt the fix, edit its Project Structure block to match the directories its own objectstack.config.ts imports.

@objectstack/verify@18.3.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 15:19
83d7e4a

Sections follow what the reader must do; each entry is placed by its own declarations (Metadata-protocol:, the Clause-②: arm, the BREAKING banner, the ADR-0087 disposition). The package CHANGELOG keeps the level grouping.

其他修复 — other fixes

  • d08b859: docs(verify): the BootOptions.multiTenant: 'posture-only' doc comment says what the stand-in walls and what it does not

    Clause-②: no
    Metadata-protocol: none

    Doc comments only; no behaviour changes. The multiTenant doc comment said a 'posture-only' boot stamps no organization_id, scopes no query, and that a cross-organization fixture booted this way "would assert nothing". Since ADR-0131 (D8, D11) the organization wall keys on the posture in force, so that was the opposite of what the code does:

    • Insert. The engine lands a write in the caller's active organization, and the SQL driver stamps an absent organization_id from it.
    • Read. Layer 0 and the driver scope project one verdict, which walls under isolated (organization_id = active org) and group (IN accessible_org_ids).

    The comment now says a cross-organization fixture can boot this way, as long as it measures the wall: the other organization's row is hidden, its own row is served, and the stamp is checked. It also names what the stand-in does not give a fixture, for which multiTenant: true with @objectstack/organizations is still needed: the default-organization bootstrap, per-organization seed replay, the walled membership-policy gate, and the deployment declarations on the org-scoping service. It notes that databaseDriver: 'memory' refuses a walled posture. The orgContext comment no longer says multiTenant: true is the only cross-organization proof.

  • Updated dependencies [ce12ad2]

  • Updated dependencies [2282aa8]

  • Updated dependencies [ea025d3]

  • Updated dependencies [af1e1e0]

  • Updated dependencies [2f21b48]

  • Updated dependencies [0ea90c7]

  • Updated dependencies [dbf7e04]

  • Updated dependencies [a9f654e]

  • Updated dependencies [b49b752]

  • Updated dependencies [d8a9cc5]

  • Updated dependencies [b53d9f3]

  • Updated dependencies [8eeb3b3]

  • Updated dependencies [4f05de4]

  • Updated dependencies [95f818d]

  • Updated dependencies [b3f7344]

  • Updated dependencies [d90657b]

  • Updated dependencies [ff26bc4]

  • Updated dependencies [21a13f1]

  • Updated dependencies [c68869c]

  • Updated dependencies [162f668]

  • Updated dependencies [43714ca]

  • Updated dependencies [c3fb185]

  • Updated dependencies [63e3873]

  • Updated dependencies [9bf6598]

  • Updated dependencies [f515124]

  • Updated dependencies [a98693d]

  • Updated dependencies [8c0306f]

  • Updated dependencies [a3009cb]

  • Updated dependencies [416d9dd]

  • Updated dependencies [d9bf777]

  • Updated dependencies [898703f]

  • Updated dependencies [179598e]

  • Updated dependencies [bb91ae8]

  • Updated dependencies [fb91e97]

  • Updated dependencies [d434481]

  • Updated dependencies [f9f27a0]

  • Updated dependencies [f9f27a0]

  • Updated dependencies [f9f27a0]

  • Updated dependencies [6328a07]

  • Updated dependencies [02b1392]

  • Updated dependencies [c26d643]

  • Updated dependencies [416a760]

  • Updated dependencies [76a1275]

  • Updated dependencies [13e85be]

  • Updated dependencies [13dc413]

  • Updated dependencies [eab4bba]

  • Updated dependencies [cb8988e]

  • Updated dependencies [e744244]

  • Updated dependencies [6279efe]

  • Updated dependencies [e438a55]

  • Updated dependencies [2205863]

  • Updated dependencies [2205863]

  • Updated dependencies [dabe771]

  • Updated dependencies [dabe771]

  • Updated dependencies [9fab076]

  • Updated dependencies [1f1eda4]

  • Updated dependencies [74801d1]

  • Updated dependencies [74801d1]

  • Updated dependencies [17b5152]

  • Updated dependencies [ef7067f]

  • Updated dependencies [031d97a]

  • Updated dependencies [0c47e87]

  • Updated dependencies [7064615]

  • Updated dependencies [e01edb2]

  • Updated dependencies [7e9f814]

  • Updated dependencies [da46b2a]

  • Updated dependencies [479ad08]

  • Updated dependencies [4f93696]

  • Updated dependencies [f79fa74]

  • Updated dependencies [b358e79]

  • Updated dependencies [5374967]

  • Updated dependencies [8a218fc]

  • Updated dependencies [96e7a43]

  • Updated dependencies [31a6ca7]

  • Updated dependencies [1539aad]

  • Updated dependencies [cdd9f0a]

  • Updated dependencies [3fe9d25]

  • Updated dependencies [2ecf6a7]

  • Updated dependencies [b29a88c]

  • Updated dependencies [fbe344b]

  • Updated dependencies [354dfcd]

  • Updated dependencies [1d7816d]

  • Updated dependencies [d887aa9]

  • Updated dependencies [2a40f98]

  • Updated dependencies [39fd5fb]

  • Updated dependencies [e1ab32e]

  • Updated dependencies [5aad4e0]

  • Updated dependencies [b2b3124]

  • Updated dependencies [5bd3d2f]

  • Updated dependencies [14ab711]

  • Updated dependencies [67cf0b1]

  • Updated dependencies [e46e25c]

  • Updated dependencies [81b4599]

  • Updated dependencies [c13ef04]

  • Updated dependencies [6eaf404]

  • Updated dependencies [c13ef04]

  • Updated dependencies [79ed6f7]

  • Updated dependencies [40320f1]

  • Updated dependencies [40320f1]

  • Updated dependencies [0eb2d21]

  • Updated dependencies [49d652e]

  • Updated dependencies [045cfa4]

  • Updated dependencies [685d9f3]

  • Updated dependencies [0fb0dda]

  • Updated dependencies [693a729]

  • Updated dependencies [9fa70b4]

  • Updated dependencies [e3470ed]

  • Updated dependencies [9ac5ae5]

  • Updated dependencies [58d4ee6]

  • Updated dependencies [c082af6]

  • Updated dependencies [6dbff8e]

  • Updated dependencies [c63ce2a]

  • Updated dependencies [12b5aec]

  • Updated dependencies [8b1740a]

  • Updated dependencies [b88519e]

  • Updated dependencies [e5b5c5b]

  • Updated dependencies [a315560]

  • Updated dependencies [835b2d1]

  • Updated dependencies [d4ff1a0]

  • Updated dependencies [10f27b6]

  • Updated dependencies [10f27b6]

  • Updated dependencies [fe9e856]

  • Updated dependencies [d4ff1a0]

  • Updated dependencies [ae16687]

  • Updated dependencies [efdf11a]

  • Updated dependencies [2655ebd]

  • Updated dependencies [14e9685]

  • Updated dependencies [0cd578f]

  • Updated dependencies [a33fce2]

  • Updated dependencies [ee9a8d6]

  • Updated dependencies [385aa12]

  • Updated dependencies [5b5cff1]

  • Updated dependencies [79d5dac]

  • Updated dependencies [8bb2c31]

  • Updated dependencies [8bb2c31]

  • Updated dependencies [8bb2c31]

  • Updated dependencies [8d79bbd]

  • Updated dependencies [b8f4acc]

  • Updated dependencies [4145210]

  • Updated dependencies [194e66c]

  • Updated dependencies [57c51aa]

  • Updated dependencies [3604371]

  • Updated dependencies [1ed3360]

  • Updated dependencies [a8ec04d]

  • Updated dependencies [73b7270]

  • Updated dependencies [6762dd9]

  • Updated dependencies [6762dd9]

  • Updated dependencies [291bf44]

  • Updated dependencies [556f323]

  • Updated dependencies [b445807]

  • Updated dependencies [b445807]

  • Updated dependencies [6abcb72]

  • Updated dependencies [7735d1d]

  • Updated dependencies [7735d1d]

  • Updated dependencies [7735d1d]

  • Updated dependencies [0c8c27e]

  • Updated dependencies [7739573]

  • Updated dependencies [99aeee6]

  • Updated dependencies [bc4c963]

  • Updated dependencies [86edcaf]

  • Updated dependencies [ad3a640]

  • Updated dependencies [a0cc51f]

  • Updated dependencies [da7bcc8]

  • Updated dependencies [8d66cbe]

  • Updated dependencies [fbaacfe]

  • Updated dependencies [dbf576b]

  • Updated dependencies [a1d8bf6]

  • Updated dependencies [4fa4f2c]

  • Updated dependencies [2dc69d7]

  • Updated dependencies [a0413fc]

  • Updated dependencies [76b4ae4]

  • Updated dependencies [76b4ae4]

  • Updated dependencies [e5c5035]

  • Updated dependencies [85663a5]

  • Updated dependencies [33ab543]

  • Updated dependencies [21e5979]

  • Updated dependencies [f57eddc]

  • Updated dependencies [a2f434d]

  • Updated dependencies [eac6d96]

  • Updated dependencies [0368504]

  • Updated dependencies [6663a66]

  • Updated dependencies [a3014cb]

  • Updated dependencies [ca64f6f]

  • Updated dependencies [fbb8bec]

  • Updated dependencies [f557d90]

  • Updated dependencies [b8b3208]

  • Updated dependencies [aad9ad5]

  • Updated dependencies [86f2afe]

  • Updated dependencies [e29a1b1]

  • Updated dependencies [72c0152]

  • Updated dependencies [524befb]

  • Updated dependencies [81d00a4]

  • Updated dependencies [b2b4972]

  • Updated dependencies [e0aed00]

  • Updated dependencies [481ade6]

  • Updated dependencies [a75ddba]

  • Updated dependencies [e270dab]

  • Updated dependencies [af52253]

  • Updated dependencies [875d007]

  • Updated dependencies [f40b777]

  • Updated dependencies [72b08ee]

  • Updated dependencies [bf658d1]

  • Updated dependencies [3702be4]

  • Updated dependencies [ca167f6]

  • Updated dependencies [4a5396b]

  • Updated dependencies [a3d24c9]

  • Updated dependencies [6dc06b2]

  • Updated dependencies [a7de844]

  • Updated dependencies [ecd944f]

  • Updated dependencies [9d29223]

  • Updated dependencies [7538f27]

  • Updated dependencies [21c4bdd]

  • Updated dependencies [2c683cf]

  • Updated dependencies [79d78b4]

  • Updated dependencies [04141a6]

  • Updated dependencies [2efd903]

  • Updated dependencies [86c9d53]

  • Updated dependencies [bfdaba4]

  • Updated dependencies [a7c2595]

  • Updated dependencies [8441a01]

  • Updated dependencies [db44327]

  • Updated dependencies [0027c4a]

  • Updated dependencies [9cd54fa]

  • Updated dependencies [684f2f6]

  • Updated dependencies [64599ec]

  • Updated dependencies [869fa76]

  • Updated dependencies [dd9921e]

  • Updated dependencies [2fc3861]

  • Updated dependencies [d5151a7]

  • Updated dependencies [bc496bf]

  • Updated dependencies [01eab23]

  • Updated dependencies [74aae1e]

  • Updated dependencies [2b4b970]

  • Updated dependencies [47c3e12]

  • Updated dependencies [e063a87]

  • Updated dependencies [ca54eac]

  • Updated dependencies [6d9419b]

  • Updated dependencies [03cc871]

  • Updated dependencies [7811c3f]

  • Updated dependencies [755e285]

  • Updated dependencies [92493e3]

  • Updated dependencies [8f25cee]

  • Updated dep...

Read more

@objectstack/types@18.3.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 15:19
83d7e4a

Sections follow what the reader must do; each entry is placed by its own declarations (Metadata-protocol:, the Clause-②: arm, the BREAKING banner, the ADR-0087 disposition). The package CHANGELOG keeps the level grouping.

需手动处理 — action required: narrowed, with no ADR-0087 conversion

  • 1617b7f: fix(lint,metadata-protocol,rest,runtime,types): a roll-up the engine refuses at every recompute is refused at save and publish, and a child write that landed answers its success (#28661)

    Clause-②: no (narrowing)
    Metadata-protocol: narrowed
    Skills-docs: none — skills/objectstack-data/rules/relationships.md and rules/field-types.md teach summaryOperations only with sum over a numeric amount and count; no file under skills/** teaches a sum/avg over a text field, and every taught example stays admitted.

    BREAKING (narrowing) for a summary field whose sum or avg names a child field the engine does not aggregate (and, on os validate, os build and the runtime object save/publish door, a min / max whose answer is not a number, which os lint already refused).

    • Before. A published summary field with sum or avg over a child text field published as valid (publish-drafts, the draft's _diagnostics: { valid: true }, os validate, os build). Every later create, update and delete of a child then answered 500 INTERNAL_ERROR, though the write had landed and the parent's valid roll-ups had recomputed: the recompute's aggregate refuses the pair after the commit. A client that retried a create made a duplicate.
    • Now, at the doors. The roll-up rule moves from os lint alone into the authoring-rule registry (lintSummaryRollups, gating, on os validate, os build, os lint and the runtime object save/publish door). A sum or avg over a child field outside the aggregate × field-type table (AGGREGATE_FIELD_TYPE_COMPATIBILITY, the table the engine's aggregate door reads) is refused under rollup/non-numeric-aggregand, at objects.<parent>.fields.<field>.summaryOperations.field, with the accepted child types and count as the fix. The min/max arm os lint already reported keeps its message and now also gates os validate, os build and the runtime object save/publish door. A child staged as a draft in the same publish batch is judged with it. os lint no longer reports the rule a second time.
    • Now, after a write. When a parent's roll-up cannot be recomputed after a child write — a roll-up stored before this gate, or any other recompute failure — POST, PATCH and DELETE /api/v1/data/:object[/:id] answer the write's own success (201/200, the same body the write answers when every roll-up recomputes), on both RestServer and the runtime dispatcher. The stale summary column is logged at error, with the fix, once per field per process (the memory is never cleared; a restart reports it again). The protocol's createData/updateData/deleteData still throw ERR_SUMMARY_RECOMPUTE carrying written, so the import runner and seed loader read it as before. The recogniser and the earned-answer carrier (isSummaryRecomputeRefusal, earnedAnswerOf, staleSummaryAnswerLines) are new exports of @objectstack/types, the one package the protocol, rest and runtime all depend on.
    • Not refused. A summaryOperations.object naming no object (the card's qsf_nope) is still admitted: an unmanaged install (install-local --unmanaged) saves a package's parent before the child its roll-up reads, so a door refusing a missing child would refuse that install.
    • Who moves. No examples/** or hotcrm roll-up is refused: every sum/avg/min/max there aggregates a number or currency child field.
  • 9bdfe37: fix(plugins)!: team and position recipients reach every holder past 10,000 rows in sharing and approvals, and a team, position or role holder read that fails is refused loudly instead of answered with nobody

    Clause-②: yes (narrowing)
    Metadata-protocol: none

    BREAKING only where a sys_team_member, sys_user_position or sys_member read fails: the team and position expansions and the user-scoped holder checks used to answer nobody (an empty list, or false) then, and now refuse.

    This completes the family the role and membership-tier fix started. The team and position reads still read once with a 10,000-row bound and turned any read error into nobody.

    • Every holder, nobody else. In plugin-sharing, TeamGraphService.expandUsers (sys_team_member, every team sharing recipient) and the assignment limb of PositionGraphService.expandPositionUsers (sys_user_position, every position sharing recipient) now read in pages of 10,000 ordered by id until the holders are exhausted. In plugin-approvals, the team approver and the assignment limb of the position approver (and every caller of the position expansion: escalation targets, reassign checks, resolveAs expressions) do the same. Past 10,000 holders, the holders past the 10,000th row used to get no grant and no approval slot. They now do.
    • Smaller groups are unchanged. A team or position held by fewer than 10,000 people issues one read, as before, and answers the same set.
    • Loud, never short or empty. A read that fails, or a page the driver returned without honouring the id cursor, now throws.
      • Sharing, whole-rule passes: the expansion rejects, so that rule's reconcile or revoke pass fails, its callers log their existing per-rule warning, and the grants it already holds stay. Before, a failed team read answered [], and a failed assignment read answered the sys_member.role holders alone, so the retired-recipient revoke took grants away from people who still held the recipient.
      • Sharing, per-user recompute (a write to sys_team_member or sys_user_position): the holder checks teamHasMember, holdsRole and PositionGraphService.holdsPosition now refuse a failed read instead of answering false. The recompute logs recipient membership re-evaluation failed for that rule and user, and the user's grants stay. Before, an outage revoked that user's grants.
      • Approvals: the approver slot falls back to its literal (team:ID or position:NAME) and logs could not be expanded — a directory or read-filter lookup failed with its error. Before, a failed team read logged "expanded to nobody", and a failed assignment read routed a position approver to its membership-tier holders alone, which looked like a complete slate.
    • Ownership queues: a position specification now contributes nobody on a sys_user_position outage. A queue team specification whose sys_team_member read fails still contributes nobody, as before. A queue position specification whose sys_user_position read fails now contributes nobody too, on the expansion and on the queue's holder check alike, where before it contributed that position's sys_member.role transition-source holders. That is the posture the queue declares for a failed member-table read, reached here for the first time. Both now log once. Whether a queue should refuse instead is left to a ruling.
    • A missing object is still no holders. A stack that never registers, or never creates, sys_team_member, sys_user_position or sys_member keeps answering no holders through it. That is judged by the new isObjectAbsentError(error, readObject) in @objectstack/types: the shared missing-table predicate for that object, or the engine's OBJECT_NOT_FOUND naming that object. Any other error, including an absence of a different object, stays loud.

    The remedy. None is needed. A refusal means the membership table could not be read: fix that read, and the next expansion succeeds. A failed read is never cached.

自动迁移 — migrated for you: narrowed, with a conversion objectstack migrate meta applies

  • 3238750: feat(spec)!: oauth2 and app-credential join declarative connector-instance auth, and the runtime oauth2 variant is retired (ADR-0143 S0, #28137)

    Clause-②: yes (narrowing)
    Metadata-protocol: narrowed

    An upstream that takes no static credential (an OAuth 2.0 API, a Feishu, DingTalk or WeCom app) had no open-tier way to be declared: the instance auth stopped at four static kinds, and the runtime authentication shape had an oauth2 variant that held a client secret and a refresh token inline and that nothing ran. ADR-0143 opens the grant-bearing kinds on the instance, with the secret as a reference.

    • @objectstack/spec: ConnectorInstanceAuthSchema gains oauth2 (D2.1), one strict shape per grant (authorization_code with PKCE always on, client_credentials for a confidential client), and app-credential (D2.2, vendor feishu / dingtalk / wecom, appId, a required credentialRef). The publish door refuses a missing tokenUrl, clientId or authorizationUrl at its key under auth, except on provider: 'mcp' over http, where the MCP authorization flow discovers them. An inline clientSecret, refreshToken, accessToken or appSecret is refused by name with the credentialRef prescription. signed is not a...
Read more

@objectstack/trigger-schedule@18.3.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 15:19
83d7e4a

Sections follow what the reader must do; each entry is placed by its own declarations (Metadata-protocol:, the Clause-②: arm, the BREAKING banner, the ADR-0087 disposition). The package CHANGELOG keeps the level grouping.

新增能力 — new capabilities

  • 2282aa8: Shipped in 18.2.0, whose CHANGELOG missed this entry; listed here one release late.

    feat(trigger-schedule): a scheduled run whose due time passed while no kernel was ticking is recorded as missed in the flow's run history, and is not run

    Clause-②: yes (widening)
    Metadata-protocol: none

    A hosted kernel that is stopped, evicted or restarting does not tick, and a scheduled flow due in that gap used to leave no trace: nothing ran and nothing said so, so an app could not tell a sweep had not happened. Now the next kernel to bind the flow records each such due time once, as a run-history entry with status missed:

    • What the entry says. startedAt is the due time that passed and completedAt the instant the next kernel detected it. No step ran, so steps is empty; trigger.type is schedule. The entry is listed by listRuns (and GET /api/v1/automation/:name/runs), ?status=missed selects it, and it is durable history in sys_automation_run, aged out like every other terminal run.
    • Record only. Nothing is run for a missed due time. It is never replayed or caught up; the flow's next due time runs as usual.
    • What counts as a silence. The flow's job was still armed when its kernel stopped (its sys_job row still active: a kernel that stops ticking cancels nothing, while disabling, republishing or withdrawing a flow does), and a due time of the schedule that kernel armed falls after its last fire and before the next kernel bound the flow. A kernel that ticked through records nothing, and a flow that was deliberately unbound records nothing for the time it was unbound.
    • A refusal is not a silence. A kernel that refuses the flow on purpose (scheduled work switched off for it, a missing acting organization, an unreadable schedule) now disarms the job by its name even when it never bound it, so the next kernel to run the flow does not read that kernel's life as missed due times. FlowTrigger gains an optional release(flowName), which the engine calls when its scheduled-work policy refuses a flow before the trigger is reached.
    • Never a run that happened. A due time whose tick window is claimed in the dispatch ledger (sys_flow_dispatch) was dispatched, landed or died in flight, and is not recorded. With no persisted ledger the check is skipped, and a ledger read that fails records nothing for that due time; the trigger says either at warn, naming the flow.
    • Bounded by the run history's own cap. At most the run history's per-flow cap (maxTerminalRunsPerFlow, 100 by default) of the newest due times are recorded per flow per detection, since the store keeps no more terminal rows than that per flow. One warn line names the flow, how many earlier due times were not recorded and the earliest of them.
    • Two replicas, one row. The entry's id is derived from the flow and the due time, so replicas that detect the same silence write one history row.

    DbJobAdapter gains readArming(name) (adapter-local, like setReplayGuard; not part of IJobService), which reads how a job's sys_job row was left. AutomationEngine gains recordMissedRun(...) and missedRunLimit(), the run stores expose their configured maxTerminalRunsPerFlow, and TERMINAL_RUN_STATUSES and the sys_automation_run.status options gain missed.

其他修复 — other fixes

  • 21e5979: fix(trigger-schedule): a time-relative flow whose timeRelative descriptor is refused now reads bound: false on the automation status, not bound: true

    Clause-②: no
    Metadata-protocol: none

    TimeRelativeTrigger.start refused an invalid config.timeRelative descriptor (both withinDays and offsetDays, an unknown key, schedule nested inside the descriptor) by logging "not bound" and returning. The engine read that return as a successful bind, so GET /api/v1/automation/_status reported the flow as bound: true with no unboundCause, and no sweep ever ran.

    The refusal now throws, the same way the trigger's organization and deployment-policy refusals already do. The engine records the flow as unbound: its status row reads bound: false with unboundCause: 'binding_failure', and the boot-time binding audit lists it. The warn line the server logs is unchanged, and the thrown text is the same sentence. A re-bind with a broken descriptor also cancels the sweep an earlier valid bind armed.

    Nothing changes for a valid descriptor. A host that calls TimeRelativeTrigger.start directly, outside the engine, should expect it to throw on a refused descriptor.

  • Updated dependencies [ea025d3]

  • Updated dependencies [2f21b48]

  • Updated dependencies [0ea90c7]

  • Updated dependencies [dbf7e04]

  • Updated dependencies [a9f654e]

  • Updated dependencies [b49b752]

  • Updated dependencies [d8a9cc5]

  • Updated dependencies [b53d9f3]

  • Updated dependencies [8eeb3b3]

  • Updated dependencies [4f05de4]

  • Updated dependencies [95f818d]

  • Updated dependencies [b3f7344]

  • Updated dependencies [d90657b]

  • Updated dependencies [ff26bc4]

  • Updated dependencies [21a13f1]

  • Updated dependencies [c68869c]

  • Updated dependencies [162f668]

  • Updated dependencies [43714ca]

  • Updated dependencies [63e3873]

  • Updated dependencies [9bf6598]

  • Updated dependencies [a98693d]

  • Updated dependencies [7dd5a2f]

  • Updated dependencies [d9bf777]

  • Updated dependencies [804a2b0]

  • Updated dependencies [bb91ae8]

  • Updated dependencies [d434481]

  • Updated dependencies [f9f27a0]

  • Updated dependencies [02b1392]

  • Updated dependencies [c26d643]

  • Updated dependencies [13e85be]

  • Updated dependencies [13dc413]

  • Updated dependencies [eab4bba]

  • Updated dependencies [e438a55]

  • Updated dependencies [2205863]

  • Updated dependencies [9fab076]

  • Updated dependencies [74801d1]

  • Updated dependencies [17b5152]

  • Updated dependencies [ef7067f]

  • Updated dependencies [031d97a]

  • Updated dependencies [0c47e87]

  • Updated dependencies [e01edb2]

  • Updated dependencies [f79fa74]

  • Updated dependencies [b358e79]

  • Updated dependencies [5374967]

  • Updated dependencies [8a218fc]

  • Updated dependencies [1539aad]

  • Updated dependencies [cdd9f0a]

  • Updated dependencies [3fe9d25]

  • Updated dependencies [2ecf6a7]

  • Updated dependencies [fbe344b]

  • Updated dependencies [354dfcd]

  • Updated dependencies [d887aa9]

  • Updated dependencies [39fd5fb]

  • Updated dependencies [5aad4e0]

  • Updated dependencies [5bd3d2f]

  • Updated dependencies [81b4599]

  • Updated dependencies [c13ef04]

  • Updated dependencies [6eaf404]

  • Updated dependencies [79ed6f7]

  • Updated dependencies [40320f1]

  • Updated dependencies [49d652e]

  • Updated dependencies [685d9f3]

  • Updated dependencies [0fb0dda]

  • Updated dependencies [e3470ed]

  • Updated dependencies [9ac5ae5]

  • Updated dependencies [c082af6]

  • Updated dependencies [6dbff8e]

  • Updated dependencies [c63ce2a]

  • Updated dependencies [12b5aec]

  • Updated dependencies [8b1740a]

  • Updated dependencies [b88519e]

  • Updated dependencies [e5b5c5b]

  • Updated dependencies [d4ff1a0]

  • Updated dependencies [10f27b6]

  • Updated dependencies [fe9e856]

  • Updated dependencies [d4ff1a0]

  • Updated dependencies [ae16687]

  • Updated dependencies [efdf11a]

  • Updated dependencies [2655ebd]

  • Updated dependencies [14e9685]

  • Updated dependencies [0cd578f]

  • Updated dependencies [a33fce2]

  • Updated dependencies [385aa12]

  • Updated dependencies [8bb2c31]

  • Updated dependencies [8d79bbd]

  • Updated dependencies [b8f4acc]

  • Updated dependencies [4145210]

  • Updated dependencies [194e66c]

  • Updated dependencies [57c51aa]

  • Updated dependencies [3604371]

  • Updated dependencies [1ed3360]

  • Updated dependencies [73b7270]

  • Updated dependencies [6762dd9]

  • Updated dependencies [556f323]

  • Updated dependencies [b445807]

  • Updated dependencies [6abcb72]

  • Updated dependencies [7735d1d]

  • Updated dependencies [7739573]

  • Updated dependencies [bc4c963]

  • Updated dependencies [86edcaf]

  • Updated dependencies [ad3a640]

  • Updated dependencies [fbaacfe]

  • Updated dependencies [dbf576b]

  • Updated dependencies [4fa4f2c]

  • Updated dependencies [76b4ae4]

  • Updated dependencies [0368504]

  • Updated dependencies [6663a66]

  • Updated dependencies [fbb8bec]

  • Updated dependencies [f557d90]

  • Updated dependencies [aad9ad5]

  • Updated dependencies [e0aed00]

  • Updated dependencies [72b08ee]

  • Updated dependencies [bf658d1]

  • Updated dependencies [3702be4]

  • Updated dependencies [4a5396b]

  • Updated dependencies [9d29223]

  • Updated dependencies [7538f27]

  • Updated dependencies [21c4bdd]

  • Updated dependencies [2c683cf]

  • Updated dependencies [86c9d53]

  • Updated dependencies [bfdaba4]

  • Updated dependencies [8441a01]

  • Updated dependencies [db44327]

  • Updated dependencies [0027c4a]

  • Updated dependencies [684f2f6]

  • Updated dependencies [869fa76]

  • Updated dependencies [2fc3861]

  • Updated dependencies [74aae1e]

  • Updated dependencies [2b4b970]

  • Updated dependencies [e063a87]

  • Updated dependencies [03cc871]

  • Updated dependencies [7811c3f]

  • Updated dependencies [755e285]

  • Updated dependencies [07585bf]

  • Updated dependencies [9cbe507]

  • Updated dependencies [2695de7]

  • Updated dependencies [0a4b0fb]

  • Updated dependencies [b373263]

  • Updated dependencies [d3b0a40]

  • Updated dependencies [ed8b237]

  • Updated dependencies [a68430a]

  • Updated dependencies [6da8c3c]

  • Updated dependencies [1ea3fe9]

  • Updated dependencies [3e0fd7a]

  • Updated dependencies [f552fa8]

  • Updated dependencies [dc4755c]

  • Updated dependencies [0a52b7d]

  • Updated dependencies [4793aa4]

  • Updated dependencies [119931d]

  • Updated dependencies [3a054cb]

  • Updated dependencies [f934a41]
    ...

Read more

@objectstack/trigger-record-change@18.3.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 15:19
83d7e4a

Sections follow what the reader must do; each entry is placed by its own declarations (Metadata-protocol:, the Clause-②: arm, the BREAKING banner, the ADR-0087 disposition). The package CHANGELOG keeps the level grouping.

新增能力 — new capabilities

  • 7abe151: feat(runtime): an admin can disable a type: 'module' package of an installed app, and a disabled package's navigation contributions and record-change flows stop (#28117)

    Clause-②: yes (widening) — PATCH /api/v1/packages/:id/disable now accepts a module of an installed app, which it refused 422 WRITABLE_PACKAGE_REQUIRED before. Nothing is narrowed.
    Metadata-protocol: none

    A suite ships one artifact: its type: 'app' package and several type: 'module' packages. A customer who does not use a module can now switch it off in their environment, and "disabled" keeps its one meaning: hidden, refused on the data plane, data kept.

    • The disable door (@objectstack/runtime). An admin with manage_metadata may disable a package whose manifest.type is 'module' and whose manifest.dependencies directly name an installed package of manifest.type 'app', unless its own manifest.scope is system or cloud. This holds for a suite booted from code and for one installed from the marketplace: both leave the same row. Every other read-only package answers the disable door 422 WRITABLE_PACKAGE_REQUIRED as before: the app itself, a plugin, a platform package, a module that declares no app or whose app is not installed. The exemption is for disable only. The module stays read-only for authoring, so edit, publish, revert, delete and overwrite still refuse it, and its served row still says writable: false.
    • Navigation (@objectstack/objectql). SchemaRegistry.applyNavContributions drops the contributions of a disabled package, so getApp, getAllApps and the protocol's app reads (GET /api/v1/meta/app, GET /api/v1/meta/app/:name) no longer list a disabled module's menu entries. They come back on enable. A disabled package's contribution into a missing group is not relocated and raises no nav_contribution_group_missing diagnostic.
    • Record-change flows (@objectstack/trigger-record-change). A flow bound to an object whose owning package is disabled is not run for a write made in-process (an app hook's ctx.api.object(...), another flow, a service), which the engine still accepts. The record is written; the flow runs again once the package is enabled. Writes through the data plane were already refused.
    • Already in place, and now pinned on the module path: the data plane's 404 OBJECT_PACKAGE_DISABLED for the module's objects, and the per-environment state file that replays a disable at boot.

    An app hook that reads a disabled module's object through ctx.api.object(name) is served the kept records: the in-process read is not refused, and this release does not change that. The marketplace handbook says so under "Disabling a module".

  • a00f335: feat: a hook (shipped body included) and a record-triggered flow can set the save notice the create / update answer carries (#28251)

    Clause-②: yes (widening: HookContext gains the optional notice member, FlowNodeAction gains save_notice, the flows translation group gains notices, and AutomationContext gains the optional saveNotice; nothing an existing stack writes is refused)
    Metadata-protocol: added

    A single-record create or update has answered with an optional notice since #28247, but only an in-process caller that passed onNotice could set it. A shipped hook body could not reach that listener, and neither could a flow, so an app that wanted to tell the saver "Sent Acme to your manager for approval" had to fake it with a record:alert banner. Now there is one channel with two faces:

    • Hooks: ctx.notice(message). HookContextSchema.notice is the member. The engine binds it at every insert / update dispatch (beforeInsert, afterInsert, beforeUpdate, afterUpdate), so every dispatch site gets it, and it calls the write's own onNotice listener. A write nobody listens on (a batch, a seed, an import, a flow node's own write) sets nothing, and the call is a no-op there. Anything but a non-empty string is refused with a TypeError naming the call, and the write with it. Delete and read dispatches carry no member. The sandbox marshals the member into a shipped body (QuickJS), and no capability is needed to call it.
    • Flows: the save_notice node. { type: 'save_notice', config: { message } } renders its {{ }} message and sets it on the write that started the run. The record-change trigger runs the flow inside that write and hands the run the write's channel (AutomationContext.saveNotice). The flow parse refuses the node, at any region depth, in a flow whose start is not a create or update record trigger (record-(before|after)-(create|insert|update|write)), because no write waits on such a run. At run time the node is refused by name where the parse cannot see it: a leg resumed after an approval or a wait, a subflow or map child (they get no channel), and a run started any other way.
    • Translatable where it is rendered. A flow notice is picked from flows.<flow>.notices.<node_id>.message (flowNoticeMessageKey) in the writer's locale before its holes are filled, and the authored template renders when nothing is picked. os i18n extract scaffolds the key, the coverage gate demands it, and objectstack validate refuses a key over an unknown flow, an unknown node, or a node that is not a save_notice. The answer's notice stays a plain string: the sentence arrives already rendered.

    Scope: single-record create and update only. A batch, a clone and a write a flow node makes pass no listener, so they carry no notice.

其他修复 — other fixes

-...

Read more

@objectstack/trigger-api@18.3.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 15:19
83d7e4a

Sections follow what the reader must do; each entry is placed by its own declarations (Metadata-protocol:, the Clause-②: arm, the BREAKING banner, the ADR-0087 disposition). The package CHANGELOG keeps the level grouping.

其他修复 — other fixes

Read more

@objectstack/studio@18.3.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 15:17
83d7e4a

Sections follow what the reader must do; each entry is placed by its own declarations (Metadata-protocol:, the Clause-②: arm, the BREAKING banner, the ADR-0087 disposition). The package CHANGELOG keeps the level grouping.

其他修复 — other fixes

Read more

@objectstack/spec@18.3.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 15:19
83d7e4a

Important

This release note is truncated. The changelog entry for @objectstack/spec@18.3.0 is
695,069 characters; the GitHub Releases API rejects any body over
125,000. The complete entry is in packages/spec/CHANGELOG.md.


Sections follow what the reader must do; each entry is placed by its own declarations (Metadata-protocol:, the Clause-②: arm, the BREAKING banner, the ADR-0087 disposition). The package CHANGELOG keeps the level grouping.

需手动处理 — action required: narrowed, with no ADR-0087 conversion

  • dbf7e04: feat(spec)!: retirement kits whose conversion retired at or below protocol 16.x leave the tree; the strict unknown-key refusal names the protocol upgrade guide

    Clause-②: no (narrowing)
    Metadata-protocol: narrowed

    BREAKING for a source or a stored sys_metadata row still carrying a key whose ≤16.x kit aged out.

    • The rule (ADR-0087, amended 2026-10-26, #25767 ruling D2 = A as scoped by ruling 6434038284). A retirement kit — the retiredKey() tombstone and its D2 conversion with fixtures — lives through the protocol major after the one it retired in and is removed when the next major is cut. At 18 that removes the kits whose conversion retired at or below 16.x; 19 repeats the move one major on. The kit ages out; the migration chain's step stays.
    • Before. Such a key met a retiredKey() tombstone: the parse refused it with the key's own prescription in the error, os migrate meta --from 16 listed and applied the mechanical edit, and a stored row in that shape was rewritten at read by the ≤16.x conversions.
    • Now. Where the tombstone sat in a strict container, the key is gone from the shape, so a strictObject refuses it as an unknown key. The refusal carries one more sentence, after its fix channels and ahead of its history sentence (RETIRED_KEY_UPGRADE_GUIDE_SENTENCE, exported with PROTOCOL_UPGRADE_GUIDE_URL from @objectstack/spec): it says that keys retired before the previous major are no longer prescribed in the error and names the protocol upgrade guide, https://objectstack.ai/docs/protocol-upgrade, where the prescription lives; the CHANGELOG of the release that retired the key carries it too. A tombstone that stays (its container strips unknown keys) ends its error with a sentence that says the edit left os migrate meta with the 16.x conversions, to be applied by hand or on a release that still carries the conversion, and links the guide.
    • The chain stays. MIGRATION_SUPPORT_FLOOR stays 16; step 17 and its entry files stay, and os migrate meta --from 16 still replays 16 → 18, listing every surviving conversion and semantic to-do. Step 17's graduated list drops the ids of the 45 conversions that aged out, so their edits are no longer applied for you.
    • Stored rows. A sys_metadata row still carrying such a spelling is no longer rewritten at read; canonicalize it once with os migrate meta --stored --apply on 18.2 or earlier, before moving to 18.3. app-hidden-to-unpublished is among the removed conversions, so a pre-split hidden: true app row is no longer rewritten to _unpublished by any seam, and the artifact door's refusal of it by id is removed as moot (@objectstack/metadata-core).
    • What left. 50 D2 conversions with retiredAfter at or below 16.x (5 at 15.0.0, 45 at 16.1.0) and their fixtures; 55 tombstones already [RETIRED] at @objectstack/spec@17.0.0 whose container is strict; 61 [RETIRED] lines of the authorable surface and their liveness-ledger rows. 58 same-age tombstones whose container strips unknown keys stay, because deleting one would turn a refusal into a silent strip; the four inline-credential refusals are not retirements and are untouched.
    • The deletion gate. check:authorable-surface proof 1 ages a tombstone one major (was two). A deleted [RETIRED] line is dated by an exact RETIRED_KEYS_BY_MAJOR row or, new, by the previous major's release tag: a line already [RETIRED] on the surface at @objectstack/spec@<current major − 1>.0.0 was retired before that cut. The tag is immutable, so neither a row a PR writes nor one it deletes moves the clock; where the tag cannot be read the gate dates by the registry alone and says so. No gate was added.
    • Who moves. No examples/**, ui/ or hotcrm source in this repository writes a removed key (the ui/ hits for the removed names are the console's own same-named props and type mirrors, not readers of the retired spec keys, per the liveness ledger's measurements). A consumer source still carrying one meets the strict refusal and the guide link at os validate.
  • b49b752: BREAKING fix(i18n)!: the translation bundle a console downloads leaves out Studio's metadataForms

    Clause-②: yes (narrowing)
    Metadata-protocol: narrowed

    GET /i18n/translations/:locale served the i18n service's whole locale tree, so every console open downloaded metadataForms, the translations of Studio's metadata-type authoring forms: 75 KB of the 221 KB raw zh-CN bundle on examples/app-crm. No browser reads that group from the bundle. Studio's forms already arrive translated, because GET /meta (and /meta/types) resolves metadataForms.<type> on the server, from the same i18n service, before it serves each form.

    The route now serves every group except the ones the server resolves itself. The spec names them in SERVER_RESOLVED_TRANSLATION_GROUPS, which today holds only metadataForms. GetTranslationsResponseSchema.translations is now ServedTranslationDataSchema: the platform face without those groups, and closed, so a body that still carries one fails it. The runtime copies the tree and never edits the service's own one, which is the tree /meta translates from. The ETag still hashes the bytes served, so a warm reopen still gets 304.

    Why this is breaking, and why minor. A response body that carries metadataForms was accepted by the published GetTranslationsResponseSchema and now fails it, the exported GetTranslationsResponse type loses that member, and the route stops serving a group it served. The published protocol records the move: the authorable surface gains the served face's keys (ServedTranslationData) and GetTranslationsResponse:requestedLocale, and the response face a client parses against is narrower, so the kind is narrowed and both packages that carry it — the spec that declares the face and the runtime that serves it — ship minor. What an author writes into a translation bundle does not move.

    What to do. Nothing, unless you read metadataForms from this route. Read it from GET /meta/types instead, which serves each form already translated into the requested locale, rather than from the bundle this route returns. Authoring is unchanged: metadataForms stays a group on every bundle face (TranslationData, PlatformTranslationData), in i18n:extract and in the i18n service.

    Also on this route: requestedLocale, which the door has always served when it fell back to another locale (zh served as zh-CN), is now declared on GetTranslationsResponseSchema, optional. It was served undeclared, so no generated client could read it.

  • b53d9f3: refactor(types,plugin-calendar,plugin-gantt,plugin-list,plugin-view)!: the flat object-calendar and object-gantt field-mapping keys leave the published schemas; producers emit the calendar / gantt block (#25923)

    Clause-②: yes (narrowing)
    Metadata-protocol: narrowed

Read more

@objectstack/skills@18.3.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 15:19
83d7e4a

Sections follow what the reader must do; each entry is placed by its own declarations (Metadata-protocol:, the Clause-②: arm, the BREAKING banner, the ADR-0087 disposition). The package CHANGELOG keeps the level grouping.

需手动处理 — action required: narrowed, with no ADR-0087 conversion

  • e3470ed: feat(security)!: current_user.queues is resolved by the platform from the ownership-queue service, and queues joins the reserved current_user keys (#27016)

    Clause-②: no (narrowing) — queues joins RESERVED_RLS_MEMBERSHIP_KEYS, so an object's membershipKeys may no longer declare a key named queues, and a registered membership resolver's queues answer is screened off. Nothing is widened past what the platform itself resolves: where no app supplied the key, a policy reading current_user.queues matched no rows before, and now it matches the caller's own queues.
    Metadata-protocol: narrowed

    BREAKING accept-set narrowing in @objectstack/spec (MembershipKeyNameSchema, read through ObjectSchema.membershipKeys) and at the membership resolver screen in @objectstack/plugin-security.

    • Now. A row-level security policy can read current_user.queues: the machine names of the ownership queues the caller belongs to in the active organization, directly or through a position, team or unit, as each queue's members define. The security plugin resolves it from the ownership-queue service's queuesOf, the same membership answer the claim gate's isMember reads, once per request and only when a policy about to compile reads the key. For example owner_id == null && queue in current_user.queues lets queue members read the unowned records sitting in their queues.
    • One source. No other source can supply the key. A registered rls-membership-resolver that answers queues has that answer dropped with a WARN, a rlsMembership bag entry or a queues field on the execution context is never read, and a context copied for another user or organization does not carry the set. With no signed-in user, no ownership-queue service, or a membership read that fails, the key stays unresolved and every policy reading it matches no rows.
    • Who moves. An object that declares membershipKeys: { queues: … }, or a membership resolver that lists queues among its keys. Census on this repository at 7eb613c: none in packages, examples, qa, skills or content.

    The remedy. Rename the membership key. queues is resolved by the platform; choose a name that says what your set holds.

  • a33fce2: feat(spec,plugin-form)!: ObjectForm lays a form out and no longer picks its container; the object-form block drops formType: 'split' and SplitForm retires, while the block keeps opening drawer and modal forms itself

    Clause-②: no (narrowing)

    Metadata-protocol: narrowed (the object-form block's formType refuses split; its splitDirection, splitSize, splitResizable and sections[].pane, the object-master-detail-form block's sections[].pane, and FormViewSchema.splitDirection, are tombstones)

    BREAKING accept-set narrowing on the object-form page block and the form view (@objectstack/spec), and a removed export (@object-ui/plugin-form). ADR-0050, the renderer half of #27155.

    The model: a form's formType says how it lays out its sections. Where it opens is the caller's. On the object-form block the caller is the block itself, so drawer and modal stay there as the two containers the block opens; split drew two panes and had no other reader, so it leaves with its renderer.

    • FROM → TO (block). formType: 'split' → formType: 'tabbed' (one tab per section) or delete the value for simple; delete each section's pane and the block's splitDirection / splitSize / splitResizable. To edit child records beside the parent, use an object-master-detail-form block. formType: 'drawer' and formType: 'modal' are unchanged. The object-master-detail-form block's parent sections share the shape, so delete each section's pane there too.
    • FROM → TO (form view). splitDirection on a form view → delete it. It joins splitSize and splitResizable, which left in the same release.
    • The one-line fix. Delete the retired value and keys. os migrate meta --from 17 lists the edits for existing sources, and --write applies them. A stored page or view row is converted when it is read (the form-view-presentation-types-removed conversion now reaches object-form page blocks, the section pane of object-master-detail-form blocks, and splitDirection too), so a block saved with split keeps loading as a simple form.
    • What the parse says. formType: 'split' is refused by name with its prescription ("'split' was removed from the object-form formType enum in @objectstack/spec 18.3.0 (ADR-0050) — …"); each tombstoned key is refused on every formType. TypeScript rejects each at the authoring site.
    • @object-ui/plugin-form. ObjectForm renders the simple, tabbed and wizard layouts only; a formType of drawer or modal on a node handed straight to <ObjectForm> now renders the inline form, and the host opens its own drawer or dialog around it (the console's New and Edit dialogs already mount ModalForm themselves). The registered object-form block routes drawer and modal through the new internal ObjectFormContainer, which hands the node to DrawerForm or ModalForm as before. SplitForm, SplitFormProps and SplitFormSchema are no longer exported, and the block no longer publishes the splitDirection, splitSize and splitResizable inputs. FROM import { SplitForm } from '@object-ui/plugin-form' → TO a TabbedForm, or the form renderer's fieldPanes for a host that composes panes itself.
    • @object-ui/types. ObjectFormSchema.formType is 'simple' | 'tabbed' | 'wizard' | 'drawer' | 'modal', and its splitDirection / splitSize / splitResizable are the spec's tombstones by reference; the section pane member is gone.
    • Console. Studio's page-block inspector no longer offers Split for an object-form (its engine.inspector.pageBlock.option.formType.split string is removed from @object-ui/i18n), the record page passes no split keys, and a view whose type is not one of the three form layouts is no longer taken for a form (@object-ui/console).
    • Supersedes one line of the form view type entry (#27155). That entry says splitDirection stays declared; in this release it leaves too.
    • Unchanged. drawerSide, drawerWidth, modalSize and modalCloseButton keep their readers: the block's drawer and modal containers. The enforce-or-remove question for drawerSide / drawerWidth on form views stays on #27153.

自动迁移 — migrated for you: narrowed, with a conversion objectstack migrate meta applies

  • a9f654e: feat!: ownership queues: records wait in a queue until a member claims them, and assignment rules route new records (ADR-0142)

    Clause-②: yes (narrowing)
    Metadata-protocol: narrowed

    BREAKING for one shape only: an object whose records have a user owner (ownership: 'user', the default) can no longer declare its own field named queue. The platform now injects a queue column on every such object, and registerObject refuses the declaration with 422 INVALID_METADATA (ObjectQueueColumnRefusedError), naming the object. An objectExtensions entry that declares queue on such an object is not refused, because an extension is folded over its object at read time: its queue is ignored, the platform's column stands, and a warning names the extension. The remedy: rename the business field (for example support_queue) and copy its data across, or, when the object's records have no user owner, declare ownership: 'business_unit', 'org' or 'none'. The upgrade guide entry is object-queue-field-reserved.

    Beside that, two enum additions: ShareRecipientType gains queue (a sharing rule can now name a queue as its recipient), and the queue approver type is authorable again (it leaves NON_AUTHORABLE_APPROVER_TYPES, so it is no longer marked deprecated). Two metadata types are new: queue and assignment_rule.

    • @objectstack/spec: new queue and assignment_rule metadata types (QueueSchema, AssignmentRuleSchema) and the stack keys queues and assignmentRules. A declared queue's members are positions. New IOwnershipQueueService contract, registered under ownership-queue. queue joins the injected system columns of ownership: 'user' objects and PUBLIC_FORM_SERVER_MANAGED_FIELDS. APPROVER_VALUE_BINDINGS.queue reads both a declared queue and an organization's sys_queue row, and APPROVER_ORG_SCOPED.queue is true. The explain rule kinds queue_membership and queue_claim are new. QUEUE_NOT_FOUND (422) is registered under plugin-security.
    • @objectstack/objectql: objects with ownership: 'user' get a system-managed queue column beside owner_id. Declaring your own queue field on one is refused at registration (INVALID_METADATA), and an extension's own queue field on one is ig...
Read more

@objectstack/setup@18.3.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 15:17
83d7e4a

Sections follow what the reader must do; each entry is placed by its own declarations (Metadata-protocol:, the Clause-②: arm, the BREAKING banner, the ADR-0087 disposition). The package CHANGELOG keeps the level grouping.

其他修复 — other fixes

Read more