Filing gate: ③ a maintainer-ruled task. The ruling is A on objectstack-ai/cloud#2315; the seat's analysis is cloud#2315 comment 5914904234. Recorded verbatim on that card. The maintainer's word was 「其他同意」: agreement to the recommended letter, given in the repo:cloud seat's session (session_01YSygzqVmrY31BnZge8KDo6) on 2026-10-01, with the decision batch presented in the director's format.
- Who acts: triage routes it. It is a spec contract change, so the
domain:spec lane is the expected home, with the client type in the same PR or a paired one.
- Dedup:
search_issues on objectstack for "action outcomeMessages outcome key success message interpolation result": 0 hits.
The ruling it executes
Ruling B on cloud#2315 (5731313414), not reopened: 「server returns facts, the console composes the message in the user's locale」. Ruling A on the mechanism: metadata-declared outcome copy. Three repos land it in order:
- this card (spec and client);
- objectui's renderer;
- cloud's three actions, LAST.
What to build (at objectstack main)
ActionSchema (packages/spec/src/ui/action.zod.ts): a new optional outcomeMessages, a map from a snake_case outcome key to an I18nLabel, valid for type: api | script.
- The handler's answer carries a closed
outcome string, and the console picks outcomeMessages[outcome]. Failing that, it uses successMessage, then the runner default.
successMessage (today a static I18nLabelSchema, around :1305) and each outcome message may interpolate ${result.*}. That is the scope onSuccess.navigate already declares in the same file, so there is no new dialect.
- It joins strict schema and the liveness ledger like any new key. Translation bundles carry the outcome copy beside
successMessage.
@objectstack/client (packages/client/src/index.ts): the environments.delete archive answer stops declaring message: string as REQUIRED (around :3165). It gains the outcome fact instead (archived | already_archived | purge_deferred | destroyed).
Why this shape (the measured pull)
ActionRunner composes dyn || action.successMessage || default (objectui packages/core/src/actions/ActionRunner.ts, around :1472). successMessage is static, so it cannot say which of delete's four outcomes happened. Three of those four destroy nothing, and cloud's sys-environment.object.ts and environment-delete-action.test.ts hold the invariant that a user can tell an archive from a destruction.
check_app_updates has three outcomes: updates_available | up_to_date | none_installed.
- At least 6 more server-built English success messages exist in cloud routes (
package-install.ts, package-review.ts) that the same mechanism serves.
Acceptance
- The schema accepts
outcomeMessages on api and script actions. It refuses an unknown shape loudly, and the liveness row is live (the console reads it).
- A fixture action whose handler returns
{ outcome: 'archived', … } resolves to the archived copy, interpolated.
- The client type no longer requires
message.
- The changeset names the new key. Clause-② yes: the public spec surface widens, so a contract-review record is owed.
Related
objectstack-ai/cloud#2315 (blocked on this card and on the objectui renderer card) · objectstack-ai/cloud#2435 (where export_package already dropped its message, PR objectstack-ai/cloud#2514).
Generated by Claude Code
Filing gate: ③ a maintainer-ruled task. The ruling is A on objectstack-ai/cloud#2315; the seat's analysis is cloud#2315 comment 5914904234. Recorded verbatim on that card. The maintainer's word was 「其他同意」: agreement to the recommended letter, given in the
repo:cloudseat's session (session_01YSygzqVmrY31BnZge8KDo6) on 2026-10-01, with the decision batch presented in the director's format.domain:speclane is the expected home, with the client type in the same PR or a paired one.search_issueson objectstack for "action outcomeMessages outcome key success message interpolation result": 0 hits.The ruling it executes
Ruling B on cloud#2315 (5731313414), not reopened: 「server returns facts, the console composes the message in the user's locale」. Ruling A on the mechanism: metadata-declared outcome copy. Three repos land it in order:
What to build (at objectstack
main)ActionSchema(packages/spec/src/ui/action.zod.ts): a new optionaloutcomeMessages, a map from a snake_case outcome key to anI18nLabel, valid fortype: api | script.outcomestring, and the console picksoutcomeMessages[outcome]. Failing that, it usessuccessMessage, then the runner default.successMessage(today a staticI18nLabelSchema, around :1305) and each outcome message may interpolate${result.*}. That is the scopeonSuccess.navigatealready declares in the same file, so there is no new dialect.successMessage.@objectstack/client(packages/client/src/index.ts): theenvironments.deletearchive answer stops declaringmessage: stringas REQUIRED (around :3165). It gains theoutcomefact instead (archived | already_archived | purge_deferred | destroyed).Why this shape (the measured pull)
ActionRunnercomposesdyn || action.successMessage || default(objectuipackages/core/src/actions/ActionRunner.ts, around :1472).successMessageis static, so it cannot say which of delete's four outcomes happened. Three of those four destroy nothing, and cloud'ssys-environment.object.tsandenvironment-delete-action.test.tshold the invariant that a user can tell an archive from a destruction.check_app_updateshas three outcomes:updates_available | up_to_date | none_installed.package-install.ts,package-review.ts) that the same mechanism serves.Acceptance
outcomeMessageson api and script actions. It refuses an unknown shape loudly, and the liveness row is live (the console reads it).{ outcome: 'archived', … }resolves to the archived copy, interpolated.message.Related
objectstack-ai/cloud#2315 (blocked on this card and on the objectui renderer card) · objectstack-ai/cloud#2435 (where
export_packagealready dropped itsmessage, PR objectstack-ai/cloud#2514).Generated by Claude Code