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
Status check from the triage seat, on the maintainer's instruction. 2026-09-28T09:35Z.
Provenance. Executed on the maintainer's instruction. In the triage seat's chat (session session_01AavokzJ5DndAwitDXvKy4U, 2026-09-28), the seat's owned-card review listed this card under item 5 (a reminder on each card held by baozhoutao), and the maintainer replied, verbatim: 「v18 还没开始。其他同意,长期项目: 具体列出来按照总监决策的格式和我讨论」.
@baozhoutao: this card is assigned to you. It has no labels, and has not moved since 2026-09-16. It depends on objectstack-ai/objectstack#18386 landing and being released, and that card has not moved either.
Still yours: a line on the next step keeps it.
Not being worked: clear the assignee, and triage grades and routes it.
Takeover: this card now sits with the domain:spec @ objectui seat, blocked on its framework half. Session session_012UwY3ahMixEFkfTUxMVkYm, seat domain:spec#1, 2026-09-29T15:30Z. ⛔ Not a dispatch.
Provenance. The maintainer, in this seat's chat, 2026-09-29, verbatim: 「objectui#9600(baozhoutao),objectui#7650(os-zhuang) 这两个任务如果没有人在处理,你也可以接手」.
Nobody is working it. Read at this stamp:
No PR or branch carries this card's number.
The card had no labels, and has not moved since 2026-09-16. The triage status check 5867307137 (2026-09-28) has no answer.
So in this act (read back): baozhoutao is unassigned, huangyiirene is assigned, and the card gets pm:blocked and domain:ui (the fix lands in packages/plugin-grid and packages/app-shell). ⛔ No priority is set: grading is triage's.
objectstack#18386 was claimed at 2026-09-29T15:27Z by the objectstack domain:spec seat 5 (os-justin, claim 5893328151), also on the maintainer's instruction.
When it closes, the unlock sweep also checks that a published @objectstack release carries GET /data/:object/export?template=true before this card returns to the queue. That is the consumer-installable rule, so the merge alone does not reopen it.
When unblocked, this seat claims it. The scope is the card's own: the 「下载模板」 button calls the template endpoint; buildImportTemplateCsv and its helpers are deleted if nothing else calls them; importTargetFields stays unchanged.
The response header X-Export-Template-Projection says how the columns were narrowed (writable, readable or none).
Before this card's button can call the endpoint, a framework release must carry e5c7d07ed4. The objectstack card stays open only for its acceptance 6, which is a hand check in four spreadsheet apps.
Pointer from objectstack: two things the 「下载模板」 wiring must honour, from the #18386 acceptance-6 machine verification · 2026-09-30T16:13Z
domain:spec seat 5 of objectstack (session_01Sfe5YjBLwB9J3y8fvm2xq1). ⛔ Not a claim; no state change here. Readings: objectstack-ai/objectstack#18386 comment 5914688255.
The import must carry the same locale as the download. A template writes option labels in the request's locale, and the import door accepts a translated label only under the import request's locale. Measured: a zh-CN template imported under an en request answers invalid_option ("Status: "待办" is not a known option"). The same file imported under zh-CN is accepted. Boolean 是/否 is locale-independent. The import wizard must send the locale the template was downloaded in, or the user's current locale if the two cannot differ.
Triage: blocked-card audit. The blocker landed upstream (objectstack-ai/objectstack#18386), but it is not installable until objectstack's next release
Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-01T03:41Z. ⛔ Not a claim, ⛔ not a dispatch.
Holder's choice: keep the claim and resume once 17.6.0 is published, or release (Release:), and triage moves it to pm:on-hold with that Restart-when:. ⛔ Triage changes no assignee or state on a held card.
GET /data/:object/export?template=true is now judged by the import door: the object must expose import (create or update), and the caller must hold create on the object.
allowExport is no longer needed.
A caller without create gets 403 PERMISSION_DENIED.
An object that exposes neither create nor update gets 405.
For the template button: offer it to a user who can create records of the object, not to a user who can export. The language binding noted earlier (a zh-CN template is imported back with a zh-CN request) is unchanged.
One caveat for release text: the still-pending objectstack .changeset/18386-export-import-template.md keeps its old sentence ("the same two permission checks as the export"), by the maintainer's choice. The authoritative description is the 20896 patch entry.
Owned-card re-check (24 h without an update) · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-02T05:11Z. ⛔ Not a claim; the state and the assignee are the holder's.
The server half is now installable. objectstack's 17.6.0 (tag commit 617f25f8a4, on npm since 2026-10-02T03:03Z) carries every PR this card waited on: #20517, #20683, #20904, and #20977 (8f784959cf, the import-door judgement in the pointer above). My audit note (5924264637) said this card waits for that release; that condition is now met.
Next for the holder: resume against the released server, keeping the pointer above's permission reading (offer the template to a user who can create records).
If the change also needs a 17.6.0-only export from @objectstack/spec, it rides objectui#11438, the 17.6.0 bump.
Triage: unlocked and graded — pm:blocked → pm:queue, priority:p2, area:records. The gate this card was parked on is met
Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-03T09:15Z. ⛔ Not a claim, ⛔ not a dispatch.
The maintainer cleared the assignee at 2026-10-03T09:12Z. It answers ruling batch 1, item 5: the card returns to the lane.
The gate is met, read now. The takeover comment 5893390206 set two conditions: objectstack-ai/objectstack#18386 closes, and a published@objectstack release carries the template endpoint.
The import-door judgement (objectstack PR #20977, 8f784959cf) is an ancestor of the 17.6.0 release commit 617f25f8a4. 17.6.0 is on npm. My re-check 5945986604 read the same for the card's other server PRs.
The grade, which nobody had set:bug · p2 · domain:ui · area:records. Every object's import template lists columns the user cannot write. Each import shows them, so it is user-visible, with no data loss.
For the claim:
the scope is the card's own: 「下载模板」 calls the template endpoint;
the pointer 5924464752's permission reading holds: offer the template to a user who can create records of the object;
if the change needs a 17.6.0-only export from the @objectstack/* packages, it rides objectui#11438's trunk (A′), which resolves 17.6.0. objectui main still resolves 17.5.0.
Claim: PM loop round 1
Session: session_01FjqrwXPfSMkSfkKYDSRkN2
Account: os-zhuang (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-9600-import-template-endpoint
Worktree: objectui-issue-9600
Domain: domain:ui
Seat: domain:ui#1
File surface:
packages/plugin-grid/src/ImportWizard.tsx: the 「下载模板」 button requests the server's import template, and buildImportTemplateCsv, exampleForField, firstOptionValue and downloadTextFile are deleted where nothing else calls them;
packages/plugin-grid/src/importTemplate.test.ts: its template cases go. Its autoMapColumns cases stay, moved if the file goes;
packages/data-objectstack/src/**: only if the template request goes through the adapter's existing export path;
packages/app-shell/src/views/ObjectView.tsx: only the button's offer gate (a user who can create records of the object, per 5924464752);
i18n strings the button needs, and changesets for each package that moves.
importTargetFields stays unchanged, as the card says. The dev stops on a breach and explains it in the report.
Container & model: M, mode:subagent, model: opus (default judgment tier; dispatch-gates --tier --repo objectstack-ai/objectui printed "no path-derived mandate" for ImportWizard.tsx, data-objectstack/src/index.ts and ObjectView.tsx)
Clause-②: yes
Why yes: the route may add an option or method to @object-ui/data-objectstack's published adapter to request the template. That widens a published surface, and the seat cannot rule the route out before the dev measures it, so it is declared yes.
Thread-read: 5967629688
Serial constraints cleared: read 2026-10-03T12:35Z.
The 17.6 trunk objectui#11531: none of these files is in its 81. Under ruling A″ (5968177777) the trunk takes no more slices. If this change needs a 17.6.0-only export, the dev stops and reports; it does not push onto the trunk.
os-dev-report
{
"issue": 9600,
"status": "done",
"branch": "claude/issue-9600-import-template-endpoint",
"pr": "#11556",
"session": "session_01FjqrwXPfSMkSfkKYDSRkN2 (mode:subagent run of the PM's session)",
"premise_still_valid": true,
"summary": "The upload step's Download template button now fetches the server's import template, GET /api/v1/data/:object/export?template=true (xlsx). It goes through a new optional DataSource.downloadImportTemplate(resource), returning a Promise that resolves to a Blob, which ObjectStackAdapter implements on the export route's request path. The new method sends only template=true. The CSV builder is deleted with buildImportTemplateCsv, exampleForField, firstOptionValue and downloadTextFile, and so is importTemplate.test.ts. Its autoMapColumns case moved to autoMapColumns.test.ts with a control case. The button is offered when the data source has the method and usePermissions().can(object,'create') holds, the same permission source ObjectView reads. A 405, a 403 and any other failure each show their own inline message, and no file is saved. To keep the locale the same as the import's, the template request stamps the client locale as Accept-Language, the way client.data.import does; in the console both requests then carry the authenticated fetch's html-lang Accept-Language. A parity test asserts the two requests match. importTargetFields is unchanged. No dependency was added. Route: no @objectstack/client version (17.5.0 or 17.6.0) has a template request, since 17.6.0 data.export has no template option. So the main move to 17.6.0 did not change the route.",
"tests": "On HEAD 20a1b5b, the merge of origin/main 6158e4c. Build: turbo run build --filter='@object-ui/app-shell^...' --concurrency=2, exit 0, 'Tasks: 28 successful, 28 total'. The types dist has downloadImportTemplate in data.d.ts, and plugin-grid type-checks code that reads the member through that d.ts. Type-check exit 0 for @object-ui/types, data-objectstack, plugin-grid, i18n and app-shell; --listFilesOnly shows the new tests in the programs. vitest run --maxWorkers=2 packages/data-objectstack/ packages/plugin-grid/ plus three app-shell files (identityImport.test.ts, identityImportSavedMapping.test.tsx, importTargetFields.test.ts): 'Test Files 262 passed (262) Tests 2774 passed (2774)'. vitest run packages/i18n/ packages/types/: 'Test Files 425 passed (425) Tests 10564 passed | 13 skipped'. Ablations through objectstack scripts/ablation-replace.mjs, on src (the tests import ./index and ../ImportWizard directly, so no dist is involved); each restored with blob == HEAD and git diff HEAD empty: (1) locale stamp removed: 'x carries the client locale on both requests when one is set', Tests 1 failed | 6 passed; (2) can(objectName,'create') removed: 'x is not offered to a user who cannot create records of the object', 1 failed | 7 passed; (3) identity wrapper downloadImportTemplate: undefined removed: 'x withholds the generic import template even when the base carries it as an own property', 1 failed | 19 passed. The first locale-stamp attempt was a no-op: the tool refused because the replacement was not unique and nothing ran; it was redone with a unique marker. Lint, a declared narrowing: eslint, with the same config and inline-config as the per-package pnpm lint, on the 18 changed .ts/.tsx files that still exist. JSON: files 18, errors 0, ignored 0. eslint.config.js has 0 parserOptions/projectService hits, so the lint is not type-aware and untouched files' verdicts cannot move. Full app-shell suite (974 files) not run locally; declared to CI.",
"mcp_calls": "0",
"api_writes": "3 — all through scripts/pm write tools via the fleet-write relay (each = one POST /repos/objectstack-ai/objectstack/dispatches): (1) pr_create POST /repos/objectstack-ai/objectui/pulls → #11556, read-back 11483 bytes sent = stored, identical; (2) label-write.mjs --issue 11556 --assign os-zhuang → POST /repos//issues/11556/assignees, read-back MATCHES; (3) this os-dev-report comment POST /repos//issues/9600/comments via post-stamped.mjs. git push (4 pushes of the branch) is not REST. No label written beyond labeler.yml's.",
"gates": [
{
"command": "pnpm exec turbo run build --filter='@object-ui/app-shell^...' --concurrency=2",
"exit": 0,
"verdict": "Tasks: 28 successful, 28 total (os-verify-lock VERDICT command-exit 0)"
},
{
"command": "pnpm --filter @object-ui/{types,data-objectstack,plugin-grid,i18n,app-shell} run type-check (joined with &&)",
"exit": 0,
"verdict": "=== app-shell tc ok (last of five markers); os-verify-lock VERDICT command-exit 0"
},
{
"command": "pnpm exec vitest run --maxWorkers=2 packages/data-objectstack/ packages/plugin-grid/ + 3 app-shell import test files",
"exit": 0,
"verdict": "Test Files 262 passed (262) / Tests 2774 passed (2774)"
},
{
"command": "pnpm exec vitest run --maxWorkers=2 packages/i18n/ packages/types/",
"exit": 0,
"verdict": "Test Files 425 passed (425) / Tests 10564 passed | 13 skipped (10577)"
},
{
"command": "pnpm check:i18n-keys",
"exit": 0,
"verdict": "Every in-scope call-site key resolves against the en pack (3265 keys)"
},
{
"command": "pnpm check:i18n-drift",
"exit": 0,
"verdict": "Every changed en value was followed by all nine translation packs."
},
{
"command": "pnpm check:i18n-dead-keys",
"exit": 0,
"verdict": "report; grid.import.templateDownloadFailed / templateNotPermitted not candidates (0 hits)"
},
{
"command": "pnpm check:unused-deps",
"exit": 0,
"verdict": "Every gated declaration has a consumer in the package that declares it."
},
{
"command": "pnpm check:control-bytes",
"exit": 0,
"verdict": "check-control-bytes: OK (scanned 10154 tracked text file(s))"
},
{
"command": "pnpm check:new-line-citations",
"exit": 0,
"verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s)"
},
{
"command": "pnpm check:changeset-claims",
"exit": 0,
"verdict": "report-only; 2 pending bodies name en.ts / data.ts (6661 console.nav strings, 9309 QueryParams filter) — read, both still true"
},
{
"command": "pnpm check:pending-changeset-literals",
"exit": 0,
"verdict": "No test source names a pending changeset."
},
{
"command": "node scripts/check-changeset-presence.mjs",
"exit": 0,
"verdict": "19 source file(s) of 5 released package(s) changed, and this change declares 1 changeset(s)"
},
{
"command": "pnpm changeset:check; node scripts/check-changeset-overwrite.mjs",
"exit": 0,
"verdict": "No changeset declares a major bump. / No pre-existing changeset was modified or deleted."
},
{
"command": "check-test-path-roots, check:phantom-deps, check:self-import, check:unreferenced-sources, check-vi-mock-{specifiers,inherit,override-shape}, check-type-check-coverage, check-lint-coverage",
"exit": 0,
"verdict": "each OK (43/43 packages compile tests; 46/46 linted)"
},
{
"command": "eslint (package config, inline-config on) over 18 changed files",
"exit": 0,
"verdict": "files 18 errors 0 ignored 0 (declared narrowing)"
},
{
"command": "pnpm check:readme-exports",
"exit": 1,
"verdict": "NOT MEASURED, reason: prerequisite — every package dist must exist ('type entry ./dist/index.d.ts is not on disk -- run pnpm build first'); no README or barrel changed"
},
{
"command": "node scripts/check-eager-locale-catalogues.mjs",
"exit": 2,
"verdict": "NOT MEASURED, reason: PREREQUISITE NOT MET (apps/console/dist/eager-closure.json missing)"
},
{
"command": "live round trip against a 17.6.0 server",
"exit": null,
"verdict": "NOT MEASURED, reason: optional per dispatch; server half verified upstream (objectstack#18386 acceptance 6)"
},
{
"command": "CI on PR #11556 head 20a1b5b",
"exit": null,
"verdict": "in_progress at report time: 42 check runs = 20 success, 3 skipped, 19 in_progress, 0 failed"
}
],
"files_changed": [
".changeset/9600-import-template-from-server.md",
"packages/types/src/data.ts",
"packages/data-objectstack/src/index.ts",
"packages/data-objectstack/src/downloadImportTemplate.test.ts",
"packages/plugin-grid/src/ImportWizard.tsx",
"packages/plugin-grid/src/tests/importTemplateDownload.test.tsx",
"packages/plugin-grid/src/autoMapColumns.test.ts",
"packages/plugin-grid/src/importTemplate.test.ts (deleted)",
"packages/app-shell/src/views/identityImport.ts",
"packages/app-shell/src/views/tests/identityImport.test.ts",
"packages/i18n/src/locales/{en,zh,ja,ko,de,fr,es,pt,ru,ar}.ts"
],
"deviations": [
"Beyond the claim's file surface: (1) packages/types/src/data.ts adds the optional DataSource.downloadImportTemplate member. The adapter method's contract lives on the interface the wizard feature-detects through. (2) packages/app-shell/src/views/identityImport.ts (+ its test) adds one line, downloadImportTemplate: undefined. The identity wrapper withholds the generic template, because the server template describes the object's generic import door, not the identity pipeline; the wrapper's convention is to null surfaces it must not offer. Both are named in the PR body for the seat to amend the claim.",
"Offer gate: placed inside ImportWizard, not in ObjectView. ObjectView.tsx is untouched. The rule is typeof dataSource.downloadImportTemplate === 'function' && usePermissions().can(objectName,'create'), the same permission source ObjectView reads, so every host gets it, ExcelImportBar included. No new ImportWizardProps prop.",
"Error surface: an inline message on the upload step (the wizard's existing error line) instead of a toast. plugin-grid has no toast dependency, and the card says no new frontend dependencies.",
"Filename: not read from Content-Disposition. The method returns a Blob, the same shape as exportDownload. The wizard keeps grid.import.templateFileName + '.xlsx'.",
"Chose a dedicated method over a template flag on ExportDownloadRequest. A flag would type combinations the server 400s (template with filter, sort, limit, header or format csv). Named for the at-tier contract review.",
"Local test scope narrowed for app-shell: 3 import-related files, not the 974-file suite. The full suite is declared to CI.",
"Commit trailers use the AGENTS.md model-free pair (Claude-Session + Co-authored-by: Claude), not the harness's model-named Co-Authored-By, per objectui AGENTS.md's precedence.",
"Merged origin/main (6158e4c) per the PM's mid-task update, with a merge commit; no rebase or force. All gates were re-run on the merged head 20a1b5b."
],
"line_budget": "n/a (no skills/** touched)",
"open_questions": [
{
"question": "Locale across sessions. The pointer 5915156263 says the wizard must send the locale the template was downloaded in, or the current locale if the two cannot differ. Within one wizard session they cannot differ: both requests carry the same Accept-Language, and a language switch remounts MetadataProvider (key={language}), which unmounts the wizard. Across sessions they can: download in zh, switch the UI to en, import later. The server's workbook carries no machine-readable locale marker (buildImportTemplateWorkbook writes none), so the wizard cannot learn the download locale from the file. The upstream measurement says that case answers invalid_option.",
"options": [
"A: accept the current-locale reading as implemented. Both requests share one locale channel, asserted by a parity test.",
"B: objectstack writes the locale into the template (a workbook property or defined name). objectui reads it on upload and sends it on the import. This is a cross-repo feature, plus a new import-request locale override in the adapter.",
"C: objectstack's import door accepts option labels in every configured locale, not only the request's."
],
"recommendation": "A for this card. Real business need: no named user has hit the cross-session case, and it was measured only upstream. Long-term: B or C belong in objectstack, the producer, not in a client-side guess such as remembering a download locale in localStorage. AI-proofing: A adds no lenient fallback. Startup scope: no expansion without named pull. Revisit only with evidence."
},
{
"question": "For the at-tier contract review: should the template stay a dedicated optional DataSource.downloadImportTemplate(resource), or move to a template flag on ExportDownloadRequest?",
"options": [
"A: dedicated method (implemented). No illegal combinations, and it can be feature-detected.",
"B: ExportDownloadRequest.template?: boolean. It mirrors the wire 1:1, but the type then admits combinations the server refuses."
],
"recommendation": "A, because contract tightening beats a loud server 400: the illegal states cannot be written."
}
],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed — ImportWizardProps.fields[].options and the options passthrough in importTargetFields have no reader after this PR. The client template was the only one, and the card kept importTargetFields unchanged. Retire both together in a follow-up. Dedupe words: ImportWizardProps options, importTargetFields options, dead prop. In PR Acceptance notes.",
"carrier: 承接者:无 · noted, not filed — the adapter's raw-fetch export path (exportDownload, and now downloadImportTemplate) does not send the client's X-Environment-Id, which client.data.import sends when an environment is set. No objectui host sets environmentId, so it is dormant. Dedupe words: X-Environment-Id, exportDownload, fetchImpl, environment header. In PR Acceptance notes."
]
}
✅ Seat ACCEPT of PR objectui#11556 at head 20a1b5b1f (dev report 5970004502). The at-tier contract review is owed before ready (Clause-②: yes)
domain:ui seat 1 · session_01FjqrwXPfSMkSfkKYDSRkN2 · 2026-10-03T14:19Z. Reviewed against GitHub, not the report.
Shape and surface
Draft, base main. The first line is Fixes #9600, with no other closing keyword. Clause-②: yes is at line start. The PR assignee is os-zhuang.
20 files, +557 / −137, no governed path, no content/docs/releases/.
Claim 5969227398 amended here, in public. Two paths are beyond its declared surface, and both are accepted:
packages/types/src/data.ts: the optional DataSource.downloadImportTemplate(resource): Promise<Blob> the wizard feature-detects through. It is the published-surface widening the claim declared Clause-②: yes for.
packages/app-shell/src/views/identityImport.ts (and its test): downloadImportTemplate: undefined, so the identity wrapper withholds the generic template, as it nulls the other import surfaces it must not offer.
ObjectView.tsx is untouched: the offer gate sits in ImportWizard.
The diff, read
Gone:buildImportTemplateCsv, exampleForField, firstOptionValue and downloadTextFile, as the card orders, and importTemplate.test.ts. Its autoMapColumns case moved to autoMapColumns.test.ts, so the coverage stays.
The request:ObjectStackAdapter.downloadImportTemplate sends only template=true on the export route through the shared fetchExportBlob, stamping Accept-Language from client.getLocale() when one is set.
The offer: the button shows when the data source has the method and usePermissions().can(object, 'create') holds, per the pointer 5924464752. A 405 shows the import's own not-allowed message, a 403 templateNotPermitted, and anything else templateDownloadFailed. No file is saved and there is no CSV fallback.
importTargetFields is unchanged. No dependency is added (@object-ui/permissions was already a plugin-grid dependency; check:phantom-deps OK).
Changeset prose, checked sentence by sentence against the diff
minor for @object-ui/types and @object-ui/data-objectstack, which add surface; patch for the other three. ✓ (objectui's fixed group never takes major).
"lists only the columns this caller can import … dropdowns for select and boolean columns, and includes an instructions sheet": the server's behaviour per objectstack's pointer 5909839793 ✓.
"When the client has a locale set (getClient().setLocale), the request carries it as Accept-Language": the downloadImportTemplate body ✓. The clause "the way the import request does" is left for the contract review to test against @objectstack/client.
"A 405 … a 403 … and any other failure each show a message on the upload step, and no file is saved": templateDownloadErrorMessage ✓.
"the identity-import data source (sys_user) withholds downloadImportTemplate" ✓.
The dev's open questions
Locale across sessions: A, decided here (a validation-strategy call, not escalated).
Within one wizard session the two requests cannot differ: both stamp the same locale, and a language switch remounts the wizard.
Across sessions (download in zh, switch to en, import later) the import answers invalid_option: loudly, with no data loss.
Making it work needs objectstack to record the locale in the workbook, or to accept labels in every locale. That is a producer-side feature with no named user pulling it.
Under the four axes, A for this card. It goes to Acceptance notes, ⛔ not filed.
A dedicated method vs a template flag on ExportDownloadRequest: for the contract review to judge.
Out-of-scope (both already in the PR's Acceptance notes, ⛔ not filed)
ImportWizardProps.fields[].options and importTargetFields' options passthrough lose their last reader. Dead-prop cleanup with no reach.
The raw-fetch export path does not send X-Environment-Id. It is dormant: no objectui host sets an environment.
Still owed before ready
The at-tier contract review on this head (dispatched now, isolated).
Contract review not run: the at-tier reviewer died on the tier's quota (HTTP 429) before reading anything · domain:ui seat 1 · session_01FjqrwXPfSMkSfkKYDSRkN2 · 2026-10-03T14:21Z.
PR objectui#11556 (head 20a1b5b1f) stays draft, outside the queue, carrying needs:contract-review. That is the safe state, ⛔ not a failure.
⛔ The quota downgrade does not reach review: 「豁免对象是派发,复核 ⛔ 不随派发档位免除」. So no lower-tier review stands in for it, and the seat does not self-review.
Next: the seat re-dispatches the isolated at-tier reviewer on its next fire once the tier answers. The only bypass is the maintainer reviewing it in person.
No reset time came with the refusal. The seat's hourly Routine is the retry clock.
Landed and verified by content: closed completed by PR objectui#11556 · domain:ui seat 1 · session_01FjqrwXPfSMkSfkKYDSRkN2 · 2026-10-03T15:41Z.
The merge: PR objectui#11556 went through the merge queue to main as b253c4e28 at 2026-10-03T15:41Z, after the at-tier PASS 5970513578 on its head.
Content check on mainb253c4e28 against its first parent d0c0c7fe9:
main
parent
downloadImportTemplate in packages/types/src/data.ts
2
0
async downloadImportTemplate in packages/data-objectstack/src/index.ts
1
0
buildImportTemplateCsv in packages/plugin-grid/src/ImportWizard.tsx
0
3
the control export const ImportWizard
1
1
No other card closed in that window.
Carried by this seat: the card's acceptance 3 (a downloaded template, filled in, re-imports clean) was not measured live. The contract record names it as a post-merge browser check against 17.6.0. It is on the seat post, ⛔ not a reason to reopen.
pm:dispatched is removed in the same act. domain:ui, area:records, the type and the priority stay.
Activity
objectstack-fleet commentedon Sep 28, 2026
Status check from the triage seat, on the maintainer's instruction. 2026-09-28T09:35Z.
Provenance. Executed on the maintainer's instruction. In the triage seat's chat (session
session_01AavokzJ5DndAwitDXvKy4U, 2026-09-28), the seat's owned-card review listed this card under item 5 (a reminder on each card held by baozhoutao), and the maintainer replied, verbatim: 「v18 还没开始。其他同意,长期项目: 具体列出来按照总监决策的格式和我讨论」.@baozhoutao: this card is assigned to you. It has no labels, and has not moved since 2026-09-16. It depends on objectstack-ai/objectstack#18386 landing and being released, and that card has not moved either.
objectstack-fleet commentedon Sep 29, 2026
Takeover: this card now sits with the
domain:spec @ objectuiseat, blocked on its framework half. Sessionsession_012UwY3ahMixEFkfTUxMVkYm, seatdomain:spec#1, 2026-09-29T15:30Z. ⛔ Not a dispatch.Blocked-by: objectstack-ai/objectstack#18386
Provenance. The maintainer, in this seat's chat, 2026-09-29, verbatim: 「objectui#9600(baozhoutao),objectui#7650(os-zhuang) 这两个任务如果没有人在处理,你也可以接手」.
Nobody is working it. Read at this stamp:
5867307137(2026-09-28) has no answer.So in this act (read back):
baozhoutaois unassigned,huangyiireneis assigned, and the card getspm:blockedanddomain:ui(the fix lands inpackages/plugin-gridandpackages/app-shell). ⛔ No priority is set: grading is triage's.The gate.
domain:specseat 5 (os-justin, claim5893328151), also on the maintainer's instruction.@objectstackrelease carriesGET /data/:object/export?template=truebefore this card returns to the queue. That is the consumer-installable rule, so the merge alone does not reopen it.When unblocked, this seat claims it. The scope is the card's own: the 「下载模板」 button calls the template endpoint;
buildImportTemplateCsvand its helpers are deleted if nothing else calls them;importTargetFieldsstays unchanged.objectstack-fleet commentedon Sep 30, 2026
Pointer from objectstack: the framework half of this card has landed · 2026-09-30T11:00Z
domain:specseat 5 of objectstack (session_01Sfe5YjBLwB9J3y8fvm2xq1). ⛔ Not a claim; no state change here.e5c7d07ed4.GET /api/v1/data/:object/export?template=trueanswers the xlsx import template: the columns an import stores, required columns with no default marked*, dropdowns forselect/radio/ boolean, and an instructions sheet.X-Export-Template-Projectionsays how the columns were narrowed (writable,readableornone).e5c7d07ed4. The objectstack card stays open only for its acceptance 6, which is a hand check in four spreadsheet apps.Generated by Claude Code
objectstack-fleet commentedon Sep 30, 2026
Pointer from objectstack: two things the 「下载模板」 wiring must honour, from the #18386 acceptance-6 machine verification · 2026-09-30T16:13Z
domain:specseat 5 of objectstack (session_01Sfe5YjBLwB9J3y8fvm2xq1). ⛔ Not a claim; no state change here. Readings: objectstack-ai/objectstack#18386 comment5914688255.enrequest answersinvalid_option("Status: "待办" is not a known option"). The same file imported under zh-CN is accepted. Boolean 是/否 is locale-independent. The import wizard must send the locale the template was downloaded in, or the user's current locale if the two cannot differ.allowExporton the object (the export gates run first), so an importer without export permission gets403 EXPORT_NOT_PERMITTED. The import template (GET /data/:object/export?template=true) sits behind the EXPORT gate (allowExport): a caller who may import but not export cannot download it — gate it by the import door instead? (from #18386 acceptance-6 verification) objectstack#20896 asks the maintainer whether the template should follow the import door instead. The seat recommends A (the import door). The button's 403 handling depends on that answer.Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
Triage: blocked-card audit. The blocker landed upstream (objectstack-ai/objectstack#18386), but it is not installable until objectstack's next release
Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T03:41Z. ⛔ Not a claim, ⛔ not a dispatch.@huangyiirene: this card is
pm:blockedon objectstack-ai/objectstack#18386 and assigned to you.completed. Its template endpoint landed ase5c7d07ed4, which is not in the published 17.5.0 (release commit0f6dcac5e9).@objectstack/*> 17.5.0 (Version Packages chore: version packages objectstack#20639, 17.6.0, carries it).Release:), and triage moves it topm:on-holdwith thatRestart-when:. ⛔ Triage changes no assignee or state on a held card.Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
Pointer from objectstack: the import template now needs the import (create) permission, not export
From the objectstack
domain:specseat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1) · 2026-10-01. ⛔ Not a claim.8f784959cf. It carries ruling A on The import template (GET /data/:object/export?template=true) sits behind the EXPORT gate (allowExport): a caller who may import but not export cannot download it — gate it by the import door instead? (from #18386 acceptance-6 verification) objectstack#20896, which the maintainer agreed to.GET /data/:object/export?template=trueis now judged by the import door: the object must expose import (create or update), and the caller must hold create on the object.allowExportis no longer needed.403 PERMISSION_DENIED..changeset/18386-export-import-template.mdkeeps its old sentence ("the same two permission checks as the export"), by the maintainer's choice. The authoritative description is the 20896 patch entry.Generated by Claude Code
objectstack-fleet commentedon Oct 2, 2026
Owned-card re-check (24 h without an update) · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-02T05:11Z. ⛔ Not a claim; the state and the assignee are the holder's.617f25f8a4, on npm since 2026-10-02T03:03Z) carries every PR this card waited on: #20517, #20683, #20904, and #20977 (8f784959cf, the import-door judgement in the pointer above). My audit note (5924264637) said this card waits for that release; that condition is now met.@objectstack/spec, it rides objectui#11438, the 17.6.0 bump.Generated by Claude Code
objectstack-fleet commentedon Oct 3, 2026
Triage: unlocked and graded —
pm:blocked→pm:queue,priority:p2,area:records. The gate this card was parked on is metTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-03T09:15Z. ⛔ Not a claim, ⛔ not a dispatch.The maintainer cleared the assignee at 2026-10-03T09:12Z. It answers ruling batch 1, item 5: the card returns to the lane.
The gate is met, read now. The takeover comment
5893390206set two conditions: objectstack-ai/objectstack#18386 closes, and a published@objectstackrelease carries the template endpoint.completedat 2026-09-30T22:44Z.8f784959cf) is an ancestor of the 17.6.0 release commit617f25f8a4. 17.6.0 is on npm. My re-check5945986604read the same for the card's other server PRs.The grade, which nobody had set:
bug· p2 ·domain:ui·area:records. Every object's import template lists columns the user cannot write. Each import shows them, so it is user-visible, with no data loss.For the claim:
5924464752's permission reading holds: offer the template to a user who can create records of the object;@objectstack/*packages, it rides objectui#11438's trunk (A′), which resolves 17.6.0. objectuimainstill resolves 17.5.0.Generated by Claude Code
1 remaining item
objectstack-fleet commentedon Oct 3, 2026
Claim: PM loop round 1
Session:
session_01FjqrwXPfSMkSfkKYDSRkN2Account:
os-zhuang(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-9600-import-template-endpointWorktree:
objectui-issue-9600Domain:
domain:uiSeat:
domain:ui#1File surface:
packages/plugin-grid/src/ImportWizard.tsx: the 「下载模板」 button requests the server's import template, andbuildImportTemplateCsv,exampleForField,firstOptionValueanddownloadTextFileare deleted where nothing else calls them;packages/plugin-grid/src/importTemplate.test.ts: its template cases go. ItsautoMapColumnscases stay, moved if the file goes;packages/data-objectstack/src/**: only if the template request goes through the adapter's existing export path;packages/app-shell/src/views/ObjectView.tsx: only the button's offer gate (a user who can create records of the object, per5924464752);importTargetFieldsstays unchanged, as the card says. The dev stops on a breach and explains it in the report.Container & model:
M,mode:subagent,model: opus(default judgment tier;dispatch-gates --tier --repo objectstack-ai/objectuiprinted "no path-derived mandate" forImportWizard.tsx,data-objectstack/src/index.tsandObjectView.tsx)Clause-②: yes
Why yes: the route may add an option or method to
@object-ui/data-objectstack's published adapter to request the template. That widens a published surface, and the seat cannot rule the route out before the dev measures it, so it is declaredyes.Thread-read: 5967629688
Serial constraints cleared: read 2026-10-03T12:35Z.
5968177777) the trunk takes no more slices. If this change needs a 17.6.0-only export, the dev stops and reports; it does not push onto the trunk.plugin-grid'sObjectGrid.tsx,index.tsxand README, notImportWizard.tsx.objectui validateandobjectui checkjudge through the strict authoring face (objectui#5250, slice A) #11069 are disjoint.plugin-form) and objectui#11539 (app-shellMetadataProvider.tsx) are disjoint by file.Generated by Claude Code
objectstack-fleet commentedon Oct 3, 2026
os-dev-report
{
"issue": 9600,
"status": "done",
"branch": "claude/issue-9600-import-template-endpoint",
"pr": "#11556",
"session": "session_01FjqrwXPfSMkSfkKYDSRkN2 (mode:subagent run of the PM's session)",
"premise_still_valid": true,
"summary": "The upload step's Download template button now fetches the server's import template, GET /api/v1/data/:object/export?template=true (xlsx). It goes through a new optional DataSource.downloadImportTemplate(resource), returning a Promise that resolves to a Blob, which ObjectStackAdapter implements on the export route's request path. The new method sends only template=true. The CSV builder is deleted with buildImportTemplateCsv, exampleForField, firstOptionValue and downloadTextFile, and so is importTemplate.test.ts. Its autoMapColumns case moved to autoMapColumns.test.ts with a control case. The button is offered when the data source has the method and usePermissions().can(object,'create') holds, the same permission source ObjectView reads. A 405, a 403 and any other failure each show their own inline message, and no file is saved. To keep the locale the same as the import's, the template request stamps the client locale as Accept-Language, the way client.data.import does; in the console both requests then carry the authenticated fetch's html-lang Accept-Language. A parity test asserts the two requests match. importTargetFields is unchanged. No dependency was added. Route: no @objectstack/client version (17.5.0 or 17.6.0) has a template request, since 17.6.0 data.export has no template option. So the main move to 17.6.0 did not change the route.",
"tests": "On HEAD 20a1b5b, the merge of origin/main 6158e4c. Build: turbo run build --filter='@object-ui/app-shell^...' --concurrency=2, exit 0, 'Tasks: 28 successful, 28 total'. The types dist has downloadImportTemplate in data.d.ts, and plugin-grid type-checks code that reads the member through that d.ts. Type-check exit 0 for @object-ui/types, data-objectstack, plugin-grid, i18n and app-shell; --listFilesOnly shows the new tests in the programs. vitest run --maxWorkers=2 packages/data-objectstack/ packages/plugin-grid/ plus three app-shell files (identityImport.test.ts, identityImportSavedMapping.test.tsx, importTargetFields.test.ts): 'Test Files 262 passed (262) Tests 2774 passed (2774)'. vitest run packages/i18n/ packages/types/: 'Test Files 425 passed (425) Tests 10564 passed | 13 skipped'. Ablations through objectstack scripts/ablation-replace.mjs, on src (the tests import ./index and ../ImportWizard directly, so no dist is involved); each restored with blob == HEAD and git diff HEAD empty: (1) locale stamp removed: 'x carries the client locale on both requests when one is set', Tests 1 failed | 6 passed; (2) can(objectName,'create') removed: 'x is not offered to a user who cannot create records of the object', 1 failed | 7 passed; (3) identity wrapper downloadImportTemplate: undefined removed: 'x withholds the generic import template even when the base carries it as an own property', 1 failed | 19 passed. The first locale-stamp attempt was a no-op: the tool refused because the replacement was not unique and nothing ran; it was redone with a unique marker. Lint, a declared narrowing: eslint, with the same config and inline-config as the per-package pnpm lint, on the 18 changed .ts/.tsx files that still exist. JSON: files 18, errors 0, ignored 0. eslint.config.js has 0 parserOptions/projectService hits, so the lint is not type-aware and untouched files' verdicts cannot move. Full app-shell suite (974 files) not run locally; declared to CI.",
"mcp_calls": "0",
"api_writes": "3 — all through scripts/pm write tools via the fleet-write relay (each = one POST /repos/objectstack-ai/objectstack/dispatches): (1) pr_create POST /repos/objectstack-ai/objectui/pulls → #11556, read-back 11483 bytes sent = stored, identical; (2) label-write.mjs --issue 11556 --assign os-zhuang → POST /repos//issues/11556/assignees, read-back MATCHES; (3) this os-dev-report comment POST /repos//issues/9600/comments via post-stamped.mjs. git push (4 pushes of the branch) is not REST. No label written beyond labeler.yml's.",
"gates": [
{
"command": "pnpm exec turbo run build --filter='@object-ui/app-shell^...' --concurrency=2",
"exit": 0,
"verdict": "Tasks: 28 successful, 28 total (os-verify-lock VERDICT command-exit 0)"
},
{
"command": "pnpm --filter @object-ui/{types,data-objectstack,plugin-grid,i18n,app-shell} run type-check (joined with &&)",
"exit": 0,
"verdict": "=== app-shell tc ok (last of five markers); os-verify-lock VERDICT command-exit 0"
},
{
"command": "pnpm exec vitest run --maxWorkers=2 packages/data-objectstack/ packages/plugin-grid/ + 3 app-shell import test files",
"exit": 0,
"verdict": "Test Files 262 passed (262) / Tests 2774 passed (2774)"
},
{
"command": "pnpm exec vitest run --maxWorkers=2 packages/i18n/ packages/types/",
"exit": 0,
"verdict": "Test Files 425 passed (425) / Tests 10564 passed | 13 skipped (10577)"
},
{
"command": "pnpm check:i18n-keys",
"exit": 0,
"verdict": "Every in-scope call-site key resolves against the en pack (3265 keys)"
},
{
"command": "pnpm check:i18n-drift",
"exit": 0,
"verdict": "Every changed en value was followed by all nine translation packs."
},
{
"command": "pnpm check:i18n-dead-keys",
"exit": 0,
"verdict": "report; grid.import.templateDownloadFailed / templateNotPermitted not candidates (0 hits)"
},
{
"command": "pnpm check:unused-deps",
"exit": 0,
"verdict": "Every gated declaration has a consumer in the package that declares it."
},
{
"command": "pnpm check:control-bytes",
"exit": 0,
"verdict": "check-control-bytes: OK (scanned 10154 tracked text file(s))"
},
{
"command": "pnpm check:new-line-citations",
"exit": 0,
"verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s)"
},
{
"command": "pnpm check:changeset-claims",
"exit": 0,
"verdict": "report-only; 2 pending bodies name en.ts / data.ts (6661 console.nav strings, 9309 QueryParams filter) — read, both still true"
},
{
"command": "pnpm check:pending-changeset-literals",
"exit": 0,
"verdict": "No test source names a pending changeset."
},
{
"command": "node scripts/check-changeset-presence.mjs",
"exit": 0,
"verdict": "19 source file(s) of 5 released package(s) changed, and this change declares 1 changeset(s)"
},
{
"command": "pnpm changeset:check; node scripts/check-changeset-overwrite.mjs",
"exit": 0,
"verdict": "No changeset declares a major bump. / No pre-existing changeset was modified or deleted."
},
{
"command": "check-test-path-roots, check:phantom-deps, check:self-import, check:unreferenced-sources, check-vi-mock-{specifiers,inherit,override-shape}, check-type-check-coverage, check-lint-coverage",
"exit": 0,
"verdict": "each OK (43/43 packages compile tests; 46/46 linted)"
},
{
"command": "eslint (package config, inline-config on) over 18 changed files",
"exit": 0,
"verdict": "files 18 errors 0 ignored 0 (declared narrowing)"
},
{
"command": "pnpm check:readme-exports",
"exit": 1,
"verdict": "NOT MEASURED, reason: prerequisite — every package dist must exist ('type entry ./dist/index.d.ts is not on disk -- run pnpm build first'); no README or barrel changed"
},
{
"command": "node scripts/check-eager-locale-catalogues.mjs",
"exit": 2,
"verdict": "NOT MEASURED, reason: PREREQUISITE NOT MET (apps/console/dist/eager-closure.json missing)"
},
{
"command": "live round trip against a 17.6.0 server",
"exit": null,
"verdict": "NOT MEASURED, reason: optional per dispatch; server half verified upstream (objectstack#18386 acceptance 6)"
},
{
"command": "CI on PR #11556 head 20a1b5b",
"exit": null,
"verdict": "in_progress at report time: 42 check runs = 20 success, 3 skipped, 19 in_progress, 0 failed"
}
],
"files_changed": [
".changeset/9600-import-template-from-server.md",
"packages/types/src/data.ts",
"packages/data-objectstack/src/index.ts",
"packages/data-objectstack/src/downloadImportTemplate.test.ts",
"packages/plugin-grid/src/ImportWizard.tsx",
"packages/plugin-grid/src/tests/importTemplateDownload.test.tsx",
"packages/plugin-grid/src/autoMapColumns.test.ts",
"packages/plugin-grid/src/importTemplate.test.ts (deleted)",
"packages/app-shell/src/views/identityImport.ts",
"packages/app-shell/src/views/tests/identityImport.test.ts",
"packages/i18n/src/locales/{en,zh,ja,ko,de,fr,es,pt,ru,ar}.ts"
],
"deviations": [
"Beyond the claim's file surface: (1) packages/types/src/data.ts adds the optional DataSource.downloadImportTemplate member. The adapter method's contract lives on the interface the wizard feature-detects through. (2) packages/app-shell/src/views/identityImport.ts (+ its test) adds one line, downloadImportTemplate: undefined. The identity wrapper withholds the generic template, because the server template describes the object's generic import door, not the identity pipeline; the wrapper's convention is to null surfaces it must not offer. Both are named in the PR body for the seat to amend the claim.",
"Offer gate: placed inside ImportWizard, not in ObjectView. ObjectView.tsx is untouched. The rule is typeof dataSource.downloadImportTemplate === 'function' && usePermissions().can(objectName,'create'), the same permission source ObjectView reads, so every host gets it, ExcelImportBar included. No new ImportWizardProps prop.",
"Error surface: an inline message on the upload step (the wizard's existing error line) instead of a toast. plugin-grid has no toast dependency, and the card says no new frontend dependencies.",
"Filename: not read from Content-Disposition. The method returns a Blob, the same shape as exportDownload. The wizard keeps grid.import.templateFileName + '.xlsx'.",
"Chose a dedicated method over a template flag on ExportDownloadRequest. A flag would type combinations the server 400s (template with filter, sort, limit, header or format csv). Named for the at-tier contract review.",
"Local test scope narrowed for app-shell: 3 import-related files, not the 974-file suite. The full suite is declared to CI.",
"Commit trailers use the AGENTS.md model-free pair (Claude-Session + Co-authored-by: Claude), not the harness's model-named Co-Authored-By, per objectui AGENTS.md's precedence.",
"Merged origin/main (6158e4c) per the PM's mid-task update, with a merge commit; no rebase or force. All gates were re-run on the merged head 20a1b5b."
],
"line_budget": "n/a (no skills/** touched)",
"open_questions": [
{
"question": "Locale across sessions. The pointer 5915156263 says the wizard must send the locale the template was downloaded in, or the current locale if the two cannot differ. Within one wizard session they cannot differ: both requests carry the same Accept-Language, and a language switch remounts MetadataProvider (key={language}), which unmounts the wizard. Across sessions they can: download in zh, switch the UI to en, import later. The server's workbook carries no machine-readable locale marker (buildImportTemplateWorkbook writes none), so the wizard cannot learn the download locale from the file. The upstream measurement says that case answers invalid_option.",
"options": [
"A: accept the current-locale reading as implemented. Both requests share one locale channel, asserted by a parity test.",
"B: objectstack writes the locale into the template (a workbook property or defined name). objectui reads it on upload and sends it on the import. This is a cross-repo feature, plus a new import-request locale override in the adapter.",
"C: objectstack's import door accepts option labels in every configured locale, not only the request's."
],
"recommendation": "A for this card. Real business need: no named user has hit the cross-session case, and it was measured only upstream. Long-term: B or C belong in objectstack, the producer, not in a client-side guess such as remembering a download locale in localStorage. AI-proofing: A adds no lenient fallback. Startup scope: no expansion without named pull. Revisit only with evidence."
},
{
"question": "For the at-tier contract review: should the template stay a dedicated optional DataSource.downloadImportTemplate(resource), or move to a template flag on ExportDownloadRequest?",
"options": [
"A: dedicated method (implemented). No illegal combinations, and it can be feature-detected.",
"B: ExportDownloadRequest.template?: boolean. It mirrors the wire 1:1, but the type then admits combinations the server refuses."
],
"recommendation": "A, because contract tightening beats a loud server 400: the illegal states cannot be written."
}
],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed — ImportWizardProps.fields[].options and the options passthrough in importTargetFields have no reader after this PR. The client template was the only one, and the card kept importTargetFields unchanged. Retire both together in a follow-up. Dedupe words: ImportWizardProps options, importTargetFields options, dead prop. In PR Acceptance notes.",
"carrier: 承接者:无 · noted, not filed — the adapter's raw-fetch export path (exportDownload, and now downloadImportTemplate) does not send the client's X-Environment-Id, which client.data.import sends when an environment is set. No objectui host sets environmentId, so it is dormant. Dedupe words: X-Environment-Id, exportDownload, fetchImpl, environment header. In PR Acceptance notes."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 3, 2026
✅ Seat ACCEPT of PR objectui#11556 at head
20a1b5b1f(dev report5970004502). The at-tier contract review is owed before ready (Clause-②: yes)domain:uiseat 1 ·session_01FjqrwXPfSMkSfkKYDSRkN2· 2026-10-03T14:19Z. Reviewed against GitHub, not the report.Shape and surface
main. The first line isFixes #9600, with no other closing keyword.Clause-②: yesis at line start. The PR assignee isos-zhuang.content/docs/releases/.5969227398amended here, in public. Two paths are beyond its declared surface, and both are accepted:packages/types/src/data.ts: the optionalDataSource.downloadImportTemplate(resource): Promise<Blob>the wizard feature-detects through. It is the published-surface widening the claim declaredClause-②: yesfor.packages/app-shell/src/views/identityImport.ts(and its test):downloadImportTemplate: undefined, so the identity wrapper withholds the generic template, as it nulls the other import surfaces it must not offer.ObjectView.tsxis untouched: the offer gate sits inImportWizard.The diff, read
buildImportTemplateCsv,exampleForField,firstOptionValueanddownloadTextFile, as the card orders, andimportTemplate.test.ts. ItsautoMapColumnscase moved toautoMapColumns.test.ts, so the coverage stays.ObjectStackAdapter.downloadImportTemplatesends onlytemplate=trueon the export route through the sharedfetchExportBlob, stampingAccept-Languagefromclient.getLocale()when one is set.usePermissions().can(object, 'create')holds, per the pointer5924464752. A 405 shows the import's own not-allowed message, a 403templateNotPermitted, and anything elsetemplateDownloadFailed. No file is saved and there is no CSV fallback.importTargetFieldsis unchanged. No dependency is added (@object-ui/permissionswas already aplugin-griddependency;check:phantom-depsOK).Changeset prose, checked sentence by sentence against the diff
minorfor@object-ui/typesand@object-ui/data-objectstack, which add surface;patchfor the other three. ✓ (objectui's fixed group never takesmajor).5909839793✓.getClient().setLocale), the request carries it asAccept-Language": thedownloadImportTemplatebody ✓. The clause "the way the import request does" is left for the contract review to test against@objectstack/client.templateDownloadErrorMessage✓.sys_user) withholdsdownloadImportTemplate" ✓.The dev's open questions
invalid_option: loudly, with no data loss.templateflag onExportDownloadRequest: for the contract review to judge.Out-of-scope (both already in the PR's Acceptance notes, ⛔ not filed)
ImportWizardProps.fields[].optionsandimportTargetFields' options passthrough lose their last reader. Dead-prop cleanup with no reach.X-Environment-Id. It is dormant: no objectui host sets an environment.Still owed before ready
Generated by Claude Code
objectstack-fleet commentedon Oct 3, 2026
Contract review not run: the at-tier reviewer died on the tier's quota (HTTP 429) before reading anything ·
domain:uiseat 1 ·session_01FjqrwXPfSMkSfkKYDSRkN2· 2026-10-03T14:21Z.20a1b5b1f) stays draft, outside the queue, carryingneeds:contract-review. That is the safe state, ⛔ not a failure.Generated by Claude Code
objectstack-fleet commentedon Oct 3, 2026
Landed and verified by content: closed
completedby PR objectui#11556 ·domain:uiseat 1 ·session_01FjqrwXPfSMkSfkKYDSRkN2· 2026-10-03T15:41Z.The merge: PR objectui#11556 went through the merge queue to
mainasb253c4e28at 2026-10-03T15:41Z, after the at-tier PASS5970513578on its head.Content check on
mainb253c4e28against its first parentd0c0c7fe9:maindownloadImportTemplateinpackages/types/src/data.tsasync downloadImportTemplateinpackages/data-objectstack/src/index.tsbuildImportTemplateCsvinpackages/plugin-grid/src/ImportWizard.tsxexport const ImportWizardNo other card closed in that window.
Carried by this seat: the card's acceptance 3 (a downloaded template, filled in, re-imports clean) was not measured live. The contract record names it as a post-merge browser check against 17.6.0. It is on the seat post, ⛔ not a reason to reopen.
pm:dispatchedis removed in the same act.domain:ui,area:records, the type and the priority stay.Generated by Claude Code
fix(plugin-grid,data-objectstack,types): the import wizard's Download…
feat(rest): GET /data/:object/export?template=true answers an xlsx im…