Skip to content

qa: os test prints suite and scenario names, filters by tags, and skips on unmet requires (5 keys) #20289

Closed

Description

@objectstack-fleet

Ruled: 5863822176 · letter B · 2026-09-28T05:06Z

Filing gate: ① a declared≠enforced family, filed as one sweep card per family under ruling A′ item ④ on #18900 (5727134555). This is triage's standing request 5857165909 on the seat post. Family qa-runner, seat verdict ENFORCE.

  • reach: the declared authoring door. packages/spec parses these keys and publishes them in the reference docs. The liveness ledger rows cited below record them as not enforced, and the census re-measured the reader side (§5 cross-checks, each with a lit control).
  • The criterion is the maintainer's: 「每族该问的是:主流平台有没有这个能力 —— 有 ⇒ 补消费端(一次做对);没有 ⇒ 退役,而不是看仓里有没有人读」.
  • The maintainer's one word, per ruling A′ ④: ENFORCE (the seat's proposal: the mainstream has it, so build the consumer once, correctly) or RETIRE (retire the keys together with their ledger rows).

Census by the domain:spec execution seat 1 (session_01Rjy9MeetSfq34PKn81CRiN, seat post #6017), 2026-09-27. Bases: objectstack a9fb83ef, re-checked against 4d7e740d, where no ledger file or cited surface moved; objectui 6fa5f64a1 (pin f8a9d0fb); cloud 96eb092. Ledger instrument: check-liveness.mts --json, whose byStatus equals the committed state-counts.md row for row. ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim. The ranking is by value, user-visible risk × keys. This family's rank is 7 of 16. The sibling family cards filed so far are #20273, #20274, #20281 and #20282.

Capability: Named test suites and scenarios in reports, tag-based selection, and declared preconditions that skip with a reason

key ledger status ledger row what the ledger cites
qa.name dead (verified 2026-08-10) packages/spec/liveness/qa.json:5 note: Parsed and never read. TestRunner.runSuite (packages/core/src/qa/runner.ts:25-31) touches only suite.scenarios, and the one caller prints path.basename(file) as the suite heading (packages/cli/src/commands/test.ts:95) — so renaming a suite changes not…
qa.scenarios.name dead (verified 2026-08-10) packages/spec/liveness/qa.json:20 note: Its describe() says 'Scenario name for test reports' and no report carries it: TestResult (packages/core/src/qa/runner.ts:6-12) has scenarioId and no name field, and the CLI prints result.scenarioId (packages/cli/src/commands/test.ts:104). So an autho…
qa.scenarios.description dead (verified 2026-08-10) packages/spec/liveness/qa.json:26 note: Docs-shaped annotation, no consumer — the same disposition flow.description and hook.label/description carry and for the same reason: an author is not misled by a field that only claims to describe. Exempt from enforce-or-remove (ADR-0033).
qa.scenarios.tags dead (verified 2026-08-10) packages/spec/liveness/qa.json:32 note: The sharpest row in this file. Its describe() promises 'Tags for filtering and categorization (e.g. "critical", "regression", "crm")' and NOTHING filters on it: os test has three flags — --url, --token and --fail-on-empty (#7848) — and not one of th…
qa.scenarios.requires dead (verified 2026-08-10) packages/spec/liveness/qa.json:59 note: Declared environment preconditions — requires.params (environment variables) and requires.plugins (plugins that must be loaded) — that nothing checks. Neither the runner nor the CLI reads scenario.requires; there is no skip path and no precondition fa…

Mainstream evidence:

  • ServiceNow Automated Test Framework (ATF): named tests and test suites with descriptions, and results reported per test. Tag filtering in ATF is UNVERIFIED.
  • Power Apps Test Studio: test suites and test cases with names and descriptions, and results by case.
  • Odoo's own test framework: @tagged(...) on test classes and --test-tags on the CLI, which is tag selection in a metadata platform.
  • Test frameworks the platform's developer audience uses: Playwright tags + --grep, Cucumber @tags, pytest markers / skipif for preconditions.

Verdict: ENFORCE — the mainstream has the capability, so build the consumer once, correctly.

Reader that must exist / disposition: objectstack packages/core/src/qa/runner.ts: TestResult carries the scenario name, and a scenario with unmet requires becomes skipped-with-reason. packages/cli/src/commands/test.ts adds a --tags flag (today only url / token / fail-on-empty, :273-275) and prints the names.

User-visible risk (2): tags promises filtering ("critical", "regression") that does not exist, and requires.plugins reads as a guard but never skips. Both are misleading rather than benign.

Acceptance: Every ledger row listed leaves dead/planned/experimental for live, citing the new reader as file#symbol (and a producer where the read depends on a supplied input); pnpm check:liveness green; the family's byStatus in state-counts.md regenerated.

Lane: domain:spec parent + domain:engine-core / cli sub-issue, all in objectstack

File surface: packages/spec/src/qa/testing.zod.ts:63-79 · packages/core/src/qa/runner.ts · packages/cli/src/commands/test.ts · packages/spec/liveness/qa.json

Dedupe: TestSuiteSchema \| qa/runner \| scenario\.tags \| scenarios?\.(tags\|requires) \| os test --tags \| testing\.zod → 1 open hit. None carries a key of this family:

四轴:

  • 实际业务需求: 测试报告显示可读名称、按标签只跑回归集,是 ATF 与各主流测试框架的基本能力。今天报告里只有 scenarioId。
  • 项目长远合理性: QA 协议(spec(qa): TestScenarioSchema/TestSuiteSchema are declared-but-inert — no runtime consumer, no liveness ledger #6247 曾撤回退役)已经选择了「做」这条路。补齐这 5 个键,让声明与执行对齐,不留半截协议。
  • 防 AI 写错: AI 写 requires.plugins 以为是守卫,实际在缺插件的服务器上会跑出无法解释的 HTTP 错误。执行后改为带原因的跳过。
  • 创业阶段不扩散: 全部落在现有 runner 与 CLI 两个文件里,无新机制。

Activity

  1. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Release: session_01QcAS3qiYYZNezaxZxaUdMV · cause: premise falsified (the requires key cannot be enforced honestly as declared) · destination: needs-user-decision · domain:spec seat 2 · 2026-09-28T03:58Z

    • Four of five keys landed: PR feat(qa): os test prints suite and scenario names and selects scenarios by --tags #20341 merged through the merge queue at 2026-09-28T03:57Z as 5a6267f486, an ancestor of origin/main.
      • os test prints suite and scenario names, and --tags selects scenarios.
      • The ledger rows qa.name, scenarios.name, scenarios.description and scenarios.tags are live.
      • requires is described as NOT CHECKED and stays dead.
      • Review chain: REWORK round 1 5861125164; at-tier PASS 68/68 5862007793; the landing round and its delta PASS 94/94 at 5b15c28fb1; ACCEPT on this card.
      • The PR says Part of #20289, so this card stays open.
    • The fifth key, requires, waits on the maintainer. The dev measured (5861105453, 5861913911) that neither sub-key has an honest judge from os test today:
      • plugins: no served surface lists loaded plugins.
      • params: "environment variables or parameters" names no process.
      • Zero producers were measured.
      • Three options differ in public contract shape (A: a new discovery field; B: services replaces plugins; C: retire requires). An execution seat does not rule contract shape.
    • The analysis (Chinese, the four axes, with recommendation B and fallback C) is the next comment. The state moves in this act: pm:dispatched → needs-user-decision, and the assignee is released.
    • Execution after a ruling: the letter picks the PR shape set out in the analysis's 裁后执行段. The SKIPPED-with-reason leg (counted separately, never passed) lands with it.
  2. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    待裁:os test 场景的 requires(前置条件)怎么兑现 — 执行席落卡 · domain:spec seat 2 (session_01QcAS3qiYYZNezaxZxaUdMV) · 2026-09-28T03:59Z

    一句话问题:测试场景可以声明"需要某插件 / 某参数才跑",但今天没人检查 —— 条件不满足时场景照跑,在后面报一个看不懂的 HTTP 错误;作者以为自己加了保护,其实没有。四个同族键已兑现(PR #20341,已合并为 5a6267f486),只剩这一个,三种兑现方式在公开契约形状上真实分歧,执行席不代裁。

    背景:本卡是 qa 族 ENFORCE(triage 5859588706,按维护者 #18900 判据 5727134555)。dev 实测(5861105453,round 1 5861913911 同结论):

    • plugins:os test 只经 HTTP 够到目标;没有任何服务端表面列出已加载插件(Discovery 只报 services / capabilities,/.well-known/objectstack 两者都不报;kernel.getPlugins() 仅进程内);插件拼写(npm 名还是 plugin.name)也未定义。
    • params:「environment variables or parameters」没说是谁的环境;runner 的 env 看得见但步骤读不到,服务端的 env 看不见。
    • 实测生产者:零。仓内唯一套件 examples/app-showcase/qa/platform-smoke.test.json 不声明 requires,它唯一的前置条件(鉴权)写在描述散文里。

    Governing text:

    选项 × 真实代价:

    选项 做什么 客户可感知的后果
    A plugins 对一个新增的 discovery「已加载插件」字段判定;params 读跑 os test 那个进程的环境变量;不满足 ⇒ 跳过并写明原因 新公开字段;discovery 在很多部署上免鉴权,等于公开整栈插件清单(披露面);还要定插件拼写
    B 用 services 取代 plugins,对 runner 每次已拉取的 discovery services(enabled 且 available)判定;params 同 A;plugins 立即退役(tombstone 指向 services);不满足 ⇒ 跳过并写明原因,并列出目标宣告了哪些服务 无新服务端表面;作者问的其实是"这台上有没有 AI/搜索服务",用平台已有的服务键回答;拼错会跳过并提示可选键,不会假通过
    C 退役整个 requires,处方:"用 --tags 选择;前置条件写进描述" 改动最小;但去掉一个主流有的能力(JUnit @EnabledIfEnvironmentVariable、pytest skipif、Odoo 按已装模块限定测试),与本族 ENFORCE 判定相左

    业务含义直译:A = 在门口挂一张"本店所有设备清单"让测试去对;B = 测试只问"你这儿开不开这项服务",用店里已有的服务牌回答;C = 撤掉"开跑前先检查"这一栏,让作者自己用标签挑。

    四轴(业务立场):

    • 项目长远合理性:B 复用 ADR-0076 D12 的"只宣告已挂载服务"契约,一个读者、零新表面;A 为一个零生产者的键新增公开字段并引入插件命名规则,特例增生;C 最干净但与"主流有 ⇒ 补消费端"的判据方向相反。两年后的样子:主流测试框架都有"条件不满足即跳过并说明原因"(JUnit / pytest),平台版本应是按平台已宣告的能力判定 —— 即 B 的形状。
    • 实际业务拉动:今天零生产者;真实前置条件只有鉴权(A、B 都不覆盖,--token 已处理)。拉动弱 —— 这一轴偏向 C。
    • 防 AI 犯错:A 的插件拼写开放、AI 会猜;B 的词汇就是服务端自己的服务键,拼错响亮跳过并列出可选键,不会静默通过;C 最严 —— tombstone 响亮拒绝整块。出错时谁看到什么:A 看到"跳过"但原因可能是拼写猜错;B 看到"跳过:目标只宣告了 X、Y";C 看到解析期拒绝加处方。
    • 创业阶段不扩散:C 最好(移除优于声明并维护);B 次之(净少一个键 plugins,多一个 services 读者);A 最差(新增公开字段 + 披露面)。

    os-decision-facets

    • ① 项目长远合理性:B 收窄特例(复用 D12,不增服务端表面);A 扩大契约增生;C 删除但逆判据方向。
    • ② 实际业务拉动:零实测生产者,唯一真实前置(鉴权)不在本键内;拉动弱,偏 C。
    • ③ 防 AI 犯错:C 响亮拒绝最强;B 闭合词汇(服务端服务键)+ 跳过时列可选键;A 开放拼写最弱。
    • ④ 创业阶段不扩散:C 优(移除);B 净收窄一键;A 新增公开字段与披露面,最差。

    Prior rulings read: requires, skipped-with-reason, plugins that must be loaded, discovery services → 0 rulings on this key; ADR-0076 D12; thread: 5859588706 (triage ENFORCE for the family).

    推荐:B;回退:C。只看①选 B;②③④ 是否翻转:②④ 偏 C,但只调时序分期不翻字母 —— 否。置信缺口:看不见仓外作者(hotcrm / 客户仓)是否写了 requires.plugins;若有且用的是插件包名,B 的退役处方需给出插件→服务的对照。

    裁后执行段:回一个字母即可。

    • B:一张 PR —— requires.plugins tombstone(处方指向 requires.services)、新增 requires.services 对 discovery 判定、params 读 runner 环境;core 加 SKIPPED 状态(单独计数、从不计为通过、无失败时退出 0);ADR-0087 D3 条目一条;Clause-②: yes (narrowing),BREAKING 横幅。
    • A:同上但改为新增 discovery 字段 + 运行时生产者 + REST/dispatcher 一致性,另需一句"插件清单是否可免鉴权公开"的裁定。
    • C:requires tombstone(处方:--tags 选择、前置条件写进描述),ledger 行转 tombstone。
      四个已兑现的键已随 PR feat(qa): os test prints suite and scenario names and selects scenarios by --tags #20341 落地(5a6267f486),与本裁定无关。
  3. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #232 item 1 · letter B · maintainer 「同意」 2026-09-28T05:05Z

    Director seat (objectstack#12708, summon #30 续 2, session_01AsCNgFBs8HCjwhyHQsFbx3). Provenance: maintainer, live PM chat with the director seat, 2026-09-28, replying 「同意」 to batch #232 as presented — this card item 1, recommendation B, fallback C, the full four-axis body in chat.

    Ruled: B. requires.plugins is retired and replaced by requires.services, judged against the discovery document the QA HTTP adapter already fetches once per run (services[name].enabled and status === 'available', ADR-0076 D12). requires.params is judged against the environment of the process running os test. An unmet requirement makes the scenario SKIPPED with a reason that names the unmet key and, for services, the services the target declares. A (a new discovery field listing loaded plugins) and C (retire the whole requires block) are not taken.

    Readings that decided it:

    Execution parameters (ruled in the same stroke):

    • One PR on this card, domain:spec, under the cross-domain exception the seat's 裁后执行段 sets out (triage confirms the lane): requires.plugins → retiredKey() tombstone whose prescription names requires.services; requires.services (array of service keys) judged against discovery; requires.params read from the os test process environment; packages/core TestResult gains a SKIPPED status counted separately and never counted as passed, exit 0 when nothing failed; os test prints the skip reason per scenario. ADR-0087 D3 entry for plugins; the ledger row qa.scenarios.requires flips to live citing the reader. Clause-②: yes (narrowing), minor with the BREAKING banner (never major).
    • Pins: an unmet services entry skips and lists the declared services; a met one runs; an unmet params entry skips naming the variable; an all-skipped run is not a pass, and under --fail-on-empty it exits 1; a plugins key is refused at parse with the prescription.
    • Confidence gap carried into the PR: if any out-of-repo suite authored requires.plugins with package names, the tombstone prescription gives the plugin → service mapping; none is known.

    State: needs-user-decision → pm:queue in the same stroke; the Ruled line goes on the body. The four sibling keys landed via PR #20341 (5a6267f486) and are unaffected.

  4. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3
    Session: session_01ARcDurZ5j34RdqsGgc4jgH
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-20289-requires-services
    Worktree: objectstack-issue-20289
    Domain: domain:spec
    Seat: domain:spec#4 (seat post #18917)
    File surface: the fifth key of the qa-runner family, requires, executing ruling B (5863822176). It is one PR under the cross-domain exception the ruling sets out, with every end declared:

    Ruling carried: 5863822176 · batch #232 item 1 · letter B · maintainer 「同意」. The measurement it rests on is seat 2's release 5863072096 and its decision box 5863075827.

    domain:spec seat 4 · #18917 · session_01ARcDurZ5j34RdqsGgc4jgH

  5. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 20289,
      "status": "done",
      "branch": "claude/issue-20289-requires-services",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/20511",
      "session": "session_01ARcDurZ5j34RdqsGgc4jgH (mode:subagent, the PM's id, stamped on every commit's Claude-Session trailer; identity = the branch named by claim 5876608230, verified as the newest Claim on the card)",
      "premise_still_valid": true,
      "summary": "Ruling B executed in one draft PR (#20511, Fixes #20289, head 75d17a11). spec: requires.params is judged against the os test process environment (set and non-empty); requires.services is new, z.array(CoreServiceName), judged against the target's discovery services map (enabled true and status available, ADR-0076 D12); requires.plugins is a retiredKey() tombstone whose prescription names requires.services and carries the plugin to service mapping derived from CORE_SERVICE_PROVIDER. ADR-0087: RETIRED_KEYS_BY_MAJOR[18] qa/TestScenario:requires.plugins plus D3 qa-scenario-requires-plugins-retired, no D2 (a suite is a loose JSON file). core: TestRunner judges requires before the first step (setup included), TestResult gains status passed/failed/skipped and a skipped report (reason, unmet[], availableServices), passed stays and is false on a skip; TestExecutionAdapter gains optional readTargetServices, which HttpTestAdapter answers from the SAME memoised discovery probe (the document is now kept instead of read for routes.data and dropped), so no fetch is added. cli: os test prints each skip with its reason, counts skips apart, and an all-skipped run prints no SUCCESS, exits 0, and exits 1 under --fail-on-empty. Ledger qa.scenarios.requires dead to live (qa 9/0). Mechanism assumptions measured: (1) holds with a precision: the probe exists and is memoised once per adapter (= per run) but was lazy, fired only by record actions; requires.services is a second trigger of that one probe, never a second request (pinned: 4 CLI runs, 4 discovery hits). (2) holds: zero requires reads on main, row dead. (3) --fail-on-empty failed only an empty glob and an empty --tags selection; deselected scenarios never reach the runner and appear only on the --tags line, skipped ones were selected and refused by their own precondition, printed with a reason and counted on the summary. (4) non-strict object, retiredKey route; nested key spelled defKey:requires.plugins like api/RestApiConfig:documentation.enabled.",
      "tests": "All at code head 75d17a11 (the docs-only regeneration commit came after the targeted pass on the same tree). spec test (local): Test Files 572 passed, Tests 16797 passed and 1 todo. spec test:repo: 39 files, 694 passed (includes the new tree-scoped pin requires-plugins-retirement.test.ts). core test: 59 files, 1555 passed; src/qa alone 69 passed. cli os test files: unit tier (qa-suite-schema-load, qa-requires-summary, qa-tags-selection, vitest-tiers-partition) 4 files 46 passed; integration tier (qa-requires-skip-run, qa-names-and-tags-run; they spawn bin/run-dev.js against a node:http stub, run locally because this diff adds a spawn file) 2 files 15 passed; the rest of the cli suite declared to CI; qa-empty-glob-exit-code.e2e.test.ts was named and matched no project here (nightly e2e tier) so NOT MEASURED, reason: not a member of the unit or integration project. typecheck exit 0 for spec, core and cli, each with check:test-typecheck OK. Type-channel reverse check: the ts-expect-error on an authored requires.plugins in spec testing.test.ts is consumed, and core/cli tests typecheck requires.services, a key only the rebuilt d.ts carries. check:generated: first ✗ 1 of 15 stale (check:docs), --fix regenerated content/docs/references/qa/testing.mdx, recheck ✓ All 15 generated artifacts are up to date. check:liveness: qa 9 classified (live 9), state-counts.md is current. Ablation via scripts/ablation-replace.mjs on the committed tree: runner.ts anchor 'if (unmet.length === 0) return undefined;' flipped to a greater-or-equal-zero comparison, anchor 1 to 0, blob 77e3f4c5a76c to 5df21094f9fd; runner.test.ts 7 failed / 23 passed of 30, exactly the seven skip-asserting pins, with the met-service CONTROL, the no-probe pin, the failed-status pin and the 20 pre-existing tests green. Expected direction red, observed red. Restore: blob 77e3f4c5a76c == HEAD, git diff HEAD empty. No dist leg (relative src import). Lint narrowing, measured: the 25 changed paths asked of eslint.config.mjs: 16 linted, 9 ignored (md/mdx/json); eslint --no-inline-config --format json over the 16: 16 results, 0 errors, 0 warnings; parserOptions.project and projectService null for all 16, so untouched verdicts cannot move. Not measured: a booted showcase; the skip path was driven end to end against a stub answering discovery like both producers.",
      "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 75d17a11 derived 122 families; all 122 ran, each exit code written to disk; --ran: '122 derived, 122 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero). 120 exit 0. First pass: 4 exited 3 PREREQUISITE NOT MET (check:skill-examples, check:dual-build-cjs-loads, check:i18n-coverage, check:type-check-debt); after building the named prerequisites (68 turbo tasks, 67 cached; plus a direct core build because the ablation restore had moved runner.ts's mtime past core's d.ts) all 4 exited 0, type-check-debt: '--re-measure: OK — 4 ledger entr(ies) re-measured, none above its recorded number'. 2 exit 1, neither caused by this diff: node scripts/check-empty-changeset.mjs --base origin/main (the DELIBERATE CORRECTION of the pending note .changeset/20289-os-test-names-tags.md, expected red, runs only in the non-required Check Changeset job; confirmation requested in the PR body); pnpm check:platform-checklist (areas/identity-auth.json ABSENT SYMBOL packages/plugins/plugin-auth/src/auth-plugin.ts#twoFactor; the same single problem at base fc0db22b and at main 2b24b8b8, control runs in a detached worktree, removed after; this diff touches neither file). CI read once after the PR: 32 check-runs on 75d17a11, 10 success, 2 skipped, 20 in_progress, 0 failed; in_progress is the honest value, convergence belongs to the PM.",
      "line_budget": "n/a: no skills/** and no governed surface in the diff",
      "files_changed": [
        ".changeset/20289-os-test-names-tags.md (pending note of PR #20341, one bullet corrected: DELIBERATE CORRECTION)",
        ".changeset/20289-requires-services-skip.md",
        "content/docs/deployment/cli.mdx (text this PR falsified)",
        "content/docs/references/qa/testing.mdx (generated by check:generated --fix)",
        "docs/qa/platform-checklist/areas/cli.json (text this PR falsified; revision 5 to 6)",
        "packages/cli/src/commands/test.ts",
        "packages/cli/test/qa-requires-skip-run.test.ts",
        "packages/cli/test/qa-requires-summary.test.ts",
        "packages/cli/test/qa-suite-schema-load.test.ts",
        "packages/core/src/qa/adapter.ts (optional readTargetServices on the adapter interface)",
        "packages/core/src/qa/http-adapter.test.ts",
        "packages/core/src/qa/http-adapter.ts",
        "packages/core/src/qa/runner.test.ts",
        "packages/core/src/qa/runner.ts",
        "packages/spec/liveness/README.md",
        "packages/spec/liveness/qa.json",
        "packages/spec/liveness/state-counts.md (generated by gen:liveness-counts)",
        "packages/spec/scripts/liveness/check-liveness.mts (header comment only)",
        "packages/spec/src/migrations/entries/retired-keys/18.qa__TestScenario__requires.plugins.ts",
        "packages/spec/src/migrations/entries/semantic/18.qa-scenario-requires-plugins-retired.ts",
        "packages/spec/src/migrations/registry.ts (generated by gen:migration-registry)",
        "packages/spec/src/qa/requires-plugins-retirement.test.ts",
        "packages/spec/src/qa/testing.test.ts",
        "packages/spec/src/qa/testing.zod.ts",
        "packages/spec/vitest.repo-tests.json"
      ],
      "deviations": [
        "File surface beyond claim 5876608230, each needed or made-false-by-this-change: packages/core/src/qa/adapter.ts (the interface between adapter and runner; the optional method is how the runner receives the discovery services); packages/core/src/qa/http-adapter.test.ts; three cli test files (qa-requires-skip-run new, qa-requires-summary new, qa-suite-schema-load extended); packages/spec/src/qa/requires-plugins-retirement.test.ts plus its line in packages/spec/vitest.repo-tests.json (the kit's tree-scoped absence pin, inside the radius @objectstack/spec already declares); three texts this PR falsifies, the class the #20341 REWORK named: content/docs/deployment/cli.mdx, docs/qa/platform-checklist/areas/cli.json, the check-liveness.mts header comment.",
        "The pending release note .changeset/20289-os-test-names-tags.md (PR #20341, unreleased) had one bullet saying requires is still checked by nothing; it would ship false beside this PR's note in the same release, so the bullet is rewritten. This is the DELIBERATE CORRECTION class check-empty-changeset.mjs names: that gate is red on purpose, skip-changeset is not applied, and the PR body asks for a person's confirmation. If the release consumes that note before this lands, the edit must be dropped at merge.",
        "requires.services is closed over CoreServiceName rather than an open string array. The ruling says 'array of service keys'; both discovery producers key services by CoreServiceName members only, so the closed enum loses nothing and turns a misspelling into a parse refusal (contract tightening) while a valid key the target does not declare still skips loudly. Named in the PR's Acceptance notes; a one-line change to z.array(z.string()) if the seat reads the ruling as open.",
        "The ADR-0087 marker is written, as the gate reads it, in the changeset .changeset/20289-requires-services-skip.md; the PR body names the disposition in words (registered qa-scenario-requires-plugins-retired) because the marker's comment form is sanitizer-poison on GitHub.",
        "Container restart around 19:43Z: the first check:generated --fix never acquired the lock (the log shows it waiting), so nothing was regenerated then. After the restart it ran in a fresh slot and the recheck read '✓ All 15 generated artifacts are up to date'; the pushed head c8f50d88 was pushed after the restart, then 75d17a11 with the regenerated reference page.",
        "Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude); the harness reminder asked for a model-named Co-Authored-By line, which the pre-push hook refuses and AGENTS.md outranks.",
        "origin/main (now 2b24b8b8, 6 commits past base) was not merged in: a driver-free merge-tree onto it is clean and the one shared file, migrations/registry.ts, is disjoint prose on main's side. CI validates the merge ref.",
        "Verify-lock holds (shared-box seconds): cli closure build 269s; the locked targeted pass with --fix 375s; typechecks plus full spec/core suites 971s (16m11s); ablation 2s; prerequisite build 4s; core rebuild 8s. The first build and the first --fix attempt returned exit 99 (queue timeout) behind 9-10 minute holders and were re-queued with the kept slot.",
        "Cleanup: node_modules is removed after this comment posts and the worktree is then removed with git worktree remove (never --force); the control worktree objectstack-issue-20289-base and the fetched ref refs/os-dev/issue-20289/main are already removed."
      ],
      "mcp_calls": "0",
      "api_writes": "3 REST writes, all through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, 204, then the relay run): (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, draft, run 36481324626, PR #20511, body read back byte-identical at 13429 bytes; (2) label-write assign, POST /repos/objectstack-ai/objectstack/issues/20511/assignees os-warren, run 36481407619, read back MATCHES (no label added; the four labels on the PR were written by others); (3) this os-dev-report comment, POST /repos/objectstack-ai/objectstack/issues/20289/comments via post-stamped. Also 5 git pushes to the branch (the empty-branch probe and 4 commit pushes), which are not REST writes.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: named producer — docs/qa/platform-checklist/areas/identity-auth.json:1599 cites packages/plugins/plugin-auth/src/auth-plugin.ts#twoFactor, which that file no longer declares (last touched by 7d630889, #20429); `pnpm check:platform-checklist` exits 1 with 'ABSENT SYMBOL' on main 2b24b8b8 and on fc0db22b, so every PR's run of that gate inherits the red · evidence: control runs of node scripts/check-platform-checklist.mjs in a detached worktree at both commits, '1 problem(s)', the same line · dedupe words: platform-checklist ABSENT SYMBOL twoFactor, identity-auth.json auth-plugin.ts, check:platform-checklist red main",
        "carrier: none (承接者:无) · noted, not filed: a suite whose scenarios array is empty, run without --tags, still prints 'SUCCESS: All 0 scenarios passed.' and exits 0 even under --fail-on-empty; the all-skipped posture this PR adds deliberately does not widen to it. In the PR's Acceptance notes."
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT · PR #20511 at head 75d17a115ee806c21e916caf4c3c3cbce7e32e56 · 2026-09-28T21:16Z

    domain:spec seat 4 (session_01ARcDurZ5j34RdqsGgc4jgH), reviewer of record under claim 5876608230, as amended in place after the fact (its "Amended" line). This seat took the readings below on GitHub or on the tree.

    check reading
    Shape Draft, base main. Body line 1 Fixes #20289, and Clause-②: yes (narrowing) stands alone.
    Scope 25 files, +1536/−70: ruling B (5863822176) end to end. requires.plugins is tombstoned with the plugin → service mapping. requires.services is judged against the discovery document the QA HTTP adapter already fetches (one memoised probe, no new request, field or route). requires.params is judged against the os test process environment. TestResult gains status and skipped. os test prints skips and gets the all-skipped exit posture. Plus the ADR-0087 D3 entry, the ledger row → live, and three minor changesets with the BREAKING banner. The paths outside the original claim are admitted by the amendment, each judged necessary by the record.
    Contract review At-tier record PASS 5878604417, same head. Every execution parameter and all five ruled pins are present and load-bearing. requires.services closed over CoreServiceName is inside the ruling: both discovery producers write exactly the 16 CoreServiceName keys, so an open array would only turn a misspelling into a permanent skip. TestResult is additive for every in-repo reader. The ablation reading matches (7 of 30 red, exactly the skip pins).
    Deliberate red Check Changeset is red on purpose: the one-bullet rewrite of PR #20341's pending note .changeset/20289-os-test-names-tags.md, which would otherwise ship false beside this PR's note in the same release. The record names the note, quotes old and new, and judges the new bullet true. The gate's source prescribes "do not restore it" and stays red; the job runs on pull_request only (no merge_group); the record on the PR names the gate and cause. All three conditions for landing a red of this class hold. Its skipped steps (ADR-0087 disposition, no-major) were run by the dev at this head, exit 0, and the record judged them from the gates' sources.
    CI at this head 32 success, 2 skipped (roster, check-expected-skips.mjs --pr 20511, exit 0), 1 failure = the deliberate Check Changeset. git merge-tree against origin/main 9449512a is clean. Since the base, main has not moved state-counts.md, the liveness README, the reference page, spec-changes.json or the upgrade guide, so rule A owes no sync.
    Governed / size Not governed (check-governed-merges.mjs --pr 20511: 0 of 25 paths), 1606 changed lines.

    Out-of-scope findings:

    Landing order: this PR lands before #20294's PR #20512. Both edit packages/spec/liveness/state-counts.md (an os-regen path), so #20512 takes a sync-and-regenerate round after this merges.

    Landing next: ready + auto-merge through the relay. At MERGED, the Fixes line closes this card.

    domain:spec seat 4 · #18917 · session_01ARcDurZ5j34RdqsGgc4jgH

  7. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #20511 → main 0bbe4005e82fce2058238720cac7ba5cd2f182d5 · 2026-09-28T21:49Z

    domain:spec seat 4 (session_01ARcDurZ5j34RdqsGgc4jgH), landing record for claim 5876608230 (as amended). ACCEPT 5878795654 on the at-tier PASS 5878604417. Merged through the merge queue at 21:48Z, with the deliberate Check Changeset red of the accepted class.

    Verified on origin/main:

    Close-out: the PR's Fixes line closed this card completed. pm:dispatched comes off in this act, and the assignee stays as the record of who landed it. The qa-runner family is now whole: the four sibling keys (PR #20341) plus requires here. The qa ledger reads 9 live / 0 dead.

    domain:spec seat 4 · #18917 · session_01ARcDurZ5j34RdqsGgc4jgH

  8. added a commit that references this issue on Sep 29, 2026
    0bbe400
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:specenhancementNew feature or requestpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions