You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
console: FormPage's post-submit redirect uses the authored URL verbatim — undecided whether it is a console route or an arbitrary destination #4190
Found by the window.location.assign / window.location.href = sweep that objectui#4181 asked for ("the set may be larger"). Filed unassigned, observation-class — see the reachability note before grading this.
What
apps/console/src/components/FormPage.tsx:574, the submitBehavior.kind === 'redirect' branch:
behavior.url comes straight from the form's authored submitBehavior metadata and is used verbatim. location.assign bypasses React Router's basename, so on a console served under a mount (the framework CLI configures one for every embedded deployment) an authored console-relative path such as /objects/lead resolves to the ORIGIN root and leaves the SPA — the same class objectui#4181 fixed in SetupPage.
Why this is NOT simply "route it through withConsoleBase()"
That is the fix objectui#4181 applied to the three auth surfaces, and it does not transfer here. withConsoleBase() prefixes anything that does not begin with /_, so an absolute target would be mangled rather than fixed:
So the real question is upstream of the code: what is submitBehavior.url declared to mean?
If it is an arbitrary destination, today's behaviour is correct for the absolute case and the relative case is an authoring error the spec should reject or normalize.
If it is a console route, it needs the mount prefix — and the absolute case needs to be either rejected at publish time or explicitly allowed through a same-origin check.
Either way the fix wants a same-origin guard rather than an unconditional prefix, which is a contract decision, not a one-line patch. Worth noting the surface is reachable anonymously: mode: 'public' forms render at /f/:slug for visitors with no session, and an external thank-you page is a genuinely plausible authored intent there — which is exactly why guessing the semantics would be wrong.
Reachability — why finding and not a graded bug
It needs the intersection of two conditions, and neither is confirmed to exist today:
a console served under a mount (invisible on a bare / deployment, where prefixed and unprefixed spellings coincide — the same blind spot that hid objectui#4181); and
a form authored with a console-relative submitBehavior.url.
No such form was found in this repo's examples while sweeping, so nothing is known to hit it. Recording it rather than sitting on it, per filing discipline; the semantics question is the useful half and it survives regardless of whether any form triggers the bug today.
The rest of the sweep was clean
For the record, so the next reader does not repeat it — every other full-page navigation producer in apps/console is correct as written:
pages/auth/LoginPage.tsx (3 sites), pages/auth/RegisterPage.tsx (3), pages/auth/SetupPage.tsx (2) — all route through the shared console-mount helper as of objectui#4181.
pages/auth/OAuthConsentPage.tsx:109 and pages/auth/followAuthorize.ts:30,45 — OAuth redirect_uri / authorize targets, external and server-absolute by design. Prefixing these would break them.
pages/system/ApprovalsInboxPage.tsx:512 — a signed download URL, already prefixed with the SERVER base when relative and passed through when absolute. Storage URL, not an SPA route.
Anchors verified live: apps/console/src/components/FormPage.tsx:574 is setTimeout(() => window.location.assign(behavior.url), delay) in the redirect branch, with the union mirrored from spec at :57-60 and read at :556; and the contrast the card draws is real — SetupPage.tsx routes through withConsoleBase (imported :27, with the rationale docblock at :33/:54) while this site does not.
Held, for the card's own reason and one more. The card's: reachability needs a mounted console and a console-relative authored submitBehavior.url, and no such form exists in-repo, so nothing is known to hit it. The additional one: the useful half is not a patch at all — it is what submitBehavior.url is declared to mean, and that declaration lives in packages/spec (FormView.submitBehavior), i.e. in the objectstack contract, not in this repo. Promoting it here would dispatch a console fix whose correct shape is not yet decided; withConsoleBase unconditionally is demonstrably wrong for the absolute case the card shows.
Exit path, recorded so the next round does not re-derive it: the disposition is a spec ruling (arbitrary destination vs console route, plus whether a same-origin guard is required), which belongs to the domain:spec seat in objectstack. If this stays held through another cadence pass, the right escalation is a decision card filed there, with this card as the consumer-side evidence — not a needs-user-decision label here, which would put the question in front of a seat that cannot answer it.
Sweep note kept for the record: the card's clean-sweep list (auth pages via the mount helper, OAuth targets external by design, the signed download URL) is the useful negative result — the next reader should not repeat that sweep.
Maintainer ruling — 2026-08-11. From the four-lens decision review (platform long-term coherence / measured business pull / AI-agent error-resistance / startup scope discipline); the maintainer accepted the recommendation set in full.
Cross-seat transfer per protocol (ruled today): the exit of this card is a packages/spec semantics ruling on submitBehavior.url, which belongs to the spec seat — a decision card has been filed in objectstack (domain:spec + needs-user-decision, linked below in this thread once created). This card waits on that ruling; the finding label stays until the upstream card is decided and the consumer-side work (if any) can be scoped.
Recorded by the triage seat Routine (#5474 pilot) on the maintainer's direct instruction — ruling record, not a claim.
Follow-up to the transfer above: the spec decision card is objectstack-ai/objectstack#7496 (domain:spec, needs-user-decision). This card waits on it — treat "objectstack#7496 decided" as this finding's restart condition.
Recorded by the triage seat Routine (#5474 pilot) on the maintainer's direct instruction — ruling record, not a claim.
Findings triage (cadence re-check): HOLD — stays finding, blocked on the upstream ruling.
Premise re-verified on origin/main @ 6d8231c: the verbatim redirect is still live — apps/console/src/components/FormPage.tsx:1018setTimeout(() => window.location.assign(behavior.url), delay) (drifted from the card's cited :574; same code). The card's core point stands: withConsoleBase() does not transfer here because the authored URL's intent (console route vs. arbitrary destination) is undecided.
That intent question is exactly objectstack#7497-adjacent decision objectstack#7692's sibling — the open decision card objectstack#7496 ("what does an authored submitBehavior.url mean") — and this card is its named downstream dependent (the ④-flag pair on the seat post). Nothing to do here until that ruling lands; when it does, the executing card inherits the decided semantics.
Correction to the previous comment (same round): upstream objectstack#7496 is no longer an open decision — it has been ruled and is now pm:dispatched in the spec lane. The hold verdict is unchanged, but the restart condition sharpens: re-grade this card when objectstack#7496's implementing PR merges — at that point the authored-URL semantics are decided and this card either queues with them inherited or closes as absorbed.
Findings triage: promoted — finding → pm:queue. The upstream ruling this card dead-ended on has landed: objectstack-ai/objectstack#7496 closed 12:54Z today via merged PR objectstack-ai/objectstack#7657 — submitBehavior.url is ruled relative-only, declared-field interpolation, URL-escaped, spec-side enforcement included. The "undecided whether it is a console route or an arbitrary destination" premise is therefore resolved: arbitrary absolute destinations are out of contract.
File face re-verified at the unlock (per the merged-ref re-check discipline): on objectui origin/main @ b16be40, apps/console/src/components/FormPage.tsx:1102 still executes window.location.assign(behavior.url) verbatim in the redirect case — the consumer-side hardening (treat the ruled relative-only URL as an in-shell route / refuse out-of-contract values loudly) has not been done by the spec PR (objectstack-side only) and no objectui PR in flight covers it (PR search: only closed #4279, the #4109 nesting work). Scope for the dev: align the redirect arm with the #7496 ruling against the current spec pin, and pin the contract with a parse of the emitted value.
Note for the objectui seat: the spec pin must cover PR #7657's schema change before dispatch (pin-staleness check at dispatch time, per the pin-lag rule).
PM dispatch-time pin-staleness check (session session_017Qqyix2QcnpUC9XeYVDzx3): ⛔ HELD — the precondition the promotion named is not yet met.
The triage promotion above requires the @objectstack/spec pin to cover objectstack#7657's schema change before dispatch. Checked: this repo's pin is 17.0.0-rc.6, which predates #7657 (merged 2026-08-11 12:54Z), and the pin has not been bumped since — #4535's residue is explicitly queued behind the same bump. Implementing the redirect hardening against a pin that lacks the ruled submitBehavior.url shape would pin the wrong contract (the pin-lag rule this repo already enforces).
Restart condition, sharpened: dispatch when the spec pin lands at a version containing #7657's FormView.submitBehavior enforcement — the same unlock event #4535-item-4 waits on, so the two should be checked together at that bump. Scope then inherits the ruling as promoted: the redirect arm treats the URL as an in-shell relative route (router navigation under the console mount, not location.assign verbatim), refuses out-of-contract absolute values loudly, and pins the contract with a parse of the emitted value.
Not a claim; recorded so the next pass reads one comment instead of re-deriving the check.
Activity
claude commentedon Aug 11, 2026
Findings triage (first grade; premise check @
origin/maine1ade8f): HOLD —findingstands.Anchors verified live:
apps/console/src/components/FormPage.tsx:574issetTimeout(() => window.location.assign(behavior.url), delay)in the redirect branch, with the union mirrored from spec at:57-60and read at:556; and the contrast the card draws is real —SetupPage.tsxroutes throughwithConsoleBase(imported:27, with the rationale docblock at:33/:54) while this site does not.Held, for the card's own reason and one more. The card's: reachability needs a mounted console and a console-relative authored
submitBehavior.url, and no such form exists in-repo, so nothing is known to hit it. The additional one: the useful half is not a patch at all — it is whatsubmitBehavior.urlis declared to mean, and that declaration lives inpackages/spec(FormView.submitBehavior), i.e. in the objectstack contract, not in this repo. Promoting it here would dispatch a console fix whose correct shape is not yet decided;withConsoleBaseunconditionally is demonstrably wrong for the absolute case the card shows.Exit path, recorded so the next round does not re-derive it: the disposition is a spec ruling (arbitrary destination vs console route, plus whether a same-origin guard is required), which belongs to the
domain:specseat in objectstack. If this stays held through another cadence pass, the right escalation is a decision card filed there, with this card as the consumer-side evidence — not aneeds-user-decisionlabel here, which would put the question in front of a seat that cannot answer it.Sweep note kept for the record: the card's clean-sweep list (auth pages via the mount helper, OAuth targets external by design, the signed download URL) is the useful negative result — the next reader should not repeat that sweep.
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
claude commentedon Aug 11, 2026
Maintainer ruling — 2026-08-11. From the four-lens decision review (platform long-term coherence / measured business pull / AI-agent error-resistance / startup scope discipline); the maintainer accepted the recommendation set in full.
Cross-seat transfer per protocol (ruled today): the exit of this card is a
packages/specsemantics ruling onsubmitBehavior.url, which belongs to the spec seat — a decision card has been filed in objectstack (domain:spec+needs-user-decision, linked below in this thread once created). This card waits on that ruling; the finding label stays until the upstream card is decided and the consumer-side work (if any) can be scoped.Recorded by the triage seat Routine (#5474 pilot) on the maintainer's direct instruction — ruling record, not a claim.
Generated by Claude Code
claude commentedon Aug 11, 2026
Follow-up to the transfer above: the spec decision card is objectstack-ai/objectstack#7496 (
domain:spec,needs-user-decision). This card waits on it — treat "objectstack#7496 decided" as this finding's restart condition.Recorded by the triage seat Routine (#5474 pilot) on the maintainer's direct instruction — ruling record, not a claim.
Generated by Claude Code
os-zhuang commentedon Aug 11, 2026
Findings triage (cadence re-check): HOLD — stays
finding, blocked on the upstream ruling.Premise re-verified on
origin/main@6d8231c: the verbatim redirect is still live —apps/console/src/components/FormPage.tsx:1018setTimeout(() => window.location.assign(behavior.url), delay)(drifted from the card's cited :574; same code). The card's core point stands:withConsoleBase()does not transfer here because the authored URL's intent (console route vs. arbitrary destination) is undecided.That intent question is exactly objectstack#7497-adjacent decision objectstack#7692's sibling — the open decision card objectstack#7496 ("what does an authored
submitBehavior.urlmean") — and this card is its named downstream dependent (the ④-flag pair on the seat post). Nothing to do here until that ruling lands; when it does, the executing card inherits the decided semantics.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 11, 2026
Correction to the previous comment (same round): upstream objectstack#7496 is no longer an open decision — it has been ruled and is now
pm:dispatchedin the spec lane. The hold verdict is unchanged, but the restart condition sharpens: re-grade this card when objectstack#7496's implementing PR merges — at that point the authored-URL semantics are decided and this card either queues with them inherited or closes as absorbed.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 11, 2026
Findings triage: promoted —
finding→pm:queue. The upstream ruling this card dead-ended on has landed: objectstack-ai/objectstack#7496 closed 12:54Z today via merged PR objectstack-ai/objectstack#7657 —submitBehavior.urlis ruled relative-only, declared-field interpolation, URL-escaped, spec-side enforcement included. The "undecided whether it is a console route or an arbitrary destination" premise is therefore resolved: arbitrary absolute destinations are out of contract.File face re-verified at the unlock (per the merged-ref re-check discipline): on objectui
origin/main@b16be40,apps/console/src/components/FormPage.tsx:1102still executeswindow.location.assign(behavior.url)verbatim in theredirectcase — the consumer-side hardening (treat the ruled relative-only URL as an in-shell route / refuse out-of-contract values loudly) has not been done by the spec PR (objectstack-side only) and no objectui PR in flight covers it (PR search: only closed #4279, the #4109 nesting work). Scope for the dev: align theredirectarm with the #7496 ruling against the current spec pin, and pin the contract with a parse of the emitted value.Note for the objectui seat: the spec pin must cover PR #7657's schema change before dispatch (pin-staleness check at dispatch time, per the pin-lag rule).
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
yinlianghui commentedon Aug 13, 2026
PM dispatch-time pin-staleness check (session
session_017Qqyix2QcnpUC9XeYVDzx3): ⛔ HELD — the precondition the promotion named is not yet met.The triage promotion above requires the
@objectstack/specpin to cover objectstack#7657's schema change before dispatch. Checked: this repo's pin is17.0.0-rc.6, which predates #7657 (merged 2026-08-11 12:54Z), and the pin has not been bumped since — #4535's residue is explicitly queued behind the same bump. Implementing the redirect hardening against a pin that lacks the ruledsubmitBehavior.urlshape would pin the wrong contract (the pin-lag rule this repo already enforces).Restart condition, sharpened: dispatch when the spec pin lands at a version containing #7657's
FormView.submitBehaviorenforcement — the same unlock event #4535-item-4 waits on, so the two should be checked together at that bump. Scope then inherits the ruling as promoted: theredirectarm treats the URL as an in-shell relative route (router navigation under the console mount, notlocation.assignverbatim), refuses out-of-contract absolute values loudly, and pins the contract with a parse of the emitted value.Not a claim; recorded so the next pass reads one comment instead of re-deriving the check.
Generated by Claude Code
Generated by Claude Code
yinlianghui commentedon Aug 17, 2026
标签矫正(objectui 车道 PM,会话
session_01GTRjn8xBqp75dk7kFupVRt):补挂pm:blocked,与上一条 ⛔ HELD 评论配对成机器可读半边 —— 裸pm:queue会让每轮选批都重付一次「读链才知不可派」的成本。Blocked-by:
@objectstack/specpin bump(本仓 pin17.0.0-rc.6未覆盖 objectstack#7657 的FormView.submitBehavior裁定形状;载具为 #4636 / PR #4639 一族)。解锁判据不变:pin 落到含 #7657 schema 的版本即派,与 #4535-item-4、#4016/#4017 同一解锁事件,届时一并复核。Generated by Claude Code
yinlianghui commentedon Aug 17, 2026
解锁(objectui 车道 PM,会话
session_01GTRjn8xBqp75dk7kFupVRt):pm:blocked摘除,回pm:queue。解锁事件 = PR #4953 将@objectstack/specpin 升到 17.0.0 GA(已落 main),覆盖 os#7496 裁定的FormView.submitBehavior形状(os#7657,08-11 落 main)。按晋级评论既定 scope 派发:redirect臂按裁定当 in-shell 相对路由走 router 导航、越契约绝对值响亮拒绝、以发射值解析钉契约;与 #4535-item-4 同解锁事件,选批时一并复核。下批可派。Generated by Claude Code
yinlianghui commentedon Aug 17, 2026
认领(objectui 车道分片 PM,会话
session_01GTRjn8xBqp75dk7kFupVRt,批次 20):branchclaude/issue-4190-submit-redirect-contract。GA pin 解锁后首派,按晋级评论既定 scope:FormPage的redirect臂按 os#7496 裁定把 URL 当 in-shell 相对路由走 router 导航、越契约绝对值响亮拒绝、以发射值解析钉契约。Generated by Claude Code
refactor(console): 拒绝原因随 refused 标志走,不再借 error 通道 (#4190)
docs(console): 记下 client-side spec 校验的仓内先例与实测为零的打包成本 (#4190)
fix(console): FormPage 的 redirect 按 os#7496 裁定当 in-shell 相对路由,越契约目标响亮…