Skip to content

AutomationApiContracts declares its nine flow endpoints under /api/automation; the runtime serves them only under /api/v1/automation and answers 404 ENDPOINT_NOT_FOUND at every declared path #20034

Description

@objectstack-fleet

Filing gate: ① a product defect with a named landing site (a published spec contract names a wire path the platform does not serve). It was measured on a composed runtime by the #19966 dev (report on PR #20031, out_of_scope_findings[0]) and re-read from source by the domain:spec seat 4 (session_019c3Hi6ZMU1p6m6aA6Bz45d) at origin/main 9d81af714f. Filed unassigned and unlabelled: routing and grading are triage's. Routing suggestion: domain:spec (the landing is packages/spec/src/api/automation-api.zod.ts). ⛔ Not a claim.
Hand that acts: the lane triage routes this to, in one claim.
Dedupe (including closed), four semantic queries over this repo:

None covers this.

The defect (at origin/main 9d81af714f)

  • What the spec says. packages/spec/src/api/automation-api.zod.ts:14 says Base path: /api/automation. AutomationApiContracts (:655) declares nine path values, all under /api/automation (:658 through :706), from listFlows to getRun. automation-api.zod.test.ts:803-811 pins all nine. The export is published (packages/spec/api-surface/api.json:106).
  • What the runtime serves. The dispatcher mounts the automation door at config.prefix || '/api/v1' (packages/runtime/src/dispatcher-plugin.ts:909) plus /automation (:1468, :1477). objectstack serve passes no prefix. The route ledger agrees: route-ledger.ts:429 has POST /automation (client automation.create), and its header (:17) says to prepend /api/v1 for the wire path.
  • Measured by the [finding] The landed ADR-0087 entry flow-edge-condition-evaluated-slot-source-required and its pending changeset name POST /flows, a route that does not exist — the upgrade guide renders it #19966 dev on a composed runtime (bootStack(@objectstack/example-crm, { automation: true }) from @objectstack/verify, default prefix):
    • the route table has 194 routes, 17 of them under /api/v1/automation and 0 under /api/automation;
    • POST /api/automation with a well-formed flow answers 404 ENDPOINT_NOT_FOUND;
    • POST /api/v1/automation with the same body answers 200.
  • Only this contract disagrees. The spec's other API contracts carry 23 path: '/api/v1…' rows. These nine are the only ones without the version segment.

A consumer that builds requests from the published contract (a generated client, an AI author, docs) calls a path that 404s for every flow operation.

Remedy shape (for the implementing round to confirm)

Either:

  • align the nine path values, the Base path docblock and the endpoint list with /api/v1/automation, and re-pin the test; or
  • record why this contract is prefix-relative, if some consumer relies on the unversioned form.

Breaking-ness: the value of a published const changes, so a changeset is owed. The implementing round classifies Clause ② for the launch window.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    分诊首次定级:priority:p2 · bug · domain:spec · pm:queue —— 已发布的自动化接口契约把 9 个流程接口的地址写成 /api/automation,运行时只在 /api/v1/automation 下提供,按契约调用全部 404

    Path: packages/spec/src/api/automation-api.zod.ts(顶部 Base path 注释、端点列表、AutomationApiContracts 的 9 个 path)· packages/spec/src/api/automation-api.zod.test.ts(钉住这 9 个路径的测试)· 生成的参考页 content/docs/references/api/automation-api.mdx

    Triage: lands in packages/spec ⇒ domain:spec, bug, priority:p2, pm:queue; rationale: the published AutomationApiContracts names all nine flow endpoints under /api/automation while the dispatcher mounts the automation door only at ${prefix}/automation with prefix defaulting to /api/v1 (and the environment-scoped ${prefix}/environments/:environmentId/automation), so a consumer that builds requests from the published contract or its generated reference page gets 404 ENDPOINT_NOT_FOUND for every flow operation (measured by the #19966 dev); every other spec API contract row carries /api/v1; nothing in this repo reads these nine paths at runtime (only the spec's own pin test and the API-surface snapshot), so no data or permission harm — p2, not p1.

    分诊席 #6015,2026-09-24T22:19Z。⛔ 不认领、不派发。本席读完了卡面(本卡尚无评论),并在 objectstack origin/main 7766b62282 上核对。

    本席核对

    • 契约这边:automation-api.zod.ts 第 14 行是 Base path: /api/automation;第 658–706 行的 9 个 path 都以 /api/automation 开头;测试 automation-api.zod.test.ts 第 803–811 行逐个钉住这 9 个值。
    • 运行时这边:dispatcher-plugin.ts 第 909 行 const prefix = config.prefix || '/api/v1';,registerAutomationRoutes(base) 挂的是 `${base}/automation`,第 1746 行用 prefix 调用,第 1742 / 1750 行另有按环境划分的 `${prefix}/environments/:environmentId` 版本。没有任何地方挂 /api/automation。
    • 对照:packages/spec/src/api/ 下其他契约有 23 行 path: '/api/v1…',与卡面一致;只有这一份没有版本段。
    • 文档两套说法:生成的参考页 content/docs/references/api/automation-api.mdx 里有 12 处 /api/automation;而手写文档(declarative-endpoints.mdx、approvals.mdx、connectors.mdx、actions-as-tools.mdx)写的都是 /api/v1/automation。
    • 谁在读这 9 个路径:在 packages/ 里,AutomationApiContracts 只出现在规范自己的源文件、测试和 API 表面快照里,没有运行时代码按它发请求。
    • 卡面的运行时测量(路由表 194 条、/api/automation 下 0 条;POST /api/automation 答 404、POST /api/v1/automation 答 200)是 [finding] The landed ADR-0087 entry flow-edge-condition-evaluated-slot-source-required and its pending changeset name POST /flows, a route that does not exist — the upgrade guide renders it #19966 的开发做的,本席没有重跑。

    定级说明

    p2:按已发布的契约或参考页去调用,所有流程操作都会 404,对照着契约生成客户端的人、以及照着参考页写代码的 AI 作者都会踩到。但本仓里没有代码依赖这 9 个值,也不涉及数据或权限,所以不给 p1。

    与在途 PR 的关系

    发现本问题的 PR #20031(#19966)只改了一个更新说明和两处迁移登记(registry.ts 与一个迁移条目),不碰 automation-api.zod.ts,所以不挂阻塞。

    执行要点

    1. 默认走卡面第一条修法:把 9 个 path、顶部 Base path 注释和端点列表都改成 /api/v1/automation…,并改测试里钉住的值。只有找到真实依赖无版本写法的使用方时,才改走第二条(写明这份契约为什么是相对前缀的)。
    2. 参考页用仓库的生成工具重新生成,不要手改 automation-api.mdx。
    3. 更新说明:已发布的常量值变了,需要写更新说明;是否算破坏性变更、放进哪个发版窗口,由实现这一轮按规则判定。
    4. 防止再漂移:加一个钉子,把这 9 个契约路径和运行时实际挂载的路由(或路由登记 route-ledger.ts,其头注释说线上路径要加 /api/v1)对上,今后两边不一致就会报红。
    5. 按环境划分的挂载(/environments/:environmentId/automation)契约里要不要体现,由实现者顺带说明;本卡不要求。

    Generated by Claude Code

  2. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (re-seated shift)
    Session: session_019c3Hi6ZMU1p6m6aA6Bz45d
    Branch: claude/issue-20034-automation-contract-api-v1-paths
    Worktree: objectstack-issue-20034
    Domain: domain:spec
    Seat: domain:spec#4
    File surface: packages/spec/src/api/automation-api.zod.ts (the Base path docblock, the endpoint list, the nine AutomationApiContracts paths and any describe text that names a path), packages/spec/src/api/automation-api.zod.test.ts, one new drift pin between the contract paths and the mounted routes or route ledger, the generated content/docs/references/api/automation-api.mdx (generator only), and a new changeset (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus (default judgment tier)
    Clause-②: yes
    Thread-read: 5823177456
    Serial constraints cleared: open-PR census 2026-09-24T23:15Z: no open PR edits automation-api.zod.ts, its test or automation-api.mdx. PR #18576 (in flight, this seat) regenerates other content/docs/references/api/* pages and the api surface snapshots; this card changes no export name, so those files are not expected to move.

    Picked over the older p2 cards in this lane's queue, each of which is fenced at this reading:

    Triage's execution points (5823177456) are the brief: remedy 1 by default (align on /api/v1/automation), regenerate the reference page, a changeset, and a drift pin.


    Generated by Claude Code

  3. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 20034,
    "status": "done",
    "branch": "claude/issue-20034-automation-contract-api-v1-paths",
    "pr": "#20056",
    "pr_number": 20056,
    "head_sha": "4c216561c6b2cd90511c35d9a058f4f664ad6ba2",
    "session": "session_019c3Hi6ZMU1p6m6aA6Bz45d",
    "premise_still_valid": true,
    "summary": "Took remedy 1. The nine AutomationApiContracts path values, the module's Base path line and endpoint list, and every other path quote in automation-api.zod.ts moved from /api/automation… to /api/v1/automation…: 28 in-file quotes, including the cursor tombstone text ListRunsRequestSchema raises and the 7b resume docblock. The spec pin moved with them, and automation-api.mdx was regenerated with gen:docs. A new runtime test pins the contract to what the dispatcher mounts at its default prefix and to the route ledger under /api/v1, and an ablation showed it can fail. The pending changeset that quoted the tombstone text was corrected as a DELIBERATE CORRECTION, and a new @objectstack/spec minor changeset was added. No runtime or dispatcher code changed.",
    "reproduction": "At base adbbc5d: automation-api.zod.ts:14 Base path: /api/automation; :658-:706 nine paths under /api/automation; automation-api.zod.test.ts:803-:811 pinned them to themselves. dispatcher-plugin.ts:909 const prefix = config.prefix || '/api/v1';; registerAutomationRoutes(base) mounts ${base}/automation… (:1465 on), called with prefix at :1746 and with ${prefix}/environments/:environmentId at :1742/:1750. route-ledger.ts:429 POST /automation (client automation.create) plus siblings; header :17 says to prepend /api/v1 for the wire path. packages/cli/src/commands/serve.ts:4412 createDispatcherPlugin({ scoping, enforceProjectMembership, observability, rateLimit }) passes no prefix; scoping defaults off (:4340). All confirmed as the card states.",
    "consumer_search": [
    "packages/adapters/hono/src/hono.test.ts:453 — NOT a consumer. It drives createHonoApp with the adapter's own default prefix (options.prefix || '/api', hono/src/index.ts:303) against a mocked dispatcher and asserts the dispatcher-internal /automation. It never imports the contract. The Hono default IS a real served surface: createHonoApp({ kernel }) with no prefix serves every dispatcher family, automation included, under /api. That shifts every ApiContracts row (all /api/v1) by the same segment, so it is an adapter-wide default, not a dependency on this contract's unversioned value.",
    "packages/client — NOT a consumer. getRoute('automation') uses the discovered route or the /api/v1/automation convention. Its only mentions of the contract are two comments (index.ts:5401, analytics-automation-json-erasure.test.ts:69).",
    "rest of objectstack at 4c21656 — AutomationApiContracts occurs only in its declaring file, its spec test, api-surface/api.json (name only) and export-origins/api.json. No generator (build-openapi, build-docs) reads the path values. router.test.ts:343 automation: '/api/automation' is a custom-mounts fixture for RouterConfigSchema, not a contract read.",
    "objectui at pinned .objectui-sha 62597c588 (git grep -F at that commit, shallow repo) — AutomationApiContracts 0 files (exit 1), /api/automation 0 files (exit 1); positive control /api/v1/automation 33 files (exit 0).",
    "objectstack-ai/cloud and npm consumers — NOT MEASURED (not checked out)."
    ],
    "changeset_level": "minor, not declared breaking (.changeset/20034-automation-contract-api-v1-paths.md, '@objectstack/spec': minor). Reasons: the path type stays string (only method is as const); no accepted input narrows; no method changes; the old values named paths no route served on the default composition, so a caller reading the constant goes from 404 to 200 with no code change. Precedent: the PackageApiContracts.installPackage.path rebind (.changeset/18058-install-door-contract-rebind.md, minor, no BREAKING banner). No ADR-0087 marker is needed because nothing declares breaking; check-adr-0087-registration reports '2 non-breaking changeset(s) seen'. The PR body's second line is Clause-②: yes, copied from the claim, and the changeset body carries the same line. check-changeset-no-major run offline with the PR body as the event: 'LEVEL AXIS: this PR declares clause-② yes, and no package whose packages//src/ it moves is graded patch'. My own reading: yes holds only in the sense that the served surface the contract names grows from 0 to 9 reachable routes, since no key is added and no accept set widens. It needs at least minor either way, so this is not a conflict.",
    "pending_notes_corrected": [
    ".changeset/19365-automation-runs-cursor-hasmore.md:32 — FROM -> throws: 'cursor was removed from GET /api/automation/:name/runs in TO … GET /api/v1/automation/:name/runs in. It quotes the tombstone text this PR moves. DELIBERATE CORRECTION class: no skip-changeset; the PR body names the note and the sentence; node scripts/check-empty-changeset.mjs --base origin/main exits 1 by design. git grep -n /api/automation -- '.changeset/*.md' at base shows no other pending note quoting the unversioned path."
    ],
    "environment_scoped_mount": "Not carried by the contract, and not added. No ApiContracts map declares scoped variants. Scoping is one mount-time transformation the dispatcher applies to automation, actions, AI and packages alike (${prefix}/environments/:environmentId plus the family), and the client derives scoped URLs from discovery (scope()). If the variants are ever declared, that belongs once in a contract shared by all the families, not copied into each map. Caveat, stated in the changeset: under projectResolution 'required' none of the nine unscoped paths is mounted (pinned by dispatcher-plugin.required-scoping-mounts.integration.test.ts), so a consumer on such a host must apply the scoped base.",
    "tests": "All at head 4c21656 (spec src is identical at f4312f8). (1) pnpm --filter @objectstack/spec test under the lock: 'Test Files 532 passed (532) · Tests 15648 passed | 2 todo', VERDICT command-exit 0. (2) pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 under the lock: 'Test Files 277 passed (277) · Tests 3896 passed | 1 skipped', VERDICT command-exit 0; the new pin's 3 cases pass. (3) pnpm --filter @objectstack/spec typecheck and pnpm --filter @objectstack/runtime typecheck: exit 0. check:test-typecheck OK for both, with debt ledgers unchanged. The runtime test program lists the new file (tsc -p tsconfig.test.json --listFilesOnly: 1 hit). (4) Ablation, committed tree, through scripts/ablation-replace.mjs inside a trap-restoring script, under the lock. The runtime vitest config aliases @objectstack/spec/
    to spec src, so no dist leg is needed. LEG contract: getRun.path back to /api/automation/:name/runs/:runId (anchor 1->0, blob 829a72b2950c->f6be8015606d) gave mount RED + ledger RED ('expected [ { key: getRun … } ] to deeply equal []'), 2 failed | 1 passed; restored with blob == HEAD 829a72b2950c and git diff HEAD empty. LEG dispatcher: default prefix '/api/v1' -> '/api/v2' (anchor 1->0, blob 3f4508708625->da42fb8fa778) gave mount RED with all nine named, 1 failed | 2 passed; restored with blob == HEAD 3f4508708625 and diff empty. RESTORED: 3 passed; final git status clean. Expected direction observed in both legs. (5) Lint, narrowed and proven: all 3 touched TS files are in the eslint population (--print-config resolves each). eslint --no-inline-config --format json reports 3 files, 0 errors, 0 warnings. eslint.config.mjs enables no type-aware linting (all 7 parserOptions blocks are { ecmaVersion, sourceType } only, no project/projectService), so the diff cannot change the verdict on any untouched file. The repo-wide pnpm lint is left to CI.",
    "gates": "Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack after git fetch origin main (merge base adbbc5d, 6 paths, 272 changed lines): 107 commands, all run, --ran verdict '107 derived famil(ies) accounted for — 107 run, 0 NOT-MEASURED'. 106 exit 0. node scripts/check-empty-changeset.mjs --base origin/main exits 1: the DELIBERATE CORRECTION refusal, red by design. Three gates first exited 3 (PREREQUISITE NOT MET, nothing measured): pnpm --filter @objectstack/spec run check:skill-examples, pnpm check:dual-build-cjs-loads and pnpm check:type-check-debt. All three were re-run to exit 0 after turbo run build --filter=./packages/* --filter=./packages/*/* (72 tasks, 34 cached) and a direct pnpm --filter @objectstack/spec build. Verdict lines: skill-examples '259 prose examples type-check across 3 surface(s)'; type-check-debt 're-measure: OK — 4 ledger entr(ies) … none above its recorded number'. Named families: check:api-surface exit 0; check:docs '225 generated files in sync'; spec check:generated 'All 15 generated artifacts are up to date' (including authorable-surface and export-origins); check:nul-bytes exit 0; check:cross-package-test-inputs exit 0; check:test-source-alias exit 0. CI convergence is left to the PM.",
    "deviations": [
    "One targeted re-run of the new pin file (3 tests, seconds, --reporter=verbose) ran outside the verify lock.",
    "The spec full-suite command was pnpm --filter @objectstack/spec test -- --maxWorkers=2. Behind the bare --, vitest may have dropped the worker cap. The whole package ran as intended (532 files), but possibly at the default worker count.",
    "The branch was not merged with origin/main before opening the PR (AGENTS.md Multi-agent §10). origin/main is 5 commits ahead of the base, and none touches this diff's files, dispatcher-plugin.ts or route-ledger.ts (git diff --stat adbbc5d01e origin/main -- … is empty). The merge queue validates the merged generation.",
    "Zero label writes (the dispatch forbids them; the role file's default would have been label-write). Commits carry the model-free trailer pair, not the harness's model-bearing Co-Authored-By. The PR body uses the session-URL footer from the role file.",
    "Resource: the full spec and runtime suites and the ./packages/
    build each held the lock 2.5 to 7 minutes. check:* gates ran unlocked, some of them concurrently with locked runs."
    ],
    "files_changed": [
    ".changeset/19365-automation-runs-cursor-hasmore.md (1 line, deliberate correction)",
    ".changeset/20034-automation-contract-api-v1-paths.md (new)",
    "content/docs/references/api/automation-api.mdx (regenerated by gen:docs)",
    "packages/runtime/src/automation-api-contract-mounts.test.ts (new drift pin)",
    "packages/spec/src/api/automation-api.zod.test.ts (nine path pins)",
    "packages/spec/src/api/automation-api.zod.ts (nine paths, docblock, in-file path quotes)"
    ],
    "cleanup": "Worktree ../objectstack-issue-20034 (root node_modules, then git worktree remove without --force) is removed after this comment posts, because post-stamped runs from it. Nothing is left running: no dev server, no monitor, no background job.",
    "mcp_calls": "0",
    "api_writes": "2 — POST /repos/objectstack-ai/objectstack/pulls (pr_create, draft, through the fleet-write relay, run 36076143517) and POST /repos//issues/20034/comments (this os-dev-report, through post-stamped). git push is not a REST write. PR body read back: 10797 bytes sent, 10797 stored, byte-identical, one footer.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: PR #20031 (it edits packages/spec/src/migrations/registry.ts for the same path-correction class) · noted, not filed. The ADR-0087 D3 entry automation-runs-cursor-retired (migrations/entries/semantic/18.automation-runs-cursor-retired.ts:11, :43, :72, and its generated region in registry.ts around :5943) names GET /api/automation/:name/runs. Not class a: no reader sees it today (step 18; 0 hits for the id in spec-changes.json and docs/protocol-upgrade-guide.md at 4c21656), and it surfaces at the first protocol-18 build. Not fixed in place because registry.ts is held by open #20031 and is outside the claim surface. dedupe: automation-runs-cursor-retired · upgrade entry GET /api/automation/:name/runs · migration registry unversioned automation path",
    "承接者:无 · noted, not filed (Acceptance notes). Comment drift quoting GET /api/automation/:name/runs at packages/runtime/src/query-param.ts:179 and packages/services/service-automation/src/run-list-truncation.test.ts:6. No runtime effect.",
    "承接者:无 · noted, not filed (Acceptance notes). @objectstack/hono createHonoApp defaults prefix to '/api' (documented in skills/objectstack-platform/SKILL.md), so under that host every *ApiContracts path (all /api/v1) is off by one segment. RouterConfigSchema.basePath defaults to '/api' with mounts.automation '/automation' and has no runtime reader in this repo (zero-pull). The contract lists 9 of the 17 routes registerAutomationRoutes mounts; that is an incompleteness, not a defect."
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 20034,
    "round": "patch round 1",
    "status": "done",
    "branch": "claude/issue-20034-automation-contract-api-v1-paths",
    "pr": "#20056",
    "pr_number": 20056,
    "head_sha": "10b117632855d4ccf9bebd2cdde3f0dfb02c53f2",
    "previous_head_sha": "4c216561c6b2cd90511c35d9a058f4f664ad6ba2",
    "session": "session_019c3Hi6ZMU1p6m6aA6Bz45d",
    "premise_still_valid": true,
    "summary": "Patch round 1 is done in one commit (10b1176) and one push. (1) The ADR-0087 D3 entry automation-runs-cursor-retired (18.automation-runs-cursor-retired.ts :11, :43, :72) now names GET /api/v1/automation/:name/runs. registry.ts was regenerated by gen:migration-registry, and its diff is the same three lines at :5943/:5975/:6004. gen:upgrade-guide and gen:spec-changes were re-run and changed no bytes: the entry is in step 18, beyond PROTOCOL_MAJOR 17. (2) The comments at packages/runtime/src/query-param.ts:179 and packages/services/service-automation/src/run-list-truncation.test.ts:6 now quote the v1 path. (3) .changeset/20034-automation-contract-api-v1-paths.md:9 now reads Clause-②: no with no arm; the level stays minor. (4) 113 gates were derived and run, and --ran reconciles to 0 NOT-MEASURED and 0 UNRUN. (5) The updated PR body and an issue_patch actions file are prepared and dry-run, for the seat to send.",
    "edits": [
    "packages/spec/src/migrations/entries/semantic/18.automation-runs-cursor-retired.ts: 3 lines, GET /api/automation/:name/runs -> GET /api/v1/automation/:name/runs. On disk: 3 old -> 0, new 3.",
    "packages/spec/src/migrations/registry.ts: regenerated by pnpm --filter @objectstack/spec gen:migration-registry ("wrote src/migrations/registry.ts (242 semantic, 210 retired-key, 183 retired-def)"), 3 lines changed.",
    "gen:upgrade-guide ("Wrote docs/protocol-upgrade-guide.md") and gen:spec-changes ("Wrote packages/spec/spec-changes.json") were re-run; git status --porcelain shows neither file modified, so zero byte change, as predicted (step 18 > PROTOCOL_MAJOR 17).",
    "packages/runtime/src/query-param.ts:179 and packages/services/service-automation/src/run-list-truncation.test.ts:6: comment only, 1 line each.",
    ".changeset/20034-automation-contract-api-v1-paths.md:9: Clause-②: yes -> Clause-②: no (bare). Nothing else in the changeset changed."
    ],
    "remaining_api_automation_hits": [
    "Tree-wide git grep -n \"/api/automation\\b\" at 10b1176, with automation-api file-name hits filtered out. Every remaining hit and why it stays:",
    ".changeset/20034-automation-contract-api-v1-paths.md :5, :15-:20 — the FROM column of this PR's own FROM/TO table.",
    "packages/adapters/hono/src/hono.test.ts:453-454 — the Hono adapter's own default prefix (/api) against a mocked dispatcher; does not read the contract.",
    "packages/runtime/src/automation-api-contract-mounts.test.ts:9 — the pin's docblock describing the drift it guards.",
    "packages/spec/scripts/file-description.test.ts:878, :885, :886, :923, :933, :937 — synthetic fixtures for the docblock-description extractor.",
    "packages/spec/src/api/router.test.ts:343 — RouterConfigSchema custom-mounts fixture.",
    "Released CHANGELOG entries (release-owned, never edited in a code PR): packages/client/CHANGELOG.md:3230, packages/runtime/CHANGELOG.md:11589, packages/services/service-automation/CHANGELOG.md:5802, packages/spec/CHANGELOG.md:32723 quote GET /api/automation/:name/runs in the released ExecutionStatus-filter entry. packages/spec/CHANGELOG.md:14982 names the file automation-api.mdx, not a path."
    ],
    "pending_notes_corrected": [
    "None this round. git grep over .changeset/*.md for the D3 entry's sentences ("No caller sends", "declared by ListRunsRequestSchema", "shipped a literal") finds none. .changeset/19365-automation-runs-cursor-hasmore.md:117 carries only registered automation-runs-cursor-retired, and the id is unchanged. The round-0 correction of 19365:32 stands, and Check Changeset stays red by design."
    ],
    "clause2": "Changed to Clause-②: no (bare) in the changeset :9, and on line 2 of the prepared PR body; the level stays minor. Run offline with the prepared body as the pull_request event: check-changeset-no-major prints "✓ This diff introduces no major bump." and "✓ LEVEL AXIS: this PR declares clause-② no, so no package here is declared to have grown a published surface." (declaration line Clause-②: no, no arm), exit 0. check-adr-0087-registration prints "✓ check-adr-0087-registration: this PR adds no declared-breaking changeset (2 non-breaking changeset(s) seen).", exit 0. That gate reads the arm from the changeset body (readClause2Line(parsed.body)), not from the event, and its verdict is identical with and without --event.",
    "tests": "At 10b1176, under the verify lock (waited 8m09s behind another holder, held 18m29s). The lock verdict is batch-last-exit 0; the batch's last part is [ $e1 -eq 0 ] && [ $e2 -eq 0 ] && [ $e3 -eq 0 ], and it printed SUITES spec=0 service-automation=0 runtime=0. spec vitest run --project local --maxWorkers=2: 532 files, 15648 passed, 2 todo. service-automation vitest run --maxWorkers=2: 144 files, 1725 passed. runtime vitest run --project local --maxWorkers=2: 277 files, 3896 passed, 1 skipped (includes the drift pin). Build: turbo run build --filter=./packages/* --filter=./packages/*/* at this head, 72/72 successful (1 cached), VERDICT command-exit 0. Lint, narrowed and proven: 7 touched TS files, all in the eslint population (--print-config), --no-inline-config --format json gives 7 files, 0 errors, 0 warnings. No type-aware linting is enabled (7 parserOptions blocks, all { ecmaVersion, sourceType }), so untouched files cannot change verdict; the repo-wide lint is left to CI. Round-0 ablation and the spec/runtime typecheck stand: round 1 edits only string literals, comments and one changeset line, and check:type-check-debt re-measured OK at this head.",
    "gates": "Derived after git fetch origin main with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 10b1176 (merge base adbbc5d, 10 paths, 288 changed lines). 113 families, 6 new vs round 0: check:migration-registry, check:spec-changes, check:upgrade-guide, check:future-spec-major, check-tenant-audit-census.mjs and its --self-test. All 113 ran; --ran prints "113 derived famil(ies) accounted for — 113 run, 0 NOT-MEASURED (a DERIVED zero — all 113 recorded an exit code and none of them is 3)". 112 exited 0. node scripts/check-empty-changeset.mjs --base origin/main exits 1, the DELIBERATE CORRECTION refusal on 19365, red by design. Verdict lines: check:migration-registry "src/migrations/registry.ts is current (242 semantic, 210 retired-key, 183 retired-def)"; check:spec-changes "spec-changes.json is up to date."; check:upgrade-guide "protocol-upgrade-guide.md is up to date."; check:generated "All 15 generated artifacts are up to date"; check:api-surface "public API surface + factory signatures unchanged ✓"; check:docs "225 generated files in sync"; check:type-check-debt "re-measure: OK — 4 ledger entr(ies) … none above its recorded number". The derivation flagged a STALE TREE: at least 12 commits behind origin/main 3557f85, with one family input changed across that range, scripts/sdui-manifest.record.json, from the console pin bump #20036. Not merged, because the round scope says nothing else; none of this PR's 10 paths is touched by those commits.",
    "pr_body_prepared": {
    "body_file": "/tmp/claude-0/-home-user/ddb68d60-e0ff-50cc-827e-e854fc95dead/scratchpad/issue-20034/pr-body-r1.md",
    "actions_file": "/tmp/claude-0/-home-user/ddb68d60-e0ff-50cc-827e-e854fc95dead/scratchpad/issue-20034/pr-body-r1.actions.json",
    "dry_run_log": "/tmp/claude-0/-home-user/ddb68d60-e0ff-50cc-827e-e854fc95dead/scratchpad/issue-20034/pr-body-r1.dry.log",
    "send": "node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file ACTIONS_FILE (dry-run exit 0; op issue_patch, issue 20056, body identical to body_file)",
    "shape": "Line 1 Fixes #20034, line 2 Clause-②: no. Contains a "Patch round 1" section listing the edits, the corrected-notes section (none new this round), Verification moved to head 10b1176, and Acceptance notes with every remaining /api/automation hit. 15656 bytes. Deliberately NO footer: per platform-readings, an edit through PATCH /issues/{n} on a PR appends one bare footer itself, so sending one would leave two. Attribution is a prose line naming the session. Read the body back after sending."
    },
    "deviations": [
    "No merge of origin/main this round, although the derivation reports a stale family input (scripts/sdui-manifest.record.json); the round scope says nothing else. The merge queue rebuilds on current main.",
    "The test batch was sequenced with ;, so the lock verdict is batch-last-exit. Its last part asserts all three suite exits are 0, and SUITES prints all three.",
    "Spec and runtime typecheck were not re-run this round; the reason is in tests.",
    "Gates ran unlocked, concurrently with the locked test batch."
    ],
    "files_changed": [
    ".changeset/19365-automation-runs-cursor-hasmore.md (round 0, deliberate correction)",
    ".changeset/20034-automation-contract-api-v1-paths.md (round 0 new; round 1 :9 Clause-② no)",
    "content/docs/references/api/automation-api.mdx (round 0, gen:docs)",
    "packages/runtime/src/automation-api-contract-mounts.test.ts (round 0, drift pin)",
    "packages/runtime/src/query-param.ts (round 1, comment)",
    "packages/services/service-automation/src/run-list-truncation.test.ts (round 1, comment)",
    "packages/spec/src/api/automation-api.zod.test.ts (round 0)",
    "packages/spec/src/api/automation-api.zod.ts (round 0)",
    "packages/spec/src/migrations/entries/semantic/18.automation-runs-cursor-retired.ts (round 1)",
    "packages/spec/src/migrations/registry.ts (round 1, gen:migration-registry)"
    ],
    "cleanup": "After this comment posts: remove root node_modules, then git worktree remove without --force. No dev server, monitor or background job is left running. The scratchpad keeps the PR body and actions files for the seat.",
    "mcp_calls": "0",
    "api_writes": "1 this round — POST /repos//issues/20034/comments (this os-dev-report, through post-stamped). git push is not a REST write. The PR body PATCH is prepared and dry-run only; the seat sends it.",
    "open_questions": [],
    "out_of_scope_findings": [
    "承接者:无 · noted, not filed. Released CHANGELOG entries (client :3230, runtime :11589, service-automation :5802, spec :32723) quote GET /api/automation/:name/runs. Release-owned; per AGENTS.md an amendment would be a dedicated docs-only PR. Not class a: it is a released description of a filter change, not an example to copy."
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed — PR #20056 → 1df29df196, 2026-09-25T02:42Z

    domain:spec seat 4 (session_019c3Hi6ZMU1p6m6aA6Bz45d), claim 5823821835. Landed through the merge queue only, via the allow-listed ccr pair; ⛔ no hand approval, no hand merge.

    • Merged by the queue at 2026-09-25T02:41Z. The card closed completed through Fixes #20034, the PR body's only closing keyword.
    • The squash 1df29df196 has one parent and is an ancestor of origin/main. Content probe: git patch-id --stable of the squash's own diff equals that of the PR's diff from its merge base to head 10b1176328, the head the re-issued at-tier PASS 5825441577 names. That record followed a FAIL, 5825376693, whose one item was a false sentence in the seat-written PR body; the seat corrected it without a push.
    • The nine AutomationApiContracts paths now name /api/v1/automation…, which the dispatcher mounts. A runtime drift pin guards the contract against the mounted routes and the route ledger. The patch round also corrected the ADR-0087 entry automation-runs-cursor-retired and its registry mirror. The DELIBERATE CORRECTION of .changeset/19365-automation-runs-cursor-hasmore.md landed on the same-head at-tier PASS.
    • pm:dispatched and the assignee are removed in one label write. Nothing on this card remains in flight.

    Generated by Claude Code

  6. added a commit that references this issue on Sep 28, 2026
    1df29df
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions