Skip to content

Commit 2695de7

Browse files
docs(spec): import job failed also covers a job whose every row failed (#28087)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} --> _Requested by **Tesla** · [project thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6Jao7PCFNnUR8ZRYzq6dv7Zyh4)_ Fixes #27734 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:只改 spec 里导入任务状态的说明文字:`failed` 不再只写「致命错误中止」,也写明「至少有一行、但没有任何一行被创建、更新或跳过」的任务同样以 `failed` 结束;`error` 字段的说明同步补上这一情形,并重新生成参考文档。 - 为什么改:服务端按裁决 B(记录 6509036166)已在 #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 #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`). <details><summary>Spec build, generated artifacts, tests, typecheck</summary> - `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 </details> <details><summary>Derived gates (`dispatch-gates --commands`, 97 families)</summary> - 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). </details> --- _Generated by [Claude Code](https://claude.ai/code/session_01L9qibz3A9wkHngU9obhv1g)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 062a232 commit 2695de7

3 files changed

Lines changed: 30 additions & 5 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
docs(spec): `ImportJobStatus` and `ImportJobProgressSchema.error` describe `failed` as the REST server now ends it: a job aborted on a fatal error, or a job whose every row failed (#27734)
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
Only the declared meaning changes; no enum value, key, type or export does, and the wire is unchanged.
11+
12+
- **`ImportJobStatus`.** `failed` covered only a job aborted on a fatal error. Since the async import worker in `@objectstack/rest` ends a job that had at least one row and created, updated or skipped none of them `failed` (maintainer ruling B, record 6509036166), the docblock and the value comments say so. `succeeded` still covers a job with no rows, a job whose every row was skipped and a job whose rows only partly failed; a dry run follows the same rule. 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`.
13+
- **`ImportJobProgressSchema.error`.** Described as the fatal error only; it now also says that, when every row failed, it carries one sentence pointing at the job's `results`, which hold each row's reason. The reference page `api/export` is regenerated from it.

‎content/docs/references/api/export.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ const result = CreateImportJobRequestSchema.parse(data);
186186
| **percentComplete** | `number` | ✅ | processed / total as a percentage |
187187
| **undoable** | `boolean` | ✅ | Whether this job can still be logically rolled back (undo log captured, terminal state, not yet reverted) |
188188
| **revertedAt** | `string` | optional | When the job was undone / rolled back (ISO 8601): set only once every row is reverted. An undo that left rows unreverted sets nothing, and the job stays undoable for those rows. |
189-
| **error** | `string` | optional | Fatal error message (when status = failed) |
189+
| **error** | `string` | optional | Why the job failed (when status = failed): 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 |
190190
| **startedAt** | `string` | optional | Processing start timestamp (ISO 8601) |
191191
| **completedAt** | `string` | optional | Completion timestamp (ISO 8601) |
192192
| **createdAt** | `string` | ✅ | Job creation timestamp (ISO 8601) |
@@ -214,7 +214,7 @@ const result = CreateImportJobRequestSchema.parse(data);
214214
| **percentComplete** | `number` | ✅ | processed / total as a percentage |
215215
| **undoable** | `boolean` | ✅ | Whether this job can still be logically rolled back (undo log captured, terminal state, not yet reverted) |
216216
| **revertedAt** | `string` | optional | When the job was undone / rolled back (ISO 8601): set only once every row is reverted. An undo that left rows unreverted sets nothing, and the job stays undoable for those rows. |
217-
| **error** | `string` | optional | Fatal error message (when status = failed) |
217+
| **error** | `string` | optional | Why the job failed (when status = failed): 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 |
218218
| **startedAt** | `string` | optional | Processing start timestamp (ISO 8601) |
219219
| **completedAt** | `string` | optional | Completion timestamp (ISO 8601) |
220220
| **createdAt** | `string` | ✅ | Job creation timestamp (ISO 8601) |

‎packages/spec/src/api/export.zod.ts‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -488,12 +488,21 @@ export const IMPORT_JOB_MAX_ROWS = 50_000;
488488
/**
489489
* Import Job Status — the states the import worker actually moves a job
490490
* through (`succeeded`, not `completed`, is the success terminal).
491+
*
492+
* `succeeded` and `failed` split on whether the job accomplished anything, not
493+
* on whether some row failed (maintainer ruling B, record 6509036166). A job
494+
* that had at least one row and created, updated or skipped none of them ends
495+
* `failed`, as does a job aborted on a fatal error. A job with no rows, a job
496+
* whose every row was skipped and a job whose rows only partly failed end
497+
* `succeeded`. A dry run follows the same rule. A cancel wins over that
498+
* counting rule, so over both of its outcomes, but not over a fatal abort: an
499+
* error thrown after the job was cancelled still ends it `failed`.
491500
*/
492501
export const ImportJobStatus = z.enum([
493502
'pending', // Row persisted, worker not yet started
494503
'running', // Worker streaming through the batch
495-
'succeeded', // Finished (rows may still have per-row errors)
496-
'failed', // Aborted on a fatal error
504+
'succeeded', // Finished with a row created, updated or skipped, or with no rows (other rows may still have per-row errors)
505+
'failed', // Aborted on a fatal error, or every row failed (rows existed and none was created, updated or skipped)
497506
'cancelled', // Cancelled by the caller before completion
498507
]);
499508
export type ImportJobStatus = z.input<typeof ImportJobStatus>;
@@ -539,7 +548,10 @@ export const ImportJobProgressSchema = lazySchema(() => z.object({
539548
percentComplete: z.number().min(0).max(100).describe('processed / total as a percentage'),
540549
undoable: z.boolean().describe('Whether this job can still be logically rolled back (undo log captured, terminal state, not yet reverted)'),
541550
revertedAt: z.string().optional().describe('When the job was undone / rolled back (ISO 8601): set only once every row is reverted. An undo that left rows unreverted sets nothing, and the job stays undoable for those rows.'),
542-
error: z.string().optional().describe('Fatal error message (when status = failed)'),
551+
error: z.string().optional().describe(
552+
'Why the job failed (when status = failed): the fatal error that aborted it, or, when every row failed, '
553+
+ 'one sentence pointing at the job\'s `results`, which carry each row\'s reason',
554+
),
543555
startedAt: z.string().optional().describe('Processing start timestamp (ISO 8601)'),
544556
completedAt: z.string().optional().describe('Completion timestamp (ISO 8601)'),
545557
createdAt: z.string().describe('Job creation timestamp (ISO 8601)'),

0 commit comments

Comments
 (0)