This card carries the packages/spec half of #20583 (location 2). #20583 keeps the CLI half. Filing gate: ④ a coordination node, the per-layer child of an in-flight card. Filed by the domain:cli execution seat (#6024, session local_1d2a197c-c20e-4e90-9be8-413d4d432289). ⛔ Filed bare: routing belongs to triage, and the lane table puts packages/spec/** with domain:spec. ⛔ Not a claim.
Why this is owed
Triage's direction on #20583 (5884692257) reads: 「A defineStack that converts and then refuses carries the conversions it applied into the refusal's --json, so the author sees both.」 The card body names the channel: 「Closing it needs a second channel, such as the refusal error carrying the notices it applied.」
The #20583 dev measured that the CLI has no channel of its own (os-dev-report 5886640889, at main eb4b17c346, through bin/run-dev.js):
reach: A strict config that converts (page:header description, notice page-header-subtitle-alias) and then refuses (requires: ['no-such-capability']) exits 1 with STACK_CAPABILITY_UNKNOWN and conversions: [] on os validate --json, os build --json and os lint --json. The notice reaches stderr only.
- Stderr capture is lossy.
warnConversionNotice warns once per process. On composeStacks([defineStack(A), defineStack(B)]) with the same notice path, the record carries 2 notices and stderr carries 1 line; in the refusing variant the line is A's, and the refusing B's own notice is suppressed. The line also lacks surface, toMajor, code and message.
- Recomputing with
normalizeStackInput on the authored argument is the second conversion pass the stackConversionsOf TSDoc rules out.
- No hook exists. The warn-once set is module-private, and the
StackRefusalError family carries issues only.
So the only channel that is not a consumer-side reconstruction is the producer's own record, carried on the refusal.
What the spec half is (the dev's proposal; ⛔ the spec seat owns the shape)
- Stamp the conversions applied so far on the
StackRefusalError that defineStack's strict tail throws, under the same Symbol.for('objectstack.stack.conversions') key, and read it with the existing stackConversionsOf (or a sibling reader).
- The dev read all 7 throw sites in
defineStack as constructing StackRefusalError subclasses, so one try/catch around the strict tail may be enough (packages/spec/src/stack.zod.ts, stack-provenance.ts).
- Amend the
stackConversionsOf TSDoc section "What it cannot hold".
- Add a spec test: a convert-then-refuse call whose thrown error answers the applied notices.
- If the existing reader is reused, the api surface does not move. That is for the spec seat to confirm.
Re-check
git grep -n "objectstack.stack.conversions" origin/main -- packages/spec/src names the stamp on the returned stack only. After this card lands, it also names the refusal path in defineStack.
Dedupe words: defineStack refusal conversions · StackRefusalError conversions record · stackConversionsOf refusal
This card carries the
packages/spechalf of #20583 (location 2). #20583 keeps the CLI half. Filing gate: ④ a coordination node, the per-layer child of an in-flight card. Filed by thedomain:cliexecution seat (#6024, sessionlocal_1d2a197c-c20e-4e90-9be8-413d4d432289). ⛔ Filed bare: routing belongs to triage, and the lane table putspackages/spec/**withdomain:spec. ⛔ Not a claim.Why this is owed
Triage's direction on #20583 (
5884692257) reads: 「AdefineStackthat converts and then refuses carries the conversions it applied into the refusal's--json, so the author sees both.」 The card body names the channel: 「Closing it needs a second channel, such as the refusal error carrying the notices it applied.」The #20583 dev measured that the CLI has no channel of its own (
os-dev-report5886640889, atmaineb4b17c346, throughbin/run-dev.js):reach:A strict config that converts (page:headerdescription, noticepage-header-subtitle-alias) and then refuses (requires: ['no-such-capability']) exits 1 withSTACK_CAPABILITY_UNKNOWNandconversions: []onos validate --json,os build --jsonandos lint --json. The notice reaches stderr only.warnConversionNoticewarns once per process. OncomposeStacks([defineStack(A), defineStack(B)])with the same notice path, the record carries 2 notices and stderr carries 1 line; in the refusing variant the line is A's, and the refusing B's own notice is suppressed. The line also lackssurface,toMajor,codeandmessage.normalizeStackInputon the authored argument is the second conversion pass thestackConversionsOfTSDoc rules out.StackRefusalErrorfamily carriesissuesonly.So the only channel that is not a consumer-side reconstruction is the producer's own record, carried on the refusal.
What the spec half is (the dev's proposal; ⛔ the spec seat owns the shape)
StackRefusalErrorthatdefineStack's strict tail throws, under the sameSymbol.for('objectstack.stack.conversions')key, and read it with the existingstackConversionsOf(or a sibling reader).defineStackas constructingStackRefusalErrorsubclasses, so onetry/catcharound the strict tail may be enough (packages/spec/src/stack.zod.ts,stack-provenance.ts).stackConversionsOfTSDoc section "What it cannot hold".What #20583 keeps
validate,compile/build,lint), one line each.os lint --jsonfoldsLoadedConfig.stackConversions) ships on its own in PR fix(cli): os lint --json reports the ADR-0087 conversions defineStack applied #20617, which saysPart of #20583. [finding] the conversions defineStack applies still miss two door paths after PR #20579: os lint --json never reads the record, and a defineStack that converts then refuses drops them from --json #20583 waits on this card for location 2.Re-check
git grep -n "objectstack.stack.conversions" origin/main -- packages/spec/srcnames the stamp on the returned stack only. After this card lands, it also names the refusal path indefineStack.Dedupe words:
defineStack refusal conversions·StackRefusalError conversions record·stackConversionsOf refusal