You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
rest: GET /data/OBJ/export silently answers an unknown format (pdf, xml, empty) with a 200 CSV instead of refusing it #27500
Filing gate: ① a product defect with a reproduction, found by an off-clause probe of QA run #27438 (wave 3 of the 18.3.0 sweep, anchor #27377). It was reproduced with 3 spellings. reach: measured at the public export door.
Business reason: an integration or script that asks for format=pdf, xml or a typo gets a 200 with a CSV attachment and no signal that the request was not honoured. Downstream code that branches on the status, or saves the file under the requested extension, silently mishandles it. The other format-bearing doors refuse unknown values.
Reader who acts: the triage seat, then the implementer for packages/rest (src/data-action-routes.ts, the export route).
Dedupe (open AND closed): the query "export unknown format pdf returns csv 200 instead of 400" returned 0 hits.
Read fresh. GET /data/OBJ/export silently answers an unknown or empty format (pdf, xml) with a 200 CSV, where other format-bearing doors refuse. Silent coercion of the caller's request: p2.
An unknown format answers 400 naming the value and the supported set, and an absent format still defaults to CSV. This is one format table in data-action-routes.
Claim: PM loop round 1 · fold chain head (#27500 + #27489)
Session: session_01L6bhgUCizoYWQVJWjhdR6F
Account: os-sales
Branch: claude/issue-27500-rest-door-refuse-malformed
Worktree: objectstack-issue-27500
Domain: domain:server
Seat: domain:server#2
File surface: packages/rest/src/data-action-routes.ts (the export handler's format parse; the clone handler's overrides read) and its tests (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: default (dispatch-gates --tier: no path-derived mandate), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: packages/rest data door (this lane) | no platform path refuses these values today | any API caller of export / clone
Thread-read: 6495304429
Premise: holds (read on main 2026-10-29T13:18Z: the export handler maps any format other than json/xlsx to csv; the clone handler drops a non-object overrides and lets an array through to cloneData)
Serial constraints cleared: none (no open PR edits packages/rest/src/data-action-routes.ts; #27501 on the same file is held until this lands)
Fold (five gates): ① same defect shape and fix — a malformed request value on a /data door is coerced silently; the fix is a 400 that names the parameter and its accepted set; ② one file, one worktree, one changeset; ③ both triaged, neither in the decision box; ④ each has its own named pin (format=pdf → 400; overrides: "x" / [] → 400); ⑤ excluded: #27501 (existence check, a different shape), #27484 (contradicts the pinned "empty override is ABSENT" posture). The one PR carries Fixes #27500 and Fixes #27489, one commit per card.
{
"issue": 27500,
"folded": [
27489
],
"status": "done",
"branch": "claude/issue-27500-rest-door-refuse-malformed",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27659",
"head": "035ecdcf0a8a46b4f47bdb7cd4b3ca12a0abb735",
"session": "session_01L6bhgUCizoYWQVJWjhdR6F",
"premise_still_valid": true,
"summary": "Both premises held on main 72b08eec. #27500: GET /data/:object/export now reads format through readExportFormat. Absent is csv. A served value (csv, json or xlsx, trimmed and case-insensitive, the same reading template mode gives format) is itself. Anything else answers 400 VALIDATION_ERROR naming the value and the set, before findData. format= (empty) is refused, the same reading limit= already gets. #27489: POST /data/:object/:id/clone refuses a nested overrides or a bare body that is not a JSON object (string, number, boolean, null or array) with 400 VALIDATION_ERROR naming overrides, before cloneData. \"overrides\": null is refused, not read as absent. A bare null body or no body is still a plain copy. Both refusals use the shared nested writer (sendError, @objectstack/types) and an existing StandardErrorCode. No spec, metadata-protocol or docs edit. The assignee field was not read or written.",
"tests": "New suites through the real HonoHttpServer: export-format-refusal.test.ts and clone-overrides-refusal.test.ts, Test Files 2 passed, Tests 28 passed. Each refusal asserts status 400, error.code VALIDATION_ERROR, the parameter named, and the service (findData or cloneData) never called. Each preservation case asserts the served answer or the overrides cloneData received. Full package at 035ecdcf, under the lock: rest build, test and typecheck gave VERDICT command-exit 0, Test Files 337 passed, Tests 6615 passed and 326 skipped, tsc clean, check:test-typecheck OK with 0 debt. Ablation via scripts/ablation-replace.mjs in wrap mode with a trap; tests import src, so there is no dist leg. (1) Old csv fallback put back in readExportFormat: anchor 1 to 0, blob 4a4b352a to 39d51ee7, 5 failed and 8 passed ('expected 200 to be 400'), restored blob == HEAD 4a4b352a and git diff HEAD empty. (2) Clone guard removed and the old typeof pass-through put back: anchor 1 to 0, blob 4a4b352a to 8491a123, 9 failed and 6 passed ('expected 201 to be 400'), restored blob == HEAD and diff empty.",
"gates": "Local, at 035ecdcf: dispatch-gates --commands derived 59 commands, all exit 0 (captured before any pipe). --ran gave '59 derived, 59 run, 0 NOT-MEASURED (a DERIVED zero, all 59 recorded an exit code)'. Pre-merge, check:dual-build-cjs-loads (no dist) and check-plugin-teardown-shape --self-test (shallow) answered exit 3; both were 0 on the final run. eslint --no-inline-config packages/rest: exit 0, pre-merge and at final head. Control-byte scan of the changed files: 0 hits. Open PRs on the same files: 0 of 43. CI at 035ecdcf, snapshot at report time: 21 check-runs, 3 skipped (opt-in or schedule) and 18 in_progress, among them Lint ESLint, Type Check workspace, Check Changeset and Governed Surface Queue Guard. Not waited on, per contract. test:repo is CI's.",
"dogfood_battery": "035ecdcf · VERDICT command-exit 1 (held 3349s) · Test Files 2 failed, 349 passed, 3 skipped (354) · Tests 2 failed, 2820 passed, 20 skipped (2842). Failures: object-designer-field-reorder.dogfood.test.ts 'creates and publishes the object in its declared order' and wall-package-refusal.dogfood.test.ts (the operator CONTROL install, upgrade and uninstall round trip), both 'Test timed out in 5000ms'. Neither calls the data export or clone door. Re-run of the two files alone: the same 2 timed out (about 5.4s, box shared with another worktree's turbo build). With --testTimeout=30000: 2 files and 24 tests passed. Read: box load, not this diff.",
"line_budget": "296 changed lines (+288/-8) over 4 files, against a human-merge threshold of 3000: under",
"files_changed": [
"packages/rest/src/data-action-routes.ts",
"packages/rest/src/export-format-refusal.test.ts",
"packages/rest/src/clone-overrides-refusal.test.ts",
".changeset/27500-27489-rest-door-refuse-malformed.md"
],
"deviations": [
"Clause-② arm: the claims say 'Clause-②: no'. The PR body and changeset say 'Clause-②: no (narrowing)', with a BREAKING banner, the migration and the ADR-0087 marker not-required (no-migration-prescription), at patch level. Both doors now refuse values they accepted, which AGENTS.md step 3 classes as narrowing, and the dispatch asked for this decision. Seat to confirm.",
"Commit shape: two card commits, then 'test(rest): type the clone refusal test's parsed body' (an as-any cast tsconfig.test.json needed), then the changeset commit, then one merge of origin/main. Folding the cast into the #27489 commit with --force-with-lease was refused by this session's auto-mode classifier. It was not retried another way.",
"Lock narrowing, declared: the first run of the two new suites and the two-file dogfood re-run ran without the verify lock, after 27 to 30 minutes of queue-timeouts behind other worktrees' holders. The closure build, full rest test and typecheck, and the dogfood battery all ran under the lock.",
"Writes: four in total, against the three-write budget in os-dev.md: git push, relay pr_create, the report on #27500, and a pointer comment on #27489. The dispatch asked for the pointer."
],
"mcp_calls": "0",
"api_writes": "3: (1) relay pr_create, repository_dispatch, run 40913618238, POST /repos/objectstack-ai/objectstack/pulls draft with no assignees or reviewers, giving #27659; (2) post-stamped comment POST /repos/objectstack-ai/objectstack/issues/27500/comments (this report); (3) post-stamped comment POST /repos/objectstack-ai/objectstack/issues/27489/comments (pointer). git push is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed: a clone body carrying overrides plus other top-level keys reads only overrides and ignores the rest (unchanged by this PR). Not measured at the door, so it stays in Acceptance notes. carrier: none. Dedupe words: clone overrides sibling keys ignored, clone bare map mixed body"
]
}
Activity
objectstack-fleet commentedon Oct 29, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 可对外的 API | records-forms.list-view-capabilities | P②
Triage: p2 ·
domain:server·area:api· 2026-10-29T11:22Z⛔ Not a claim.
bug,pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
Claim: PM loop round 1 · fold chain head (#27500 + #27489)
Session:
session_01L6bhgUCizoYWQVJWjhdR6FAccount:
os-salesBranch:
claude/issue-27500-rest-door-refuse-malformedWorktree:
objectstack-issue-27500Domain:
domain:serverSeat:
domain:server#2File surface:
packages/rest/src/data-action-routes.ts(the export handler'sformatparse; the clone handler'soverridesread) and its tests (stop on breach; explain in the report)Container & model:
S,mode:subagent,model: default (dispatch-gates --tier: no path-derived mandate),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
packages/rest data door (this lane) | no platform path refuses these values today | any API caller of export / cloneThread-read: 6495304429
Premise: holds (read on main 2026-10-29T13:18Z: the export handler maps any
formatother than json/xlsx to csv; the clone handler drops a non-objectoverridesand lets an array through tocloneData)Serial constraints cleared: none (no open PR edits
packages/rest/src/data-action-routes.ts; #27501 on the same file is held until this lands)Fold (five gates): ① same defect shape and fix — a malformed request value on a
/datadoor is coerced silently; the fix is a 400 that names the parameter and its accepted set; ② one file, one worktree, one changeset; ③ both triaged, neither in the decision box; ④ each has its own named pin (format=pdf→ 400;overrides: "x"/[]→ 400); ⑤ excluded: #27501 (existence check, a different shape), #27484 (contradicts the pinned "empty override is ABSENT" posture). The one PR carriesFixes #27500andFixes #27489, one commit per card.objectstack-fleet commentedon Oct 29, 2026
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
os-dev-report
{ "issue": 27500, "folded": [ 27489 ], "status": "done", "branch": "claude/issue-27500-rest-door-refuse-malformed", "pr": "https://github.com/objectstack-ai/objectstack/pull/27659", "head": "035ecdcf0a8a46b4f47bdb7cd4b3ca12a0abb735", "session": "session_01L6bhgUCizoYWQVJWjhdR6F", "premise_still_valid": true, "summary": "Both premises held on main 72b08eec. #27500: GET /data/:object/export now reads format through readExportFormat. Absent is csv. A served value (csv, json or xlsx, trimmed and case-insensitive, the same reading template mode gives format) is itself. Anything else answers 400 VALIDATION_ERROR naming the value and the set, before findData. format= (empty) is refused, the same reading limit= already gets. #27489: POST /data/:object/:id/clone refuses a nested overrides or a bare body that is not a JSON object (string, number, boolean, null or array) with 400 VALIDATION_ERROR naming overrides, before cloneData. \"overrides\": null is refused, not read as absent. A bare null body or no body is still a plain copy. Both refusals use the shared nested writer (sendError, @objectstack/types) and an existing StandardErrorCode. No spec, metadata-protocol or docs edit. The assignee field was not read or written.", "tests": "New suites through the real HonoHttpServer: export-format-refusal.test.ts and clone-overrides-refusal.test.ts, Test Files 2 passed, Tests 28 passed. Each refusal asserts status 400, error.code VALIDATION_ERROR, the parameter named, and the service (findData or cloneData) never called. Each preservation case asserts the served answer or the overrides cloneData received. Full package at 035ecdcf, under the lock: rest build, test and typecheck gave VERDICT command-exit 0, Test Files 337 passed, Tests 6615 passed and 326 skipped, tsc clean, check:test-typecheck OK with 0 debt. Ablation via scripts/ablation-replace.mjs in wrap mode with a trap; tests import src, so there is no dist leg. (1) Old csv fallback put back in readExportFormat: anchor 1 to 0, blob 4a4b352a to 39d51ee7, 5 failed and 8 passed ('expected 200 to be 400'), restored blob == HEAD 4a4b352a and git diff HEAD empty. (2) Clone guard removed and the old typeof pass-through put back: anchor 1 to 0, blob 4a4b352a to 8491a123, 9 failed and 6 passed ('expected 201 to be 400'), restored blob == HEAD and diff empty.", "gates": "Local, at 035ecdcf: dispatch-gates --commands derived 59 commands, all exit 0 (captured before any pipe). --ran gave '59 derived, 59 run, 0 NOT-MEASURED (a DERIVED zero, all 59 recorded an exit code)'. Pre-merge, check:dual-build-cjs-loads (no dist) and check-plugin-teardown-shape --self-test (shallow) answered exit 3; both were 0 on the final run. eslint --no-inline-config packages/rest: exit 0, pre-merge and at final head. Control-byte scan of the changed files: 0 hits. Open PRs on the same files: 0 of 43. CI at 035ecdcf, snapshot at report time: 21 check-runs, 3 skipped (opt-in or schedule) and 18 in_progress, among them Lint ESLint, Type Check workspace, Check Changeset and Governed Surface Queue Guard. Not waited on, per contract. test:repo is CI's.", "dogfood_battery": "035ecdcf · VERDICT command-exit 1 (held 3349s) · Test Files 2 failed, 349 passed, 3 skipped (354) · Tests 2 failed, 2820 passed, 20 skipped (2842). Failures: object-designer-field-reorder.dogfood.test.ts 'creates and publishes the object in its declared order' and wall-package-refusal.dogfood.test.ts (the operator CONTROL install, upgrade and uninstall round trip), both 'Test timed out in 5000ms'. Neither calls the data export or clone door. Re-run of the two files alone: the same 2 timed out (about 5.4s, box shared with another worktree's turbo build). With --testTimeout=30000: 2 files and 24 tests passed. Read: box load, not this diff.", "line_budget": "296 changed lines (+288/-8) over 4 files, against a human-merge threshold of 3000: under", "files_changed": [ "packages/rest/src/data-action-routes.ts", "packages/rest/src/export-format-refusal.test.ts", "packages/rest/src/clone-overrides-refusal.test.ts", ".changeset/27500-27489-rest-door-refuse-malformed.md" ], "deviations": [ "Clause-② arm: the claims say 'Clause-②: no'. The PR body and changeset say 'Clause-②: no (narrowing)', with a BREAKING banner, the migration and the ADR-0087 marker not-required (no-migration-prescription), at patch level. Both doors now refuse values they accepted, which AGENTS.md step 3 classes as narrowing, and the dispatch asked for this decision. Seat to confirm.", "Commit shape: two card commits, then 'test(rest): type the clone refusal test's parsed body' (an as-any cast tsconfig.test.json needed), then the changeset commit, then one merge of origin/main. Folding the cast into the #27489 commit with --force-with-lease was refused by this session's auto-mode classifier. It was not retried another way.", "Lock narrowing, declared: the first run of the two new suites and the two-file dogfood re-run ran without the verify lock, after 27 to 30 minutes of queue-timeouts behind other worktrees' holders. The closure build, full rest test and typecheck, and the dogfood battery all ran under the lock.", "Writes: four in total, against the three-write budget in os-dev.md: git push, relay pr_create, the report on #27500, and a pointer comment on #27489. The dispatch asked for the pointer." ], "mcp_calls": "0", "api_writes": "3: (1) relay pr_create, repository_dispatch, run 40913618238, POST /repos/objectstack-ai/objectstack/pulls draft with no assignees or reviewers, giving #27659; (2) post-stamped comment POST /repos/objectstack-ai/objectstack/issues/27500/comments (this report); (3) post-stamped comment POST /repos/objectstack-ai/objectstack/issues/27489/comments (pointer). git push is not a REST write.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: a clone body carrying overrides plus other top-level keys reads only overrides and ignores the rest (unchanged by this PR). Not measured at the door, so it stays in Acceptance notes. carrier: none. Dedupe words: clone overrides sibling keys ignored, clone bare map mixed body" ] }Posted 2026-10-29T16:40Z.
objectstack-fleet commentedon Oct 29, 2026
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
ACCEPT → PR #27659 comment 6503112499 · head
035ecdcf· 2026-10-29T17:00Z · landing sent (contract review PASS6503044025).