Repository navigation
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) #20896
Description
Activity
objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsos-decision-facets
决策请求:导入模板的下载,该挂在「导出」权限后面,还是「导入」权限后面? · 2026-09-30T16:11Z
domain:specseat 5(session_01Sfe5YjBLwB9J3y8fvm2xq1)。#18386 验收 6 的机器验证测到:showcase 里没有一个人能下载导入模板。原因是模板接口沿用了导出的两道闸,而导出权限自 spec 17 起默认不给。这张卡请你定模板该跟哪道闸走。一句话问题
模板里一条数据都没有,只有这个人能写的列和填写说明。现在却要求他有「整表导出」的权限才能下载,于是只能导入、不能导出的人拿不到模板。
Governing text
- feat(rest): 导出接口新增 ?template=true —— 输出只含「可填列」的 xlsx 导入模板 #18386 正文「实现要点」:「权限沿用现有两道闸(
enforceApiAccess('export')+enforceExportPermission)」。 - spec 17
export-axis-opt-in:读记录与批量复制整表是两种不同的特权(Salesforce「Export Reports」、Dynamics「Export to Excel」同样分开);member_default有意不带导出权限。spec 18admin-export-wildcard-removed又去掉了管理员的通配导出。
前提(每条带复查方法)
- 模板走的是导出闸。 复查:
rest-server.ts:9633、:9636两道导出闸都在:9672的模板分支之前。 - showcase 里人人 403。 复查:验证读数
5914688255第 1 步;examples/app-showcase/src/security/permission-sets.ts里没有allowExport。 - 模板不含记录。 复查:
import-template.ts,数据行为 0,列经getWritableFields按字段级权限收窄。
选项与代价
选项 做什么 客户看到的后果 A 模板改走导入闸: enforceApiAccess('import')加该对象的新建权限;URL 不变能导入的人就能下载模板;导出权限仍只管真正的整表导出 B 维持导出闸(卡片原文) 想让某人导入,就得同时给他整表导出的特权;showcase 要给导入角色加 allowExportC 两道闸任一通过即可 能导入的人和能导出的人都能下载;规则变成两条,解释起来更绕 业务直译:A = 模板属于「导入」这件事;B = 模板属于「导出」这件事;C = 两边都算。
四轴(业务立场)
- 长远合理性: 主流做法是模板跟着导入走:比如 Dataverse 的「下载导入模板」要求导入或新建权限,不要求导出权限。A 与之一致。B 会逼运营为了一个空表格授出整表导出权限,破坏 spec 17 刚立起来的职责隔离。
- 实际业务拉动: objectui#9600 的「下载模板」按钮就在导入向导里,点它的人本来就是在导入。
- 防 AI 犯错 / 安全: 模板只暴露这个人本来就能读到的字段名和选项,列还经字段级权限收窄,没有记录数据。A 不会放出任何数据。
- 创业阶段不扩散: A 只改模板分支的闸;C 要多维护一条规则。
推荐
A。 只看①选 A;②③④ 是否翻转:否。回退:C。
置信缺口:「导入闸」具体用enforceApiAccess('import')加对象新建权限,还是复用导入接口现有的整套检查顺序,要等实现时逐条对照导入接口来定。自检
- 这不是席位能定的:它改的是一个接口的权限归属,而这一点写在卡片的设计原文里。
- 两个选项都能执行,不会卡在别处。
裁后执行
- A: 在 feat(rest): 导出接口新增 ?template=true —— 输出只含「可填列」的 xlsx 导入模板 #18386 当前这轮修复落地后,本席另开一轮(同一文件
rest-server.ts的模板分支):模板改走导入闸。补钉子:有新建权限、没有导出权限的人能拿到模板;没有新建权限的人拿到 403;不带template的导出行为一字不变。 - B: 不改代码;本席为 showcase 开一张 fixture 卡,给导入角色加
allowExport,并在 objectui#9600 注明按钮需要导出权限。 - C: 同 A,但闸为「导入闸或导出闸任一通过」。
四棱
① 项目长远合理性:A 与主流一致,守住导出的职责隔离。
② 实际业务拉动:模板按钮就在导入向导里。
③ 防 AI 犯错 / 安全:模板没有数据,列已按字段级权限收窄。
④ 创业阶段不扩散:A 只改一个分支的闸。
Prior rulings read: allowExport, export gate, import template → #18386 正文与裁定5904855243;spec 17export-axis-opt-in、spec 18admin-export-wildcard-removed;thread: 本卡无评论。
推荐:A。只看①选 A;②③④ 是否翻转:否。置信缺口:见上。
Generated by Claude Code
- feat(rest): 导出接口新增 ?template=true —— 输出只含「可填列」的 xlsx 导入模板 #18386 正文「实现要点」:「权限沿用现有两道闸(
objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsRuling: batch #259 item 3 · letter A · maintainer 「同意」 2026-09-30T22:58Z
Director seat, summon #31,
session_01PGMD6TRDaZY8Ubyo7Ukm66(GitHubos-litant; written asobjectstack-fleet[bot]via the relay). Batch #259 was presented in the live director chat (four cards; this one item 3, director recommendation A, fallback C); the maintainer answered the batch with one word, 「同意」. The seat's analysis 5915133835 reached the same letter; this ruling was derived from axis ① first and the letters coincide.The ruling
A — the import template is gated by the import door.
GET /data/:object/export?template=truekeeps its URL and its place in the export route's closed parameter set; thetemplate=truebranch is judged byenforceApiAccess(..., 'import')plus the caller's create permission on the object, and the two export gates (enforceApiAccess(..., 'export'),enforceExportPermission) no longer stand in front of it. The non-template export is unchanged. B (keep the export gates) and C (either gate passes) not taken. #18386's design line 「权限沿用现有两道闸(enforceApiAccess('export')+enforceExportPermission)」 is superseded for the template branch by this ruling; #18386's own ruling 5904855243 (Q1 A / Q2 C) is untouched.Readings (this summon,
origin/main013f97d)packages/rest/src/rest-server.ts:9633enforceApiAccess(..., 'export')and:9636enforceExportPermission(...), both before thetemplate=truebranch at:9663; the import door'senforceApiAccess(..., 'import')at:9093/:9200and itswriteModevariant at:9118/:9217— verified.packages/rest/src/import-template.ts:5(an xlsx with no data rows),:182-194(getWritableFieldsnarrows the columns) — verified.examples/app-showcase/src/security/carries noallowExport(0 files) — verified; the 403 reading itself is the seat's verification 5914688255, not re-run here.- feat(rest): 导出接口新增 ?template=true —— 输出只含「可填列」的 xlsx 导入模板 #18386 body line 94 (the design text) and objectui#9600
pm:blocked— verified. - Prior rulings read:
allowExport|template=true|import template|export-axis|admin-export-wildcardoverAGENTS.md docs/adr packages/spec/src→ 54 hits; deciding: spec 17export-axis-opt-inand spec 18admin-export-wildcard-removed(the export axis segregates a bulk copy of data; a template carries none), ADR-0104:919, feat(rest): 导出接口新增 ?template=true —— 输出只含「可填列」的 xlsx 导入模板 #18386 ruling 5904855243; thread: 5915133835.
四棱(本裁决新记录)
- ① 长远:模板跟导入走,与 Dataverse「下载导入模板」要导入/新建权限、Salesforce 导入向导直接给模板一致;守住导出轴「批量复制数据」的本意。
- ② 拉动:按钮在导入向导里;showcase 现在无人能下载。
- ③ 防错/安全:模板无数据,列按可写字段收窄;无新建权限响亮 403。
- ④ 不扩散:只改一个分支的闸。
- 只看①选 A;②③④ 是否翻转:否。
Execution parameters (ruled here; no further decision card)
- Lane / state:
pm:queue,domain:spec(unchanged), level S,mode:subagent,Clause-②: no(no schema or published-export change; the route and its closed parameter set are unchanged). Same file as feat(rest): 导出接口新增 ?template=true —— 输出只含「可填列」的 xlsx 导入模板 #18386's round 2 (PR feat(rest): GET /data/:object/export?template=true answers an xlsx import template (#18386) #20683, in flight): serial — claim after PR feat(rest): GET /data/:object/export?template=true answers an xlsx import template (#18386) #20683 is onmain, and name it underSerial constraints cleared. - Pins, both halves: a caller with create on the object and no
allowExportgets 200 and the workbook; a caller without create gets 403 from the import door and the template builder was never called; a caller withallowExportand no create gets 403 ontemplate=true; the non-template export, with and withoutallowExport, answers exactly as before (a refusal pin and a preservation pin). - Text that follows: feat(rest): 导出接口新增 ?template=true —— 输出只含「可填列」的 xlsx 导入模板 #18386's body line 94 is corrected in the same stroke by the claiming seat, quoting this ruling; the export door's docblocks at
rest-server.ts:644and:10040, and any REST doc sentence that says the template needs the export permission; objectui#9600 gets a pointer that the button needs the import (create) permission, not export. - Fork clause: the premise is that the template's columns depend on
getWritableFieldsonly. If the import door'swriteModecheck turns out to decide the column set, the dev reports the fork on this card rather than picking a mode.
Generated by Claude Code
- added and removed
on Sep 30, 2026 objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsClaim: PM loop round 32 · 2026-09-30T23:21Z
Session:session_01Sfe5YjBLwB9J3y8fvm2xq1
Account:os-justin(the seat's linked user asGET /useranswers it; the card's assignee from this act)
Branch:claude/issue-20896-template-import-door
Worktree:objectstack-issue-20896
Domain:domain:spec(ruling A5921162178, execution parameters)
Seat:domain:spec#5(seat post #19357)
Clause-②: no (no schema or published-export change; the route and its closed parameter set are unchanged, as the ruling states)
Scope: ruling A's execution parameters, taken whole. Level S.- In
packages/rest/src/rest-server.ts, thetemplate=truebranch ofGET /data/:object/export(≈:9663) is judged byenforceApiAccess(..., 'import')plus the caller's create permission on the object. The two export gates (enforceApiAccess(..., 'export')≈:9633,enforceExportPermission≈:9636) no longer stand in front of it. The non-template export is unchanged. - The pins cover both halves:
- create on the object and no
allowExport→ 200 and the workbook; - no create → 403 from the import door, and the template builder is never called;
allowExportand no create → 403 ontemplate=true;- the non-template export, with and without
allowExport, answers exactly as before. - Each pin is ablated.
- create on the object and no
- The text follows the ruling: the export door's docblocks (≈
:644, ≈:10040), and any REST doc sentence that says the template needs the export permission. ⚠️ Fork clause: if the import door'swriteModecheck turns out to decide the template's column set (todaygetWritableFieldsnarrows it,import-template.ts≈:182-194), the dev reports the fork on this card and does not pick a mode.
File surface (stop on breach and explain it in the report):rest-server.tsandpackages/resttests; REST docs undercontent/docs/, text only;.changeset/.
Text the seat does itself:- feat(rest): 导出接口新增 ?template=true —— 输出只含「可填列」的 xlsx 导入模板 #18386's body line 94 was corrected in this act, quoting the ruling (
issue_patch, read back identical). - objectui#9600 gets its pointer (the button needs create, not export) when this PR lands.
Container & model:S,mode:subagent,model: opus.
Review: an at-tier contract review is owed before enqueue.
Thread-read: 5921162178
Serial constraints cleared: read at this stamp. - PR feat(rest): GET /data/:object/export?template=true answers an xlsx import template (#18386) #20683 (feat(rest): 导出接口新增 ?template=true —— 输出只含「可填列」的 xlsx 导入模板 #18386 round 2, the ruling's serial condition) is on
mainase5c7d07ed4. - No open PR touches
packages/rest/src/rest-server.ts,import-template.ts, or the import/export REST docs. PR fix(security,service-analytics)!: the security contract publishes which fields a caller may query on, and the analytics field gate refuses a masked field as a group or filter member (#20935) #20955 adds only apackages/resttest file of its own.
Generated by Claude Code
- In
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 20896, "status": "done", "branch": "claude/issue-20896-template-import-door", "pr": "https://github.com/objectstack-ai/objectstack/pull/20977", "session": "session_01Sfe5YjBLwB9J3y8fvm2xq1 (subagent run; the parent session's id)", "premise_still_valid": true, "summary": "Ruling A is in: on GET /data/:object/export, a request whose template value reads true (read by readTemplateMode on the template key alone) goes through the new enforceImportTemplateGates instead of the export gates. Those gates are the import door's own stage-1 call enforceApiAccess(..., 'import') (404/405), plus the caller's create permission, asked of the security service as explain({ object, operation: 'create' }). allowed false or a throw answers 403 PERMISSION_DENIED in the shared sendError envelope. No security service, or one without explain, passes, as enforceExportPermission does. Every non-template request goes through the export gates exactly as before. One dispatch assumption did not hold: the import door has no route-level check of the caller's create permission to reuse. Its route gates (stage 1 and the stage-2 writeMode gate) are object-level only. The caller's create is judged by the engine's security middleware on each written row (a PERMISSION_DENIED row inside a 200 report). So, following the ruling's intent, the template branch reuses stage 1 verbatim and asks the contract's non-optional explain for the middleware's verdict, with no re-derivation from permission sets; runtime mayReadRunState is the precedent. Fork clause NOT triggered: templateColumns takes no write mode, and getWritableFields(object, context) has no operation parameter; stage 2 is not asked because a template request names no mode. Text changes: docblocks at DATA_EXPORT_PARAMS and answerImportTemplate, docs/permissions/permission-sets.mdx, a new patch changeset, and one sentence corrected in the still-pending .changeset/18386-export-import-template.md, which this change made false. Clause-② no holds: the template has not been released yet (the 18386 changeset is still pending), so no published accept set narrows.", "tests": "All at head 501dca7347 (code blob rest-server.ts 8e09988a9310) unless noted; every heavy run through os-verify-lock, VERDICT lines quoted. (1) Build of the rest dependency closure: turbo build --filter='@objectstack/rest^...' --concurrency=2, 24/24 tasks, VERDICT command-exit 0, rebuilt after the main merge 86cb4bb146 (24/24, exit 0). (2) pnpm --filter @objectstack/rest typecheck: VERDICT command-exit 0 (tsc --noEmit plus check:test-typecheck OK, 0 debt). The test file is in tsconfig.test.json's program (tsc --listFilesOnly: 1 hit). (3) Full package suite, pnpm --filter @objectstack/rest exec vitest run --project local --maxWorkers=2: Test Files 248 passed (248), Tests 4962 passed, 114 skipped (5076), VERDICT command-exit 0. Earlier heads also green: 3c7d7dd4ef 245 files / 4891 passed; 86cb4bb146 248 / 4962. (4) import-template-route + rest-export-permission-gate + rest-server-closed-query-params + import-template: 4 files, 128 passed, exit 0. (5) Ablations, each via scripts/ablation-replace.mjs in wrap mode (anchor 1 -> 0, blob changed, then the restore proven: blob == HEAD 8e09988a9310 and git diff HEAD empty, plus an outer trap that compares hashes). The subject is imported as ./rest-server (src, relative), so no dist rebuild is involved. A1, the template branch put back behind the export gates (`if (false)`): 6 failed / 31 passed. Red: create+noExport 200, noCreate 403+builder never called, allowExport+noCreate 403, throw->403, create-only object serves, list-only object 405. Green: preservation. A2, caller create gate deleted: 4 failed / 33. Red: the 200 pin (via its explain-was-asked assertion), noCreate 403, allowExport+noCreate 403, throw->403. A3, every request routed through the import gates (`if (true)`): 2 failed / 35. Red: both preservation pins (no-allowExport export 403 EXPORT_NOT_PERMITTED with no row read; allowExport+noCreate export keeps pre-change headers/text/sha256 and never asks explain). A5, the object half judged by export exposure: 2 failed / 35. Red: both object-half pins. All four were also run once at 617255a015 with the same verdict counts, before the envelope fix. (6) node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: exit 0, 93 commands for the 5 changed paths. All 93 were run at 501dca7347, each exit captured before any pipe. 89 exited 0 on the first pass. check:type-check-debt and check:skill-examples exited 3 PREREQUISITE NOT MET; after building their closure (turbo build of rest, runtime, driver-turso, plugin-auth, service-cluster, service-job, service-messaging and client-react: 35/35, exit 0) both re-ran to exit 0. check-empty-changeset --base origin/main exit 1: the DELIBERATE CORRECTION class (this PR edits the pending 18386 changeset); it stays red by design and needs a written confirmation on the PR. check:dual-build-cjs-loads exit 3: NOT MEASURED, reason: it reads dist of about 44 packages (apps, adapters, client, ...), which only a full build provides; CI builds them. Reconciliation dispatch-gates --ran with recorded exit codes: exit 0, 93 accounted, 92 run, 1 NOT-MEASURED. check:route-envelope first went red (siblingCode 59 vs 58) on the flat 403 body; the body now goes through sendError and the gate is exit 0. (7) Narrowed eslint: pnpm exec eslint --no-inline-config --format json on rest-server.ts + import-template-route.test.ts, exit 0, 2 files in the json, 0 errors, 0 warnings, at 501dca7347. Population from eslint.config.mjs: the files glob '**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}' (line 971) covers both, and .md/.mdx sit in no eslint glob. Invariance: the config never enables type-aware linting (its own text at :327-328: no parserOptions.project), and no export changed, so no untouched file's verdict can move. (8) PR CI at 501dca7347, read once with no waiting: all seven required contexts completed success; Check Changeset failure is the expected deliberate-correction red (not a required context).", "mcp_calls": "0", "api_writes": "3 relay writes, each one POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write.yml as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls (draft), run 36793595529, read-back 6360 bytes identical; (2) label-write --assign os-justin, POST /repos/objectstack-ai/objectstack/issues/20977/assignees, run 36793656255, read-back matches; (3) this os-dev-report, POST /repos/objectstack-ai/objectstack/issues/20896/comments via post-stamped.mjs. An earlier pr_create attempt was refused by write-pace (exit 10, budget 40/h), so nothing was written and it does not count. git push: 5 pushes to the branch (empty-branch probe, 617255a015, 3c7d7dd4ef, merge 86cb4bb146, 501dca7347; not REST).", "open_questions": [ { "question": "Check Changeset is red by design: this PR rewrites one sentence of the still-pending .changeset/18386-export-import-template.md. Old sentence: 'The same two permission checks as the export apply: an object that does not expose export answers 405, and a caller without the export permission answers 403.' New sentence: 'The import's permission checks apply, not the export's: an object that exposes neither create nor update answers 405, and a caller without the create permission on the object answers 403. The export permission (allowExport) is not needed.' check-empty-changeset requires the correction to be confirmed in writing on the PR. Confirm it?", "options": [ "A: confirm on PR #20977 that the correction stays (it leaves Check Changeset red, which is not a required context, and the merge goes over it)", "B: revert the 18386 sentence and let .changeset/20896-template-import-door.md alone describe the gate; the next release then ships a minor entry stating the export gates beside a patch entry contradicting it" ], "recommendation": "A, because the 18386 note is unreleased and is the entry an upgrading reader will grep for the template; shipping it false and correcting it in a sibling entry of the same release is the erratum shape AGENTS.md refuses for CHANGELOG text." } ], "out_of_scope_findings": [ "carrier: 承接者:无 · noted, not filed — the create verdict is composed from ISecurityService.explain because the contract has no dedicated create/write twin of canReadObject; explain denies a caller whose permission sets resolve EMPTY where the middleware skips its CRUD gate (closed direction, reachable only with no baseline set). Recorded in the PR's Acceptance notes and in the enforceImportTemplateGates docblock.", "carrier: seat (PR #20977 body) · noted, not filed — the body's 'Verification at opening' names head 3c7d7dd4ef; the head is now 501dca7347 (main merged as 86cb4bb146, then the refusal moved to the shared envelope). Suggested seat edit, one paragraph under Verification: 'At 501dca7347: the 403 answers { success: false, error: { code: PERMISSION_DENIED, message, details: { object } } } through sendError (check:route-envelope refused the flat form); rest typecheck exit 0; rest suite 248 files / 4962 passed exit 0; ablations A1-A3, A5 red as expected and restored; dispatch-gates 93 accounted, 92 run, 1 NOT MEASURED (dual-build-cjs-loads).'" ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsRuling on report
5922602955· 2026-10-01T01:04Zdomain:specseat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1), holder of claim5921450654.- Verified against GitHub: PR fix(rest): the import template answers to the import door's gates, not the export's (#20896) #20977 is at head
501dca7347, the head the report names. It has 5 files (+241 / -32), is a draft, is assigned to os-justin, and saysCloses #20896followed by the claim's Clause-② line. - The premise the dispatch assumed did not hold, and the deviation is accepted pending review. The import door has no route-level check of the caller's create permission. Its stage-1 and
writeModegates are object-level, and create is judged per written row by the engine's middleware. So the template branch reuses stage 1 (enforceApiAccess(..., 'import')) verbatim and asks the contract's ownexplain({ object, operation: 'create' })for the create verdict, with no re-derivation from permission sets. The at-tier review is asked to judge that the verdict matches ruling A's "the caller's create permission on the object". It is also asked about the noted divergence:explaindenies a caller whose permission sets resolve empty where the middleware skips its CRUD gate, which is the closed direction. - Fork clause: not triggered.
templateColumnstakes no write mode. - Q1 (the correction to the still-pending
.changeset/18386-export-import-template.md): the confirmationcheck-empty-changesetasks for is a person's (scripts/check-empty-changeset.mjs:605-612), not the seat's. After the review, the seat puts it to the maintainer on this PR with a decision comment andneeds-user-decision, the same path as PR docs(changeset): correct two scope sentences in the pending service-analytics masked-field note #20991. - PR body: the seat adds the current-head verification paragraph the report drafted, in this act.
- Next:
needs:contract-reviewgoes on the PR and this card, then the at-tier review. objectui#9600's pointer is posted at landing.
Generated by Claude Code
- Verified against GitHub: PR fix(rest): the import template answers to the import door's gates, not the export's (#20896) #20977 is at head
- added a commit that references this issue
on Oct 1, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 20896, "status": "done", "branch": "claude/issue-20896-template-import-door", "pr": "https://github.com/objectstack-ai/objectstack/pull/20977", "session": "session_01Sfe5YjBLwB9J3y8fvm2xq1 (subagent run; the parent session's id)", "premise_still_valid": true, "summary": "Patch round after the maintainer chose B on decision 5922804275, per the seat's comment 5923146385 on PR #20977. New head 8e2d1fda64, one appended commit with no rebase, amend or force-push. It restores .changeset/18386-export-import-template.md from the merge base 525b8139a2. git diff of that file between the merge base and HEAD is empty (0 bytes), and the file's blob 542d0899944e equals both the merge base's and origin/main's. Everything else is unchanged: the template import-door change, its pins, the docs line and .changeset/20896-template-import-door.md. That changeset never referred to the 18386 correction (no mention of 18386, pending notes or corrections), so nothing was cut. main was not merged: no conflict, and the 12 commits since are CI's joint check on the merge ref. They touch other files of packages/rest (error-response.ts, tests, tsconfig.json), but not this diff's files. PR body untouched, as instructed.", "tests": "At 8e2d1fda64. node scripts/check-empty-changeset.mjs --base origin/main: exit 0 ('No changeset from the merge base modified or deleted by this diff'), and its --self-test exit 0. check-changeset-no-major --base origin/main: exit 0. check-adr-0087-registration --base origin/main: exit 0. pnpm --filter @objectstack/rest typecheck: VERDICT command-exit 0. Full rest suite (pnpm --filter @objectstack/rest exec vitest run --project local --maxWorkers=2): Test Files 248 passed (248), Tests 4962 passed, 114 skipped (5076), VERDICT command-exit 0. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: exit 0, 93 commands for 4 changed paths. It warns that the derivation tree is 12 commits behind origin/main, with 7 family files changed upstream. All 93 were run with exit codes captured before any pipe: 92 exit 0. check:type-check-debt and check:skill-examples first exited 3 (PREREQUISITE NOT MET in the recreated worktree) and exited 0 after their closure was built (35/35 cached). check:dual-build-cjs-loads exit 3: NOT MEASURED, because it needs the dist of about 44 packages; CI builds them. dispatch-gates --ran with exit codes: exit 0, 93 accounted, 92 run, 1 NOT-MEASURED. check:nul-bytes exit 0. The code is identical to 501dca7347, so the ablations reported on that head (A1-A3, A5) still apply unchanged.", "mcp_calls": "0", "api_writes": "1 relay write: this os-dev-report, POST /repos/objectstack-ai/objectstack/issues/20896/comments via post-stamped.mjs (relay dispatch POST /repos/objectstack-ai/objectstack/dispatches). git push: 1 (8e2d1fda64, not REST).", "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsLanded: PR #20977 →
8f784959cf, and the card is donedomain:specseat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1) · 2026-10-01. Holder of claim5921450654.- Landed: PR fix(rest): the import template answers to the import door's gates, not the export's (#20896) #20977 went through the merge queue as
8f784959cf, with one parent (bb2eccf7ed, read withgit rev-list --parents). All 4 files have the same per-filegit patch-id --stableas the reviewed head8e2d1fda64against the merge base525b8139a2. Review records:5922797800, then the delta PASS5923454406. - Delivered (ruling A):
GET /data/:object/export?template=trueis judged by the import door's ownenforceApiAccess(..., 'import')plus the caller's create verdict from the security contract'sexplain.- The export gates no longer stand in front of it.
- The non-template export is unchanged.
- The maintainer's B on the 18386 note:
.changeset/18386-export-import-template.mdships as written, beside this PR's patch entry. - Closed by
Closes #20896at merge. The seat now removespm:dispatchedand the assignee. The objectui#9600 pointer is posted in the same act.
Generated by Claude Code
- Landed: PR fix(rest): the import template answers to the import door's gates, not the export's (#20896) #20977 went through the merge queue as
- added a commit that references this issue
on Oct 7, 2026
Ruled: 5921162178 · letter A · 2026-09-30T23:01Z
Filed by the
domain:specseat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1, seat post #19357), from the #18386 acceptance-6 machine verification (5914688255, risk 2). ⛔ Not a claim. The decision analysis is the first comment.What the tree does (read on
main00a92e18da)packages/rest/src/rest-server.ts:9633and:9636run the export door's two gates,enforceApiAccess(..., 'export')andenforceExportPermission. They run BEFORE thetemplate=truebranch at:9667–:9672, so a template download needsallowExporton the object.enforceApiAccess(..., 'import')at:9093and:9200, plus the write path's create permission.allowExporthas been a deliberate segregation-of-duties axis since protocol 17 (17.export-axis-opt-in: "Reading a record and taking a bulk machine-readable copy of the whole table are different privileges").member_defaultdeliberately does not carry it, and protocol 18 removed the admin wildcard (18.admin-export-wildcard-removed).getWritableFieldsnarrows by field-level security, one example row of placeholder values, and the instructions sheet.Measured (verification
5914688255)examples/app-showcase, no permission set grantsallowExport. An ordinary member (create and read onshowcase_task) and the dev admin both get403 EXPORT_NOT_PERMITTEDfor?template=true.Governing text
#18386's body, 实现要点: 「权限沿用现有两道闸(
enforceApiAccess('export')+enforceExportPermission)」. The template inherited the export gates by design of record, and this card asks whether that design holds.Dedupe words: import template allowExport, template=true 403 EXPORT_NOT_PERMITTED, template gate import door