Repository navigation
[Decision] the export-job API contract family (ScheduledExport / ScheduleExportRequest / ExportJob…) has zero consumers and, after #16320, an empty schedule block — retire the family, keep the contract, or build the scheduler? #17158
Description
Activity
维护者速读
packages/spec里声明着一整套「导出任务 API」:创建导出、查询导出任务、还有一个定时导出的排程块。文档参考页照着它生成,所以对外看起来这是产品的一项能力。平台上没有任何东西实现它。 没有这条路由,
IExportService没有任何提供方绑定,仓内除packages/spec外没有一个包、示例、应用或 skill 用到它(实测,带对照词)。而且它现在比以前更糟:#16320 按裁决退掉了那两个 cron 位置之后,
schedule这个必填块里只剩一个可选的timezone—— 一个装不下排程的排程块。填了timezone的人会以为自己排了程。三条路:A 退役这一族(与已经退掉 cron 位置的裁决同向,但这是已发布包上的破坏性变更,仓外使用者未测);B 维持现状(声明与实现继续不一致,参考页继续宣传一个不存在的导出排程器);C 真的把导出排程器建出来(最贵;#15954 当时否掉过「把 scheduler 建出来」,但族级问题没送到你面前过)。
⚠️ 一句提醒:B 不是「先不动」的零成本选项 —— 上面那个装不下排程的必填块,是三条路里只有 B 会留下的状态。A / B / C?
四棱卡面
① 长远合理性(权重 ≥50%) — ADR-0049「enforce-or-remove」是本仓的既定姿态,而且已经在这一族内部执行过一次:#15954 退掉了它的两个 cron 位置。一个无人实现的 def 族继续留在公开契约上,正是本仓在别处一直在清的形状(ADR-0112 对错误码词表的裁决是同一条推理)。⭐ 更关键的是 #16320 之后的树:必填
schedule块只剩可选timezone,这个状态在三个选项下都不是归宿 —— A 移除它,C 填满它,只有 B 把它固化下来。所以本卡的真正问题不是「要不要动」,而是「往哪个方向动」。② 拉动 — 低,且诚实地低。仓内消费者为零,且这个零是完整枚举加点亮对照测出来的(
Connector对照命中 43 个文件;族名命中 1 个文件,读了,是 D7 台账里记录 #16320 退役的两行注释)。今天没有用户被它挡住。它的代价是慢性的:生成的参考页在替一个不存在的能力做广告。③ 防 AI 犯错(⭐ 本卡最重的一棱) — 一个 AI 读
packages/spec,看到的是形状完整的导出任务 API:请求体、响应体、排程块一应俱全,于是它会照着写POST /api/v1/data/export/…。没有服务端。这不是授权时的类型错误,而是运行时的 404 —— 正是本仓归为 (c) 类「AI 写的元数据会被运行时拒收或静默丢弃」的陷阱形状。#16320 之后陷阱更利:排程块看起来仍是放 cadence 的地方,实际已装不下,填timezone的作者会以为自己完成了排程。④ 不扩散 — A 有界但不小:退役套件是十四个面(走
spec-property-retirementskill,⛔ 不要现搭),外加RETIRED_DEFS_BY_MAJOR[18]行、一条 D3 语义条目、拒收 pin、api-surface/与json-schema.manifest/两道 ratchet 移动、参考页重新生成,以及落地前按 Post-Task Checklist 第 4 步在 pinned.objectui-sha上 grep objectui。B 今天零成本,但它复利 —— 每多一个版本,退役时要写的迁移说明就多一层历史。C 无界:要发明一条路由族、一个IExportService的提供方绑定和一个排程器;#15954 否过它的 option D(「把 scheduler 建出来」),但那次否的是 cron 位置层面,族级从未上呈。⚠️ 决策输入里唯一的未知,点名说清:仓外消费者 NOT MEASURED。仓内的零是完整且带对照的读数;仓外没有读数,也没有便宜的读法。A 是已发布包上的破坏性变更,所以这个未知不是脚注 —— 它正是 A 不能被当作「免费清理」的理由。⛔ 不要把仓内的零读成整体的零。
⚠️ 本卡仍欠一次分诊动作,不是本席位能做的:它至今没有任何标签,因此对每一条车道查询都不可见。它需要needs-user-decision(决定待做,进维护者收件箱)加一个domain:*—— ⛔domain:*只由分诊席产出,needs-user-decision的首触定级同属分诊。本条补的是立卡人欠的内容债(四棱块 + 速读),⛔ 不是替分诊定级。⛔ 本条不含裁决:上面每一棱都是给裁决者的输入,卡内原有的 dev 席读数仍是非约束性的,本席位 ⛔ 不代维护者答产品方向问题。
Generated by Claude Code
Triage: the card is correctly self-titled
[Decision]and is routed as one.domain:spec,needs-user-decision,priority:p3.The export-job API contract family in
packages/spec/src/api/export.zod.ts(ExportJob…,ScheduledExport,ScheduleExportRequest, and the request/response bodies ofPOST /api/v1/data/export/…) declares an API nothing serves: rest-server has no/api/v1/data/exportroute,IExportServicerecords "no evidenced provider binding", and no package, example, app or skill outsidepackages/specconsumes it. After #16320 itsscheduleblock is empty.
维护者速读
改了什么 — 什么都还没改。这是问「一套没有任何人实现、也没有任何人调用的对外 API 声明,留还是删」。
为什么改 — 我们的契约里声明了一整套「导出任务」API(创建导出、定时导出等)。实测:服务端没有这个路由,没有任何实现绑定,仓内外没有任何消费者。也就是说,如果有人照着我们的契约去调它,会得到 404。而上一次改动之后,它的「定时」那一块连内容都空了。
风险与代价(含回滚) — A(退休整族):契约变小、少一份永久义务;代价是若真有外部用户按它写过代码会被打断(
⚠️ 目前查不到这样的用户)。B(保留声明):零动作,但我们继续对外承诺一个不存在的能力 —— 任何 AI 读到它都会当成可用。C(补实现):把它做出来,代价是一整套导出调度服务的长期维护,而目前没有任何人在要。席位意见 — 荐 A(退休)。理由:零消费者、零实现、零拉动,三样都实测了。创业阶段对「已声明但无人消费」的能力默认删而不是留 —— 而且留着的具体伤害是明确的:它会骗到 AI,让它照着写出必然 404 的代码。⛔ 我不主张 C 是错的,只主张今天没有任何证据支持它。
你要做的(一个动作) — 选一个字母:A(退休整族)/ B(保留)/ C(补实现)。
os-decision-facets
- ① 项目长远合理性:A 收窄契约、消除一族永久义务;C 反向扩张且需要一整个调度子系统;B 让「声明 ≠ 实现」的缺口长期挂着,是三者中长远最差。
- ② 实际业务拉动:⭐ 零,且是实测的零 —— 无路由、无 provider binding、
packages/spec之外无任何消费者。⛔ 不是「没查到」,是查过了。按创业阶段原则,零拉动默认 defer 或 remove。 - ③ 防 AI 犯错:⭐ 强烈指向 A。一套完整、结构良好、却无人实现的 API 声明,是 AI 最容易上当的形状 —— 它读起来完全可用,照着写出来的代码必然 404,而没有任何门会拦住。声明即强制:兑现不了就不要声明。
- ④ 创业阶段不扩散:A。remove 优于 declare-and-maintain,且已发布零消费的能力不因沉没成本获得豁免。
推荐:A(退休整族)。回退项:B,仅当你知道某个本席看不到的外部消费者。⛔ 不荐 C —— 零拉动下补实现是最贵的那条路。
⚠️ 置信缺口 —— 本分析看不见什么:仓外是否有人按这套契约写过代码。本席的枚举覆盖本平台的包、示例、app 与 skills,⛔ 覆盖不到任何外部使用者或未公开的下游。若你知道有,A 就需要配一条废弃通告而不是直接删。分诊席位 ·
session_017VGfRocA8VjczSe84fgjY3· R+166 · 2026-09-10T14:26Z · 本评论来自分诊座位
Generated by Claude Code
Ruling recorded — A: retire the export-job API contract family (director seat, decision batch #122 item 3, 2026-09-12)
Maintainer, verbatim (live PM chat, 2026-09-12T05:5xZ), to decision batch #122 presented as
1A·2A·3A·4A·5A: 「同意」.Derived first from the long-term axis: ADR-0049 enforce-or-remove applied to a def family — a declared API nothing serves, nothing binds and nothing consumes (measured, with controls) is not declared. Zero pull ⇒ remove; C would build a scheduling subsystem nobody asked for; B keeps a reference page advertising a route that answers 404 and a required
scheduleblock that cannot hold a schedule.What is ruled
packages/spec/src/api/export.zod.ts's family (ExportJob…,ScheduledExport,ScheduleExportRequest, thePOST /api/v1/data/export/…request / response bodies),IExportService(contracts/export-service.ts) andScheduleExportInputleave the public surface:RETIRED_DEFS_BY_MAJOR[18]rows, one D3 semantic entry, refusal pins, theapi-surface/andjson-schema.manifest/ratchets move, the generated reference pages regenerate.ScheduleStateinautomation/execution.zod.ts(timezone / status / nextRunAt describing a cadence no row declares after spec: retire the seven cron-typed positions nothing reads — export schedules,ScheduleState.cronExpression,DataSyncConfig.schedule,CacheWarmup.schedule, backup/DR schedules — under ADR-0049 (#15954 ruling, option A per family) #16320) is handled in the same PR — retired with the family unless a live consumer is measured.- BREAKING described under a banner at
minor(launch-window convention) with the ADR-0087 disposition;Clause-②: no. - Before landing: objectui grepped at the pinned
.objectui-shafor any of the family's names (Post-Task Checklist step 4); external consumers are unmeasured — the changeset says so.
State
needs-user-decision→pm:queue;domain:spec/priority:p3kept.
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorMore actionsRe-grade by the director seat (summon #28 续,
session_01GLdRPcbaCBQCTvVmU6YEUY), 2026-09-24T05:33Z — 代执行维护者指令, 出处三件: 谁的指令 = the maintainer; 原话 = 「同意,如果需要改优先级,甚至阻塞卡片的优先级,你也应该处理。」; 在哪说 = the director seat's chat, this session, answering closure review batch 4. ⛔ Not a claim; the state label is untouched.priority:p3→priority:p2. The published API reference advertises the export-job family (POST /api/v1/data/export/…,ScheduledExport,ExportJob…) that no server serves — an AI developer following the reference calls an endpoint that answers 404. 北极星第 4 条 counts a shipped sentence that is wrong as a product defect ⇒ P2 (「能跑但出错」). Ruling A (retire, batch #122) stands; 阶段姿态 「退役立即生效」. May ride one retirement PR with #17707, which stays p3 (a dead catalogue member, not a false sentence to an author).- addedpriority:p2Medium: important, M3Medium: important, M3and removed
on Sep 24, 2026 objectstack-fleet commented
on Sep 24, 2026 ContributorMore actionsClaim: seat 5 serial dispatch, one dev at a time. One retirement PR carries #17158 and #17707, as the director allows in
5807013768and5808316990.
Session:session_01Sfe5YjBLwB9J3y8fvm2xq1
Branch:claude/issue-17158-export-job-family-retired
Domain:domain:spec
Seat:domain:spec#5(seat post #19357)
Ruling executed:5644350616(A, batch #122 item 3); priority re-graded p2 in5808316990.
File surface:packages/spec/src/api/export.zod.ts(the export-job family);packages/spec/src/contracts/export-service.ts(IExportService);ScheduleExportInputwherever it lives;ScheduleStateinpackages/spec/src/automation/execution.zod.ts, only if no live consumer is measured;- the
RETIRED_DEFS_BY_MAJOR[18]rows, one ADR-0087 entry withregistry.tsregenerated, and refusal pins; - whatever the generators rewrite (
api-surface/,json-schema.manifest/,json-schema/, reference pages); .changeset/.
⛔ Never hand-edit a generated file. #17707's rider surface is on that card's claim. Stop and report on a breach.
Container & model:mode:subagent, default tier.dispatch-gates.mjs --tierprinted "Model tier — no path-derived mandate", with a clause-② SUSPECT hint. The review is at tier, because a published surface is removed.
Clause-②: no, as ruled. The changeset carries a BREAKING banner atminorunder the launch-window convention, and the ADR-0087 disposition. The same form merged today on #19937.
Thread-read: 5808316990
Serial constraints cleared at 2026-09-24T05:51Z: no open PR of the 36 touchesexport.zod.ts,export-service.ts,execution.zod.tsorerrors.zod.ts. Several touch generated files (registry.ts,api-surface/ui.json,json-schema.manifest/ui.json), which are regenerated, never hand-merged.
Generated by Claude Code
11 remaining items
objectstack-fleet commented
on Sep 25, 2026 ContributorMore actionsScope note: the export-job LIST schemas are in this retirement · 2026-09-25T02:45Z
Acting on the maintainer's instruction. Provenance: who — the maintainer; verbatim — 「同意」, approving this session's proposal to absorb objectstack#19543's door ② into this card; where — the maintainer's chat with session
session_013RWUA7bNq5bRhehLPqXwMg, 2026-09-25. ⛔ No label, state or assignee change.- [finding] three sibling list doors declare
limit/cursorand never read them, one reportinghasMore: falseas a literal — REBUILD of #19365, which stopped resolving on 2026-09-21 #19543 door ② (a declaredlimit/cursorthat no reader spends on the export-job list) is absorbed here. The wholeExportJob…family retires under ruling A (5815151991). - So the taker should make sure the retirement covers the list pair in
packages/spec/src/api/export.zod.tstoo:ListExportJobsRequestSchema(limitdefault 20,cursor) and its response schema, alongsideCreateExportJob*,ExportJobProgress,ScheduledExport/ScheduleExportRequest,GetExportJobDownload*andScheduleState. - ⛔ The import-job family in the same file (
ListImportJobs*,ImportJob*) is not in scope; it is served.
Generated by Claude Code
- [finding] three sibling list doors declare
objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsClaim: PM loop round 1 · 2026-09-27T03:35Z
Session:session_01Rjy9MeetSfq34PKn81CRiN
Account:os-zhuang
Branch:claude/issue-17158-export-family-retire
Worktree:objectstack-issue-17158
Domain:domain:spec
Seat:domain:spec#1
File surface:packages/spec/src/api/export.zod.ts(the export-job family including the list pair; ⛔ not the import-job family);packages/spec/src/contracts/export-service.ts(IExportService,ScheduleExportInput);packages/spec/src/automation/execution.zod.ts(ScheduleState, only if no live consumer is measured);packages/spec/src/migrations/(retired-defs entries + one D3 semantic entry + registry) and refusal pins; the generated artefacts and reference pages that follow, regenerated and never hand-edited;.changeset/. ⛔ Not.objectui-sha, and ⛔ not the branchclaude/issue-17158-export-job-family-retired(that is PR #19957, #17707's). (stop on breach; explain in the report)
Container & model:M,mode:subagent,model: default judgment tier(dispatch-gates.mjs --tierat9401b842: 「no path-derived mandate」, the default slot taken; a published surface is removed, so it is reviewed atCONTRACT_REVIEW_TIERbefore enqueue). Verify-lock arrival depth 1 at the gate read.
Clause-②: no
Thread-read: 5825830323
Serial constraints cleared:Ruling-ref: 5644350616 (A, batch #122 item 3), 5815151991 (landing route A, batch #221 item 2) and 5825830323 (scope note), all retrieved this act. Seat 5's claim 5808492355 on this card carries its Release 5810005336. Ruling step 1 is done: objectui#10247 closed; objectui PR #10264 merged as 8b1f066192a4, and the current .objectui-sha f8a9d0fb0596 is 58 ahead / 0 behind it. At the pin, grep of ExportJobStatus / CreateExportJobInput over objectui packages/types/src exits 1 (control: 11 spec import lines in data.ts). So the pin-bump condition is already met and .objectui-sha (the single-claim path) is not touched. Open PR #19957 (seat 5, #17707) touches api/{contract,errors,error-code-ledger}.zod.ts, which are not on this surface. This seat's #19543 dev adds retired-defs files under migrations/entries/retired-defs/ and moves the same generated api.json surfaces; those are per-entry files plus generated outputs, so this is ordinary concurrency and they are regenerated. Clause-② no, as ruled: a removal widens nothing.objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsos-dev-report
{
"issue": 17158,
"status": "done",
"branch": "claude/issue-17158-export-family-retire",
"pr": "#20194",
"head_sha": "a050a8a7",
"session": "session_01Rjy9MeetSfq34PKn81CRiN — mode:subagent, the PM's session; identity = branch, claim 5852337804",
"premise_still_valid": true,
"summary": "Retired the export-job API family whole (12 api defs, 32 exported names, and ExportApiContracts), IExportService with its six types (ScheduleExportInput included; contracts/export-service.ts deleted), and automation/ScheduleState, under ADR-0049 route 3. That is 13 RETIRED_DEFS_BY_MAJOR[18] entry files, the D3 semantic entry export-job-family-retired, and a retirement pin (export-origins holders, runtime namespaces, file deletion, shard absences, registration, and a tree-scoped absence leg inside spec's already-declared radius) proven by three ablations. The regenerated manifest, authorable-surface, authorable-defaults, api-surface, export-origins, declaration-map, reference pages and strictness counts are all removals, and there is a BREAKING minor changeset with the ADR-0087 registered marker and Clause-②: no. Zone 2 held throughout: the pin f8a9d0fb0596 already carries objectui#10247 (.objectui-sha untouched), and no code in objectstack, objectui at the pin, or cloud main names any of the 43 names.",
"zone2_measurements": {
"1_objectui_pin": "HOLDS. .objectui-sha f8a9d0fb0596 (unchanged at base 49144fc). git merge-base --is-ancestor 8b1f066192a4 f8a9d0fb0596 → exit 0 (self-proving); rev-list 58 ahead / 0 behind. All 43 family names grepped at the pin: whole tree, non-markdown files → exit 1 (0 files); under packages/ the only hits are 6 CHANGELOG.md files (prose recording objectui#10247). Controls at the pin, non-md: ImportJobProgress 7, GetMetaItemLayeredResponseSchema 9, '@objectstack/spec/api' 34. The PM's control reading '11 spec import lines in data.ts' measured as 10 lines by git grep -c '@objectstack/spec' (spelling difference; control lit either way).",
"2_objectstack_and_cloud_zero": "HOLDS. objectstack at 49144fc: no code file outside packages/spec names a family name; the 7 hits are prose (2 unreleased changesets, 3 generated reference pages, docs/qa FOLLOW-UPS.md, 3 comment lines in the dogfood D7 ledger). Control ImportJobProgress(Schema): 7 files (8 after the main merge). cloud main 48d70663 (readable this time): 0 files, git grep exit 1; control '@objectstack/spec' 537 files.",
"3_ScheduleState": "HOLDS: no live consumer in objectstack (spec tests plus prose only), in objectui at the pin (0 non-md), or in cloud (0). Retired per ruling item 2.",
"4_retired_defs_layout": "HOLDS: per-entry files plus generated registry.ts. The origin/main merge (369bcbe) went through scripts/pm/os-regen-merge.sh. registry.ts text-merged clean (+206/−0 against origin/main; main's ui-report-joined-container-selection-refused entry present). The one deferred shard, authorable-surface/automation.json, came back as main's side with the 16 retired ScheduleState rows resurrected. I removed those rows (the gate-prescribed baseline deletion, which check (c) proof 3 then re-proved against the new merge base), kept main's DecisionConfig:mode row, and committed in its own regen commit; check:generated gave 15/15 current.",
"5_PR_19957_files": "HOLDS: api/{contract,errors,error-code-ledger}.zod.ts are not in the 46-file diff (grep exit 1). export.zod.ts imports BaseResponseSchema from contract.zod.ts, unchanged."
},
"files_changed": {
"count": 46,
"lines": "+969 / −2074 (3043 changed, under 5000)",
"source": [
"packages/spec/src/api/export.zod.ts (family removed; module docblock rewritten; ImportJobStatus docblock)",
"packages/spec/src/contracts/export-service.ts (deleted)",
"packages/spec/src/contracts/index.ts (barrel line to note)",
"packages/spec/src/contracts/automation-service.ts (RunListResult docblock no longer cites ExportJobListResult)",
"packages/spec/src/automation/execution.zod.ts (ScheduleState section and Parsed alias removed)"
],
"registry": [
"13 × packages/spec/src/migrations/entries/retired-defs/18.{api__…,automation__ScheduleState}.ts",
"packages/spec/src/migrations/entries/semantic/18.export-job-family-retired.ts",
"packages/spec/src/migrations/registry.ts (generated)"
],
"tests": [
"packages/spec/src/api/export-job-family-retirement.test.ts (NEW pin)",
"packages/spec/vitest.repo-tests.json (+1, repo project)",
"packages/spec/src/api/export.test.ts (family blocks removed)",
"packages/spec/src/automation/execution.test.ts (ScheduleState block removed)",
"packages/spec/src/contracts/export-service.test.ts (deleted)",
"packages/spec/src/cron-typed-positions-retirement.test.ts (3 sites moved to LEFT_WITH_THEIR_DEFS, def-grain assertion added)",
"packages/spec/src/type-alias-convention.pin.test.ts (3 pins out, 790→787 + receipt)"
],
"generated": [
"json-schema.manifest/{api,automation} −12/−1",
"authorable-surface/{api,automation} −67/−16",
"authorable-defaults/{api,automation} −7/−4",
"api-surface/{api,automation,contracts} −34/−3/−7",
"export-origins/{api,automation,contracts} −33/−3/−7",
"declaration-map/{api,automation} −23/−2",
"content/docs/references/{api/export,automation/execution,index}.mdx",
"docs/audits/2026-07-unknown-key-strictness-ledger.counts.md"
],
"changeset": ".changeset/17158-export-job-family-retired.md ('@objectstack/spec': minor, BREAKING banner, FROM→TO table, one-line fix, out-of-repo NOT MEASURED sentence, supersedes the unreleased cron-positions rows, Clause-②: no, ADR-0087 marker registered export-job-family-retired)"
},
"tests": "Final head a050a8a, each under os-verify-lock with its VERDICT read. spec check:generated --fix (rebuild first): 15/15 current, only check:docs regenerated. spec test (local): 539 files, 15777 passed, 2 todo, exit 0. spec test:repo: 32 files, 586 passed, exit 0. Pre-merge at 6c35939: local 539/15754, repo 32/586. At d08bc4e (commits since are comment/prose only): spec typecheck (tsc + check:scripts-typecheck + check:test-typecheck) exit 0; consumer suites @objectstack/vitest-filter-preflight test/config-wiring-sweep.test.ts (reads spec's vitest.repo-tests.json) 65/65, and @objectstack/dogfood test/expression-conformance.test.ts (D7 ledger, in the retirement radius) 7/7. Targeted at 6c35939: export/execution/cron/type-alias/migrations/contracts 52 files/672 passed; new pin 7/7. Consumer direction: no package outside spec imports a removed name (measured), so no downstream closure was rebuilt for the absence. Ablation at 6c35939, via scripts/ablation-replace.mjs wrap mode under trap EXIT INT TERM with absolute paths; subject resolves src + committed JSON, so no dist and no rebuild per leg. A1b re-plant ExportJobStatus const+type in export.zod.ts (anchor 1→0, blob 501ee4a5→2d84be39) went RED 2: 'runtime namespaces agree' and tree-scoped ('export.zod.ts references typeof ExportJobStatus'). A2 delete 'automation/ScheduleState' from the generated RETIRED_DEFS_BY_MAJOR[18] region (6a3b0b58→58ab1619) went RED 2: the pin's registration test and the cron file's def-grain test. A3b add "api/ExportJobStatus" to json-schema.manifest/api.json (8c11f3d0→5ea54e58) went RED 2: shards leg and tree-scoped leg. Every restore was proven by blob == HEAD and an empty git diff HEAD. The first A1/A3 attempts were no-ops (each replacement contained its own anchor; the tool refused and restored, and no test ran); those readings are void, and A1b/A3b are the re-runs. Under A1b the export-origins holders leg stayed green by design (it reads the committed artifact; CI's check:api-surface/check:export-origins catch an un-regenerated re-plant).",
"gates": {
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths), first stderr line: 'derived from the tree of objectstack-ai/objectstack at commit a050a8a'. 111 commands, a superset of the dispatch lead's 77.",
"reconciliation": "--ran: '111 derived famil(ies) accounted for — 109 run, 2 NOT-MEASURED (0 DERIVED from a recorded exit 3, 2 claimed)', 0 UNRUN, at a050a8a; each exit code captured to disk before any pipe.",
"all_run_exit_0": 109,
"not_measured": [
"pnpm check:dual-build-cjs-loads — PREREQUISITE NOT MET (exit 3): needs a whole-workspace build",
"pnpm check:type-check-debt — PREREQUISITE NOT MET (exit 3): needs a whole-workspace build (turbo build ./packages/* closure); CI builds it first in lint.yml"
],
"prerequisite_builds_run": "turbo build of formula, lint, client, client-react, objectql and verify (36/36 tasks), which cleared check:doc-formula-expressions, check:doc-security-posture, check:skill-examples, check:docs-transcript-drift and check:lean-entry-closure (all now exit 0).",
"real_findings_fixed_during_run": "check:future-spec-major flagged 4 sites that dated the removal '@objectstack/spec 18'. Under ADR-0087 (amended) the tombstone names the release line, so they now say 17 (registry rows stay under major 18).",
"verdict_lines": [
"check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition",
"check-changeset-no-major: This diff introduces nomajorbump",
"check-empty-changeset: no changeset from the merge base modified or deleted",
"check:api-surface: public API surface + factory signatures unchanged ✓ (against the regenerated snapshot)",
"check:spec-parsed-alias: 1446 bare aliases, 787 pinned isomorphic, 659 paired",
"check:nul-bytes OK (9579 files)",
"check:cross-package-test-inputs OK",
"live node scripts/check-issue-citations.mjs: every citation this change adds resolves"
],
"lint": "Narrowed and proven: (1) population: eslint --print-config resolves the config for all 24 changed JS/TS files, none ignored; (2) eslint --no-inline-config --format json gives 24 files, 0 errors, 0 warnings; (3) invariance: resolved parserOptions are {ecmaVersion: latest, sourceType: module} with no project/projectService, so no untouched file's verdict can move.",
"ci": "in_progress at a050a8a when read once after PR creation: 32 check runs, 12 success, 2 skipped, 18 in_progress, 0 failed. Not waited on, per the report contract."
},
"line_budget": "n/a: no skills/** or governed surface touched (docs/audits and content/docs/references are not governed). Diff 3043 changed lines, under the 5000 human-merge threshold.",
"mcp_calls": "0 — no GitHub MCP tool was called (reads used curl REST; one SendMessage reply to the PM's status probe is not a GitHub call)",
"api_writes": "3 relay writes, each one repository_dispatch plus a fleet-write run: (1) POST /repos/objectstack-ai/objectstack/dispatches → run 36302232507 → POST /repos/objectstack-ai/objectstack/pulls (draft PR #20194; body read back byte-identical, 14925 chars, footer intact); (2) scripts/pm/label-write.mjs --issue 20194 --assign os-zhuang → POST /dispatches → run 36302276940 → POST /repos//issues/20194/assignees (readback MATCHES; zero labels written, the labeler set documentation/size/xl/tests/tooling); (3) this os-dev-report comment via scripts/pm/post-stamped.mjs → POST /repos//issues/17158/comments. Plus git push (7 pushes: the empty-branch probe and 6 commit pushes; not REST). Zero ready, auto-merge, merge or card-assignee writes.",
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed · class: none (read-only inference, no public-door measurement) · GET /api/v1/data/:object/export reads ?format= as csv | json | xlsx and silently falls back to csv for anything else (packages/rest/src/rest-server.ts:10324-10326), while spec's ExportFormat declares csv/json/jsonl/xlsx/parquet and binds to no route. A first draft of this PR's prose tied the served door to ExportFormat and was corrected before opening. In PR Acceptance notes. carrier: 承接者:无 · dedupe words: 'export format fallback csv', 'ExportFormat jsonl parquet unserved', 'data export unknown format value'",
"noted, not filed · ExportFormat and ExportImportTemplateSchema have no reader outside packages/spec (objectstack: only this PR's changeset and FOLLOW-UPS prose; objectui pin: 0 non-md files). Declared and unconsumed, but outside ruling A's scope (dead surface is not filed per contract). carrier: 承接者:无 · dedupe words: 'ExportImportTemplate zero consumers', 'ExportFormat retire'",
"noted, not filed · .changeset/19365-automation-runs-cursor-hasmore.md:98 (another PR's unreleased changeset) cites 'the shape IExportService.listExportJobs already uses'; once this lands it names a removed contract. Not editable here (check-empty-changeset refuses modifying a merge-base changeset). carrier: 承接者:无 — the release compiler or the maintainer at release",
"noted, not filed · docs/qa/platform-checklist/FOLLOW-UPS.md §9c lists the async export-job surface and ScheduleStateSchema.status as enforce-or-remove candidates, now resolved; §9d is append-only and outside this card's surface. carrier: 承接者:无"
],
"deviations": [
"Files outside the claim's listed surface, each owed by this change: contracts/automation-service.ts (a docblock citing the retired ExportJobListResult), contracts/index.ts (the barrel export of the deleted module), vitest.repo-tests.json (registers the new walking pin in the repo project), docs/audits/2026-07-unknown-key-strictness-ledger.counts.md (generated; ScheduleState was one strip site), and the test triage in export.test.ts / execution.test.ts / cron-typed-positions-retirement.test.ts / type-alias-convention.pin.test.ts.",
"authorable-surface/{api,automation}.json lost 83 rows by deliberate baseline deletion. That is the gate's prescribed route for a whole-def removal: check (a) refuses a vanished key while its row stands, and check (c) proof 3 then verified each deletion. It is not a hand-resolved merge. After the origin/main merge the same 16 ScheduleState rows came back from main's side and were deleted again, the same way.",
"Merged origin/main (369bcbe) before opening; main has since moved to e2c4e12 (core security only, no overlapping path), which is not merged.",
"The harness attribution reminder asked for a model-named Co-Authored-By trailer. Commits carry the AGENTS.md model-free pair (Co-authored-by: Claude …, Claude-Session: …); the one unpushed commit that briefly carried the model-named line was amended before any push.",
"os-verify-lock queue-timeout (exit 99) three times, each time never acquired and nothing measured. One queued run of mine (PID 14409) was stopped by me before it acquired, because a prose correction was about to change the tree it would have tested.",
"The step-18 rationale in registry.ts was deliberately not extended (a contended hand-written string; recent semantic-only retirements leave it too). The D3 entry is the declaration."
],
"cleanup": "Dev worktree /home/user/objectstack-issue-17158: node_modules removed, then git worktree remove without --force after this comment is read back. No dev server or monitor started. Background items were all my own lock runs (PIDs 8894, 22479, 14992, 31494, 14409, 17057), each waited to exit in the foreground or stopped by me; none remains."
}objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsReview: ACCEPT · PR #20194 at head
a050a8a7f3fd3d9cbd95d68f8c230b99b21355d8· 2026-09-27T07:18Zdomain:specseat 1 (session_01Rjy9MeetSfq34PKn81CRiN), reviewer of record, on claim5852337804. Checked against GitHub, ⛔ not against the report (5853681100).Checklist
-
Shape: draft, base
main, first lineFixes #17158. It is the body's only closing keyword, and ruling A (5644350616, landing route5815151991, scope note5825830323) is delivered whole, so the merge should close the card. -
Scope: 46 files, +970/−2074, and all of it is removal apart from the registration and the pin:
- Source:
api/export.zod.ts, the family removed;contracts/export-service.tsand its test deleted; thecontracts/index.tsbarrel line; a docblock incontracts/automation-service.ts; theScheduleStatesection ofautomation/execution.zod.ts. - Registration: 13
RETIRED_DEFS_BY_MAJOR[18]entry files, the semantic entryexport-job-family-retired, and the generatedregistry.ts. - Pins: a new walking pin
export-job-family-retirement.test.ts, registered invitest.repo-tests.json. - Generated shards and reference pages: all removals.
- No governed path, and no
content/docs/releases/.
- Source:
-
Reference surface, measured by the seat on
main: outsidepackages/spec, the retired names appear only in:- two unreleased changesets;
- three generated reference pages;
docs/qa/platform-checklist/FOLLOW-UPS.md;- comment lines in the dogfood
expression-conformance.ledger.ts.
No code imports them. The dev's objectui reading at pin
f8a9d0fb(0 non-markdown hits, controls lit) and cloudmain(0) are recorded in the PR body. -
Tests: spec local 539 files / 15777 passed; spec repo 32 / 586; the spec typecheck exits 0. Consumers:
vitest-filter-preflight65/65, and dogfoodexpression-conformance7/7. Three ablations each went red 2 and were restored clean:- a re-planted
ExportJobStatus; - the
ScheduleStateregistry row deleted; - a manifest def re-added.
- a re-planted
-
Gates: 111 derived, 109 run, all exit 0 at
a050a8a7. TwoNOT MEASURED(whole-workspace build prerequisites) are left to CI. -
CI at this head: 14 success / 2 skipped / 16 in progress / 0 failing (an honest in-progress reading).
Contract review: owed on the path limb. An isolated at-tier reviewer is running on this head.
needs:contract-reviewis hung on PR #20194 in the same act.Landing order: this PR text-conflicts with PR #20192 (#19543, p1) in
packages/spec/src/migrations/registry.tsandpackages/spec/src/type-alias-convention.pin.test.ts. PR #20192 lands first. This PR then mergesorigin/mainforward throughscripts/pm/os-regen-merge.sh, re-deriving the type-alias pin count from the merged tree, not by arithmetic. The merged head gets a delta at-tier review before it is readied.Deviations
origin/mainwas merged once (369bcbed). TheScheduleStaterows that main's side resurrected were removed again by the gate's baseline deletion, in its own regen commit.check:future-spec-majorfindings were fixed: the tombstones name the release line17.- Commits carry the model-free trailer pair.
Findings, one line each
GET /api/v1/data/:object/exportreads?format=as csv, json or xlsx and falls back to csv for any other value (rest-server.ts:10324-10326). Reach is unmeasured, so ⛔ not filed → Acceptance notes.ExportFormatandExportImportTemplateSchemahave no reader outside spec. They are outside ruling A's scope → Acceptance notes..changeset/19365-…:98citesIExportService.listExportJobs, which this removes. The changeset cannot be edited here → Acceptance notes for release compilation.FOLLOW-UPS.md§9c lists candidates this resolves → Acceptance notes.
-
objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsLanded: PR #20194 →
4db1bf1715· 2026-09-27T08:14Zdomain:specseat 1 (session_01Rjy9MeetSfq34PKn81CRiN), claim5852337804. Landing record.- Merged through the merge queue at 2026-09-27T08:13:50Z as
4db1bf171545b3800a2a82045f9ee2c5456be839. Two readings: the commit is onorigin/main, and the queue branchgh-readonly-queue/main/pr-20194-…is gone. - Path to it: ACCEPT
5853726980; at-tier contract review PASS5853879759on heada050a8a7. CI on that head: 34 success / 5 skipped / 0 failed. Not governed; 3044 lines. The landing order was swapped (5853836544), so this PR went first. Generated drift was re-measured at arming: all 658 migration entries were present in the merged tree'sregistry.ts, andgit merge-treewas clean. - Verified by content on
origin/main:packages/spec/src/contracts/export-service.tsis absent (present on the parent560b724c);ExportJobStatusinapi/export.zod.tsis 1 hit (the retirement note), down from 7; the semantic entryexport-job-family-retiredis present; the type-alias pin count is 787, down from 790. Control:ImportJobProgressinapi/export.zod.ts, 5 hits on both sides. - Card: closed
completedbyFixes #17158;pm:dispatchedlifted in this act. No other card closed in the window: the only closures since 08:05Z are PRs feat(spec)!: retire the export-job API family, IExportService and ScheduleState (ADR-0049) #20194 and fix(spec)!: TursoConfigSchema refuses the turso configs the driver refuses or ignores (#19977) #20199 and cards [Decision] the export-job API contract family (ScheduledExport/ScheduleExportRequest/ExportJob…) has zero consumers and, after #16320, an emptyscheduleblock — retire the family, keep the contract, or build the scheduler? #17158 and spec:TursoConfigSchemaaccepts turso configs the driver refuses or ignores — a remoteurlbesidesyncUrlor a forced replica/localmode, a non-file:replica,syncUrl/syncundermode: remote#19977. - Downstream: PR feat!: retire GET /api/v1/automation for GET /api/v1/meta/flow; ListAiConversationsResponse declares hasMore (#19543) #20192 ([finding] three sibling list doors declare
limit/cursorand never read them, one reportinghasMore: falseas a literal — REBUILD of #19365, which stopped resolving on 2026-09-21 #19543) merges this forward in its patch round.
- Merged through the merge queue at 2026-09-27T08:13:50Z as
- added 3 commits that reference this issue
on Sep 28, 2026
Ruled: 5815151991 · letter A (cross-repo landing; objectui retires first) · 2026-09-24T13:36Z
Blocked-by: objectstack-ai/objectui#10247
Decision needed
The export-job API contract family in
packages/spec/src/api/export.zod.ts(the file's own comment calls itExportJobApiContracts:ExportJob…,ScheduledExport,ScheduleExportRequestand the request / response bodies ofPOST /api/v1/data/export/…) declares an API that nothing on the platform serves: rest-server has no/api/v1/data/exportroute,IExportService(contracts/export-service.ts:15) records "no evidenced provider binding", and no package, example, app or skill outsidepackages/specnames any of the family's schemas or types.#16320 (PR #17146) retired the family's two cron positions (
ScheduledExport.schedule.cronExpression,ScheduleExportRequest.schedule.cronExpression) under the #15954 ruling. That ruling retires POSITIONS, not defs, so the PR left the blocks — and the tree it leaves makes the family-level question visible:ScheduledExport.scheduleandScheduleExportRequest.scheduleare now REQUIRED objects whose only key is an optionaltimezone(defaulting toUTC): a schedule block with no schedule.ScheduleState(automation/execution.zod.ts, same ruling, same PR) keepstimezone,statusandnextRunAt, which describe a cadence the row no longer declares.Whether to retire the family is a maintainer decision — ADR-0049 enforce-or-remove applied to a def family rather than a key — and the contract review on #17146 asked for it to be filed as a decision, not decided by a dev seat. Nothing here is decided; the dev-seat reading at the end is non-binding.
Options
IExportService/ScheduleExportInput) leave the public surface:RETIRED_DEFS_BY_MAJOR[18]rows, one D3 semantic entry, refusal pins, theapi-surface/andjson-schema.manifest/ratchets move, generated reference pages regenerate**BREAKING**minoron@objectstack/spec(launch-window convention); zero measured in-repo consumers (control below), out-of-repo NOT MEASURED; objectui at the pinned.objectui-shamust be grepped before landing (Post-Task Checklist step 4)scheduleblocks stayIExportService, withJob.schedule.expressionas the cadenceDev-seat reading, non-binding: A is consistent with the rulings that already retired the positions; B is the status quo; C contradicts the #15954 direction unless an export scheduler is wanted on a roadmap.
Evidence (every zero beside a lit control; #17146 branch at
90377d224)git grep -l -P '\b(ScheduledExport|ScheduleExportRequest|ExportJob|ExportJobApiContracts)(Schema)?\b' -- packages examples apps skills ':!packages/spec/**'→ 1 file, and it is two comment lines in the D7 expression-conformance ledger (packages/qa/dogfood/test/expression-conformance.ledger.ts:384-385) recording the spec: retire the seven cron-typed positions nothing reads — export schedules,ScheduleState.cronExpression,DataSyncConfig.schedule,CacheWarmup.schedule, backup/DR schedules — under ADR-0049 (#15954 ruling, option A per family) #16320 retirement. Control:\bConnector(Schema)?\bon the same roots = 43 files.git grep -c 'data/export' -- packages/rest/src→ 0 files. Control:api/v1/reportsinrest-server.tslights.contracts/export-service.ts:15— "IExportServicehas no evidenced provider binding".Filed from #16320 (PR #17146) as the boundary flag its contract review asked to become a decision card; not in that card's scope.
Generated by Claude Code