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 }}
Repository navigation
console form link: a user without create right is offered a fillable create form on /forms/NAME, and learns of the refusal only on Submit #29187
Filing gate: ① user-visible defect with a named location, class b, an out-of-scope finding from the #29019 os-dev report (6573044505, PR #29184), the half of #29019's Business reason that its brief left optional · filed by domain:ui#4 · ⛔ Not a claim.
Business reason. In #29019's reproduction, a member with read but no create right opens a form link (/_console/forms/NAME), fills the whole form, and only on Submit learns that they may not create. After PR #29184 the refusal reads well, but the user has still lost their typing. The card asked that the console "ideally not offer a create form the caller cannot submit".
/apps/APP/forms/NAME renders under ConsolePermissionsScope (the pathless layout around /home and /apps/APP/* in ui/apps/console/src/App.tsx), which already holds /me/permissions.
The app-less /forms/NAME route of the reproduction sits under ProtectedRoute alone, so knowing the create right there needs a new request.
/f/SLUG is anonymous.
Doing it on one route only would split the same form's behaviour by URL.
Pick one mechanism and name it in the PR body: move /forms/NAME under ConsolePermissionsScope, or read the object's permissions on that route. One request at most, and no change to /f/SLUG.
No metadata key, no server change.
Reader.domain:ui, size S. It runs after PR #29184 lands (same file).
Dedupe. Searched FormPage create permission upfront (0) and forms route ConsolePermissionsScope (0).
Claim: PM loop round 3 · 2026-11-02T12:16Z
Session: session_01QsR2jSZFcoEaXNQkQx4KJp (card thread, os-dev); dispatched by seat session session_01QsR2jQjvWkZ7is6GXbC6DB
Account: zhuangjianguo
Branch: claude/project-thread-niy7mm (platform-assigned to the card thread; it can push only this branch)
Worktree: objectstack-issue-29187
Domain: domain:ui
Seat: domain:ui#5
File surface: ui/apps/console/src/components/FormPage.tsx, ui/apps/console/src/App.tsx (route placement only, if that is the mechanism), new FormPage tests, and a patch changeset (stop on breach; a new i18n key goes to the seat first)
Container & model: S, mode:thread, model: default tier (no Fable per the maintainer's standing rule), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: a member with read but no create right opens an authenticated form link and is offered a fillable create form | the server refuses at Submit and, since PR #29184, the refusal reads well, but the typing is lost | every member sent a form link for an object they may read but not create (or edit without update)
Thread-read: none
Premise: holds (filing body: /apps/APP/forms/NAME sits under ConsolePermissionsScope with /me/permissions in hand, while /forms/NAME at App.tsx:291 sits under ProtectedRoute alone; PR #29184 merged). Seat 4 filed it and handed it to seat 5, 12:14Z.
Serial constraints cleared: seat 4's PR #29322 (#29186) edits the same FormPage.tsx and adds form.* keys. This PR starts from main after #29322 lands, or merges main the moment it does, and merges main right before it opens. /f/SLUG does not change.
{
"issue": 29187,
"status": "done",
"branch": "claude/project-thread-niy7mm",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29407",
"head": "e8cea601",
"session": "session_01QsR2jSZFcoEaXNQkQx4KJp",
"premise_still_valid": true,
"mechanism": "move /forms/NAME under ConsolePermissionsScope (App.tsx route placement); FormPage reads resolveAffordance createFormFields / editFormFields against usePermissions()",
"summary": "The app-less /forms/NAME route is now a child of the pathless permissions layout beside /home and /apps/APP/*, so both internal form URLs read the one /auth/me/permissions answer. FormPage (internal mode) asks the affordance map's createFormFields row (create grant) for a create form and editFormFields (update grant) for ?recordId=, and when refused draws the form.noPermissionToSave sentence in a role=alert banner with no field and no Submit. /f/SLUG is unchanged. No new i18n key, no metadata key, no server change.",
"files_changed": [
".changeset/29187-form-upfront-write-refusal.md",
"ui/apps/console/src/App.tsx",
"ui/apps/console/src/components/FormPage.tsx",
"ui/apps/console/src/components/FormPage.upfrontWriteRefusal-29187.test.tsx",
"ui/apps/console/src/components/FormPage.routePermissions-29187.test.tsx"
],
"line_budget": "size S; +485/-13 at first push (5 files), of which ~370 are the two new test files; runtime diff FormPage.tsx +44, App.tsx +20/-13",
"tests": "new: FormPage.upfrontWriteRefusal-29187 8/8, FormPage.routePermissions-29187 4/4. On merged head c9f31cf0: FormPage*, InternalFormRoute*, App.* console suites + plugin-form affordanceGrantMap-12082 census = 53 files / 484 tests pass; console type-check exit 0. On final head e8cea601: console type-check exit 0 (after rebuilding @object-ui/mobile dist the merge moved), the two new files + App.homePermissions-12700 17/17. Ablation: ablation-replace swapped the guard's mode === 'internal' for a never-true compare; exactly the 4 refusal cases went red, 4 grant/public cases green; restored, git diff HEAD empty.",
"gates": "dispatch-gates --commands derived 33 on c9f31cf0; all 33 exit 0 (list: issue-29187/gates-ran.txt); --ran: 33 derived, 33 run, 0 NOT-MEASURED. Body gates on the PR body: check-changeset-no-major 0, check-adr-0087-registration 0, check-closing-keyword-parity 0, check-partof-closing-keyword 0. check-sdui-manifest 0. eslint on 4 touched files: 0 errors (warnings pre-existing). CI check-runs: in_progress at report time.",
"live": "fresh showcase + Vite source console; member via auth/admin/create-user (everyone baseline only: showcase_project read, showcase_task read+create, no edit). Before (main 5780fa49): member got fillable forms on /forms/showcase_project.form (8 inputs), /apps/showcase_app/forms/showcase_project.form and the task edit link. After: refusal sentence, 0 inputs, no Submit on all three; task create form and admin edit form unchanged; /f/contact-us unchanged with 0 permission requests. Screenshots: issue-29187/before-*.png, after-*.png.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"deviations": [
"a cold /forms/NAME deep link now also asks /me/apps (LauncherAppsProvider lives in the same scope), so the header launcher narrows as on /home; the permission check itself is the one /auth/me/permissions request. Named in Acceptance notes.",
"resolveAffordance gets no objectSchema (FormPage's object payload carries no managedBy/userActions), so the policy half reads the default bucket; grant + effective API operations decide. Named in Acceptance notes.",
"member created by create-user carries mustChangePassword; changed its password over the API before the live run.",
"first relay pr_create dispatch answered HTTP 500 (not accepted, nothing ran); the retry opened #29407, and the branch has exactly one PR."
],
"mcp_calls": "0",
"api_writes": "3 — POST /repos/objectstack-ai/objectstack/dispatches x2 (relay pr_create; first HTTP 500 not accepted), plus this post-stamped comment",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": []
}
Activity
objectstack-fleet commentedon Nov 2, 2026
domain:ui#5 · zhuangjianguo · session_01QsR2jQjvWkZ7is6GXbC6DB
Claim: PM loop round 3 · 2026-11-02T12:16Z
Session:
session_01QsR2jSZFcoEaXNQkQx4KJp(card thread, os-dev); dispatched by seat sessionsession_01QsR2jQjvWkZ7is6GXbC6DBAccount:
zhuangjianguoBranch:
claude/project-thread-niy7mm(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-29187Domain:
domain:uiSeat:
domain:ui#5File surface:
ui/apps/console/src/components/FormPage.tsx,ui/apps/console/src/App.tsx(route placement only, if that is the mechanism), new FormPage tests, and a patch changeset (stop on breach; a new i18n key goes to the seat first)Container & model: S,
mode:thread,model: default tier (no Fable per the maintainer's standing rule),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
a member with read but no create right opens an authenticated form link and is offered a fillable create form | the server refuses at Submit and, since PR #29184, the refusal reads well, but the typing is lost | every member sent a form link for an object they may read but not create (or edit without update)Thread-read: none
Premise: holds (filing body:
/apps/APP/forms/NAMEsits underConsolePermissionsScopewith/me/permissionsin hand, while/forms/NAMEatApp.tsx:291sits underProtectedRoutealone; PR #29184 merged). Seat 4 filed it and handed it to seat 5, 12:14Z.Serial constraints cleared: seat 4's PR #29322 (#29186) edits the same
FormPage.tsxand addsform.*keys. This PR starts from main after #29322 lands, or merges main the moment it does, and merges main right before it opens./f/SLUGdoes not change.objectstack-fleet commentedon Nov 2, 2026
domain:ui#5 · zhuangjianguo · session_01QsR2jSZFcoEaXNQkQx4KJp
os-dev-report
{ "issue": 29187, "status": "done", "branch": "claude/project-thread-niy7mm", "pr": "https://github.com/objectstack-ai/objectstack/pull/29407", "head": "e8cea601", "session": "session_01QsR2jSZFcoEaXNQkQx4KJp", "premise_still_valid": true, "mechanism": "move /forms/NAME under ConsolePermissionsScope (App.tsx route placement); FormPage reads resolveAffordance createFormFields / editFormFields against usePermissions()", "summary": "The app-less /forms/NAME route is now a child of the pathless permissions layout beside /home and /apps/APP/*, so both internal form URLs read the one /auth/me/permissions answer. FormPage (internal mode) asks the affordance map's createFormFields row (create grant) for a create form and editFormFields (update grant) for ?recordId=, and when refused draws the form.noPermissionToSave sentence in a role=alert banner with no field and no Submit. /f/SLUG is unchanged. No new i18n key, no metadata key, no server change.", "files_changed": [ ".changeset/29187-form-upfront-write-refusal.md", "ui/apps/console/src/App.tsx", "ui/apps/console/src/components/FormPage.tsx", "ui/apps/console/src/components/FormPage.upfrontWriteRefusal-29187.test.tsx", "ui/apps/console/src/components/FormPage.routePermissions-29187.test.tsx" ], "line_budget": "size S; +485/-13 at first push (5 files), of which ~370 are the two new test files; runtime diff FormPage.tsx +44, App.tsx +20/-13", "tests": "new: FormPage.upfrontWriteRefusal-29187 8/8, FormPage.routePermissions-29187 4/4. On merged head c9f31cf0: FormPage*, InternalFormRoute*, App.* console suites + plugin-form affordanceGrantMap-12082 census = 53 files / 484 tests pass; console type-check exit 0. On final head e8cea601: console type-check exit 0 (after rebuilding @object-ui/mobile dist the merge moved), the two new files + App.homePermissions-12700 17/17. Ablation: ablation-replace swapped the guard's mode === 'internal' for a never-true compare; exactly the 4 refusal cases went red, 4 grant/public cases green; restored, git diff HEAD empty.", "gates": "dispatch-gates --commands derived 33 on c9f31cf0; all 33 exit 0 (list: issue-29187/gates-ran.txt); --ran: 33 derived, 33 run, 0 NOT-MEASURED. Body gates on the PR body: check-changeset-no-major 0, check-adr-0087-registration 0, check-closing-keyword-parity 0, check-partof-closing-keyword 0. check-sdui-manifest 0. eslint on 4 touched files: 0 errors (warnings pre-existing). CI check-runs: in_progress at report time.", "live": "fresh showcase + Vite source console; member via auth/admin/create-user (everyone baseline only: showcase_project read, showcase_task read+create, no edit). Before (main 5780fa49): member got fillable forms on /forms/showcase_project.form (8 inputs), /apps/showcase_app/forms/showcase_project.form and the task edit link. After: refusal sentence, 0 inputs, no Submit on all three; task create form and admin edit form unchanged; /f/contact-us unchanged with 0 permission requests. Screenshots: issue-29187/before-*.png, after-*.png.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "deviations": [ "a cold /forms/NAME deep link now also asks /me/apps (LauncherAppsProvider lives in the same scope), so the header launcher narrows as on /home; the permission check itself is the one /auth/me/permissions request. Named in Acceptance notes.", "resolveAffordance gets no objectSchema (FormPage's object payload carries no managedBy/userActions), so the policy half reads the default bucket; grant + effective API operations decide. Named in Acceptance notes.", "member created by create-user carries mustChangePassword; changed its password over the API before the live run.", "first relay pr_create dispatch answered HTTP 500 (not accepted, nothing ran); the retry opened #29407, and the branch has exactly one PR." ], "mcp_calls": "0", "api_writes": "3 — POST /repos/objectstack-ai/objectstack/dispatches x2 (relay pr_create; first HTTP 500 not accepted), plus this post-stamped comment", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [] }