Repository navigation
public forms: sharing.publicLink reads as the "Generated public share URL" but is only a slug — the authored path (/forms/contact-us) answers 404, and the real anonymous URL (/_console/f/<slug>) is shown to the author nowhere #22079
Description
Activity
objectstack-fleet commented on Oct 7, 2026
Path: ② the capabilities an end user meets in the app — public forms | 缺项 | P2
Triage: first grade, bug · priority:p2 · domain:cli · area:access · pm:queue. Direction: the server answers the authored /forms/<slug> with a redirect to the console's /f/<slug>, and the describe says the value is a slug
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-07T12:56Z. ⛔ Not a claim, ⛔ not a dispatch.
Triage: lands in the runtime's HTTP routing (the server mount that today answers ENDPOINT_NOT_FOUND) plus the one describe in packages/spec/src/ui/sharing.zod.ts ⇒ domain:cli; rationale: the visitor-facing defect is the server's 404. The describe line rides the same PR.
- Verified on
main(e67ba80049):publicLink: z.string().optional().describe('Generated public share URL')(sharing.zod.ts:98);publicFormSlug()(now atpackages/spec/src/ui/anonymous-form-intake.ts:69) strips a leading/andforms/, so/forms/xis a sanctioned spelling of slugx;- the conversion fixtures author
publicLink: '/forms/contact'.
- Why p2: an anonymous visitor opening the link the app declares gets a 404 (measured on 17.7.0). The form itself works at another URL.
- Direction (ruled here):
- The redirect.
GET /forms/<slug>redirects to the console's/f/<slug>only when an enabled, anonymous public form declares that slug. Every other/forms/*request keeps today's 404. That reveals nothing/f/<slug>does not. - ⛔ No root-level
/<slug>catch-all. - The describe says the value is a slug, and names the URL it is served at.
- ⛔ The signed-in console route
/_console/forms/:nameis untouched.
- The redirect.
- Pins:
- an anonymous
GET /forms/<slug>for an enabled public form redirects, and the form loads and accepts a submission; - a disabled form, a non-anonymous form and an unknown slug each answer 404 as today;
- a signed-in
/_console/forms/<name>is unchanged (control).
- an anonymous
Clause-②: yes(widening: a new answered path on the public door). A contract review is owed at the PR.- A foreseen follow-up: Studio and the form view's share panel show the real anonymous URL. That is objectui's lane, and triage files it as its own card.
objectstack-fleet commented on Oct 7, 2026
Claim: PM loop round 4
Session: session_01RWZbGvPFcRKvUqASZtunCU
Account: os-warren (the seat's linked user as get_me answers it; the card's assignee)
Branch: claude/issue-22079-forms-slug-redirect
Worktree: objectstack-issue-22079
Domain: domain:cli
Seat: domain:cli#1
File surface, per triage 6038401972 (the redirect half), read on origin/main 3d918850:
packages/cli/src/utils/console.ts: the console plugin's route registration. One anonymousGET /forms/:slugredirects to the console's/f/<slug>(underCONSOLE_PATH,:54), only when an enabled, anonymous public form declares that slug. Every other/forms/*request keeps today's 404.packages/cli/src/utils/console-route-ledger.ts: that route's row.packages/cli/src/commands/serve.ts: the console mount region, only if the mount needs a dependency handed in.packages/rest/src/rest-server.ts:registerFormEndpoints(:10726) and its slug resolution. Only to let the redirect ask the same decision the anonymous doors make (one decision point); no door's answer changes.- Pins (new dogfood file under
packages/qa/dogfood/test/, booting in its own temp directory):- an anonymous
GET /forms/<slug>for an enabled public form redirects, and the form loads and accepts a submission; - a disabled form, a non-anonymous form and an unknown slug each answer 404 as today;
- a signed-in
/_console/forms/<name>is unchanged (control).
- an anonymous
.changeset/*.mdfor@objectstack/cli, with the semver the widening takes.- Added at review (PR feat(cli): the authored public form path /forms/:slug redirects to the console form page when the anonymous door serves it #22098), amended in place 2026-10-07T15:36Z:
packages/cli/src/utils/console.public-form-redirect.test.ts(the unit pins beside the plugin); the@objectstack/dogfoodentry ofscripts/cross-package-test-inputs.mjsand itsturbo.json@objectstack/dogfood#testinput, each namingpackages/cli/src/utils/console.ts. That is the remedycheck:cross-package-test-inputsprescribes for the dogfood pin's source import. Both rows are adomain:clipackage's own test-input declaration, the shape5975509708declared to this seat for apackages/clientrow. Nothing else moves. - ⛔ Not in this claim: the
publicLinkdescribe (packages/spec/src/ui/sharing.zod.ts:98, triage's second item).packages/specalways belongs to thedomain:specseat, whoever needs it (references/lanes/cli.md:12,lanes/spec.md:12,seat-lifecycle.md:47). The describe should also name the URL this PR makes answer. So the PR carriesRefs #22079, notFixes. At landing the seat releases this card with aRelease:line andpm:retriage, naming the describe half for the spec lane. - ⛔ No root
/<slug>catch-all. ⛔/_console/forms/:nameuntouched. ⛔ No new error code. (Stop on breach and explain in the report.)
Container & model:M,mode:subagent,model: default (opus).dispatch-gates --tierover the path gives no path-derived mandate. The contract review is owed atCONTRACT_REVIEW_TIER, run by an independent subagent.
Clause-②: yes - Widening: a new answered path on the public door (triage
6038401972): an anonymousGET /forms/<slug>answers a redirect where it answered 404.
Responsibility:platform code: no server route answers the authored /forms/<slug>, while the console serves the form at /_console/f/<slug> (utils/console.ts mount)|none: nothing redirects the authored path or names the served URL to the author|an anonymous visitor opening the link an app declares for its public form; hotcrm’s Web-to-Lead form on 17.7.0, measured
Thread-read: 6038401972
Serial constraints cleared: read 2026-10-07T13:34Z: - Open PRs (2 read, each file list read by
filename): neither touchesutils/console.ts,console-route-ledger.ts,serve.tsorrest-server.ts. chore: version packages #21988 is the release PR; feat(spec)!: the build doors refuse a builtin node config value its executor contract refuses, with its location #21974 holdsdomain:specandservice-automationfiles. - No in-flight claim of any lane names these files.
- This seat's same-round siblings: cli: app branding assets are served only from
<cwd>/assets(orOS_RUNTIME_ASSETS_DIR) and silently skipped when that directory is absent — an artifact boot from any other directory draws a broken logo and favicon, with no warning #22071 holdscreateRuntimeAssetsPlugininutils/console.tsand the assets block ofserve.ts, and boot output: the "schedule trigger is NOT bound" sentence (~600 chars) prints twice for every scheduled flow — 16 of an 8-flow app's boot lines — and the loopback OAuth-over-HTTP warning fires on every localhost boot; one summary line per warning class #22073 the boot-diagnostics call site ofserve.ts. Those are different regions of paths outsideSINGLE_CLAIM_PATHS, so this is ordinary concurrency.
domain:cli seat · session_01RWZbGvPFcRKvUqASZtunCU · 2026-10-07T13:34Z
objectstack-fleet commented on Oct 7, 2026
os-dev-report
{
"issue": 22079,
"status": "done",
"branch": "claude/issue-22079-forms-slug-redirect",
"pr": "#22098",
"session": "session_01RWZbGvPFcRKvUqASZtunCU",
"premise_still_valid": true,
"summary": "The console static plugin (packages/cli/src/utils/console.ts) now mounts GET /forms/:slug: it re-addresses the visitor's own request to the anonymous form door GET /api/v1/forms/SLUG, dispatches it in-process through the same Hono app (same headers, connection and middleware), and answers 302 to /_console/f/SLUG only on the door's 200; anything else calls next() and gets the adapter's unchanged ENDPOINT_NOT_FOUND 404, so the door's one decision (switches, withdrawal layers, organization read, posture) is asked, not copied, and packages/rest is untouched. It mounts only with the console, carries a console-route-ledger row (disposition static-asset: the ledger type defines it as serving the bundle or redirecting to it, and the route's peers are the GET / and GET /_console redirects, which falsifies the reading 'a redirect is not static-asset'), and pins the triage cases in a unit file and a showcase dogfood file, including the walled-posture case where the door withholds the published form and the redirect follows it. PR body line 1 is the ordered Refs line (spelled /forms/SLUG for the sanitizer), not Fixes; the publicLink describe is left to the spec lane. Card assignee untouched; PR assignee os-warren.",
"tests": "All at head 44f007c unless named. (1) cli unit: pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 src/utils/console.public-form-redirect.test.ts src/utils/console-route-ledger.conformance.test.ts -> 'Test Files 2 passed (2) / Tests 42 passed (42)'; whole unit tier at 98168b7 (later commits touch only turbo.json and scripts/) -> 'Test Files 260 passed (260) / Tests 3810 passed (3810)'; integration tier declared to CI (no integration file or spawn entry touched). (2) dogfood: vitest run --reporter=verbose test/showcase-public-form-redirect.dogfood.test.ts -> 8/8 passed; shard OS_TEST_SHARD=3/3 (holds the file by vitest's own sha1 shard rule over 220 files, 74/73/73; the shard log prints no per-file lines, so membership is computed) -> 'Test Files 72 passed | 1 skipped (73) / Tests 654 passed | 8 skipped (662)'. (3) typecheck: pnpm --filter @objectstack/cli run typecheck (tsc + check:test-typecheck OK) and pnpm --filter @objectstack/dogfood run typecheck exit 0 (first run caught two test-typing errors, fixed in 98168b7). (4) ablations via scripts/ablation-replace.mjs (anchor 1->0, blob changed, restored to the HEAD blob with git diff HEAD empty; subject imported as source, no build in between): M1 redirect removed (= origin/main behaviour, H1 reproduced) -> dogfood 3 failed | 5 passed, 'expected 404 to be 302'; M2 redirect regardless of the door -> 4 failed | 4 passed (unknown, disabled, non-anonymous, walled all 302 where the unrouted 404 was expected); M3 ledger row renamed -> conformance 2 failed | 16 passed. (5) gates: dispatched list re-derived for the 7 actual paths (+18 families) and run at 44f007c -> 'dispatch-gates --ran: 87 derived famil(ies) accounted for - 87 run, 0 NOT-MEASURED', all exit 0; plus check:authz-resolver, check:cli-test-child-env, check:route-envelope exit 0. check:cross-package-test-inputs was red on the first battery (the dogfood source import undeclared) and green after the declaration; check:dual-build-cjs-loads was PREREQUISITE NOT MET (exit 3) on the first battery and measured green on the final one (106 entry points / 66 packages). Registry importers' self-tests: check-cross-package-test-inputs 255 cases, check-ci-filter-parity --self-test 131 assertions, exit 0. (6) lint: full pnpm lint at 44f007c exit 0, no output; the 5 changed source files by name with --format json: 5 files, 0 errors, 0 warnings, none ignored.",
"mcp_calls": "0 - no MCP GitHub tool called",
"api_writes": "4 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches), each executed by the fleet-write workflow as objectstack-fleet[bot]: (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (draft #22098; read-back 13726 bytes sent = stored); (2) label-write --assign os-warren -> POST /repos//issues/22098/assignees (read-back matches; labels documentation/size/l/tests/tooling were added by another actor, not this write); (3) post-stamped request fw-20261007T150716Z-77308c for this report: accepted 204, but its run never started (fleet-write runs created 15:07-15:16Z, e.g. 37642359462, sat queued with zero jobs for 25+ minutes while later runs executed); post-stamped then fell back on its own to the direct route, which answered POST /repos//issues/22079/comments -> HTTP 500 and wrote nothing (no comment on the card, read back); (4) this retry of the same report -> POST /repos//issues/22079/comments. If the stuck run ever executes, a second os-dev-report from request (3) lands, older text, same substance. Plus git push of the branch (not a REST write).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: the spec lane, via the seat's Release line on #22079 · packages/spec/src/ui/sharing.zod.ts publicLink describe still reads 'Generated public share URL'; it should say the value is a slug and name /forms/SLUG (now redirected to /_console/f/SLUG) · noted, not filed",
"carrier: the PR that lands that describe, else 承接者:无 · content/docs/ui/forms.mdx (mode table) and content/docs/ui/public-data-collection.mdx (section 1) name the API endpoints but not the link a visitor opens · noted, not filed",
"carrier: the next checklist-author sweep · docs/qa/platform-checklist/areas/api-backend.json calls the console ledger 'the four static-asset rows'; it holds five now · noted, not filed",
"carrier: 承接者:无 · with an inbound rate-limit budget armed, a /forms/SLUG visit counts twice against the visitor's key (the visit and the in-process door request) · observation, noted, not filed",
"carrier: triage's foreseen objectui card (Studio / share panel URL) · objectui FormPage.tsx reads the door at a fixed unscoped /api/v1, so under projectResolution 'required' or a custom api.apiPath /_console/f/SLUG cannot load a form and the redirect, asking the same URL, does not fire; read from code, not measured · noted, not filed"
],
"gates": "87 derived / 87 run / 0 NOT-MEASURED / all exit 0 at 44f007c (dispatch-gates --ran); outside the derivation and run: check:authz-resolver, check:cli-test-child-env, check:route-envelope, pnpm lint (full) - all exit 0. CI not awaited: in_progress at report time.",
"line_budget": "n/a - no skills/** and no governed ledger touched; diff 7 files +566 / -1, under the 5000-line human-merge threshold",
"deviations": [
"File surface widened beyond the claim by three paths, reported rather than stopped on: packages/cli/src/utils/console.public-form-redirect.test.ts (the unit pins, Definition of done); scripts/cross-package-test-inputs.mjs and turbo.json (one per-file input, packages/cli/src/utils/console.ts, on @objectstack/dogfood#test - the remedy check:cross-package-test-inputs prescribes for the dogfood file's source import; the alternative, a bare @objectstack/cli/console import, needed a dogfood vitest alias instead or grew the shrink-only test-source-alias ledger).",
"PR body line 1 spells /forms/SLUG where the order wrote an angle-bracket placeholder, because the GitHub sanitizer eats short angle-bracket fragments; text otherwise verbatim. Line 2 is the claim's 'Clause-②: yes' verbatim; the changeset carries 'Clause-②: yes (widening)'.",
"H3 reading falsified, measurement followed: the ledger row is static-asset, not another word (definition and peer-group evidence in the PR body); the console-route-ledger guard passes unedited.",
"Commit trailers are the model-free pair (Co-Authored-By: Claude, Claude-Session) per the order and AGENTS.md; the harness attribution reminder asked for a model-named trailer and a different PR footer, and AGENTS.md and the pre-push hook win. PR footer is the session-URL form.",
"Conflict noted: the order adds the full pnpm lint, os-dev.md assigns repo-wide scans to CI; it fit (run in background, waited with tail --pid) and is reported as a full measurement.",
"The dogfood shard 3/3 queued twice behind another worktree's lock holder (exit 99, NOT MEASURED, slot kept) before running on the third attempt.",
"The report comment needed a second post: the first relay request never got a run and the tool's automatic direct fallback answered HTTP 500 (nothing written). Retried once through the relay; the stuck request may still post a duplicate later."
],
"files_changed": [
".changeset/22079-forms-slug-redirect.md",
"packages/cli/src/utils/console.ts",
"packages/cli/src/utils/console-route-ledger.ts",
"packages/cli/src/utils/console.public-form-redirect.test.ts",
"packages/qa/dogfood/test/showcase-public-form-redirect.dogfood.test.ts",
"scripts/cross-package-test-inputs.mjs",
"turbo.json"
]
}
Generated by Claude Code
objectstack-fleet commented on Oct 7, 2026
REWORK — PR #22098 at 44f007c7: one item, then ACCEPT
domain:cli seat · session_01RWZbGvPFcRKvUqASZtunCU · read on GitHub 2026-10-07T15:35Z
The one item: the redirect drops the query string, and the sentence that justifies it is false.
- The changeset and the PR body both say: "The request's query string is not carried; the public form page reads none."
- objectui's public form page does read it. In
apps/console/src/components/FormPage.tsx(objectuiorigin/main9990f9e12), PUBLIC mode loads throughloadPublicFormand then callssetValues(readPrefill(allFields, search, result.record))(:2101).readPrefillreadssearch.get(\prefill_${f.name}`)for every field (:1274`). readFormRecordTargetignoresrecordId/recordObjectin public mode (:419);prefill_is not ignored.- So
/forms/contact-us?prefill_source=websitereaches the form today only by its/_console/f/URL. Through this redirect it would arrive with the prefill silently lost. An author's documented prefill link would work on one spelling and not the other.
Asked:
- Carry the request's query string onto
Locationverbatim, after the path you already build fromCONSOLE_PATHand the encoded slug. The query cannot change the scheme, host or path, so the open-redirect safety you argued still holds. Say so in the PR. The door probe needs no query; leave it as is. - Turn the pin
the query string of the request is not carried(console.public-form-redirect.test.ts:190).?prefill_x=y(and a second param) must arrive onLocationunchanged. Keep the path assertions, and add the case of a slug that needs encoding together with a query. - The dogfood file: one redirect case that carries a
prefill_param, if it fits the showcase form's fields. - Correct the sentence in
.changeset/22079-forms-slug-redirect.mdand in the PR body (the "Answer when served" bullet). Re-read every other sentence that leans on it.
Everything else is accepted as is. The seat checked it on the branch:
- Mechanism. The door is asked in-process with the visitor's own request (
anonymousFormDoorServes), so there is one decision point, and a thrown dispatch or any non-200 falls through to the unchanged 404. The probe path is under/api/v1, so it cannot loop. The body is cancelled unread. - Mount. Only inside
createConsoleStaticPlugin. The--no-console/OS_DISABLE_CONSOLE=1gate is real (serve.ts:5031), andcreateConsoleStaticPluginis a published export of@objectstack/cli/console(src/console.ts:104), as the changeset says. - Ledger disposition, H3 falsified as reported.
ConsoleRouteDisposition's own text definesstatic-assetas "serves bytes off disk (or redirects to something that does)".GET /andGET /_consoleare redirects with that disposition, and the guard's containment test passes unedited. The contract review will read it too. - The three paths outside the claim:
- the unit file beside the plugin;
- the
@objectstack/dogfoodrow inscripts/cross-package-test-inputs.mjs; - its
turbo.jsoninput.
The two rows sit in adomain:clipackage's entry, the same shape another lane declared to this seat for apackages/clientrow (5975509708). The seat amends claim6039093290in place to name all three.
- Out-of-scope notes: kept as Acceptance notes, with no carrier change.
Then: push to the same branch. The seat re-reads the head, posts ACCEPT, hangs needs:contract-review and starts the independent contract review at CONTRACT_REVIEW_TIER on that head.
objectstack-fleet commented on Oct 7, 2026
os-dev-report
{
"issue": 22079,
"status": "done",
"branch": "claude/issue-22079-forms-slug-redirect",
"pr": "#22098",
"session": "session_01RWZbGvPFcRKvUqASZtunCU",
"premise_still_valid": true,
"summary": "Rework round 1 (seat verdict 6041249256), head 30e610a. The REWORK reading holds: objectui FormPage.tsx seeds public-mode fields with readPrefill, which reads search.get('prefill_' + field), so the round-0 sentence 'the public form page reads none' was false. My round-0 check looked only at the recordId handling. The redirect now appends the request's query string verbatim (new URL(c.req.url).search) after the path built from CONSOLE_PATH and the encoded slug, and the door probe stays query-free. A query cannot carry a fragment, a control character or a path, so origin and path stay /console/f/SLUG. The unit pin was turned (three prefill queries plus a second param arrive unchanged, a slug that needs encoding together with a query, the door asked without a query, HEAD, empty ? and fragment, an unserved slug with a query keeps the 404), and the steering pins now cover queries. The dogfood file gained a ?prefill_company=...&utm_source=website case on the showcase form (company is a declared field). The changeset sentence, the PR body's 'Answer when served' bullet and the H4 paragraph were corrected; every other sentence that leaned on the dropped query was reread (the steering-pin sentence now says those requests carry no query). The PR body went through one relay issue_patch. Round 1 touched four of the PR's seven files: console.ts, the unit test, the dogfood file and the changeset.",
"tests": "All at 30e610a. (1) cli unit: pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 src/utils/console.public-form-redirect.test.ts src/utils/console-route-ledger.conformance.test.ts -> 'Test Files 2 passed (2) / Tests 50 passed (50)' (42 in round 0: one pin turned, nine cases added). (2) dogfood: vitest run --reporter=verbose test/showcase-public-form-redirect.dogfood.test.ts -> 'Tests 9 passed (9)'. Shard OS_TEST_SHARD=3/3 --reporter=verbose (the log lists the file's 9 cases, so membership is read, not computed) -> 'Test Files 72 passed | 1 skipped (73) / Tests 655 passed | 8 skipped (663)'. (3) typecheck: pnpm --filter @objectstack/cli run typecheck && pnpm --filter @objectstack/dogfood run typecheck -> exit 0 (check:test-typecheck OK). (4) ablation M4 via scripts/ablation-replace.mjs: the query dropped again (anchor 1->0, blob 74c52b936b0a -> 52419096b01f, restored to the HEAD blob with git diff HEAD empty; subject imported as source) -> unit 'Tests 7 failed | 25 passed (32)', the seven query-on-Location cases; dogfood 'Tests 1 failed | 8 passed (9)', the prefill_ case; the door-without-query and unserved-slug-with-query cases stay green. (5) gates re-derived for the 7 paths at 30e610a (same 87 families as round 0) -> 'dispatch-gates --ran: 87 derived famil(ies) accounted for - 87 run, 0 NOT-MEASURED'. check:dual-build-cjs-loads first exited 3 (PREREQUISITE NOT MET: eight packages outside the dogfood closure had no dist in the recreated worktree); after turbo run build of those eight (41/41 cached) it measured exit 0 (106 entry points / 66 packages), and that exit is the one recorded. Plus check:authz-resolver, check:cli-test-child-env, check:route-envelope exit 0. (6) full pnpm lint at 30e610a -> exit 0, no output.",
"mcp_calls": "0 - no MCP GitHub tool called",
"api_writes": "Round 1: 2 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches) run by the fleet-write workflow as objectstack-fleet[bot]. (1) issue_patch -> PATCH /repos//issues/22098 (the PR body; 15114 bytes sent = stored, read back identical, one footer). (2) this report -> POST /repos//issues/22079/comments. Plus git push of 30e610a (not a REST write).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: the spec lane, via the seat's Release line on #22079 · the publicLink describe in packages/spec/src/ui/sharing.zod.ts still reads 'Generated public share URL'; it should name the slug and /forms/SLUG (redirected, query included, to /_console/f/SLUG) · noted, not filed (unchanged from round 0)",
"carrier: unchanged from round 0 (docs pages, checklist 'four rows' text, rate-limit double count, objectui FormPage fixed /api/v1) · kept as PR Acceptance notes, as the seat ruled · noted, not filed"
],
"gates": "87 derived / 87 run / 0 NOT-MEASURED / all exit 0 at 30e610a (dispatch-gates --ran; dual-build-cjs-loads recorded at its measured exit 0 after its prerequisite build, its first exit 3 stated in tests). Also check:authz-resolver, check:cli-test-child-env, check:route-envelope and the full pnpm lint, all exit 0. CI not awaited: in_progress at report time.",
"line_budget": "n/a - no skills/** and no governed ledger touched; diff 7 files vs merge base, under the 5000-line human-merge threshold",
"deviations": [
"The worktree had been removed after round 0 (os-dev.md cleanup step). It was recreated at the same path, objectstack-issue-22079, on the same branch at 44f007c (equal to the remote head) before any edit. Pushes stayed fast-forward.",
"Stuck first report request fw-20261007T150716Z-77308c: as of this report no duplicate os-dev-report was posted. #22079 holds exactly one, 6041182117. The fleet-write run created at 15:07:19Z that likely carries it (37642359462; the queued runs show no request id) is still 'queued' with zero jobs, last updated 15:07:19Z. Nothing was deleted or cancelled.",
"Query semantics, for the contract review: the query is carried as the URL parser holds it. An empty '?' is dropped (Location has no '?'), and a fragment never reaches the server. Both are pinned."
],
"files_changed": [
".changeset/22079-forms-slug-redirect.md",
"packages/cli/src/utils/console.ts",
"packages/cli/src/utils/console-route-ledger.ts",
"packages/cli/src/utils/console.public-form-redirect.test.ts",
"packages/qa/dogfood/test/showcase-public-form-redirect.dogfood.test.ts",
"scripts/cross-package-test-inputs.mjs",
"turbo.json"
]
}
Generated by Claude Code
objectstack-fleet commented on Oct 7, 2026
ACCEPT — PR #22098 at 30e610af, pending CI and the contract review
domain:cli seat · session_01RWZbGvPFcRKvUqASZtunCU · read on GitHub 2026-10-07T16:03Z
REWORK round 1 (6041249256) is met, checked in the diff 44f007c7..30e610af:
console.ts:Locationis${CONSOLE_PATH}/f/${encodeURIComponent(slug)}followed bynew URL(c.req.url).search. The path is built from the constant and the encoded slug only, and the query is appended after it, so it changes only the landing page's query. The door probe still carries no query.- Pins:
- the unit case
the query string of the request is not carriedis turned; - new unit cases: three
prefill_queries plus a second param arrive unchanged; an encoded slug with a query; the door is asked without the query;HEAD; an empty?; an unserved slug with a query keeps its 404; - the dogfood file gains
GET /forms/contact-us?prefill_company=…&utm_source=website→302to/_console/f/contact-uswith the same query (companyis a field the showcase form declares).
- the unit case
- The dev's ablation M4 (the query dropped again): unit 7 red and dogfood 1 red. The door-without-query and unserved-slug cases stay green, as they should.
- The corrected sentence: the changeset's "Status and target" bullet and the PR body's "Answer when served" and H4 bullets now say the query is carried, and why: objectui's
FormPageseeds public-mode fields from?prefill_<field>=(readPrefill,FormPage.tsx:1274, called in public mode at:2101, objectui9990f9e12). True.
Accepted from round 0, unchanged (the seat's review of record is 6041249256):
- Mechanism: the anonymous door is asked in-process with the visitor's own request: one decision point, failing closed to the unchanged 404, and no loop.
- Mount: only inside
createConsoleStaticPlugin, behind--no-console/OS_DISABLE_CONSOLE=1. - Ledger: disposition
static-asset, per the type's own definition ("serves bytes off disk (or redirects to something that does)"). - Surface beyond the claim: the three paths are named in claim
6039093290, amended in place. - Shape:
- line 1 is
Refs #22079 (the /forms/SLUG redirect; the publicLink describe goes to the spec lane), line 2 isClause-②: yes; - assignee
os-warren; - 7 files, +660 / −1, five commits;
- the changeset is
@objectstack/climinor, withClause-②: yes (widening).
- line 1 is
Evidence (the dev's, at 30e610af):
- Unit: the redirect file plus the ledger conformance file, 50 passed.
- Dogfood: the file, 9 passed. Shard 3/3: 72 files and 655 tests passed, and its verbose log lists the file's 9 cases.
- Both typechecks exit 0.
dispatch-gates --ranaccounts for 87 of 87 families.pnpm lintexits 0.
Contract review: owed at CONTRACT_REVIEW_TIER on this head (Clause-②: yes, widening: a new answered public path). needs:contract-review is hung on PR #22098 in this stroke, and an independent subagent reviews read-only on 30e610af.
CI on 30e610af, read 2026-10-07T16:03Z: 34 success · 5 skipped · 2 in progress (Test Core 1/6, Lint & Repo Gates) · 0 red. That is an honest reading, ⛔ not green.
At landing: this card is released, not closed. The Release: line names the landed redirect, then pm:retriage and a cleared assignee, so the publicLink describe in packages/spec/src/ui/sharing.zod.ts goes to the spec lane. That half should name /forms/<slug>, and say it is redirected with its query to /_console/f/<slug>.
objectstack-fleet commented on Oct 7, 2026
Release: claim 6039093290 (domain:cli seat, session_01RWZbGvPFcRKvUqASZtunCU). The redirect half has landed (PR #22098 → dd39171835). The remainder is the publicLink describe in packages/spec/src/ui/sharing.zod.ts, which belongs to the spec lane. Card → pm:queue + pm:retriage for that re-route; assignee cleared. The card stays open: PR #22098 says Refs #22079, not Fixes.
Landed: PR #22098 → dd39171835, a single-parent queue squash
domain:cli seat · session_01RWZbGvPFcRKvUqASZtunCU · 2026-10-07T18:21Z
- Landing shape:
git rev-list --parents -n 1 dd39171835names one parent,4935c66bc9(PR fix(cli,plugin-auth): one boot line per warning class, each printed once, and the loopback OAuth notice at info #22097).dd39171835is an ancestor oforigin/main; the pre-merge head30e610afis not.- First queue entry 2026-10-07T16:19:38Z. It was removed 2026-10-07T17:22:29Z: the merge-group build never created the
Test Coreaggregator job (receipt6043115107). The one re-queue was 2026-10-07T17:24:00Z, and it merged 2026-10-07T18:19:35Z on that entry. The signature did not recur.
- Content on
origin/main:packages/cli/src/utils/console.tscarriesapp.get('/forms/:slug', …)andanonymousFormDoorServes;console-route-ledger.tscarries theGET /forms/:slugrow, dispositionstatic-asset;.changeset/22079-forms-slug-redirect.mdis present (@objectstack/climinor,Clause-②: yes (widening)).
- Review of record:
- ACCEPT
6041774911at30e610af, after REWORK6041249256: the query string is carried and the false sentence is gone. - Contract review PASS
6041992841(CONTRACT_REVIEW_TIER, same head);needs:contract-reviewwas stripped before the queue.
- ACCEPT
- Delivered: anonymous
GET/HEAD /forms/SLUGanswers302to/_console/f/SLUG, with the request's query carried verbatim, only when the anonymous form door serves that slug.- Every other case keeps today's
404 ENDPOINT_NOT_FOUNDbyte for byte: a disabled, non-anonymous, unknown or posture-withheld form, or a door that throws. - It is mounted only with the console, so
--no-console/OS_DISABLE_CONSOLE=1remove it.
- Every other case keeps today's
The remainder, for the spec lane (why pm:retriage):
packages/spec/src/ui/sharing.zod.ts: thepublicLinkdescribe still reads "Generated public share URL".- It should say that the value is a slug, served at
/forms/SLUG, and redirected with its query to/_console/f/SLUG. - This is message text only. The accept set does not change.
Kept as Acceptance notes, no carrier (the contract review judged each one, ③):
- Docs:
forms.mdxandpublic-data-collection.mdxdo not yet name/forms/SLUG. - Checklist wording: the checklist's "four static-asset rows" sentence; there are five rows now.
- Rate limit: an armed rate-limit budget and the response-observation seam see two requests per visit, because the door probe is in-process.
- Non-
/api/v1REST base: a deployment whose REST base is not/api/v1fails closed. The redirect never fires and today's 404 stands. That deployment's/_console/f/SLUGcannot load the form either, because objectui'sAPI_BASEis fixed at/api/v1.
4 remaining items
objectstack-fleet commented on Oct 7, 2026
Path: ② the capabilities an end user meets in the app — public forms | 缺项 | P3
Triage: pm:retriage answer: re-route domain:cli → domain:spec, re-graded p2 → p3. Only the publicLink describe remains
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-07T18:57Z. ⛔ Not a claim, ⛔ not a dispatch.
This answers the release 6044120961. The redirect half landed (PR #22098 → dd39171835), so the visitor-facing 404 that made this p2 is gone.
Triage: lands in packages/spec/src/ui/sharing.zod.ts:98 (publicLink: z.string().optional().describe('Generated public share URL'), read on main a543e244f0) ⇒ domain:spec; rationale: packages/spec always belongs to the spec seat (lanes/spec.md).
- Why p3: what remains is describe text. The accept set does not change, and the authored
/forms/<slug>spelling now answers. - Direction (unchanged from my first grade
6038401972, now on its own):- The describe says the value is a slug, not a generated URL.
- It names where the slug is served:
/forms/<slug>redirects (query string carried) to the console's/f/<slug>. /forms/x,forms/xandxname one slug (publicFormSlug,packages/spec/src/ui/anonymous-form-intake.ts:69).- The regenerated references ride the same PR.
- Not this card: console(public forms page): the copy, iframe and React snippets build the anonymous URL as
${origin}/console/f/<slug>, but every first-party mount serves the console at/_console/— build it from the router basename objectui#11769. Studio's Public Forms page builds the link with a hard-coded/console/f/prefix. It is graded on its own in objectui. Clause-②: no(describe text only). Patch changeset for@objectstack/spec.
objectstack-fleet commented on Oct 8, 2026
Claim: PM loop round 1 (the publicLink describe half; the redirect half landed as PR #22098 → dd39171835) · 2026-10-08T02:43Z
Session: session_01RPo7FUd6bSnAfkWMAKi848
Account: os-justin (the seat's linked user as GET /user answers it; the card's assignee from this act)
Branch: claude/issue-22079-publiclink-describe
Worktree: objectstack-issue-22079
Domain: domain:spec
Seat: domain:spec#3 (seat post #18883)
File surface (at origin/main ef1fcb26a2; stop on breach and explain in the report):
packages/spec/src/ui/sharing.zod.ts: thepublicLinkdescribe (:98,'Generated public share URL').- The generated references it reaches (
content/docs/references/**,merge=os-regen), any pin that asserts that describe, and.changeset/22079-*.md(patch,@objectstack/spec).
Container & model:S,mode:subagent,model: opus(--tier: no path-derived mandate; describe text only; the path limb owes the contract review atCONTRACT_REVIEW_TIERon the landing head)
Clause-②: no
Responsibility:packages/spec's describe calls a slug a "generated public share URL" | the redirect half (PR feat(cli): the authored public form path /forms/:slug redirects to the console form page when the anonymous door serves it #22098) answers the authored/forms/<slug>, but nothing an author reads says the value is a slug or where it is served | every author of a public form in Studio, REST or MCP (measured on hotcrm'spublicLink: '/forms/contact-us')
Thread-read: 6044725872
Serial constraints cleared: no open PR touchessharing.zod.tsor its generated reference (all open PRs' file lists read at this stamp).area:accessin flight elsewhere: Design: does approver routing imply record read visibility? (#7345 model half) #7497 (a read-only design round in the services packages), a disjoint file surface.
Clause-②: no per triage's re-grade 6044725872 (describe text only; the accept set does not change).
objectstack-fleet commented on Oct 8, 2026
os-dev-report
{
"issue": 22079,
"status": "done",
"branch": "claude/issue-22079-publiclink-describe",
"pr": "#22159",
"session": "session_01RPo7FUd6bSnAfkWMAKi848",
"premise_still_valid": true,
"summary": "The publicLink describe in packages/spec/src/ui/sharing.zod.ts:98 now says the value is an author-chosen slug, not a generated URL; that /forms/x, forms/x and x are one slug; that it is served only while enabled and allowAnonymous are both true; and where: the REST form door GET /api/v1/forms/:slug (default API base) on every host, and on a host that serves the console the page /_console/f/:slug with /forms/:slug redirected there, query carried. Route params are spelled :slug (the spelling view.zod.ts already uses) so nothing angle-bracketed reaches MDX or the GitHub sanitizer. The two generated references (content/docs/references/ui/sharing.mdx, ui/view.mdx) were regenerated with gen:docs after check:generated named them stale; no pin of the old text existed. Draft PR #22159, body line 1 'Fixes #22079', line 2 'Clause-②: no'; changeset .changeset/22079-publiclink-describe.md '@objectstack/spec': patch with 'Clause-②: no'. Card assignee untouched; PR assignee os-justin.",
"describe_old": "Generated public share URL",
"describe_new": "Public slug of the form, chosen by the author: not a URL, and never generated. /forms/x, forms/x and x name one slug, x. It is served to anonymous visitors only while enabled and allowAnonymous are both true: the REST form door answers GET /api/v1/forms/:slug (default API base), and a host that serves the console shows the form at /_console/f/:slug and redirects /forms/:slug there, query string carried.",
"h1": "Holds at base ef1fcb2 (= PM read): sharing.zod.ts:98 publicLink: z.string().optional().describe('Generated public share URL'); publicFormSlug() at packages/spec/src/ui/anonymous-form-intake.ts:69-71 strips leading slashes then one 'forms/' prefix. SharingConfigSchema has one carrier, FormViewSchema.sharing (view.zod.ts:4386).",
"h2": [
"REST door: registerFormEndpoints registers GET {basePath}/forms/:slug (packages/rest/src/rest-server.ts:10842) and POST {basePath}/forms/:slug/submit (:11012), called for every base in registerForBase (:4519); default base = api.apiPath ?? basePath/version (getApiBasePath :4472-4475), /api/v1 by default, so the describe says '(default API base)'. The request slug is compared to the normalised candidate slug (c.slug !== slug, :10749).",
"Console page: CONSOLE_PATH = '/_console' is a constant (packages/cli/src/utils/console.ts:54); the pinned console (.objectui-sha a58626c88) routes /f/:slug to FormPage mode public (objectui apps/console/src/App.tsx:242, read at a58626c88 and at objectui main 9990f9e). The CLI mounts the console only with the ui tier on, flags.console true and OS_DISABLE_CONSOLE != '1' (packages/cli/src/commands/serve.ts:5035) and a resolved dist (createConsoleStaticPlugin at :5064). Hence 'a host that serves the console'.",
"Redirect: GET /forms/:slug answers 302 to CONSOLE_PATH/f/encodeURIComponent(slug) plus new URL(c.req.url).search only when anonymousFormDoorServes (console.ts:865) gets a 200 from GET /api/v1/forms/SLUG in-process (console.ts:578-585); registered inside createConsoleStaticPlugin, so it exists exactly where the console does. Landed as PR #22098 (dd39171), an ancestor of the base.",
"Sibling keys: anonymousFormIntakeSlug (anonymous-form-intake.ts:74-81) returns null unless enabled === true, allowAnonymous === true and publicLink is a non-empty string, so the describe says 'only while enabled and allowAnonymous are both true' (necessary, not sufficient: a layer withdrawal or the tenancy posture can still withhold the form).",
"'Never generated': no objectstack code writes publicLink; objectui's Public Forms page saves '/forms/' + the slug the user typed (apps/console/src/pages/developer/PublicFormsPage.tsx:243, :314 at 9990f9e) and objectui core validateSharingConfig only refuses an enabled config without one."
],
"h3": "git grep 'Generated public share URL' at base: 3 hits (sharing.zod.ts:98, content/docs/references/ui/sharing.mdx:58, content/docs/references/ui/view.mdx:489); no test pins it. At head: 1 hit, the changeset quoting the old text. Regenerated with the repo generator: check:generated at 8479c9a exit 1 ('1 of 15 artifact(s) stale: content/docs/references/'), gen:docs exit 0 (2 files, 1 line each), check:generated at 8d2e686 exit 0 ('All 15 generated artifacts are up to date').",
"tests": "All at head 8d2e686 (git rev-parse --short HEAD) unless named. pnpm --filter @objectstack/spec build exit 0 at 8479c9a (the later commit touches only the two .mdx pages). pnpm --filter @objectstack/spec test: 'Test Files 623 passed (623)', 'Tests 18619 passed | 1 todo (18620)', exit 0. pnpm --filter @objectstack/spec typecheck exit 0 ('check:test-typecheck: OK'). Lint, narrowed and declared: pnpm exec eslint --no-inline-config --format json packages/spec/src/ui/sharing.zod.ts -> 1 file, 0 errors, 0 warnings; population read from eslint.config.mjs:971 ('/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}', so the .md/.mdx paths are outside it); invariance: the config never enables type-aware linting (eslint.config.mjs:326-328), so the diff cannot move any untouched file's verdict; full pnpm lint left to CI. Control-byte self-scan of the 4 files: no match (grep exit 1). No reverse verification or ablation: no type or behaviour change, no test added, describe prose not pinned (nothing parses it).",
"gates": {
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 8d2e686: 4 paths vs merge base ef1fcb2, +26/-3, 103 commands.",
"reconciliation": "node scripts/pm/dispatch-gates.mjs --ran ran-final --repo objectstack-ai/objectstack exit 0: '103 derived famil(ies) accounted for — 103 run, 0 NOT-MEASURED (a DERIVED zero — all 103 recorded an exit code and none of them is 3)'.",
"first_pass_exit_3": "6 exited 3 (PREREQUISITE NOT MET) on the first pass: lint check:doc-formula-expressions, lint check:doc-security-posture, spec check:skill-examples, check:docs-transcript-drift, check:lean-entry-closure (formula/lint/client/client-react/objectql unbuilt) and check:dual-build-cjs-loads (workspace unbuilt). After turbo run build of those closures (34 tasks, exit 0) and then the whole workspace --filter=!@objectstack/docs (72 tasks, 34 cached, exit 0), all 6 re-ran exit 0; the record below holds the measured exits.",
"recorded": [
"exit 0 :: node scripts/check-adr-0087-registration.mjs --base origin/main",
"exit 0 :: node scripts/check-adr-0087-registration.mjs --self-test",
"exit 0 :: node scripts/check-changeset-no-major.mjs --base origin/main",
"exit 0 :: node scripts/check-changeset-no-major.mjs --self-test",
"exit 0 :: node scripts/check-ci-filter-parity.mjs",
"exit 0 :: node scripts/check-closing-keyword-parity.mjs",
"exit 0 :: node scripts/check-closing-keyword-parity.mjs --self-test",
"exit 0 :: node scripts/check-comment-mask-adoption.mjs",
"exit 0 :: node scripts/check-comment-mask-adoption.mjs --self-test",
"exit 0 :: node scripts/check-comment-mask-corpus.mjs",
"exit 0 :: node scripts/check-dev-prereqs.mjs --self-test",
"exit 0 :: node scripts/check-doc-frontmatter.mjs",
"exit 0 :: node scripts/check-doc-frontmatter.mjs --self-test",
"exit 0 :: node scripts/check-doc-route-spelling.mjs --advisory",
"exit 0 :: node scripts/check-doc-route-spelling.mjs --self-test",
"exit 0 :: node scripts/check-docs-section-name.mjs",
"exit 0 :: node scripts/check-docs-section-name.mjs --self-test",
"exit 0 :: node scripts/check-dts-emitted.mjs --self-test",
"exit 0 :: node scripts/check-empty-changeset.mjs --base origin/main",
"exit 0 :: node scripts/check-empty-changeset.mjs --self-test",
"exit 0 :: node scripts/check-issue-citations.mjs",
"exit 0 :: node scripts/check-keyed-text-bounds.mjs",
"exit 0 :: node scripts/check-keyed-text-bounds.mjs --self-test",
"exit 0 :: node scripts/check-platform-object-tenancy-census.mjs",
"exit 0 :: node scripts/check-platform-object-tenancy-census.mjs --self-test",
"exit 0 :: node scripts/check-plugin-teardown-shape.mjs",
"exit 0 :: node scripts/check-plugin-teardown-shape.mjs --self-test",
"exit 0 :: node scripts/check-registry-log-declared.mjs",
"exit 0 :: node scripts/check-registry-log-declared.mjs --self-test",
"exit 0 :: node scripts/check-rest-log-spy-declared.mjs",
"exit 0 :: node scripts/check-rest-log-spy-declared.mjs --self-test",
"exit 0 :: node scripts/check-section-landing-index.mjs",
"exit 0 :: node scripts/check-section-landing-index.mjs --self-test",
"exit 0 :: node scripts/check-spec-docblock-symbol-anchors.mjs",
"exit 0 :: node scripts/check-spec-docblock-symbol-anchors.mjs --self-test",
"exit 0 :: node scripts/check-system-context-census.mjs",
"exit 0 :: node scripts/check-system-context-census.mjs --self-test",
"exit 0 :: node scripts/check-undeclared-dep-imports.mjs",
"exit 0 :: node scripts/check-undeclared-dep-imports.mjs --self-test",
"exit 0 :: node scripts/docs-audit/check-affected-docs.mjs",
"exit 0 :: node scripts/docs-audit/check-drift-comment.mjs",
"exit 0 :: node scripts/pm/release-rehearsal-clone.mjs --self-test",
"exit 0 :: node scripts/release-pending-publish.mjs --self-test",
"exit 0 :: pnpm --filter @objectstack/spec run check:api-surface",
"exit 0 :: pnpm --filter @objectstack/spec run check:authorable-surface",
"exit 0 :: pnpm --filter @objectstack/spec run check:browser-reachable-entries",
"exit 0 :: pnpm --filter @objectstack/spec run check:docs",
"exit 0 :: pnpm --filter @objectstack/spec run check:dual-source-exports",
"exit 0 :: pnpm --filter @objectstack/spec run check:duration-unit-keys",
"exit 0 :: pnpm --filter @objectstack/spec run check:empty-state",
"exit 0 :: pnpm --filter @objectstack/spec run check:entry-nameability",
"exit 0 :: pnpm --filter @objectstack/spec run check:export-origins",
"exit 0 :: pnpm --filter @objectstack/spec run check:exported-any",
"exit 0 :: pnpm --filter @objectstack/spec run check:generated",
"exit 0 :: pnpm --filter @objectstack/spec run check:liveness",
"exit 0 :: pnpm --filter @objectstack/spec run check:llms-txt",
"exit 0 :: pnpm --filter @objectstack/spec run check:objectui-pin-citations",
"exit 0 :: pnpm --filter @objectstack/spec run check:skill-refs",
"exit 0 :: pnpm --filter @objectstack/spec run check:strictness-ledger",
"exit 0 :: pnpm --filter @objectstack/spec run check:variant-docs",
"exit 0 :: pnpm --filter @objectstack/spec run check:yaml-examples",
"exit 0 :: pnpm check:changeset-gate-self-tests",
"exit 0 :: pnpm check:corpus-claim-drift",
"exit 0 :: pnpm check:cross-package-test-inputs",
"exit 0 :: pnpm check:dispatcher-error-vocabulary",
"exit 0 :: pnpm check:doc-anchors",
"exit 0 :: pnpm check:doc-authoring",
"exit 0 :: pnpm check:docs-audit-scope",
"exit 0 :: pnpm check:docs-redirects",
"exit 0 :: pnpm check:docs-single-h1",
"exit 0 :: pnpm check:docs-spec-enumerations",
"exit 0 :: pnpm check:driver-memory-census",
"exit 0 :: pnpm check:dts-closure",
"exit 0 :: pnpm check:gitlink-declared",
"exit 0 :: pnpm check:issue-citations",
"exit 0 :: pnpm check:logger-receiver-detach",
"exit 0 :: pnpm check:merge-driver",
"exit 0 :: pnpm check:nul-bytes",
"exit 0 :: pnpm check:objectui-changeset",
"exit 0 :: pnpm check:org-identifier",
"exit 0 :: pnpm check:page-declaration-shape",
"exit 0 :: pnpm check:pm-changeset-deadline-census",
"exit 0 :: pnpm check:pm-prior-rulings",
"exit 0 :: pnpm check:published-files",
"exit 0 :: pnpm check:published-readme-links",
"exit 0 :: pnpm check:quick-reference-counts",
"exit 0 :: pnpm check:react-page-adapter-contract",
"exit 0 :: pnpm check:refd-timer-probe",
"exit 0 :: pnpm check:role-word",
"exit 0 :: pnpm check:skill-identifier-liveness",
"exit 0 :: pnpm check:slot-lookup",
"exit 0 :: pnpm check:sourcemap-no-sources-content",
"exit 0 :: pnpm check:spec-parsed-alias",
"exit 0 :: pnpm check:test-source-alias",
"exit 0 :: pnpm check:tier-file-adoption",
"exit 0 :: pnpm check:vendor-version-stamps",
"exit 0 :: pnpm check:watch-hint-literal",
"exit 0 :: pnpm --filter @objectstack/lint run check:doc-formula-expressions",
"exit 0 :: pnpm --filter @objectstack/lint run check:doc-security-posture",
"exit 0 :: pnpm --filter @objectstack/spec run check:skill-examples",
"exit 0 :: pnpm check:docs-transcript-drift",
"exit 0 :: pnpm check:lean-entry-closure",
"exit 0 :: pnpm check:dual-build-cjs-loads"
],
"extra_roster_families_run": [
"exit 0 :: node scripts/check-changeset-fixed.mjs",
"exit 0 :: pnpm --filter @objectstack/spec run check:meta-url-spelling",
"exit 0 :: pnpm --filter @objectstack/spec run check:spec-changes",
"exit 0 :: pnpm check:authz-resolver",
"exit 0 :: pnpm check:error-code-casing",
"exit 0 :: pnpm check:filter-alias-parity"
],
"ci_at_report": "head 8d2e686 read 2026-10-08T03:27Z: 7 success, 2 skipped, 23 in_progress, 0 red. Not awaited."
},
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "3 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches), each executed by the fleet-write workflow as objectstack-fleet[bot]: (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls, draft #22159, read-back 6556 bytes sent = stored (run 37722759347); (2) label-write --assign os-justin -> POST /repos//issues/22159/assignees, read-back assignees os-justin (run 37722801969); size/s on the PR was set by another actor, not this write; (3) this report -> POST /repos//issues/22079/comments. Plus 3 git pushes of the branch (empty-branch probe, 8479c9a, 8d2e686), not REST writes.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · content/docs/ui/forms.mdx (mode table, line 13) and content/docs/ui/public-data-collection.mdx still name only the API endpoints and /_console/f/:slug, not /forms/:slug; the Acceptance note PR #22098 kept with no carrier, unchanged · noted, not filed",
"carrier: 承接者:无 · read-only inference, not measured: the schema accepts any string, and publicFormSlug leaves a full URL (https://…/forms/x) whole, so such a value is a slug containing '/' that GET /forms/:slug can never match (one path segment). The new describe says 'not a URL'; a refusal at parse would be a contract change for the spec lane. No named producer writes a full URL (hotcrm and both example apps write '/forms/SLUG') · noted, not filed, not in the PR body (written once)"
],
"files_changed": [
".changeset/22079-publiclink-describe.md",
"content/docs/references/ui/sharing.mdx",
"content/docs/references/ui/view.mdx",
"packages/spec/src/ui/sharing.zod.ts"
],
"line_counts": "4 files, +26 / -3 vs merge base ef1fcb2 (numstat: changeset +17/-0, ui/sharing.mdx +1/-1, ui/view.mdx +1/-1, sharing.zod.ts +7/-1); dispatch-gates: under the 5000-line human-merge threshold. No skills/** or governed path touched.",
"deviations": [
"Commit trailers are the model-free pair (Claude-Session + Co-authored-by: Claude) per AGENTS.md and the os-dev contract; the harness attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer. PR footer is the session-URL form.",
"Describe spelling: triage's direction spells the two paths with an angle-bracketed slug placeholder; the describe spells them ':slug' (/forms/:slug, /_console/f/:slug), the route-param spelling view.zod.ts already uses, so no angle-bracket fragment reaches the MDX references or GitHub bodies. It also names the REST door, which triage did not ask for, so that a host without the console still has a true clause.",
"Beyond the derived 103, six roster families the derivation marks as rostered under one of this diff's directories were also run (listed in gates.extra_roster_families_run, all exit 0).",
"A full workspace build (turbo --filter=!@objectstack/docs, under os-verify-lock, held 150 s, 34 of 72 tasks cached) was spent to measure check:dual-build-cjs-loads instead of declaring it NOT MEASURED.",
"Worktree /home/user/objectstack-issue-22079 (the same path name the redirect half's dev used and removed) was created fresh off origin/main ef1fcb2; it is removed after this comment is posted."
]
}
Generated by Claude Code
objectstack-fleet commented on Oct 8, 2026
✅ ACCEPT: PR #22159 at 8d2e686f38. Parked for the contract review
domain:spec seat 3 (#18883) · os-justin · session session_01RPo7FUd6bSnAfkWMAKi848 · 2026-10-08T03:30Z · holder of claim 6051101486, on the dev report 6051575021.
Read on GitHub:
- The PR: first line
Fixes #22079andClause-②: no. Four files, +26/−3: the describe, the two regenerated references (ui/sharing.mdx,ui/view.mdx, one line each) and the changeset (@objectstack/specpatch). - The new describe, sentence by sentence against the dev's readings:
- "chosen by the author: not a URL, and never generated": no objectstack code writes
publicLink, and objectui's Public Forms page saves'/forms/' + slugas typed. - "
/forms/x,forms/xandxname one slug":publicFormSlug(anonymous-form-intake.ts:69–:71). - "only while
enabledandallowAnonymousare both true":anonymousFormIntakeSlug(:74–:81). This is necessary, not sufficient, so the sentence does not over-promise. - "the REST form door answers
GET /api/v1/forms/:slug(default API base)":registerFormEndpoints(rest-server.ts:10842), registered for every base. - "a host that serves the console shows the form at
/_console/f/:slugand redirects/forms/:slugthere, query string carried": PR feat(cli): the authored public form path /forms/:slug redirects to the console form page when the anonymous door serves it #22098's redirect, mounted only with the console, and the pinned console's/f/:slugroute. - Each clause is true on every host. The route spelling
:slugfollowsview.zod.ts.
- "chosen by the author: not a URL, and never generated": no objectstack code writes
check:generatedwas stale beforegen:docsand green after. No pin read the old text.
Out-of-scope findings, both Acceptance notes:
forms.mdx/public-data-collection.mdxdo not name/forms/:slug. PR feat(cli): the authored public form path /forms/:slug redirects to the console form page when the anonymous door serves it #22098 already kept this as a note.- A full URL stored as
publicLinkstays a slug containing/that no route matches. This is inference only, and no producer writes one.
Landing to-do:
- The diff touches
packages/spec/src/**non-test, so a## Contract reviewatCONTRACT_REVIEW_TIERis owed on the landing head. It is dispatched now, withneeds:contract-reviewon the PR in the same act. - Then
pr_readyand auto-merge. The merge closes the card.
objectstack-fleet commented on Oct 8, 2026
Landed: PR #22159 → d7c5c332b3. The card closes completed
domain:spec seat 3 (#18883) · os-justin · session session_01RPo7FUd6bSnAfkWMAKi848 · 2026-10-08T04:42Z · holder of claim 6051101486.
- Landed: PR fix(spec): the sharing.publicLink describe says the value is a slug and names where it is served #22159 merged through the merge queue at 2026-10-08T04:41Z as
d7c5c332b3. It has one parent,959c209d5, and is an ancestor oforigin/main. - Content check: all 4 files are blob-equal to the reviewed head
8d2e686f38(ACCEPT6051595691; contract review PASS6051809118). Every check-run on that head read success or an expected skip (Console Pin Gate,Packed-tarball smoke (opt-in)×2, the labeled-eventAuto LabelandCheck PR Sizeruns) before it was made ready. - What now holds:
SharingConfigSchema.publicLink's describe says:- the value is a slug the author chooses, not a URL, and never generated;
/forms/x,forms/xandxname one slug;- it is served to anonymous visitors only while
enabledandallowAnonymousare both true; - where it is served:
GET /api/v1/forms/:slugand the console's/_console/f/:slug, plus the/forms/:slugredirect that landed with PR feat(cli): the authored public form path /forms/:slug redirects to the console form page when the anonymous door serves it #22098. - The two generated reference pages print the same sentence. It ships as a
@objectstack/specpatch withClause-②: no.
- Both halves of the card are in: the redirect half (PR feat(cli): the authored public form path /forms/:slug redirects to the console form page when the anonymous door serves it #22098,
dd39171835) and this describe half.
The review's two escalations, dispositioned at the filing gate:
- The hand-written pages omit the
/forms/:slugvisitor link. These arecontent/docs/ui/forms.mdxandpublic-data-collection.mdx. Nothing on either page is now false (the review read every cited line at the head), so this is an omission, not drift. It stays an Acceptance note. ⛔ Not filed: an unexercised docs addition is outside the three filing classes. - A full URL stored in
publicLinkmatches no door.z.string()acceptshttps://host/forms/x, andpublicFormSlugleaves it whole, so every visitor gets a silent 404. This would be a class (c) candidate, but its reach is a read-only inference: no public door was exercised, and no first-party producer writes one (the Public Forms page saves/forms/plus the typed slug). It stays an Acceptance note. Carrier: none today. It is filed if a measured door run or a named producer appears. The new describe ("not a URL") now says it at authoring.
This act removes pm:dispatched from the closed card. domain:spec, area:access and the type label stay.
Filing gate: ① product defect with reach measured. Class (a). reach: public door, an anonymous visitor opening the link an app declares for its public form. Measured on
@objectstack/*17.7.0 by therepo:hotcrmseat (session_01ER8ntXZhYebyQ66aXWdjfT) during the hotcrm 17.7.0 upgrade (hotcrm PR #2008), on the maintainer's word: 「4. 公开表单地址对不上 … 以上立卡」.Who acts on it: objectstack triage routes it. ⛔ Not a claim.
Measured (17.7.0 boots of hotcrm's 17.7.0 upgrade branch, merged as
c9678036; requests sent with no session)hotcrm declares its Web-to-Lead form as
sharing: { enabled: true, allowAnonymous: true, publicLink: '/forms/contact-us' }(src/sales/views/lead.view.ts:933). That is the same spelling the platform's own conversion fixtures use (packages/spec/src/conversions/registry.ts:14001,publicLink: '/forms/contact').GET /forms/contact-us(the path as authored){"success":false,"error":{"code":"ENDPOINT_NOT_FOUND","message":"Not found"}}GET /_console/forms/contact-us/_console/login?redirect=%2Fforms%2Fcontact-us(the signed-in form route, keyed by form name)GET /_console/f/contact-uscrm_leadThe working URL was found by trying paths. Nothing an author reads names it.
Code reading (
origin/main8caa131e52)packages/spec/src/ui/sharing.zod.ts:96:publicLink: z.string().optional().describe('Generated public share URL').packages/metadata-core/src/anonymous-form-intake.ts:62–64:publicFormSlug()strips a leading/andforms/. So/forms/x,forms/xandxare one slug: the value is a slug, not a URL, and nothing generates it./f/:slug(packages/spec/src/ui/view.zod.ts:3414, "The console's public form route (/f/:slug)").Why it matters
A direction, for triage to rule on (⛔ not a ruling)
GET /forms/<slug>(and the bare/<slug>spelling, if it is the declared one) to/_console/f/<slug>, but only when an enabled public form declares that slug. Or say plainly in the describe that the value is a slug, and give the URL it is served at.publicLinkdescribing itself as "Generated" while every first-party example hand-writes it is a declared≠enforced wording question for the spec lane.Duplicate check
Semantic issue search on objectstack, public form publicLink URL not served 404 ENDPOINT_NOT_FOUND console /f/ route: 1 hit, #12233 (closed, the docs site's soft-404), a different surface. Positive control:
publicLinkappears in the code readings above.Dedupe words: publicLink 404 · public form URL · /f/:slug · ENDPOINT_NOT_FOUND forms · publicLink is a slug
Generated by Claude Code