Skip to content

docs(spec): import job failed also covers a job whose every row failed - #28087

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-ko6etz
Oct 30, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-ko6etz

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread
Fixes #27734

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:只改 spec 里导入任务状态的说明文字:failed 不再只写「致命错误中止」,也写明「至少有一行、但没有任何一行被创建、更新或跳过」的任务同样以 failed 结束;error 字段的说明同步补上这一情形,并重新生成参考文档。
  • 为什么改:服务端按裁决 B(记录 6509036166)已在 fix(rest)!: an async import job whose every row failed ends failed, not succeeded #27795 合并,全部行失败的任务现在结束为 failed;spec 仍说 failed 只是致命中止,客户端和 AI 作者读到的含义比实际行为窄。
  • 风险与回滚:零运行时风险,没有枚举值、键、类型或导出变化,线上形状不变;回滚即还原这三个文件。
  • 先看:packages/spec/src/api/export.zod.ts(ImportJobStatus 文档块与两行注释、error 的 describe);content/docs/references/api/export.mdx(生成结果);.changeset/27734-import-job-failed-wording.md。
  • 两分钟验证:pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec check:generated,预期 14 个产物全部最新。
  • 需要你做的:无,席位落地。

What changes

  • ImportJobStatus (packages/spec/src/api/export.zod.ts). The docblock now states the terminal rule the REST worker applies since fix(rest)!: an async import job whose every row failed ends failed, not succeeded #27795: a job that had at least one row and created, updated or skipped none of them ends failed, as does a job aborted on a fatal error; a job with no rows, an all-skipped job and a partly-failed job end succeeded; a dry run follows the same rule; a cancel wins over both. The succeeded / failed value comments are reworded to match.
  • ImportJobProgressSchema.error. The describe was "Fatal error message (when status = failed)". It now says it carries either the fatal error or, when every row failed, one sentence pointing at the job's results.
  • content/docs/references/api/export.mdx. Regenerated by gen:docs (two table rows: progress and results both show the error describe).
  • Changeset 27734-import-job-failed-wording.md, @objectstack/spec: patch (describe text ships in the package).

No enum value, key, type or export changes; the wire is unchanged. The sentence was written from PR #27795's diff (packages/rest/src/data-action-routes.ts: everyRowFailed = finalStatus === 'succeeded' && total > 0 && created + updated + skipped === 0, decided after every cancel re-check).

Tests

No behaviour changed, so no test was added and no ablation applies (prose only).

Acceptance notes

  • Out of lane, routed to the seat: packages/platform-objects/src/audit/sys-import-job.object.ts:70 still labels the error column Fatal Error. After ruling B that column also carries the "every row failed" sentence, so the label is stale the same way (note 6512498465). It is domain:server, so this PR leaves it alone.
  • The ImportJobStatus enum-value comments are TS comments, not .describe(), so they do not reach the generated reference; the docblock and the error describe are what readers of the spec see.

Verification

All readings on head 4765d36c (worktree off origin/main 2e955dde).

Spec build, generated artifacts, tests, typecheck
  • pnpm --filter @objectstack/spec build :: exit 0
  • pnpm --filter @objectstack/spec check:generated :: exit 1 before regeneration, naming exactly one stale artifact (content/docs/references/**); gen:docs :: exit 0, then check:generated is part of the sweep below :: exit 0
  • pnpm --filter @objectstack/spec typecheck :: exit 0
  • pnpm --filter @objectstack/spec test :: exit 0 — Test Files 756 passed (756), Tests 22510 passed, 1 todo
Derived gates (`dispatch-gates --commands`, 97 families)
  • 97 derived, 94 run with exit 0, 3 NOT MEASURED (exit 3, prerequisite not met), 0 unrun; dispatch-gates --ran :: exit 0.
  • Four doc/skill gates first exited 3 for an unbuilt @objectstack/lint / @objectstack/client-react; after turbo run build --filter='@objectstack/lint...' --filter='@objectstack/client-react...' (38/38 tasks) they re-ran :: exit 0 (check:doc-formula-expressions, check:doc-security-posture, check:skill-examples, check:docs-transcript-drift).
  • NOT MEASURED, left to CI: check:dual-build-cjs-loads and check:lean-entry-closure (both need a whole-workspace pnpm build), check-plugin-teardown-shape.mjs --self-test (needs an unshallow clone; it grades the checker, not this diff).

Generated by Claude Code

The REST import worker now ends a job that had rows and created, updated
or skipped none of them `failed` (maintainer ruling B, record 6509036166).
`ImportJobStatus` and `ImportJobProgressSchema.error` described `failed`
as a fatal abort only; reword them to the ruled meaning. No enum value,
key or type changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibz3A9wkHngU9obhv1g
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Oct 30, 2026
@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 5 documentable anchor(s).

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/protocol/objectql/state-machine.mdx (via /api/v1/data/import/jobs/:jobId (route, bridged from symbol ImportJobProgressSchema — its route source's handler names it))
What this run could not see
  • the SDK route bridge reached 58 of 210 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 168 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json b6c2ed9c4dd83ce986cb3d198fb82a8f3f7df5fe → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 92314971af2662831beed6a339cd9d6bf393bb05 — the merge of head 154d26cb727ffed16cc80d1522d604c2a68e7695 into base b6c2ed9c4dd83ce986cb3d198fb82a8f3f7df5fe, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 92314971af2662831beed6a339cd9d6bf393bb05 && git checkout 92314971af2662831beed6a339cd9d6bf393bb05
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b6c2ed9c4dd83ce986cb3d198fb82a8f3f7df5fe 154d26cb727ffed16cc80d1522d604c2a68e7695 && git checkout -B drift-repro b6c2ed9c4dd83ce986cb3d198fb82a8f3f7df5fe && git merge --no-ff 154d26cb727ffed16cc80d1522d604c2a68e7695

node scripts/docs-audit/affected-docs.mjs --json b6c2ed9c4dd83ce986cb3d198fb82a8f3f7df5fe

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs b6c2ed9c4dd83ce986cb3d198fb82a8f3f7df5fe → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head 4765d36c14172e30c64464551d309cb19a70ffc8 · 2026-10-30T12:58Z


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4765d36c14172e30c64464551d309cb19a70ffc8
Local-runs: none
Scope: packages/spec/src/api/export.zod.ts + packages/rest/src/data-action-routes.ts (the worker's terminal-status block and IMPORT_JOB_EVERY_ROW_FAILED), packages/core/src/utils/import-runner.ts (the counters, the dry-run preview path, the cancel checkpoint), packages/rest/src/rest-server.ts (importJobToProgress), packages/rest/src/import-job-integration.test.ts (the ruling's pins), content/docs/references/api/export.mdx (the regenerated reference)

Read against origin/main at 59547058aeb3eccde6f267293367dabf5ef82e59, which carries the server half (PR #27795, ruling B, record 6509036166). The 55 check-runs on the head are all success or skipped; none failed. The rest of the diff (the changeset and the regenerated export.mdx) is the lane seat's ACCEPT at TIER_DEFAULT; the cited code was read, never reviewed.

① Derived judgments

  • Accept set: unchanged — right. ImportJobStatus keeps the same five values in the same order; ImportJobProgressSchema.error stays z.string().optional(); no key, type, default or constraint moves anywhere in the hunk. Nothing a client sends or receives parses differently.
  • Public surface: unchanged — right. No export added, removed or renamed; the ImportJobStatus type alias is untouched, so check:api-surface owes nothing and the Spec property liveness / Type Check · workspace check-runs confirm it.
  • Generated artifacts: complete — right. A .describe() change owes gen:schema && gen:docs. At the head the only tree copies of the progress describes are export.zod.ts and content/docs/references/api/export.mdx; the stale sentence "Fatal error message (when status = failed)" is gone from the whole tree, and both error rows of the reference (progress table and the results table, since ImportJobResultsSchema extends the progress schema) carry the new text. Lint & Repo Gates is green.
  • The enum-value comments do not reach the reference — right, and acknowledged in the PR. They are TS line comments, not .describe(); the docblock and the error describe are what the generated page shows, so the docblock is where the rule was written out in full. Correct placement.
  • Docs-drift advisory — no re-verify owed. The bot lists content/docs/protocol/objectql/state-machine.mdx via the /import/jobs/:jobId route bridge; that page's two import mentions (lines 114 and 117) are about treatAsHistorical and the undo, and say nothing about the terminal status. The dev's claim that no hand-written page states the old rule holds on main as well (grep of content/docs outside references: zero hits for a fatal-only failed).
  • Metadata-protocol: none — right. The DTO is an API response shape, not an authorable metadata key; nothing an author writes changes.

② Semver level

  • @objectstack/spec: patch — right. The describe text ships in the package (zod runtime metadata and the emitted .d.ts docblock), so the diff publishes something and skip-changeset would be wrong; the kind is none, which takes patch.
  • Clause-②: no, line-initial on both the PR body and the changeset body, no arm — right: the accept set is neither wider nor narrower, and the public surface is unchanged. Metadata-protocol: none on both. Check Changeset is green on the head.
  • The changeset body names the ruling, the unchanged wire, and both reworded surfaces; it carries no BREAKING banner and owes none.

③ Boundary flags

  • Dev flag, out of lane: packages/platform-objects/src/audit/sys-import-job.object.ts:70 labels the error column Fatal Error. Confirmed stale on main (the generated en.objects.generated.ts:3050 repeats the label). It is domain:server, outside this card's file surface as the claim 6521811858 drew it, and already on the board as note 6512498465 plus the dev report's out_of_scope_findings. Escalated to the seat for routing; not a blocker for this PR.
  • Dev flag: value comments invisible in the reference. Answered in ① — correct and harmless.
  • Dev deviation: no assignee or reviewer written. Process, not contract; no action here.
  • Card precondition "land after the fix(rest)!: an async import job whose every row failed ends failed, not succeeded #27795 server PR merges". Holds: origin/main carries the every-row-failed rule at data-action-routes.ts:810-823, so the declaration describes behaviour main has.
  • open_questions: none declared. None found.
  • Three gates NOT MEASURED locally (check:dual-build-cjs-loads, check:lean-entry-closure, the plugin-teardown self-test): answered by the head's check-runs, all green.

④ Design judgment

packages/spec/src/api/export.zod.ts, judged sentence by sentence against the runtime on main and the ruling's pins (import-job-integration.test.ts:534-591):

  • "A job that had at least one row and created, updated or skipped none of them ends failed" — matches the predicate summary.total above zero and summary.created + summary.updated + summary.skipped === 0 (data-action-routes.ts:818-820), with total = rows.length (import-runner.ts:1146) and skipped counted on both skip paths (:1459, :1471), so an all-skipped job is not caught. Pin :534. Holds.
  • "as does a job aborted on a fatal error" — the catch writes failed with the thrown message (:834-837). Holds.
  • "A job with no rows, a job whose every row was skipped and a job whose rows only partly failed end succeeded" — the summary.total above-zero guard, the skipped term, and the sum being positive. Pins :568, :557, and the existing partial pin. Holds.
  • "A dry run follows the same rule" — reportPreview counts a valid preview row as created or updated (import-runner.ts:1101-1124), so a valid dry run keeps succeeded and an all-invalid one ends failed. Pins :577, :585. Holds.
  • "a cancel wins over both" — the cancel is resolved first (summary.cancelled, the in-memory flag, the durable re-read, :802-808) and the every-row rule runs only on a status still succeeded, so a cancel beats both succeeded and the counting failed; pin :591. One corner over-reads: the fatal-abort failed is written by the catch unconditionally, so a worker throw outside the per-row catch (the checkpoint flushes at import-runner.ts:1547-1548 and :1563-1564) after the cancel route has stamped the row cancelled overwrites it with failed. Pre-existing runtime, not moved by fix(rest)!: an async import job whose every row failed ends failed, not succeeded #27795, and the ruling's "cancellation still wins" was said of the B rule. should-fix: say what the runtime does — a cancel wins over the counting rule (both of its outcomes), not over a fatal abort; one clause, no shape change. Not a must-fix: no reader branches on this corner today, and the sentence is right for every path the pins cover.
  • ImportJobProgressSchema.error: "Why the job failed (when status = failed)" — the column is written only by the two failed patches (:823, :836); the cancel route writes status and completed_at, the undo writes reverted_at and the undo log; importJobToProgress emits error whenever the row has it (rest-server.ts:1103). So error present is equivalent to status = failed, as declared. "the fatal error that aborted it, or, when every row failed, one sentence pointing at the job's results, which carry each row's reason" — the constant at data-action-routes.ts:187-188 reads "Every row of this import failed and nothing was imported; each row's reason is in the job's results." Holds exactly. The fatal message is cut at 1000 characters; the describe does not claim otherwise.
  • First consumer: the progress DTO is what GET /data/import/jobs/:jobId serves, and the results DTO extends it, so both reference rows carry the new describe; ImportJobSummarySchema has no error field, so no sibling describe is left stale. The value comments on succeeded and failed restate the docblock without contradiction.
  • Attempted breaks that did not land: no enum value, key or type moved (the wire is byte-identical); the docblock's "accomplished anything" framing is immediately qualified by the enumerated cases, so the all-skipped case cannot be misread; the describe names the ruling record, which is a traceable, stable identifier, not a tracker number in an emitted error string.

No must-fix. One should-fix (the "wins over both" clause).

Implemented-by: claude/project-thread-ko6etz
Reviewed-by: session_01L6bhgV4fMGJHSdSdx3u9mK

VERDICT: PASS


Generated by Claude Code

…fatal abort

The worker's catch writes `failed` unconditionally, so an error thrown
after the cancel route stamped the job `cancelled` still ends it
`failed`. Narrow the docblock and the changeset to say so.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibz3A9wkHngU9obhv1g
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head 154d26cb727ffed16cc80d1522d604c2a68e7695 · 2026-10-30T13:32Z


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 154d26cb727ffed16cc80d1522d604c2a68e7695
Local-runs: none
Scope: packages/spec/src/api/export.zod.ts + packages/rest/src/data-action-routes.ts (the worker's terminal-status block, its catch, and the cancel route), packages/core/src/utils/import-runner.ts (the checkpoint flushes and the cancel poll), packages/rest/src/import-job-integration.test.ts (the ruling's pins, the late-cancel control as #28067 left it), content/docs/references/api/export.mdx (the ImportJobStatus section, read to confirm the docblock owes it nothing)

Delta review on record 6523565061 (head 4765d36c14172e30c64464551d309cb19a70ffc8, PASS, one should-fix). The move since that head is one PR commit, 154d26cb, and no merge from main: it touches two files, the ImportJobStatus docblock in export.zod.ts (one sentence replaced by two) and the matching sentence of the changeset body; the generated reference, the value comments and the error describe are byte-identical to the previous head. Read against origin/main at 47a8b588207621724d85647576c4277979c253f7; since the previous record's main (59547058aeb3eccde6f267293367dabf5ef82e59) the only cited file main moved is the integration test, by b1b35a3c3d (#28067, test-only: the late-cancel control now asserts its wait for the results write landed), so every line cited below was re-read at the same numbers. The 55 check-runs on the head are all completed, each success or skipped; none failed and none is in_progress. The rest of the diff (the changeset and the regenerated export.mdx) is the lane seat's ACCEPT at TIER_DEFAULT; the cited code was read, never reviewed.

① Derived judgments

  • Accept set and public surface: unchanged — stand as judged in record 6523565061. The move edits a JSDoc comment only; no enum value, key, type, default, constraint or export moved, and the wire is byte-identical to the previous head. Type Check · workspace and Spec property liveness are green on this head.
  • Generated artifacts: still complete — right, judged afresh. The move changed the docblock and not a .describe(). The ImportJobStatus section of the regenerated reference (export.mdx:242-250) renders the allowed values only, with no description line, so the docblock never reaches the page and no regeneration was owed. The two error rows the previous record judged are unchanged, and check:generated under Lint & Repo Gates is green on this head.
  • Where the rule lives — right. The docblock is the place the previous record named for the full rule (the value comments and the docblock ship in the emitted .d.ts; the reference carries only the describes), so correcting the cancel clause there, and not in a describe, was the correct and smallest edit.
  • Docs-drift advisory — stands. The bot's listing (state-machine.mdx) is unchanged and the move adds no claim a hand-written page restates: a grep of content/docs outside references for the cancel clause finds nothing.
  • Metadata-protocol: none — stands as judged in record 6523565061.

② Semver level

  • @objectstack/spec: patch — stands as judged in record 6523565061: the docblock ships in the package's emitted declarations, so the diff still publishes something and skip-changeset would still be wrong; nothing in the move raises the level.
  • Clause-②: no, line-initial on both the PR body and the changeset body, no arm; Metadata-protocol: none on both — unchanged by the move. Check Changeset is green on this head.
  • The changeset body's own sentence moved with the docblock and now states the same rule in the same words (counting rule, both outcomes, not a fatal abort) — the two surfaces agree. No BREAKING banner, none owed.

③ Boundary flags

  • The previous record's should-fix (record 6523565061, ④, the "wins over both" clause) — answered by 154d26cb. The docblock now reads: "A cancel wins over that counting rule, so over both of its outcomes, but not over a fatal abort: an error thrown after the job was cancelled still ends it failed." Judged in ④ below; it holds, and the shape is unchanged as the should-fix asked.
  • Out-of-lane label sys-import-job.object.ts:70 (Fatal Error) — stands as judged in record 6523565061. Still stale on main at 47a8b588; still domain:server, still on the board as note 6512498465 and the dev report's out_of_scope_findings. Not a blocker for this PR.
  • PR body lags the tree by one clause. The body's "What changes" still says "a cancel wins over both" and its Verification block still names the previous head. The body is prose outside the hits files and is not reviewed; the verdict is on the tree, which says the corrected rule. Noted for the seat, no finding.
  • Dev flags on value-comment visibility and the missing assignee — stand as judged in record 6523565061.
  • Card precondition "land after the server PR merges" — stands. main at 47a8b588 carries the every-row rule at data-action-routes.ts:810-823.
  • open_questions: none declared, none found. The move adds nothing outside the previous record's one finding.
  • Gates NOT MEASURED locally by the dev — answered by this head's check-runs, all green.

④ Design judgment

packages/spec/src/api/export.zod.ts. The move replaces one clause; every other sentence of the docblock, both value comments and the error describe are byte-identical to the previous head and stand as judged in record 6523565061 (the every-row predicate, the fatal catch, the three succeeded cases, the dry-run rule, and the error describe against IMPORT_JOB_EVERY_ROW_FAILED and importJobToProgress). The new clause, judged afresh against the runtime on main:

  • "A cancel wins over that counting rule, so over both of its outcomes" — the cancel is resolved first: summary.cancelled, the in-memory flag, then the durable re-read (data-action-routes.ts:802-808), and the every-row predicate is gated on finalStatus === 'succeeded' (:818-820), so a cancelled job can end neither succeeded nor the counting failed. "Both of its outcomes" is exactly the two branches of that rule. Pinned by the late-cancel control (import-job-integration.test.ts:591-634), which since test(rest): fail the late-cancel control loudly when its wait for the results write runs out #28067 also fails on an exhausted wait, so the pin now proves the worker's own final patch kept cancelled. Holds.
  • "but not over a fatal abort: an error thrown after the job was cancelled still ends it failed" — the cancel route acts only on a pending or running row, sets the flag and writes status: 'cancelled' (:869-876); a throw out of runImport after that (a flush at the checkpoint, import-runner.ts:1549-1550 and :1561-1562, sits outside the per-row catch at :1540-1543) lands in the worker's catch, which patches status: 'failed' with the message, unconditionally (:833-837). The row the cancel route stamped cancelled is overwritten. The reverse order is covered too: a job already failed is terminal, so the cancel route is a no-op success (:869, pin :332) and the row stays failed. So in every ordering where both a cancel and a fatal abort occur, the abort's failed is what the row ends with; the sentence says exactly that and no more. Holds.
  • Attempted breaks that did not land. A cancel accepted while the job is still pending: the worker returns before runImport (:766-770), so no fatal abort can follow and the clause has nothing to over-claim. A cancel that lands after the final patch: the row is terminal, the route is a no-op, the clause is not engaged. The clause names no cause for the abort and no ordering guarantee beyond "after the job was cancelled", so it cannot be read as promising that a cancel can never be lost to a late failed, which is what the previous record found the old wording did. The docblock's "wins over both" ambiguity is gone; the sentence names the rule the cancel beats by its mechanism ("counting rule"), which is the term the worker's own comment uses. The changeset body carries the same two sentences, so no sibling surface is left saying the old rule.
  • Nothing else moved in the file. The move does not touch the error describe, so the error-present-iff-failed equivalence judged in the previous record is unchanged (the cancel route still writes no error; the two failed patches remain the only writers).

No must-fix. No should-fix outstanding: the previous record's one should-fix is answered, and the move adds nothing outside it.

Implemented-by: claude/project-thread-ko6etz
Reviewed-by: session_01L6bhgV4fMGJHSdSdx3u9mK

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm

维护者速读

  • 改了什么: 只改 spec 里导入任务状态的说明文字。ImportJobStatus 的文档块写明:至少有一行、但没有任何一行被创建、更新或跳过的任务以 failed 结束,致命错误中止也是 failed。取消优先于这条计数规则,但不优先于致命中止。error 字段的 describe 同步改写,参考文档重新生成。
  • 为什么改: 服务端在 fix(rest)!: an async import job whose every row failed ends failed, not succeeded #27795 已按裁决 B 让全部行失败的任务结束为 failed。spec 原来只说 failed 是致命中止,客户端和 AI 作者读到的含义比实际行为窄。
  • 风险与回滚: 零运行时风险。没有枚举值、键、类型、默认值或导出变化,线上形状不变。回滚即 revert。
  • 先看: packages/spec/src/api/export.zod.ts(ImportJobStatus 文档块、两行值注释、error 的 describe)。
  • 两分钟验证: pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec check:generated,预期 14 个产物全部最新。
  • 需要你做的: 无。不属于受管面,契约复审已 PASS,由席位落地。
  • 席位意见: ACCEPT,可落地。

席位核对 · ACCEPT · domain:spec seat 1 (#6017) · 2026-10-30T13:50Z

Head 154d26cb727ffed16cc80d1522d604c2a68e7695. Card #27734, claim 6521811858.

  • Shape: draft to main, made by the relay. The body has Fixes #27734 alone, with Clause-②: no and Metadata-protocol: none at line start. Changeset @objectstack/spec: patch is right: the docblock and the describe ship in the emitted declarations.
  • Contract review: PASS twice from the triage seat. Record 6523565061 was on 4765d36c and carried one should-fix, the "wins over both" cancel clause. Delta record 6524181874 is on this head and judges that should-fix answered by 154d26cb, with no must-fix and no should-fix outstanding.
  • Diff read by the seat: the new clause says "A cancel wins over that counting rule, so over both of its outcomes, but not over a fatal abort". It matches the worker on main. The cancel re-check runs before the every-row predicate, which is gated on succeeded. The worker's catch patches failed unconditionally. The changeset carries the same two sentences.
  • Body lag, noted rather than patched: the PR body's "What changes" still says "a cancel wins over both", and its Verification block names the previous head. The tree and the changeset carry the corrected rule, and the 速读 above is the current one. The relay cannot patch this body without moving the attribution block.
  • Gates: check-runs on this head are 43 success, 15 skipped, 0 red. That includes Check Changeset, check:generated under Lint & Repo Gates, and Type Check. mergeable_state is clean. check-governed-merges --pr 28087: NOT governed, 3 paths, +30/−5.
  • Out of scope: the stale Fatal Error label at sys-import-job.object.ts:70 is domain:server. It is filed for triage to route as platform-objects: the import job error column is labelled "Fatal Error", but since #27676 it also holds the every-row-failed sentence #28113, and it does not block this PR.
  • Landing: pr_ready + automerge_enable in this act.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 30, 2026 13:51
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit 2695de7 Oct 30, 2026
59 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-ko6etz branch October 30, 2026 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

1 participant