fix(spec): send a top-level flow trigger to the START node config, not to a type rename - #14735

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance
Sep 3, 2026
Merged

fix(spec): send a top-level flow trigger to the START node config, not to a type rename#14735
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#14337

FlowSchema's aliases table pointed trigger at type, and the rename it prescribed cannot be taken. type is the flow KIND, so an author who follows the advice lands on Invalid option one round later with the trigger binding still nowhere. Both keys move to the guidance table, beside the object / objectName / schedule prescriptions that already name the START node's config.

No accept/reject change. A top-level trigger / triggerType was refused before and is refused now; FlowSchema's accepted keys and its type enum are untouched. Only the prescription the refusal carries changes.

The sibling was unmeasured — it is measured now, and it reproduces

The card flagged triggertype: 'type' as probably the same mistake without measuring it. Measured on origin/main (its flow.zod.ts restored on disk, everything else at this branch), verbatim:

ROUND 1 — author writes top-level `triggerType`
Unrecognized key(s) on this flow: `triggerType`. Did you mean `triggerType` -> `type`? Until this
shape was closed, these were dropped silently ...
ROUND 2 — author takes the rename: `type: 'record-after-create'`
Invalid option: expected one of "autolaunched"|"record_change"|"schedule"|"screen"|"api"

That is the same dead end as trigger: the enum the rename points at contains no record-* token at all, so the second round cannot succeed either. The alias table is probed case- and separator-insensitively (aliases[aliasProbe(key)], packages/spec/src/shared/suggestions.zod.ts:463), which is why the source spelling triggertype was answering an authored triggerType. Fixed in this PR, as the card directed.

After this change, the same two inputs:

Unrecognized key(s) on this flow: `trigger`.
- `trigger` is not a Flow field - a record-change flow binds its trigger on the START node's
`config` (`{ objectName, triggerType, condition }`, where `triggerType` is a `record-*` token
such as `record-after-create`), not at the flow top level; the flow-level `type` names the
flow kind (`record_change`), not the binding. Until this shape was closed, ...
Unrecognized key(s) on this flow: `triggerType`.
- `triggerType` is not a Flow field - it belongs on the START node's `config`
(`{ objectName, triggerType, condition }`), where a `record-*` token such as
`record-after-create` binds the lifecycle event; ...

Where the binding really lives (cited, not paraphrased)

Claim the prescription makesAnchor
the trigger binds on the START node's config, as { objectName, triggerType, condition }packages/spec/src/automation/flow-trigger-kind.ts:12
a record-* token is the lifecycle-event grammarpackages/spec/src/automation/flow-trigger-kind.ts:74 (triggerType.startsWith('record-') resolves record_change)
the flow-level type names the KIND, and contains no event tokenpackages/spec/src/automation/flow.zod.ts:669
guidance is exact-key and suppresses the rename; aliases renders "Did you mean X -> Y?"packages/spec/src/shared/strict-object.ts:122, packages/spec/src/shared/suggestions.zod.ts:445 and :463

One measured consequence, stated rather than hidden

The alias row was probe-matched, so it also answered the folded spellings; guidance is exact-spelling by design (case folding is the rename channel's job). A non-canonical triggertype therefore now gets the bare rejection instead of the rename it could not take. That is a deliberate trade - no advice beats confidently wrong advice - and it is pinned in flow.test.ts so it cannot drift back into a rename unnoticed.

Verification (all on efe488c42, the final commit; heavy runs under scripts/pm/os-verify-lock.sh)

RunVerdict
@objectstack/spec - flow.test.ts, flow-trigger-kind.test.ts, strict-object.test.ts, alias-integrity.test.ts, suggestions.test.ts, visible-when-alias-guidance.test.ts6 files / 210 tests pass (VERDICT command-exit 0)
@objectstack/spec typecheck (tsc --noEmit + check:scripts-typecheck + check:test-typecheck)pass; the test layer IS compiled - 54 files under tsconfig.test.json, so the new pins are type-checked
check:generated (after pnpm --filter @objectstack/spec build)all 15 artifacts up to date, check:authorable-surface and check:api-surface among them - the guidance change projects into no generated page
check:system-context-censusOK - 145 anchors resolve, 109 census sites
check:doc-authoringOK - 14524 customer-facing strings, no internal issue ids
consumer: @objectstack/lint (lint-flow-patterns, validate-flow-trigger-readiness, flow-variable-scope, authoring-rule-input-tier)4 files / 236 tests pass
consumer: @objectstack/service-automation (engine, canonicalize-stored-flow, flow-cold-boot-bind)3 files / 158 tests pass
24 further gates from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackpass (check:nul-bytes, check:skill-examples 256 blocks, check:doc-anchors, check:corpus-claim-drift, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:empty-changeset, check:changeset-no-major, check:adr-0087-registration, and the docs family)
check:test-completeness, check:type-check-debtexit 3, NOT MEASURED by their own definition (one needs a turbo run test log, the other a fully built workspace) - left to CI, which has both

Reverse verification

Removing the guidance entries on disk (flow.zod.ts restored to origin/main, mutation confirmed by grep counts before the run) turns exactly the three new prescription pins red:

x sends a top-level `trigger` to the START node config, never to a `type` rename
x sends a top-level `triggerType` to the START node config, never to a `type` rename
x no spelling of the removed alias renames to `type` any more
Test Files 1 failed (1) Tests 3 failed | 93 passed (96)

The two positive controls stay green under the same ablation, correctly - they pin the claim, not the change. The file was restored with git checkout HEAD -- FILE, spelled as an absolute path and the restore proved by blob hash (abc5dc708... on disk equals HEAD:packages/spec/src/automation/flow.zod.ts), with git diff HEAD empty afterwards.

The third commit on this branch re-anchors a file:line citation on content/docs/permissions/system-context.mdx from flow.zod.ts:685 to :702, because this change inserts 17 lines above it. That commit is load-bearing, not cosmetic: reverting only that file makes check:system-context-census fail with [anchor-is-not-a-read-site] on :685 plus [ledger-row-unused] on :702. The anchored line's content is byte-identical at both numbers.

Notes

  • Existing pins of the old rename are flipped, never deleted - the assertion now reads .not.toContain on the trigger -> type string, in a dedicated test that also pins the prescription.
  • Changeset: @objectstack/spec: patch, customer-facing text, no issue ids.
  • This branch is a takeover: two earlier developer sessions died in container restarts. All three inherited commits were re-read hunk by hunk and re-verified from scratch; no inherited measurement was taken on trust.

Generated by Claude Code


Generated by Claude Code

os-samand others added 3 commits September 2, 2026 21:06
…t to a `type` rename
`FlowSchema` aliased `trigger` and `triggerType` to `type`, and `type` is the
flow KIND enum (`autolaunched` | `record_change` | `schedule` | `screen` |
`api`). An author who took that rename landed on `Invalid option: expected one
of "autolaunched"|…` one round later with the trigger binding still nowhere.
Both keys move to the `guidance` table beside `object` / `objectName` /
`schedule`, naming where the binding really lives: the START node's `config`
(`{ objectName, triggerType, condition }`, `triggerType` a `record-*` token).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
…low.zod line shift
`node scripts/check-system-context-census.mjs --fix` — pure line rot from the
guidance entries added above the cited line.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/plugin-endpoints.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/approvals.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/flows.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/hooks.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/workflows.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/concepts/architecture.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/getting-started/common-patterns.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/kernel/services-checklist.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/permissions/capabilities.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/protocol/objectql/schema.mdx(via record_change (literal, a string literal in FlowSchema))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v12.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/releases/v17.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

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

Which tree this was computed on

This run read content/docs from ac50f706795e1e5df86a9fd1cf7c70112826c33c — the merge of head efe488c42ce309a010a3e9d6a490c4e74db3a8ae into base dbf115284295b1989d4648dbfbd7e5f3f96357dc, which is what actions/checkout gives a pull_request run. Not the PR head.

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

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ac50f706795e1e5df86a9fd1cf7c70112826c33c && git checkout ac50f706795e1e5df86a9fd1cf7c70112826c33c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dbf115284295b1989d4648dbfbd7e5f3f96357dc efe488c42ce309a010a3e9d6a490c4e74db3a8ae && git checkout -B drift-repro dbf115284295b1989d4648dbfbd7e5f3f96357dc && git merge --no-ff efe488c42ce309a010a3e9d6a490c4e74db3a8ae
node scripts/docs-audit/affected-docs.mjs --json dbf115284295b1989d4648dbfbd7e5f3f96357dc

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

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

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33695118192 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 27 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33697809557 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    
  • Test Core (6/6) — 失败步骤: Run this shard's tests

    @objectstack/plugin-auth:test: FAIL src/durability-swallow-repair.test.ts > #12981 batch 6 — the plugin-auth admin-audit swallows report instead of vanishing > admin-import-users :: the run-level au
    ↳ 失败原因: @objectstack/plugin-auth:test: Error: Test timed out in 10000ms.
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 34 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Re-enqueue receipt (domain:spec seat, session_017RbbUMnxkUnWhE4j94v8FE, 2026-09-03T00:33Z) — first kick, signature checked, re-queued once.

  • Kick: removed_from_merge_queue 00:29:08Z, reason CI_FAILURE, queue build 33697809557 (the earlier red build 33695118192 at 23:46Z did not eject the PR — the queue rebuilt). Kick count on this PR: 1.
  • Signatures (both outside this PR's package — the diff is packages/spec/src/automation/flow.zod.ts + its test + one docs anchor + a changeset, head efe488c42 unchanged since 21:23Z):
    1. packages/clitest/run-dev-unbuilt-workspace.e2e.test.ts › "gives up and exits instead of waiting forever" — AssertionError: expected 'SIGKILL' to be null (23:46Z build: "the harness SIGKILLed the child — it was still alive at the ceiling, cap 180000 ms"). The assertion measures the harness's own time budget, i.e. the timing class; matches the standing queue-flake anchor Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752 (16 PRs / 10 independent hits in 24 h).
    2. packages/plugin-authsrc/durability-swallow-repair.test.ts › "The durability log-level gate cannot see the catch { return null; } seeder family — 15 files outside #12923's five, and neither widening path is cheap #12981 batch 6 … admin-import-users" — Test timed out in 10000ms. Timeout class; only this PR in the 24 h window so far, package untouched by this diff.
  • Head vs main: trial merge git merge-tree --write-tree origin/main refs/pull/14735/head at origin/main224f8ea4a — clean, zero conflicts; no commit on main since 23:15Z touches the PR's files; mergeable_state: clean.
  • Decision (platform-readings rule: a kicked PR is re-queued once when the signature matches a known flaky, with this receipt on the PR; a second kick stops re-queueing and re-judges by signature): auto-merge (SQUASH) re-enabled once, now. A second kick on the same signatures ⇒ no further re-queue from this seat; the flake conversation stays on Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752.

Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33704757465 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 2 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 36 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33718051795 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 3 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 73 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33719428912 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 4 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 77 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@os-samClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge-queue kick #3 — standing down until #14832 closes; nothing on this branch needs to change.

Removed from the queue at 2026-09-03T06:00:53Z (CI_FAILURE). Failing job: group run 33719428912 → Test Core (1/6) (100535388600), packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:418 — the child was SIGKILLed at the 180000 ms ceiling (180108 ms against an 8414 ms calibration on the same runner). The preceding group run 33718051795 (job 100531333140) failed on the same line (180107 ms / 8243 ms). That is the #14832 hang, in a package this PR does not touch; the shard otherwise reports 235 files / 2734 tests passing with completeness OK.

Not re-queued: two kicks on this signature were already spent (00:29Z, 02:08Z), and the signature recurs after its fix (#14715) merged, so it is #14832's defect rather than a flake to retry through. Card #14337 is now pm:blocked on #14832 with Unlock-action: re-check PR #14735; when #14832 closes this PR gets a trial merge against origin/main, a checks read, and one re-queue.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit b91c351Sep 3, 2026
36 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-14337-flow-trigger-alias-guidance branch September 3, 2026 09:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FlowSchema's trigger alias prescribes a rename to type, and taking that advice cannot work — the binding moves to the START node's config

2 participants

@os-sam@os-project-manager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(spec): send a top-level flow trigger to the START node config, not to a type rename - #14735

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance
Sep 3, 2026
Merged

fix(spec): send a top-level flow trigger to the START node config, not to a type rename#14735
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#14337

FlowSchema's aliases table pointed trigger at type, and the rename it prescribed cannot be taken. type is the flow KIND, so an author who follows the advice lands on Invalid option one round later with the trigger binding still nowhere. Both keys move to the guidance table, beside the object / objectName / schedule prescriptions that already name the START node's config.

No accept/reject change. A top-level trigger / triggerType was refused before and is refused now; FlowSchema's accepted keys and its type enum are untouched. Only the prescription the refusal carries changes.

The sibling was unmeasured — it is measured now, and it reproduces

The card flagged triggertype: 'type' as probably the same mistake without measuring it. Measured on origin/main (its flow.zod.ts restored on disk, everything else at this branch), verbatim:

ROUND 1 — author writes top-level `triggerType`
Unrecognized key(s) on this flow: `triggerType`. Did you mean `triggerType` -> `type`? Until this
shape was closed, these were dropped silently ...
ROUND 2 — author takes the rename: `type: 'record-after-create'`
Invalid option: expected one of "autolaunched"|"record_change"|"schedule"|"screen"|"api"

That is the same dead end as trigger: the enum the rename points at contains no record-* token at all, so the second round cannot succeed either. The alias table is probed case- and separator-insensitively (aliases[aliasProbe(key)], packages/spec/src/shared/suggestions.zod.ts:463), which is why the source spelling triggertype was answering an authored triggerType. Fixed in this PR, as the card directed.

After this change, the same two inputs:

Unrecognized key(s) on this flow: `trigger`.
- `trigger` is not a Flow field - a record-change flow binds its trigger on the START node's
`config` (`{ objectName, triggerType, condition }`, where `triggerType` is a `record-*` token
such as `record-after-create`), not at the flow top level; the flow-level `type` names the
flow kind (`record_change`), not the binding. Until this shape was closed, ...
Unrecognized key(s) on this flow: `triggerType`.
- `triggerType` is not a Flow field - it belongs on the START node's `config`
(`{ objectName, triggerType, condition }`), where a `record-*` token such as
`record-after-create` binds the lifecycle event; ...

Where the binding really lives (cited, not paraphrased)

Claim the prescription makesAnchor
the trigger binds on the START node's config, as { objectName, triggerType, condition }packages/spec/src/automation/flow-trigger-kind.ts:12
a record-* token is the lifecycle-event grammarpackages/spec/src/automation/flow-trigger-kind.ts:74 (triggerType.startsWith('record-') resolves record_change)
the flow-level type names the KIND, and contains no event tokenpackages/spec/src/automation/flow.zod.ts:669
guidance is exact-key and suppresses the rename; aliases renders "Did you mean X -> Y?"packages/spec/src/shared/strict-object.ts:122, packages/spec/src/shared/suggestions.zod.ts:445 and :463

One measured consequence, stated rather than hidden

The alias row was probe-matched, so it also answered the folded spellings; guidance is exact-spelling by design (case folding is the rename channel's job). A non-canonical triggertype therefore now gets the bare rejection instead of the rename it could not take. That is a deliberate trade - no advice beats confidently wrong advice - and it is pinned in flow.test.ts so it cannot drift back into a rename unnoticed.

Verification (all on efe488c42, the final commit; heavy runs under scripts/pm/os-verify-lock.sh)

RunVerdict
@objectstack/spec - flow.test.ts, flow-trigger-kind.test.ts, strict-object.test.ts, alias-integrity.test.ts, suggestions.test.ts, visible-when-alias-guidance.test.ts6 files / 210 tests pass (VERDICT command-exit 0)
@objectstack/spec typecheck (tsc --noEmit + check:scripts-typecheck + check:test-typecheck)pass; the test layer IS compiled - 54 files under tsconfig.test.json, so the new pins are type-checked
check:generated (after pnpm --filter @objectstack/spec build)all 15 artifacts up to date, check:authorable-surface and check:api-surface among them - the guidance change projects into no generated page
check:system-context-censusOK - 145 anchors resolve, 109 census sites
check:doc-authoringOK - 14524 customer-facing strings, no internal issue ids
consumer: @objectstack/lint (lint-flow-patterns, validate-flow-trigger-readiness, flow-variable-scope, authoring-rule-input-tier)4 files / 236 tests pass
consumer: @objectstack/service-automation (engine, canonicalize-stored-flow, flow-cold-boot-bind)3 files / 158 tests pass
24 further gates from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackpass (check:nul-bytes, check:skill-examples 256 blocks, check:doc-anchors, check:corpus-claim-drift, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:empty-changeset, check:changeset-no-major, check:adr-0087-registration, and the docs family)
check:test-completeness, check:type-check-debtexit 3, NOT MEASURED by their own definition (one needs a turbo run test log, the other a fully built workspace) - left to CI, which has both

Reverse verification

Removing the guidance entries on disk (flow.zod.ts restored to origin/main, mutation confirmed by grep counts before the run) turns exactly the three new prescription pins red:

x sends a top-level `trigger` to the START node config, never to a `type` rename
x sends a top-level `triggerType` to the START node config, never to a `type` rename
x no spelling of the removed alias renames to `type` any more
Test Files 1 failed (1) Tests 3 failed | 93 passed (96)

The two positive controls stay green under the same ablation, correctly - they pin the claim, not the change. The file was restored with git checkout HEAD -- FILE, spelled as an absolute path and the restore proved by blob hash (abc5dc708... on disk equals HEAD:packages/spec/src/automation/flow.zod.ts), with git diff HEAD empty afterwards.

The third commit on this branch re-anchors a file:line citation on content/docs/permissions/system-context.mdx from flow.zod.ts:685 to :702, because this change inserts 17 lines above it. That commit is load-bearing, not cosmetic: reverting only that file makes check:system-context-census fail with [anchor-is-not-a-read-site] on :685 plus [ledger-row-unused] on :702. The anchored line's content is byte-identical at both numbers.

Notes

  • Existing pins of the old rename are flipped, never deleted - the assertion now reads .not.toContain on the trigger -> type string, in a dedicated test that also pins the prescription.
  • Changeset: @objectstack/spec: patch, customer-facing text, no issue ids.
  • This branch is a takeover: two earlier developer sessions died in container restarts. All three inherited commits were re-read hunk by hunk and re-verified from scratch; no inherited measurement was taken on trust.

Generated by Claude Code


Generated by Claude Code

os-samand others added 3 commits September 2, 2026 21:06
…t to a `type` rename
`FlowSchema` aliased `trigger` and `triggerType` to `type`, and `type` is the
flow KIND enum (`autolaunched` | `record_change` | `schedule` | `screen` |
`api`). An author who took that rename landed on `Invalid option: expected one
of "autolaunched"|…` one round later with the trigger binding still nowhere.
Both keys move to the `guidance` table beside `object` / `objectName` /
`schedule`, naming where the binding really lives: the START node's `config`
(`{ objectName, triggerType, condition }`, `triggerType` a `record-*` token).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
…low.zod line shift
`node scripts/check-system-context-census.mjs --fix` — pure line rot from the
guidance entries added above the cited line.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/plugin-endpoints.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/approvals.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/flows.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/hooks.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/workflows.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/concepts/architecture.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/getting-started/common-patterns.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/kernel/services-checklist.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/permissions/capabilities.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/protocol/objectql/schema.mdx(via record_change (literal, a string literal in FlowSchema))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v12.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/releases/v17.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

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

Which tree this was computed on

This run read content/docs from ac50f706795e1e5df86a9fd1cf7c70112826c33c — the merge of head efe488c42ce309a010a3e9d6a490c4e74db3a8ae into base dbf115284295b1989d4648dbfbd7e5f3f96357dc, which is what actions/checkout gives a pull_request run. Not the PR head.

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

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ac50f706795e1e5df86a9fd1cf7c70112826c33c && git checkout ac50f706795e1e5df86a9fd1cf7c70112826c33c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dbf115284295b1989d4648dbfbd7e5f3f96357dc efe488c42ce309a010a3e9d6a490c4e74db3a8ae && git checkout -B drift-repro dbf115284295b1989d4648dbfbd7e5f3f96357dc && git merge --no-ff efe488c42ce309a010a3e9d6a490c4e74db3a8ae
node scripts/docs-audit/affected-docs.mjs --json dbf115284295b1989d4648dbfbd7e5f3f96357dc

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

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

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33695118192 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 27 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33697809557 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    
  • Test Core (6/6) — 失败步骤: Run this shard's tests

    @objectstack/plugin-auth:test: FAIL src/durability-swallow-repair.test.ts > #12981 batch 6 — the plugin-auth admin-audit swallows report instead of vanishing > admin-import-users :: the run-level au
    ↳ 失败原因: @objectstack/plugin-auth:test: Error: Test timed out in 10000ms.
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 34 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Re-enqueue receipt (domain:spec seat, session_017RbbUMnxkUnWhE4j94v8FE, 2026-09-03T00:33Z) — first kick, signature checked, re-queued once.

  • Kick: removed_from_merge_queue 00:29:08Z, reason CI_FAILURE, queue build 33697809557 (the earlier red build 33695118192 at 23:46Z did not eject the PR — the queue rebuilt). Kick count on this PR: 1.
  • Signatures (both outside this PR's package — the diff is packages/spec/src/automation/flow.zod.ts + its test + one docs anchor + a changeset, head efe488c42 unchanged since 21:23Z):
    1. packages/clitest/run-dev-unbuilt-workspace.e2e.test.ts › "gives up and exits instead of waiting forever" — AssertionError: expected 'SIGKILL' to be null (23:46Z build: "the harness SIGKILLed the child — it was still alive at the ceiling, cap 180000 ms"). The assertion measures the harness's own time budget, i.e. the timing class; matches the standing queue-flake anchor Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752 (16 PRs / 10 independent hits in 24 h).
    2. packages/plugin-authsrc/durability-swallow-repair.test.ts › "The durability log-level gate cannot see the catch { return null; } seeder family — 15 files outside #12923's five, and neither widening path is cheap #12981 batch 6 … admin-import-users" — Test timed out in 10000ms. Timeout class; only this PR in the 24 h window so far, package untouched by this diff.
  • Head vs main: trial merge git merge-tree --write-tree origin/main refs/pull/14735/head at origin/main224f8ea4a — clean, zero conflicts; no commit on main since 23:15Z touches the PR's files; mergeable_state: clean.
  • Decision (platform-readings rule: a kicked PR is re-queued once when the signature matches a known flaky, with this receipt on the PR; a second kick stops re-queueing and re-judges by signature): auto-merge (SQUASH) re-enabled once, now. A second kick on the same signatures ⇒ no further re-queue from this seat; the flake conversation stays on Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752.

Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33704757465 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 2 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 36 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33718051795 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 3 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 73 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33719428912 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 4 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 77 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@os-samClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge-queue kick #3 — standing down until #14832 closes; nothing on this branch needs to change.

Removed from the queue at 2026-09-03T06:00:53Z (CI_FAILURE). Failing job: group run 33719428912 → Test Core (1/6) (100535388600), packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:418 — the child was SIGKILLed at the 180000 ms ceiling (180108 ms against an 8414 ms calibration on the same runner). The preceding group run 33718051795 (job 100531333140) failed on the same line (180107 ms / 8243 ms). That is the #14832 hang, in a package this PR does not touch; the shard otherwise reports 235 files / 2734 tests passing with completeness OK.

Not re-queued: two kicks on this signature were already spent (00:29Z, 02:08Z), and the signature recurs after its fix (#14715) merged, so it is #14832's defect rather than a flake to retry through. Card #14337 is now pm:blocked on #14832 with Unlock-action: re-check PR #14735; when #14832 closes this PR gets a trial merge against origin/main, a checks read, and one re-queue.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit b91c351Sep 3, 2026
36 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-14337-flow-trigger-alias-guidance branch September 3, 2026 09:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FlowSchema's trigger alias prescribes a rename to type, and taking that advice cannot work — the binding moves to the START node's config

2 participants

@os-sam@os-project-manager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(spec): send a top-level flow trigger to the START node config, not to a type rename - #14735

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance
Sep 3, 2026
Merged

fix(spec): send a top-level flow trigger to the START node config, not to a type rename#14735
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#14337

FlowSchema's aliases table pointed trigger at type, and the rename it prescribed cannot be taken. type is the flow KIND, so an author who follows the advice lands on Invalid option one round later with the trigger binding still nowhere. Both keys move to the guidance table, beside the object / objectName / schedule prescriptions that already name the START node's config.

No accept/reject change. A top-level trigger / triggerType was refused before and is refused now; FlowSchema's accepted keys and its type enum are untouched. Only the prescription the refusal carries changes.

The sibling was unmeasured — it is measured now, and it reproduces

The card flagged triggertype: 'type' as probably the same mistake without measuring it. Measured on origin/main (its flow.zod.ts restored on disk, everything else at this branch), verbatim:

ROUND 1 — author writes top-level `triggerType`
Unrecognized key(s) on this flow: `triggerType`. Did you mean `triggerType` -> `type`? Until this
shape was closed, these were dropped silently ...
ROUND 2 — author takes the rename: `type: 'record-after-create'`
Invalid option: expected one of "autolaunched"|"record_change"|"schedule"|"screen"|"api"

That is the same dead end as trigger: the enum the rename points at contains no record-* token at all, so the second round cannot succeed either. The alias table is probed case- and separator-insensitively (aliases[aliasProbe(key)], packages/spec/src/shared/suggestions.zod.ts:463), which is why the source spelling triggertype was answering an authored triggerType. Fixed in this PR, as the card directed.

After this change, the same two inputs:

Unrecognized key(s) on this flow: `trigger`.
- `trigger` is not a Flow field - a record-change flow binds its trigger on the START node's
`config` (`{ objectName, triggerType, condition }`, where `triggerType` is a `record-*` token
such as `record-after-create`), not at the flow top level; the flow-level `type` names the
flow kind (`record_change`), not the binding. Until this shape was closed, ...
Unrecognized key(s) on this flow: `triggerType`.
- `triggerType` is not a Flow field - it belongs on the START node's `config`
(`{ objectName, triggerType, condition }`), where a `record-*` token such as
`record-after-create` binds the lifecycle event; ...

Where the binding really lives (cited, not paraphrased)

Claim the prescription makesAnchor
the trigger binds on the START node's config, as { objectName, triggerType, condition }packages/spec/src/automation/flow-trigger-kind.ts:12
a record-* token is the lifecycle-event grammarpackages/spec/src/automation/flow-trigger-kind.ts:74 (triggerType.startsWith('record-') resolves record_change)
the flow-level type names the KIND, and contains no event tokenpackages/spec/src/automation/flow.zod.ts:669
guidance is exact-key and suppresses the rename; aliases renders "Did you mean X -> Y?"packages/spec/src/shared/strict-object.ts:122, packages/spec/src/shared/suggestions.zod.ts:445 and :463

One measured consequence, stated rather than hidden

The alias row was probe-matched, so it also answered the folded spellings; guidance is exact-spelling by design (case folding is the rename channel's job). A non-canonical triggertype therefore now gets the bare rejection instead of the rename it could not take. That is a deliberate trade - no advice beats confidently wrong advice - and it is pinned in flow.test.ts so it cannot drift back into a rename unnoticed.

Verification (all on efe488c42, the final commit; heavy runs under scripts/pm/os-verify-lock.sh)

RunVerdict
@objectstack/spec - flow.test.ts, flow-trigger-kind.test.ts, strict-object.test.ts, alias-integrity.test.ts, suggestions.test.ts, visible-when-alias-guidance.test.ts6 files / 210 tests pass (VERDICT command-exit 0)
@objectstack/spec typecheck (tsc --noEmit + check:scripts-typecheck + check:test-typecheck)pass; the test layer IS compiled - 54 files under tsconfig.test.json, so the new pins are type-checked
check:generated (after pnpm --filter @objectstack/spec build)all 15 artifacts up to date, check:authorable-surface and check:api-surface among them - the guidance change projects into no generated page
check:system-context-censusOK - 145 anchors resolve, 109 census sites
check:doc-authoringOK - 14524 customer-facing strings, no internal issue ids
consumer: @objectstack/lint (lint-flow-patterns, validate-flow-trigger-readiness, flow-variable-scope, authoring-rule-input-tier)4 files / 236 tests pass
consumer: @objectstack/service-automation (engine, canonicalize-stored-flow, flow-cold-boot-bind)3 files / 158 tests pass
24 further gates from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackpass (check:nul-bytes, check:skill-examples 256 blocks, check:doc-anchors, check:corpus-claim-drift, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:empty-changeset, check:changeset-no-major, check:adr-0087-registration, and the docs family)
check:test-completeness, check:type-check-debtexit 3, NOT MEASURED by their own definition (one needs a turbo run test log, the other a fully built workspace) - left to CI, which has both

Reverse verification

Removing the guidance entries on disk (flow.zod.ts restored to origin/main, mutation confirmed by grep counts before the run) turns exactly the three new prescription pins red:

x sends a top-level `trigger` to the START node config, never to a `type` rename
x sends a top-level `triggerType` to the START node config, never to a `type` rename
x no spelling of the removed alias renames to `type` any more
Test Files 1 failed (1) Tests 3 failed | 93 passed (96)

The two positive controls stay green under the same ablation, correctly - they pin the claim, not the change. The file was restored with git checkout HEAD -- FILE, spelled as an absolute path and the restore proved by blob hash (abc5dc708... on disk equals HEAD:packages/spec/src/automation/flow.zod.ts), with git diff HEAD empty afterwards.

The third commit on this branch re-anchors a file:line citation on content/docs/permissions/system-context.mdx from flow.zod.ts:685 to :702, because this change inserts 17 lines above it. That commit is load-bearing, not cosmetic: reverting only that file makes check:system-context-census fail with [anchor-is-not-a-read-site] on :685 plus [ledger-row-unused] on :702. The anchored line's content is byte-identical at both numbers.

Notes

  • Existing pins of the old rename are flipped, never deleted - the assertion now reads .not.toContain on the trigger -> type string, in a dedicated test that also pins the prescription.
  • Changeset: @objectstack/spec: patch, customer-facing text, no issue ids.
  • This branch is a takeover: two earlier developer sessions died in container restarts. All three inherited commits were re-read hunk by hunk and re-verified from scratch; no inherited measurement was taken on trust.

Generated by Claude Code


Generated by Claude Code

os-samand others added 3 commits September 2, 2026 21:06
…t to a `type` rename
`FlowSchema` aliased `trigger` and `triggerType` to `type`, and `type` is the
flow KIND enum (`autolaunched` | `record_change` | `schedule` | `screen` |
`api`). An author who took that rename landed on `Invalid option: expected one
of "autolaunched"|…` one round later with the trigger binding still nowhere.
Both keys move to the `guidance` table beside `object` / `objectName` /
`schedule`, naming where the binding really lives: the START node's `config`
(`{ objectName, triggerType, condition }`, `triggerType` a `record-*` token).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
…low.zod line shift
`node scripts/check-system-context-census.mjs --fix` — pure line rot from the
guidance entries added above the cited line.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/plugin-endpoints.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/approvals.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/flows.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/hooks.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/workflows.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/concepts/architecture.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/getting-started/common-patterns.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/kernel/services-checklist.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/permissions/capabilities.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/protocol/objectql/schema.mdx(via record_change (literal, a string literal in FlowSchema))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v12.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/releases/v17.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

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

Which tree this was computed on

This run read content/docs from ac50f706795e1e5df86a9fd1cf7c70112826c33c — the merge of head efe488c42ce309a010a3e9d6a490c4e74db3a8ae into base dbf115284295b1989d4648dbfbd7e5f3f96357dc, which is what actions/checkout gives a pull_request run. Not the PR head.

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

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ac50f706795e1e5df86a9fd1cf7c70112826c33c && git checkout ac50f706795e1e5df86a9fd1cf7c70112826c33c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dbf115284295b1989d4648dbfbd7e5f3f96357dc efe488c42ce309a010a3e9d6a490c4e74db3a8ae && git checkout -B drift-repro dbf115284295b1989d4648dbfbd7e5f3f96357dc && git merge --no-ff efe488c42ce309a010a3e9d6a490c4e74db3a8ae
node scripts/docs-audit/affected-docs.mjs --json dbf115284295b1989d4648dbfbd7e5f3f96357dc

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

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

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33695118192 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 27 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33697809557 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    
  • Test Core (6/6) — 失败步骤: Run this shard's tests

    @objectstack/plugin-auth:test: FAIL src/durability-swallow-repair.test.ts > #12981 batch 6 — the plugin-auth admin-audit swallows report instead of vanishing > admin-import-users :: the run-level au
    ↳ 失败原因: @objectstack/plugin-auth:test: Error: Test timed out in 10000ms.
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 34 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Re-enqueue receipt (domain:spec seat, session_017RbbUMnxkUnWhE4j94v8FE, 2026-09-03T00:33Z) — first kick, signature checked, re-queued once.

  • Kick: removed_from_merge_queue 00:29:08Z, reason CI_FAILURE, queue build 33697809557 (the earlier red build 33695118192 at 23:46Z did not eject the PR — the queue rebuilt). Kick count on this PR: 1.
  • Signatures (both outside this PR's package — the diff is packages/spec/src/automation/flow.zod.ts + its test + one docs anchor + a changeset, head efe488c42 unchanged since 21:23Z):
    1. packages/clitest/run-dev-unbuilt-workspace.e2e.test.ts › "gives up and exits instead of waiting forever" — AssertionError: expected 'SIGKILL' to be null (23:46Z build: "the harness SIGKILLed the child — it was still alive at the ceiling, cap 180000 ms"). The assertion measures the harness's own time budget, i.e. the timing class; matches the standing queue-flake anchor Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752 (16 PRs / 10 independent hits in 24 h).
    2. packages/plugin-authsrc/durability-swallow-repair.test.ts › "The durability log-level gate cannot see the catch { return null; } seeder family — 15 files outside #12923's five, and neither widening path is cheap #12981 batch 6 … admin-import-users" — Test timed out in 10000ms. Timeout class; only this PR in the 24 h window so far, package untouched by this diff.
  • Head vs main: trial merge git merge-tree --write-tree origin/main refs/pull/14735/head at origin/main224f8ea4a — clean, zero conflicts; no commit on main since 23:15Z touches the PR's files; mergeable_state: clean.
  • Decision (platform-readings rule: a kicked PR is re-queued once when the signature matches a known flaky, with this receipt on the PR; a second kick stops re-queueing and re-judges by signature): auto-merge (SQUASH) re-enabled once, now. A second kick on the same signatures ⇒ no further re-queue from this seat; the flake conversation stays on Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752.

Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33704757465 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 2 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 36 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33718051795 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 3 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 73 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33719428912 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 4 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 77 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@os-samClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge-queue kick #3 — standing down until #14832 closes; nothing on this branch needs to change.

Removed from the queue at 2026-09-03T06:00:53Z (CI_FAILURE). Failing job: group run 33719428912 → Test Core (1/6) (100535388600), packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:418 — the child was SIGKILLed at the 180000 ms ceiling (180108 ms against an 8414 ms calibration on the same runner). The preceding group run 33718051795 (job 100531333140) failed on the same line (180107 ms / 8243 ms). That is the #14832 hang, in a package this PR does not touch; the shard otherwise reports 235 files / 2734 tests passing with completeness OK.

Not re-queued: two kicks on this signature were already spent (00:29Z, 02:08Z), and the signature recurs after its fix (#14715) merged, so it is #14832's defect rather than a flake to retry through. Card #14337 is now pm:blocked on #14832 with Unlock-action: re-check PR #14735; when #14832 closes this PR gets a trial merge against origin/main, a checks read, and one re-queue.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit b91c351Sep 3, 2026
36 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-14337-flow-trigger-alias-guidance branch September 3, 2026 09:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FlowSchema's trigger alias prescribes a rename to type, and taking that advice cannot work — the binding moves to the START node's config

2 participants

@os-sam@os-project-manager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(spec): send a top-level flow trigger to the START node config, not to a type rename - #14735

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance
Sep 3, 2026
Merged

fix(spec): send a top-level flow trigger to the START node config, not to a type rename#14735
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#14337

FlowSchema's aliases table pointed trigger at type, and the rename it prescribed cannot be taken. type is the flow KIND, so an author who follows the advice lands on Invalid option one round later with the trigger binding still nowhere. Both keys move to the guidance table, beside the object / objectName / schedule prescriptions that already name the START node's config.

No accept/reject change. A top-level trigger / triggerType was refused before and is refused now; FlowSchema's accepted keys and its type enum are untouched. Only the prescription the refusal carries changes.

The sibling was unmeasured — it is measured now, and it reproduces

The card flagged triggertype: 'type' as probably the same mistake without measuring it. Measured on origin/main (its flow.zod.ts restored on disk, everything else at this branch), verbatim:

ROUND 1 — author writes top-level `triggerType`
Unrecognized key(s) on this flow: `triggerType`. Did you mean `triggerType` -> `type`? Until this
shape was closed, these were dropped silently ...
ROUND 2 — author takes the rename: `type: 'record-after-create'`
Invalid option: expected one of "autolaunched"|"record_change"|"schedule"|"screen"|"api"

That is the same dead end as trigger: the enum the rename points at contains no record-* token at all, so the second round cannot succeed either. The alias table is probed case- and separator-insensitively (aliases[aliasProbe(key)], packages/spec/src/shared/suggestions.zod.ts:463), which is why the source spelling triggertype was answering an authored triggerType. Fixed in this PR, as the card directed.

After this change, the same two inputs:

Unrecognized key(s) on this flow: `trigger`.
- `trigger` is not a Flow field - a record-change flow binds its trigger on the START node's
`config` (`{ objectName, triggerType, condition }`, where `triggerType` is a `record-*` token
such as `record-after-create`), not at the flow top level; the flow-level `type` names the
flow kind (`record_change`), not the binding. Until this shape was closed, ...
Unrecognized key(s) on this flow: `triggerType`.
- `triggerType` is not a Flow field - it belongs on the START node's `config`
(`{ objectName, triggerType, condition }`), where a `record-*` token such as
`record-after-create` binds the lifecycle event; ...

Where the binding really lives (cited, not paraphrased)

Claim the prescription makesAnchor
the trigger binds on the START node's config, as { objectName, triggerType, condition }packages/spec/src/automation/flow-trigger-kind.ts:12
a record-* token is the lifecycle-event grammarpackages/spec/src/automation/flow-trigger-kind.ts:74 (triggerType.startsWith('record-') resolves record_change)
the flow-level type names the KIND, and contains no event tokenpackages/spec/src/automation/flow.zod.ts:669
guidance is exact-key and suppresses the rename; aliases renders "Did you mean X -> Y?"packages/spec/src/shared/strict-object.ts:122, packages/spec/src/shared/suggestions.zod.ts:445 and :463

One measured consequence, stated rather than hidden

The alias row was probe-matched, so it also answered the folded spellings; guidance is exact-spelling by design (case folding is the rename channel's job). A non-canonical triggertype therefore now gets the bare rejection instead of the rename it could not take. That is a deliberate trade - no advice beats confidently wrong advice - and it is pinned in flow.test.ts so it cannot drift back into a rename unnoticed.

Verification (all on efe488c42, the final commit; heavy runs under scripts/pm/os-verify-lock.sh)

RunVerdict
@objectstack/spec - flow.test.ts, flow-trigger-kind.test.ts, strict-object.test.ts, alias-integrity.test.ts, suggestions.test.ts, visible-when-alias-guidance.test.ts6 files / 210 tests pass (VERDICT command-exit 0)
@objectstack/spec typecheck (tsc --noEmit + check:scripts-typecheck + check:test-typecheck)pass; the test layer IS compiled - 54 files under tsconfig.test.json, so the new pins are type-checked
check:generated (after pnpm --filter @objectstack/spec build)all 15 artifacts up to date, check:authorable-surface and check:api-surface among them - the guidance change projects into no generated page
check:system-context-censusOK - 145 anchors resolve, 109 census sites
check:doc-authoringOK - 14524 customer-facing strings, no internal issue ids
consumer: @objectstack/lint (lint-flow-patterns, validate-flow-trigger-readiness, flow-variable-scope, authoring-rule-input-tier)4 files / 236 tests pass
consumer: @objectstack/service-automation (engine, canonicalize-stored-flow, flow-cold-boot-bind)3 files / 158 tests pass
24 further gates from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackpass (check:nul-bytes, check:skill-examples 256 blocks, check:doc-anchors, check:corpus-claim-drift, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:empty-changeset, check:changeset-no-major, check:adr-0087-registration, and the docs family)
check:test-completeness, check:type-check-debtexit 3, NOT MEASURED by their own definition (one needs a turbo run test log, the other a fully built workspace) - left to CI, which has both

Reverse verification

Removing the guidance entries on disk (flow.zod.ts restored to origin/main, mutation confirmed by grep counts before the run) turns exactly the three new prescription pins red:

x sends a top-level `trigger` to the START node config, never to a `type` rename
x sends a top-level `triggerType` to the START node config, never to a `type` rename
x no spelling of the removed alias renames to `type` any more
Test Files 1 failed (1) Tests 3 failed | 93 passed (96)

The two positive controls stay green under the same ablation, correctly - they pin the claim, not the change. The file was restored with git checkout HEAD -- FILE, spelled as an absolute path and the restore proved by blob hash (abc5dc708... on disk equals HEAD:packages/spec/src/automation/flow.zod.ts), with git diff HEAD empty afterwards.

The third commit on this branch re-anchors a file:line citation on content/docs/permissions/system-context.mdx from flow.zod.ts:685 to :702, because this change inserts 17 lines above it. That commit is load-bearing, not cosmetic: reverting only that file makes check:system-context-census fail with [anchor-is-not-a-read-site] on :685 plus [ledger-row-unused] on :702. The anchored line's content is byte-identical at both numbers.

Notes

  • Existing pins of the old rename are flipped, never deleted - the assertion now reads .not.toContain on the trigger -> type string, in a dedicated test that also pins the prescription.
  • Changeset: @objectstack/spec: patch, customer-facing text, no issue ids.
  • This branch is a takeover: two earlier developer sessions died in container restarts. All three inherited commits were re-read hunk by hunk and re-verified from scratch; no inherited measurement was taken on trust.

Generated by Claude Code


Generated by Claude Code

os-samand others added 3 commits September 2, 2026 21:06
…t to a `type` rename
`FlowSchema` aliased `trigger` and `triggerType` to `type`, and `type` is the
flow KIND enum (`autolaunched` | `record_change` | `schedule` | `screen` |
`api`). An author who took that rename landed on `Invalid option: expected one
of "autolaunched"|…` one round later with the trigger binding still nowhere.
Both keys move to the `guidance` table beside `object` / `objectName` /
`schedule`, naming where the binding really lives: the START node's `config`
(`{ objectName, triggerType, condition }`, `triggerType` a `record-*` token).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
…low.zod line shift
`node scripts/check-system-context-census.mjs --fix` — pure line rot from the
guidance entries added above the cited line.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/plugin-endpoints.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/approvals.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/flows.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/hooks.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/workflows.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/concepts/architecture.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/getting-started/common-patterns.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/kernel/services-checklist.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/permissions/capabilities.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/protocol/objectql/schema.mdx(via record_change (literal, a string literal in FlowSchema))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v12.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/releases/v17.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

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

Which tree this was computed on

This run read content/docs from ac50f706795e1e5df86a9fd1cf7c70112826c33c — the merge of head efe488c42ce309a010a3e9d6a490c4e74db3a8ae into base dbf115284295b1989d4648dbfbd7e5f3f96357dc, which is what actions/checkout gives a pull_request run. Not the PR head.

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

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ac50f706795e1e5df86a9fd1cf7c70112826c33c && git checkout ac50f706795e1e5df86a9fd1cf7c70112826c33c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dbf115284295b1989d4648dbfbd7e5f3f96357dc efe488c42ce309a010a3e9d6a490c4e74db3a8ae && git checkout -B drift-repro dbf115284295b1989d4648dbfbd7e5f3f96357dc && git merge --no-ff efe488c42ce309a010a3e9d6a490c4e74db3a8ae
node scripts/docs-audit/affected-docs.mjs --json dbf115284295b1989d4648dbfbd7e5f3f96357dc

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

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

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33695118192 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 27 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33697809557 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    
  • Test Core (6/6) — 失败步骤: Run this shard's tests

    @objectstack/plugin-auth:test: FAIL src/durability-swallow-repair.test.ts > #12981 batch 6 — the plugin-auth admin-audit swallows report instead of vanishing > admin-import-users :: the run-level au
    ↳ 失败原因: @objectstack/plugin-auth:test: Error: Test timed out in 10000ms.
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 34 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Re-enqueue receipt (domain:spec seat, session_017RbbUMnxkUnWhE4j94v8FE, 2026-09-03T00:33Z) — first kick, signature checked, re-queued once.

  • Kick: removed_from_merge_queue 00:29:08Z, reason CI_FAILURE, queue build 33697809557 (the earlier red build 33695118192 at 23:46Z did not eject the PR — the queue rebuilt). Kick count on this PR: 1.
  • Signatures (both outside this PR's package — the diff is packages/spec/src/automation/flow.zod.ts + its test + one docs anchor + a changeset, head efe488c42 unchanged since 21:23Z):
    1. packages/clitest/run-dev-unbuilt-workspace.e2e.test.ts › "gives up and exits instead of waiting forever" — AssertionError: expected 'SIGKILL' to be null (23:46Z build: "the harness SIGKILLed the child — it was still alive at the ceiling, cap 180000 ms"). The assertion measures the harness's own time budget, i.e. the timing class; matches the standing queue-flake anchor Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752 (16 PRs / 10 independent hits in 24 h).
    2. packages/plugin-authsrc/durability-swallow-repair.test.ts › "The durability log-level gate cannot see the catch { return null; } seeder family — 15 files outside #12923's five, and neither widening path is cheap #12981 batch 6 … admin-import-users" — Test timed out in 10000ms. Timeout class; only this PR in the 24 h window so far, package untouched by this diff.
  • Head vs main: trial merge git merge-tree --write-tree origin/main refs/pull/14735/head at origin/main224f8ea4a — clean, zero conflicts; no commit on main since 23:15Z touches the PR's files; mergeable_state: clean.
  • Decision (platform-readings rule: a kicked PR is re-queued once when the signature matches a known flaky, with this receipt on the PR; a second kick stops re-queueing and re-judges by signature): auto-merge (SQUASH) re-enabled once, now. A second kick on the same signatures ⇒ no further re-queue from this seat; the flake conversation stays on Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752.

Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33704757465 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 2 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 36 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33718051795 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 3 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 73 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33719428912 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 4 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 77 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@os-samClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge-queue kick #3 — standing down until #14832 closes; nothing on this branch needs to change.

Removed from the queue at 2026-09-03T06:00:53Z (CI_FAILURE). Failing job: group run 33719428912 → Test Core (1/6) (100535388600), packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:418 — the child was SIGKILLed at the 180000 ms ceiling (180108 ms against an 8414 ms calibration on the same runner). The preceding group run 33718051795 (job 100531333140) failed on the same line (180107 ms / 8243 ms). That is the #14832 hang, in a package this PR does not touch; the shard otherwise reports 235 files / 2734 tests passing with completeness OK.

Not re-queued: two kicks on this signature were already spent (00:29Z, 02:08Z), and the signature recurs after its fix (#14715) merged, so it is #14832's defect rather than a flake to retry through. Card #14337 is now pm:blocked on #14832 with Unlock-action: re-check PR #14735; when #14832 closes this PR gets a trial merge against origin/main, a checks read, and one re-queue.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit b91c351Sep 3, 2026
36 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-14337-flow-trigger-alias-guidance branch September 3, 2026 09:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FlowSchema's trigger alias prescribes a rename to type, and taking that advice cannot work — the binding moves to the START node's config

2 participants

@os-sam@os-project-manager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(spec): send a top-level flow trigger to the START node config, not to a type rename - #14735

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance
Sep 3, 2026
Merged

fix(spec): send a top-level flow trigger to the START node config, not to a type rename#14735
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#14337

FlowSchema's aliases table pointed trigger at type, and the rename it prescribed cannot be taken. type is the flow KIND, so an author who follows the advice lands on Invalid option one round later with the trigger binding still nowhere. Both keys move to the guidance table, beside the object / objectName / schedule prescriptions that already name the START node's config.

No accept/reject change. A top-level trigger / triggerType was refused before and is refused now; FlowSchema's accepted keys and its type enum are untouched. Only the prescription the refusal carries changes.

The sibling was unmeasured — it is measured now, and it reproduces

The card flagged triggertype: 'type' as probably the same mistake without measuring it. Measured on origin/main (its flow.zod.ts restored on disk, everything else at this branch), verbatim:

ROUND 1 — author writes top-level `triggerType`
Unrecognized key(s) on this flow: `triggerType`. Did you mean `triggerType` -> `type`? Until this
shape was closed, these were dropped silently ...
ROUND 2 — author takes the rename: `type: 'record-after-create'`
Invalid option: expected one of "autolaunched"|"record_change"|"schedule"|"screen"|"api"

That is the same dead end as trigger: the enum the rename points at contains no record-* token at all, so the second round cannot succeed either. The alias table is probed case- and separator-insensitively (aliases[aliasProbe(key)], packages/spec/src/shared/suggestions.zod.ts:463), which is why the source spelling triggertype was answering an authored triggerType. Fixed in this PR, as the card directed.

After this change, the same two inputs:

Unrecognized key(s) on this flow: `trigger`.
- `trigger` is not a Flow field - a record-change flow binds its trigger on the START node's
`config` (`{ objectName, triggerType, condition }`, where `triggerType` is a `record-*` token
such as `record-after-create`), not at the flow top level; the flow-level `type` names the
flow kind (`record_change`), not the binding. Until this shape was closed, ...
Unrecognized key(s) on this flow: `triggerType`.
- `triggerType` is not a Flow field - it belongs on the START node's `config`
(`{ objectName, triggerType, condition }`), where a `record-*` token such as
`record-after-create` binds the lifecycle event; ...

Where the binding really lives (cited, not paraphrased)

Claim the prescription makesAnchor
the trigger binds on the START node's config, as { objectName, triggerType, condition }packages/spec/src/automation/flow-trigger-kind.ts:12
a record-* token is the lifecycle-event grammarpackages/spec/src/automation/flow-trigger-kind.ts:74 (triggerType.startsWith('record-') resolves record_change)
the flow-level type names the KIND, and contains no event tokenpackages/spec/src/automation/flow.zod.ts:669
guidance is exact-key and suppresses the rename; aliases renders "Did you mean X -> Y?"packages/spec/src/shared/strict-object.ts:122, packages/spec/src/shared/suggestions.zod.ts:445 and :463

One measured consequence, stated rather than hidden

The alias row was probe-matched, so it also answered the folded spellings; guidance is exact-spelling by design (case folding is the rename channel's job). A non-canonical triggertype therefore now gets the bare rejection instead of the rename it could not take. That is a deliberate trade - no advice beats confidently wrong advice - and it is pinned in flow.test.ts so it cannot drift back into a rename unnoticed.

Verification (all on efe488c42, the final commit; heavy runs under scripts/pm/os-verify-lock.sh)

RunVerdict
@objectstack/spec - flow.test.ts, flow-trigger-kind.test.ts, strict-object.test.ts, alias-integrity.test.ts, suggestions.test.ts, visible-when-alias-guidance.test.ts6 files / 210 tests pass (VERDICT command-exit 0)
@objectstack/spec typecheck (tsc --noEmit + check:scripts-typecheck + check:test-typecheck)pass; the test layer IS compiled - 54 files under tsconfig.test.json, so the new pins are type-checked
check:generated (after pnpm --filter @objectstack/spec build)all 15 artifacts up to date, check:authorable-surface and check:api-surface among them - the guidance change projects into no generated page
check:system-context-censusOK - 145 anchors resolve, 109 census sites
check:doc-authoringOK - 14524 customer-facing strings, no internal issue ids
consumer: @objectstack/lint (lint-flow-patterns, validate-flow-trigger-readiness, flow-variable-scope, authoring-rule-input-tier)4 files / 236 tests pass
consumer: @objectstack/service-automation (engine, canonicalize-stored-flow, flow-cold-boot-bind)3 files / 158 tests pass
24 further gates from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackpass (check:nul-bytes, check:skill-examples 256 blocks, check:doc-anchors, check:corpus-claim-drift, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:empty-changeset, check:changeset-no-major, check:adr-0087-registration, and the docs family)
check:test-completeness, check:type-check-debtexit 3, NOT MEASURED by their own definition (one needs a turbo run test log, the other a fully built workspace) - left to CI, which has both

Reverse verification

Removing the guidance entries on disk (flow.zod.ts restored to origin/main, mutation confirmed by grep counts before the run) turns exactly the three new prescription pins red:

x sends a top-level `trigger` to the START node config, never to a `type` rename
x sends a top-level `triggerType` to the START node config, never to a `type` rename
x no spelling of the removed alias renames to `type` any more
Test Files 1 failed (1) Tests 3 failed | 93 passed (96)

The two positive controls stay green under the same ablation, correctly - they pin the claim, not the change. The file was restored with git checkout HEAD -- FILE, spelled as an absolute path and the restore proved by blob hash (abc5dc708... on disk equals HEAD:packages/spec/src/automation/flow.zod.ts), with git diff HEAD empty afterwards.

The third commit on this branch re-anchors a file:line citation on content/docs/permissions/system-context.mdx from flow.zod.ts:685 to :702, because this change inserts 17 lines above it. That commit is load-bearing, not cosmetic: reverting only that file makes check:system-context-census fail with [anchor-is-not-a-read-site] on :685 plus [ledger-row-unused] on :702. The anchored line's content is byte-identical at both numbers.

Notes

  • Existing pins of the old rename are flipped, never deleted - the assertion now reads .not.toContain on the trigger -> type string, in a dedicated test that also pins the prescription.
  • Changeset: @objectstack/spec: patch, customer-facing text, no issue ids.
  • This branch is a takeover: two earlier developer sessions died in container restarts. All three inherited commits were re-read hunk by hunk and re-verified from scratch; no inherited measurement was taken on trust.

Generated by Claude Code


Generated by Claude Code

os-samand others added 3 commits September 2, 2026 21:06
…t to a `type` rename
`FlowSchema` aliased `trigger` and `triggerType` to `type`, and `type` is the
flow KIND enum (`autolaunched` | `record_change` | `schedule` | `screen` |
`api`). An author who took that rename landed on `Invalid option: expected one
of "autolaunched"|…` one round later with the trigger binding still nowhere.
Both keys move to the `guidance` table beside `object` / `objectName` /
`schedule`, naming where the binding really lives: the START node's `config`
(`{ objectName, triggerType, condition }`, `triggerType` a `record-*` token).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
…low.zod line shift
`node scripts/check-system-context-census.mjs --fix` — pure line rot from the
guidance entries added above the cited line.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/plugin-endpoints.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/approvals.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/flows.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/hooks.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/workflows.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/concepts/architecture.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/getting-started/common-patterns.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/kernel/services-checklist.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/permissions/capabilities.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/protocol/objectql/schema.mdx(via record_change (literal, a string literal in FlowSchema))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v12.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/releases/v17.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

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

Which tree this was computed on

This run read content/docs from ac50f706795e1e5df86a9fd1cf7c70112826c33c — the merge of head efe488c42ce309a010a3e9d6a490c4e74db3a8ae into base dbf115284295b1989d4648dbfbd7e5f3f96357dc, which is what actions/checkout gives a pull_request run. Not the PR head.

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

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ac50f706795e1e5df86a9fd1cf7c70112826c33c && git checkout ac50f706795e1e5df86a9fd1cf7c70112826c33c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dbf115284295b1989d4648dbfbd7e5f3f96357dc efe488c42ce309a010a3e9d6a490c4e74db3a8ae && git checkout -B drift-repro dbf115284295b1989d4648dbfbd7e5f3f96357dc && git merge --no-ff efe488c42ce309a010a3e9d6a490c4e74db3a8ae
node scripts/docs-audit/affected-docs.mjs --json dbf115284295b1989d4648dbfbd7e5f3f96357dc

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

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

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33695118192 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 27 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33697809557 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    
  • Test Core (6/6) — 失败步骤: Run this shard's tests

    @objectstack/plugin-auth:test: FAIL src/durability-swallow-repair.test.ts > #12981 batch 6 — the plugin-auth admin-audit swallows report instead of vanishing > admin-import-users :: the run-level au
    ↳ 失败原因: @objectstack/plugin-auth:test: Error: Test timed out in 10000ms.
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 34 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Re-enqueue receipt (domain:spec seat, session_017RbbUMnxkUnWhE4j94v8FE, 2026-09-03T00:33Z) — first kick, signature checked, re-queued once.

  • Kick: removed_from_merge_queue 00:29:08Z, reason CI_FAILURE, queue build 33697809557 (the earlier red build 33695118192 at 23:46Z did not eject the PR — the queue rebuilt). Kick count on this PR: 1.
  • Signatures (both outside this PR's package — the diff is packages/spec/src/automation/flow.zod.ts + its test + one docs anchor + a changeset, head efe488c42 unchanged since 21:23Z):
    1. packages/clitest/run-dev-unbuilt-workspace.e2e.test.ts › "gives up and exits instead of waiting forever" — AssertionError: expected 'SIGKILL' to be null (23:46Z build: "the harness SIGKILLed the child — it was still alive at the ceiling, cap 180000 ms"). The assertion measures the harness's own time budget, i.e. the timing class; matches the standing queue-flake anchor Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752 (16 PRs / 10 independent hits in 24 h).
    2. packages/plugin-authsrc/durability-swallow-repair.test.ts › "The durability log-level gate cannot see the catch { return null; } seeder family — 15 files outside #12923's five, and neither widening path is cheap #12981 batch 6 … admin-import-users" — Test timed out in 10000ms. Timeout class; only this PR in the 24 h window so far, package untouched by this diff.
  • Head vs main: trial merge git merge-tree --write-tree origin/main refs/pull/14735/head at origin/main224f8ea4a — clean, zero conflicts; no commit on main since 23:15Z touches the PR's files; mergeable_state: clean.
  • Decision (platform-readings rule: a kicked PR is re-queued once when the signature matches a known flaky, with this receipt on the PR; a second kick stops re-queueing and re-judges by signature): auto-merge (SQUASH) re-enabled once, now. A second kick on the same signatures ⇒ no further re-queue from this seat; the flake conversation stays on Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752.

Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33704757465 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 2 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 36 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33718051795 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 3 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 73 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33719428912 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 4 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 77 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@os-samClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge-queue kick #3 — standing down until #14832 closes; nothing on this branch needs to change.

Removed from the queue at 2026-09-03T06:00:53Z (CI_FAILURE). Failing job: group run 33719428912 → Test Core (1/6) (100535388600), packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:418 — the child was SIGKILLed at the 180000 ms ceiling (180108 ms against an 8414 ms calibration on the same runner). The preceding group run 33718051795 (job 100531333140) failed on the same line (180107 ms / 8243 ms). That is the #14832 hang, in a package this PR does not touch; the shard otherwise reports 235 files / 2734 tests passing with completeness OK.

Not re-queued: two kicks on this signature were already spent (00:29Z, 02:08Z), and the signature recurs after its fix (#14715) merged, so it is #14832's defect rather than a flake to retry through. Card #14337 is now pm:blocked on #14832 with Unlock-action: re-check PR #14735; when #14832 closes this PR gets a trial merge against origin/main, a checks read, and one re-queue.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit b91c351Sep 3, 2026
36 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-14337-flow-trigger-alias-guidance branch September 3, 2026 09:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FlowSchema's trigger alias prescribes a rename to type, and taking that advice cannot work — the binding moves to the START node's config

2 participants

@os-sam@os-project-manager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(spec): send a top-level flow trigger to the START node config, not to a type rename - #14735

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance
Sep 3, 2026
Merged

fix(spec): send a top-level flow trigger to the START node config, not to a type rename#14735
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#14337

FlowSchema's aliases table pointed trigger at type, and the rename it prescribed cannot be taken. type is the flow KIND, so an author who follows the advice lands on Invalid option one round later with the trigger binding still nowhere. Both keys move to the guidance table, beside the object / objectName / schedule prescriptions that already name the START node's config.

No accept/reject change. A top-level trigger / triggerType was refused before and is refused now; FlowSchema's accepted keys and its type enum are untouched. Only the prescription the refusal carries changes.

The sibling was unmeasured — it is measured now, and it reproduces

The card flagged triggertype: 'type' as probably the same mistake without measuring it. Measured on origin/main (its flow.zod.ts restored on disk, everything else at this branch), verbatim:

ROUND 1 — author writes top-level `triggerType`
Unrecognized key(s) on this flow: `triggerType`. Did you mean `triggerType` -> `type`? Until this
shape was closed, these were dropped silently ...
ROUND 2 — author takes the rename: `type: 'record-after-create'`
Invalid option: expected one of "autolaunched"|"record_change"|"schedule"|"screen"|"api"

That is the same dead end as trigger: the enum the rename points at contains no record-* token at all, so the second round cannot succeed either. The alias table is probed case- and separator-insensitively (aliases[aliasProbe(key)], packages/spec/src/shared/suggestions.zod.ts:463), which is why the source spelling triggertype was answering an authored triggerType. Fixed in this PR, as the card directed.

After this change, the same two inputs:

Unrecognized key(s) on this flow: `trigger`.
- `trigger` is not a Flow field - a record-change flow binds its trigger on the START node's
`config` (`{ objectName, triggerType, condition }`, where `triggerType` is a `record-*` token
such as `record-after-create`), not at the flow top level; the flow-level `type` names the
flow kind (`record_change`), not the binding. Until this shape was closed, ...
Unrecognized key(s) on this flow: `triggerType`.
- `triggerType` is not a Flow field - it belongs on the START node's `config`
(`{ objectName, triggerType, condition }`), where a `record-*` token such as
`record-after-create` binds the lifecycle event; ...

Where the binding really lives (cited, not paraphrased)

Claim the prescription makesAnchor
the trigger binds on the START node's config, as { objectName, triggerType, condition }packages/spec/src/automation/flow-trigger-kind.ts:12
a record-* token is the lifecycle-event grammarpackages/spec/src/automation/flow-trigger-kind.ts:74 (triggerType.startsWith('record-') resolves record_change)
the flow-level type names the KIND, and contains no event tokenpackages/spec/src/automation/flow.zod.ts:669
guidance is exact-key and suppresses the rename; aliases renders "Did you mean X -> Y?"packages/spec/src/shared/strict-object.ts:122, packages/spec/src/shared/suggestions.zod.ts:445 and :463

One measured consequence, stated rather than hidden

The alias row was probe-matched, so it also answered the folded spellings; guidance is exact-spelling by design (case folding is the rename channel's job). A non-canonical triggertype therefore now gets the bare rejection instead of the rename it could not take. That is a deliberate trade - no advice beats confidently wrong advice - and it is pinned in flow.test.ts so it cannot drift back into a rename unnoticed.

Verification (all on efe488c42, the final commit; heavy runs under scripts/pm/os-verify-lock.sh)

RunVerdict
@objectstack/spec - flow.test.ts, flow-trigger-kind.test.ts, strict-object.test.ts, alias-integrity.test.ts, suggestions.test.ts, visible-when-alias-guidance.test.ts6 files / 210 tests pass (VERDICT command-exit 0)
@objectstack/spec typecheck (tsc --noEmit + check:scripts-typecheck + check:test-typecheck)pass; the test layer IS compiled - 54 files under tsconfig.test.json, so the new pins are type-checked
check:generated (after pnpm --filter @objectstack/spec build)all 15 artifacts up to date, check:authorable-surface and check:api-surface among them - the guidance change projects into no generated page
check:system-context-censusOK - 145 anchors resolve, 109 census sites
check:doc-authoringOK - 14524 customer-facing strings, no internal issue ids
consumer: @objectstack/lint (lint-flow-patterns, validate-flow-trigger-readiness, flow-variable-scope, authoring-rule-input-tier)4 files / 236 tests pass
consumer: @objectstack/service-automation (engine, canonicalize-stored-flow, flow-cold-boot-bind)3 files / 158 tests pass
24 further gates from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackpass (check:nul-bytes, check:skill-examples 256 blocks, check:doc-anchors, check:corpus-claim-drift, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:empty-changeset, check:changeset-no-major, check:adr-0087-registration, and the docs family)
check:test-completeness, check:type-check-debtexit 3, NOT MEASURED by their own definition (one needs a turbo run test log, the other a fully built workspace) - left to CI, which has both

Reverse verification

Removing the guidance entries on disk (flow.zod.ts restored to origin/main, mutation confirmed by grep counts before the run) turns exactly the three new prescription pins red:

x sends a top-level `trigger` to the START node config, never to a `type` rename
x sends a top-level `triggerType` to the START node config, never to a `type` rename
x no spelling of the removed alias renames to `type` any more
Test Files 1 failed (1) Tests 3 failed | 93 passed (96)

The two positive controls stay green under the same ablation, correctly - they pin the claim, not the change. The file was restored with git checkout HEAD -- FILE, spelled as an absolute path and the restore proved by blob hash (abc5dc708... on disk equals HEAD:packages/spec/src/automation/flow.zod.ts), with git diff HEAD empty afterwards.

The third commit on this branch re-anchors a file:line citation on content/docs/permissions/system-context.mdx from flow.zod.ts:685 to :702, because this change inserts 17 lines above it. That commit is load-bearing, not cosmetic: reverting only that file makes check:system-context-census fail with [anchor-is-not-a-read-site] on :685 plus [ledger-row-unused] on :702. The anchored line's content is byte-identical at both numbers.

Notes

  • Existing pins of the old rename are flipped, never deleted - the assertion now reads .not.toContain on the trigger -> type string, in a dedicated test that also pins the prescription.
  • Changeset: @objectstack/spec: patch, customer-facing text, no issue ids.
  • This branch is a takeover: two earlier developer sessions died in container restarts. All three inherited commits were re-read hunk by hunk and re-verified from scratch; no inherited measurement was taken on trust.

Generated by Claude Code


Generated by Claude Code

os-samand others added 3 commits September 2, 2026 21:06
…t to a `type` rename
`FlowSchema` aliased `trigger` and `triggerType` to `type`, and `type` is the
flow KIND enum (`autolaunched` | `record_change` | `schedule` | `screen` |
`api`). An author who took that rename landed on `Invalid option: expected one
of "autolaunched"|…` one round later with the trigger binding still nowhere.
Both keys move to the `guidance` table beside `object` / `objectName` /
`schedule`, naming where the binding really lives: the START node's `config`
(`{ objectName, triggerType, condition }`, `triggerType` a `record-*` token).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
…low.zod line shift
`node scripts/check-system-context-census.mjs --fix` — pure line rot from the
guidance entries added above the cited line.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/plugin-endpoints.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/approvals.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/flows.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/hooks.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/workflows.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/concepts/architecture.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/getting-started/common-patterns.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/kernel/services-checklist.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/permissions/capabilities.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/protocol/objectql/schema.mdx(via record_change (literal, a string literal in FlowSchema))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v12.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/releases/v17.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

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

Which tree this was computed on

This run read content/docs from ac50f706795e1e5df86a9fd1cf7c70112826c33c — the merge of head efe488c42ce309a010a3e9d6a490c4e74db3a8ae into base dbf115284295b1989d4648dbfbd7e5f3f96357dc, which is what actions/checkout gives a pull_request run. Not the PR head.

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

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ac50f706795e1e5df86a9fd1cf7c70112826c33c && git checkout ac50f706795e1e5df86a9fd1cf7c70112826c33c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dbf115284295b1989d4648dbfbd7e5f3f96357dc efe488c42ce309a010a3e9d6a490c4e74db3a8ae && git checkout -B drift-repro dbf115284295b1989d4648dbfbd7e5f3f96357dc && git merge --no-ff efe488c42ce309a010a3e9d6a490c4e74db3a8ae
node scripts/docs-audit/affected-docs.mjs --json dbf115284295b1989d4648dbfbd7e5f3f96357dc

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

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

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33695118192 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 27 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33697809557 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    
  • Test Core (6/6) — 失败步骤: Run this shard's tests

    @objectstack/plugin-auth:test: FAIL src/durability-swallow-repair.test.ts > #12981 batch 6 — the plugin-auth admin-audit swallows report instead of vanishing > admin-import-users :: the run-level au
    ↳ 失败原因: @objectstack/plugin-auth:test: Error: Test timed out in 10000ms.
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 34 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Re-enqueue receipt (domain:spec seat, session_017RbbUMnxkUnWhE4j94v8FE, 2026-09-03T00:33Z) — first kick, signature checked, re-queued once.

  • Kick: removed_from_merge_queue 00:29:08Z, reason CI_FAILURE, queue build 33697809557 (the earlier red build 33695118192 at 23:46Z did not eject the PR — the queue rebuilt). Kick count on this PR: 1.
  • Signatures (both outside this PR's package — the diff is packages/spec/src/automation/flow.zod.ts + its test + one docs anchor + a changeset, head efe488c42 unchanged since 21:23Z):
    1. packages/clitest/run-dev-unbuilt-workspace.e2e.test.ts › "gives up and exits instead of waiting forever" — AssertionError: expected 'SIGKILL' to be null (23:46Z build: "the harness SIGKILLed the child — it was still alive at the ceiling, cap 180000 ms"). The assertion measures the harness's own time budget, i.e. the timing class; matches the standing queue-flake anchor Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752 (16 PRs / 10 independent hits in 24 h).
    2. packages/plugin-authsrc/durability-swallow-repair.test.ts › "The durability log-level gate cannot see the catch { return null; } seeder family — 15 files outside #12923's five, and neither widening path is cheap #12981 batch 6 … admin-import-users" — Test timed out in 10000ms. Timeout class; only this PR in the 24 h window so far, package untouched by this diff.
  • Head vs main: trial merge git merge-tree --write-tree origin/main refs/pull/14735/head at origin/main224f8ea4a — clean, zero conflicts; no commit on main since 23:15Z touches the PR's files; mergeable_state: clean.
  • Decision (platform-readings rule: a kicked PR is re-queued once when the signature matches a known flaky, with this receipt on the PR; a second kick stops re-queueing and re-judges by signature): auto-merge (SQUASH) re-enabled once, now. A second kick on the same signatures ⇒ no further re-queue from this seat; the flake conversation stays on Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752.

Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33704757465 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 2 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 36 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33718051795 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 3 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 73 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33719428912 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 4 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 77 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@os-samClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge-queue kick #3 — standing down until #14832 closes; nothing on this branch needs to change.

Removed from the queue at 2026-09-03T06:00:53Z (CI_FAILURE). Failing job: group run 33719428912 → Test Core (1/6) (100535388600), packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:418 — the child was SIGKILLed at the 180000 ms ceiling (180108 ms against an 8414 ms calibration on the same runner). The preceding group run 33718051795 (job 100531333140) failed on the same line (180107 ms / 8243 ms). That is the #14832 hang, in a package this PR does not touch; the shard otherwise reports 235 files / 2734 tests passing with completeness OK.

Not re-queued: two kicks on this signature were already spent (00:29Z, 02:08Z), and the signature recurs after its fix (#14715) merged, so it is #14832's defect rather than a flake to retry through. Card #14337 is now pm:blocked on #14832 with Unlock-action: re-check PR #14735; when #14832 closes this PR gets a trial merge against origin/main, a checks read, and one re-queue.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit b91c351Sep 3, 2026
36 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-14337-flow-trigger-alias-guidance branch September 3, 2026 09:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FlowSchema's trigger alias prescribes a rename to type, and taking that advice cannot work — the binding moves to the START node's config

2 participants

@os-sam@os-project-manager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(spec): send a top-level flow trigger to the START node config, not to a type rename - #14735

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance
Sep 3, 2026
Merged

fix(spec): send a top-level flow trigger to the START node config, not to a type rename#14735
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#14337

FlowSchema's aliases table pointed trigger at type, and the rename it prescribed cannot be taken. type is the flow KIND, so an author who follows the advice lands on Invalid option one round later with the trigger binding still nowhere. Both keys move to the guidance table, beside the object / objectName / schedule prescriptions that already name the START node's config.

No accept/reject change. A top-level trigger / triggerType was refused before and is refused now; FlowSchema's accepted keys and its type enum are untouched. Only the prescription the refusal carries changes.

The sibling was unmeasured — it is measured now, and it reproduces

The card flagged triggertype: 'type' as probably the same mistake without measuring it. Measured on origin/main (its flow.zod.ts restored on disk, everything else at this branch), verbatim:

ROUND 1 — author writes top-level `triggerType`
Unrecognized key(s) on this flow: `triggerType`. Did you mean `triggerType` -> `type`? Until this
shape was closed, these were dropped silently ...
ROUND 2 — author takes the rename: `type: 'record-after-create'`
Invalid option: expected one of "autolaunched"|"record_change"|"schedule"|"screen"|"api"

That is the same dead end as trigger: the enum the rename points at contains no record-* token at all, so the second round cannot succeed either. The alias table is probed case- and separator-insensitively (aliases[aliasProbe(key)], packages/spec/src/shared/suggestions.zod.ts:463), which is why the source spelling triggertype was answering an authored triggerType. Fixed in this PR, as the card directed.

After this change, the same two inputs:

Unrecognized key(s) on this flow: `trigger`.
- `trigger` is not a Flow field - a record-change flow binds its trigger on the START node's
`config` (`{ objectName, triggerType, condition }`, where `triggerType` is a `record-*` token
such as `record-after-create`), not at the flow top level; the flow-level `type` names the
flow kind (`record_change`), not the binding. Until this shape was closed, ...
Unrecognized key(s) on this flow: `triggerType`.
- `triggerType` is not a Flow field - it belongs on the START node's `config`
(`{ objectName, triggerType, condition }`), where a `record-*` token such as
`record-after-create` binds the lifecycle event; ...

Where the binding really lives (cited, not paraphrased)

Claim the prescription makesAnchor
the trigger binds on the START node's config, as { objectName, triggerType, condition }packages/spec/src/automation/flow-trigger-kind.ts:12
a record-* token is the lifecycle-event grammarpackages/spec/src/automation/flow-trigger-kind.ts:74 (triggerType.startsWith('record-') resolves record_change)
the flow-level type names the KIND, and contains no event tokenpackages/spec/src/automation/flow.zod.ts:669
guidance is exact-key and suppresses the rename; aliases renders "Did you mean X -> Y?"packages/spec/src/shared/strict-object.ts:122, packages/spec/src/shared/suggestions.zod.ts:445 and :463

One measured consequence, stated rather than hidden

The alias row was probe-matched, so it also answered the folded spellings; guidance is exact-spelling by design (case folding is the rename channel's job). A non-canonical triggertype therefore now gets the bare rejection instead of the rename it could not take. That is a deliberate trade - no advice beats confidently wrong advice - and it is pinned in flow.test.ts so it cannot drift back into a rename unnoticed.

Verification (all on efe488c42, the final commit; heavy runs under scripts/pm/os-verify-lock.sh)

RunVerdict
@objectstack/spec - flow.test.ts, flow-trigger-kind.test.ts, strict-object.test.ts, alias-integrity.test.ts, suggestions.test.ts, visible-when-alias-guidance.test.ts6 files / 210 tests pass (VERDICT command-exit 0)
@objectstack/spec typecheck (tsc --noEmit + check:scripts-typecheck + check:test-typecheck)pass; the test layer IS compiled - 54 files under tsconfig.test.json, so the new pins are type-checked
check:generated (after pnpm --filter @objectstack/spec build)all 15 artifacts up to date, check:authorable-surface and check:api-surface among them - the guidance change projects into no generated page
check:system-context-censusOK - 145 anchors resolve, 109 census sites
check:doc-authoringOK - 14524 customer-facing strings, no internal issue ids
consumer: @objectstack/lint (lint-flow-patterns, validate-flow-trigger-readiness, flow-variable-scope, authoring-rule-input-tier)4 files / 236 tests pass
consumer: @objectstack/service-automation (engine, canonicalize-stored-flow, flow-cold-boot-bind)3 files / 158 tests pass
24 further gates from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackpass (check:nul-bytes, check:skill-examples 256 blocks, check:doc-anchors, check:corpus-claim-drift, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:empty-changeset, check:changeset-no-major, check:adr-0087-registration, and the docs family)
check:test-completeness, check:type-check-debtexit 3, NOT MEASURED by their own definition (one needs a turbo run test log, the other a fully built workspace) - left to CI, which has both

Reverse verification

Removing the guidance entries on disk (flow.zod.ts restored to origin/main, mutation confirmed by grep counts before the run) turns exactly the three new prescription pins red:

x sends a top-level `trigger` to the START node config, never to a `type` rename
x sends a top-level `triggerType` to the START node config, never to a `type` rename
x no spelling of the removed alias renames to `type` any more
Test Files 1 failed (1) Tests 3 failed | 93 passed (96)

The two positive controls stay green under the same ablation, correctly - they pin the claim, not the change. The file was restored with git checkout HEAD -- FILE, spelled as an absolute path and the restore proved by blob hash (abc5dc708... on disk equals HEAD:packages/spec/src/automation/flow.zod.ts), with git diff HEAD empty afterwards.

The third commit on this branch re-anchors a file:line citation on content/docs/permissions/system-context.mdx from flow.zod.ts:685 to :702, because this change inserts 17 lines above it. That commit is load-bearing, not cosmetic: reverting only that file makes check:system-context-census fail with [anchor-is-not-a-read-site] on :685 plus [ledger-row-unused] on :702. The anchored line's content is byte-identical at both numbers.

Notes

  • Existing pins of the old rename are flipped, never deleted - the assertion now reads .not.toContain on the trigger -> type string, in a dedicated test that also pins the prescription.
  • Changeset: @objectstack/spec: patch, customer-facing text, no issue ids.
  • This branch is a takeover: two earlier developer sessions died in container restarts. All three inherited commits were re-read hunk by hunk and re-verified from scratch; no inherited measurement was taken on trust.

Generated by Claude Code


Generated by Claude Code

os-samand others added 3 commits September 2, 2026 21:06
…t to a `type` rename
`FlowSchema` aliased `trigger` and `triggerType` to `type`, and `type` is the
flow KIND enum (`autolaunched` | `record_change` | `schedule` | `screen` |
`api`). An author who took that rename landed on `Invalid option: expected one
of "autolaunched"|…` one round later with the trigger binding still nowhere.
Both keys move to the `guidance` table beside `object` / `objectName` /
`schedule`, naming where the binding really lives: the START node's `config`
(`{ objectName, triggerType, condition }`, `triggerType` a `record-*` token).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
…low.zod line shift
`node scripts/check-system-context-census.mjs --fix` — pure line rot from the
guidance entries added above the cited line.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/plugin-endpoints.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/approvals.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/flows.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/hooks.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/workflows.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/concepts/architecture.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/getting-started/common-patterns.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/kernel/services-checklist.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/permissions/capabilities.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/protocol/objectql/schema.mdx(via record_change (literal, a string literal in FlowSchema))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v12.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/releases/v17.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

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

Which tree this was computed on

This run read content/docs from ac50f706795e1e5df86a9fd1cf7c70112826c33c — the merge of head efe488c42ce309a010a3e9d6a490c4e74db3a8ae into base dbf115284295b1989d4648dbfbd7e5f3f96357dc, which is what actions/checkout gives a pull_request run. Not the PR head.

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

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ac50f706795e1e5df86a9fd1cf7c70112826c33c && git checkout ac50f706795e1e5df86a9fd1cf7c70112826c33c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dbf115284295b1989d4648dbfbd7e5f3f96357dc efe488c42ce309a010a3e9d6a490c4e74db3a8ae && git checkout -B drift-repro dbf115284295b1989d4648dbfbd7e5f3f96357dc && git merge --no-ff efe488c42ce309a010a3e9d6a490c4e74db3a8ae
node scripts/docs-audit/affected-docs.mjs --json dbf115284295b1989d4648dbfbd7e5f3f96357dc

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

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

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33695118192 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 27 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33697809557 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    
  • Test Core (6/6) — 失败步骤: Run this shard's tests

    @objectstack/plugin-auth:test: FAIL src/durability-swallow-repair.test.ts > #12981 batch 6 — the plugin-auth admin-audit swallows report instead of vanishing > admin-import-users :: the run-level au
    ↳ 失败原因: @objectstack/plugin-auth:test: Error: Test timed out in 10000ms.
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 34 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Re-enqueue receipt (domain:spec seat, session_017RbbUMnxkUnWhE4j94v8FE, 2026-09-03T00:33Z) — first kick, signature checked, re-queued once.

  • Kick: removed_from_merge_queue 00:29:08Z, reason CI_FAILURE, queue build 33697809557 (the earlier red build 33695118192 at 23:46Z did not eject the PR — the queue rebuilt). Kick count on this PR: 1.
  • Signatures (both outside this PR's package — the diff is packages/spec/src/automation/flow.zod.ts + its test + one docs anchor + a changeset, head efe488c42 unchanged since 21:23Z):
    1. packages/clitest/run-dev-unbuilt-workspace.e2e.test.ts › "gives up and exits instead of waiting forever" — AssertionError: expected 'SIGKILL' to be null (23:46Z build: "the harness SIGKILLed the child — it was still alive at the ceiling, cap 180000 ms"). The assertion measures the harness's own time budget, i.e. the timing class; matches the standing queue-flake anchor Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752 (16 PRs / 10 independent hits in 24 h).
    2. packages/plugin-authsrc/durability-swallow-repair.test.ts › "The durability log-level gate cannot see the catch { return null; } seeder family — 15 files outside #12923's five, and neither widening path is cheap #12981 batch 6 … admin-import-users" — Test timed out in 10000ms. Timeout class; only this PR in the 24 h window so far, package untouched by this diff.
  • Head vs main: trial merge git merge-tree --write-tree origin/main refs/pull/14735/head at origin/main224f8ea4a — clean, zero conflicts; no commit on main since 23:15Z touches the PR's files; mergeable_state: clean.
  • Decision (platform-readings rule: a kicked PR is re-queued once when the signature matches a known flaky, with this receipt on the PR; a second kick stops re-queueing and re-judges by signature): auto-merge (SQUASH) re-enabled once, now. A second kick on the same signatures ⇒ no further re-queue from this seat; the flake conversation stays on Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752.

Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33704757465 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 2 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 36 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33718051795 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 3 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 73 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33719428912 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 4 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 77 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@os-samClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge-queue kick #3 — standing down until #14832 closes; nothing on this branch needs to change.

Removed from the queue at 2026-09-03T06:00:53Z (CI_FAILURE). Failing job: group run 33719428912 → Test Core (1/6) (100535388600), packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:418 — the child was SIGKILLed at the 180000 ms ceiling (180108 ms against an 8414 ms calibration on the same runner). The preceding group run 33718051795 (job 100531333140) failed on the same line (180107 ms / 8243 ms). That is the #14832 hang, in a package this PR does not touch; the shard otherwise reports 235 files / 2734 tests passing with completeness OK.

Not re-queued: two kicks on this signature were already spent (00:29Z, 02:08Z), and the signature recurs after its fix (#14715) merged, so it is #14832's defect rather than a flake to retry through. Card #14337 is now pm:blocked on #14832 with Unlock-action: re-check PR #14735; when #14832 closes this PR gets a trial merge against origin/main, a checks read, and one re-queue.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit b91c351Sep 3, 2026
36 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-14337-flow-trigger-alias-guidance branch September 3, 2026 09:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FlowSchema's trigger alias prescribes a rename to type, and taking that advice cannot work — the binding moves to the START node's config

2 participants

@os-sam@os-project-manager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(spec): send a top-level flow trigger to the START node config, not to a type rename - #14735

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance
Sep 3, 2026
Merged

fix(spec): send a top-level flow trigger to the START node config, not to a type rename#14735
os-project-manager merged 3 commits into
mainfrom
claude/issue-14337-flow-trigger-alias-guidance

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#14337

FlowSchema's aliases table pointed trigger at type, and the rename it prescribed cannot be taken. type is the flow KIND, so an author who follows the advice lands on Invalid option one round later with the trigger binding still nowhere. Both keys move to the guidance table, beside the object / objectName / schedule prescriptions that already name the START node's config.

No accept/reject change. A top-level trigger / triggerType was refused before and is refused now; FlowSchema's accepted keys and its type enum are untouched. Only the prescription the refusal carries changes.

The sibling was unmeasured — it is measured now, and it reproduces

The card flagged triggertype: 'type' as probably the same mistake without measuring it. Measured on origin/main (its flow.zod.ts restored on disk, everything else at this branch), verbatim:

ROUND 1 — author writes top-level `triggerType`
Unrecognized key(s) on this flow: `triggerType`. Did you mean `triggerType` -> `type`? Until this
shape was closed, these were dropped silently ...
ROUND 2 — author takes the rename: `type: 'record-after-create'`
Invalid option: expected one of "autolaunched"|"record_change"|"schedule"|"screen"|"api"

That is the same dead end as trigger: the enum the rename points at contains no record-* token at all, so the second round cannot succeed either. The alias table is probed case- and separator-insensitively (aliases[aliasProbe(key)], packages/spec/src/shared/suggestions.zod.ts:463), which is why the source spelling triggertype was answering an authored triggerType. Fixed in this PR, as the card directed.

After this change, the same two inputs:

Unrecognized key(s) on this flow: `trigger`.
- `trigger` is not a Flow field - a record-change flow binds its trigger on the START node's
`config` (`{ objectName, triggerType, condition }`, where `triggerType` is a `record-*` token
such as `record-after-create`), not at the flow top level; the flow-level `type` names the
flow kind (`record_change`), not the binding. Until this shape was closed, ...
Unrecognized key(s) on this flow: `triggerType`.
- `triggerType` is not a Flow field - it belongs on the START node's `config`
(`{ objectName, triggerType, condition }`), where a `record-*` token such as
`record-after-create` binds the lifecycle event; ...

Where the binding really lives (cited, not paraphrased)

Claim the prescription makesAnchor
the trigger binds on the START node's config, as { objectName, triggerType, condition }packages/spec/src/automation/flow-trigger-kind.ts:12
a record-* token is the lifecycle-event grammarpackages/spec/src/automation/flow-trigger-kind.ts:74 (triggerType.startsWith('record-') resolves record_change)
the flow-level type names the KIND, and contains no event tokenpackages/spec/src/automation/flow.zod.ts:669
guidance is exact-key and suppresses the rename; aliases renders "Did you mean X -> Y?"packages/spec/src/shared/strict-object.ts:122, packages/spec/src/shared/suggestions.zod.ts:445 and :463

One measured consequence, stated rather than hidden

The alias row was probe-matched, so it also answered the folded spellings; guidance is exact-spelling by design (case folding is the rename channel's job). A non-canonical triggertype therefore now gets the bare rejection instead of the rename it could not take. That is a deliberate trade - no advice beats confidently wrong advice - and it is pinned in flow.test.ts so it cannot drift back into a rename unnoticed.

Verification (all on efe488c42, the final commit; heavy runs under scripts/pm/os-verify-lock.sh)

RunVerdict
@objectstack/spec - flow.test.ts, flow-trigger-kind.test.ts, strict-object.test.ts, alias-integrity.test.ts, suggestions.test.ts, visible-when-alias-guidance.test.ts6 files / 210 tests pass (VERDICT command-exit 0)
@objectstack/spec typecheck (tsc --noEmit + check:scripts-typecheck + check:test-typecheck)pass; the test layer IS compiled - 54 files under tsconfig.test.json, so the new pins are type-checked
check:generated (after pnpm --filter @objectstack/spec build)all 15 artifacts up to date, check:authorable-surface and check:api-surface among them - the guidance change projects into no generated page
check:system-context-censusOK - 145 anchors resolve, 109 census sites
check:doc-authoringOK - 14524 customer-facing strings, no internal issue ids
consumer: @objectstack/lint (lint-flow-patterns, validate-flow-trigger-readiness, flow-variable-scope, authoring-rule-input-tier)4 files / 236 tests pass
consumer: @objectstack/service-automation (engine, canonicalize-stored-flow, flow-cold-boot-bind)3 files / 158 tests pass
24 further gates from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackpass (check:nul-bytes, check:skill-examples 256 blocks, check:doc-anchors, check:corpus-claim-drift, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:empty-changeset, check:changeset-no-major, check:adr-0087-registration, and the docs family)
check:test-completeness, check:type-check-debtexit 3, NOT MEASURED by their own definition (one needs a turbo run test log, the other a fully built workspace) - left to CI, which has both

Reverse verification

Removing the guidance entries on disk (flow.zod.ts restored to origin/main, mutation confirmed by grep counts before the run) turns exactly the three new prescription pins red:

x sends a top-level `trigger` to the START node config, never to a `type` rename
x sends a top-level `triggerType` to the START node config, never to a `type` rename
x no spelling of the removed alias renames to `type` any more
Test Files 1 failed (1) Tests 3 failed | 93 passed (96)

The two positive controls stay green under the same ablation, correctly - they pin the claim, not the change. The file was restored with git checkout HEAD -- FILE, spelled as an absolute path and the restore proved by blob hash (abc5dc708... on disk equals HEAD:packages/spec/src/automation/flow.zod.ts), with git diff HEAD empty afterwards.

The third commit on this branch re-anchors a file:line citation on content/docs/permissions/system-context.mdx from flow.zod.ts:685 to :702, because this change inserts 17 lines above it. That commit is load-bearing, not cosmetic: reverting only that file makes check:system-context-census fail with [anchor-is-not-a-read-site] on :685 plus [ledger-row-unused] on :702. The anchored line's content is byte-identical at both numbers.

Notes

  • Existing pins of the old rename are flipped, never deleted - the assertion now reads .not.toContain on the trigger -> type string, in a dedicated test that also pins the prescription.
  • Changeset: @objectstack/spec: patch, customer-facing text, no issue ids.
  • This branch is a takeover: two earlier developer sessions died in container restarts. All three inherited commits were re-read hunk by hunk and re-verified from scratch; no inherited measurement was taken on trust.

Generated by Claude Code


Generated by Claude Code

os-samand others added 3 commits September 2, 2026 21:06
…t to a `type` rename
`FlowSchema` aliased `trigger` and `triggerType` to `type`, and `type` is the
flow KIND enum (`autolaunched` | `record_change` | `schedule` | `screen` |
`api`). An author who took that rename landed on `Invalid option: expected one
of "autolaunched"|…` one round later with the trigger binding still nowhere.
Both keys move to the `guidance` table beside `object` / `objectName` /
`schedule`, naming where the binding really lives: the START node's `config`
(`{ objectName, triggerType, condition }`, `triggerType` a `record-*` token).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
…low.zod line shift
`node scripts/check-system-context-census.mjs --fix` — pure line rot from the
guidance entries added above the cited line.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RbbUMnxkUnWhE4j94v8FE
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/plugin-endpoints.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/approvals.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/flows.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/hooks.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/automation/workflows.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/concepts/architecture.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/getting-started/common-patterns.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/kernel/services-checklist.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/permissions/capabilities.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/protocol/objectql/schema.mdx(via record_change (literal, a string literal in FlowSchema))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v12.mdx(via record_change (literal, a string literal in FlowSchema))
  • content/docs/releases/v17.mdx(via FlowSchema (symbol, a top-level const), record_change (literal, a string literal in FlowSchema))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

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

Which tree this was computed on

This run read content/docs from ac50f706795e1e5df86a9fd1cf7c70112826c33c — the merge of head efe488c42ce309a010a3e9d6a490c4e74db3a8ae into base dbf115284295b1989d4648dbfbd7e5f3f96357dc, which is what actions/checkout gives a pull_request run. Not the PR head.

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

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ac50f706795e1e5df86a9fd1cf7c70112826c33c && git checkout ac50f706795e1e5df86a9fd1cf7c70112826c33c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dbf115284295b1989d4648dbfbd7e5f3f96357dc efe488c42ce309a010a3e9d6a490c4e74db3a8ae && git checkout -B drift-repro dbf115284295b1989d4648dbfbd7e5f3f96357dc && git merge --no-ff efe488c42ce309a010a3e9d6a490c4e74db3a8ae
node scripts/docs-audit/affected-docs.mjs --json dbf115284295b1989d4648dbfbd7e5f3f96357dc

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

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

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33695118192 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 27 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33697809557 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    
  • Test Core (6/6) — 失败步骤: Run this shard's tests

    @objectstack/plugin-auth:test: FAIL src/durability-swallow-repair.test.ts > #12981 batch 6 — the plugin-auth admin-audit swallows report instead of vanishing > admin-import-users :: the run-level au
    ↳ 失败原因: @objectstack/plugin-auth:test: Error: Test timed out in 10000ms.
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 34 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Re-enqueue receipt (domain:spec seat, session_017RbbUMnxkUnWhE4j94v8FE, 2026-09-03T00:33Z) — first kick, signature checked, re-queued once.

  • Kick: removed_from_merge_queue 00:29:08Z, reason CI_FAILURE, queue build 33697809557 (the earlier red build 33695118192 at 23:46Z did not eject the PR — the queue rebuilt). Kick count on this PR: 1.
  • Signatures (both outside this PR's package — the diff is packages/spec/src/automation/flow.zod.ts + its test + one docs anchor + a changeset, head efe488c42 unchanged since 21:23Z):
    1. packages/clitest/run-dev-unbuilt-workspace.e2e.test.ts › "gives up and exits instead of waiting forever" — AssertionError: expected 'SIGKILL' to be null (23:46Z build: "the harness SIGKILLed the child — it was still alive at the ceiling, cap 180000 ms"). The assertion measures the harness's own time budget, i.e. the timing class; matches the standing queue-flake anchor Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752 (16 PRs / 10 independent hits in 24 h).
    2. packages/plugin-authsrc/durability-swallow-repair.test.ts › "The durability log-level gate cannot see the catch { return null; } seeder family — 15 files outside #12923's five, and neither widening path is cheap #12981 batch 6 … admin-import-users" — Test timed out in 10000ms. Timeout class; only this PR in the 24 h window so far, package untouched by this diff.
  • Head vs main: trial merge git merge-tree --write-tree origin/main refs/pull/14735/head at origin/main224f8ea4a — clean, zero conflicts; no commit on main since 23:15Z touches the PR's files; mergeable_state: clean.
  • Decision (platform-readings rule: a kicked PR is re-queued once when the signature matches a known flaky, with this receipt on the PR; a second kick stops re-queueing and re-judges by signature): auto-merge (SQUASH) re-enabled once, now. A second kick on the same signatures ⇒ no further re-queue from this seat; the flake conversation stays on Queue-flake anchor: test/run-dev-unbuilt-workspace.e2e.test.ts #14752.

Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33704757465 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 2 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 36 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33718051795 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 3 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 73 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33719428912 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 4 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 77 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@os-samClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge-queue kick #3 — standing down until #14832 closes; nothing on this branch needs to change.

Removed from the queue at 2026-09-03T06:00:53Z (CI_FAILURE). Failing job: group run 33719428912 → Test Core (1/6) (100535388600), packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:418 — the child was SIGKILLed at the 180000 ms ceiling (180108 ms against an 8414 ms calibration on the same runner). The preceding group run 33718051795 (job 100531333140) failed on the same line (180107 ms / 8243 ms). That is the #14832 hang, in a package this PR does not touch; the shard otherwise reports 235 files / 2734 tests passing with completeness OK.

Not re-queued: two kicks on this signature were already spent (00:29Z, 02:08Z), and the signature recurs after its fix (#14715) merged, so it is #14832's defect rather than a flake to retry through. Card #14337 is now pm:blocked on #14832 with Unlock-action: re-check PR #14735; when #14832 closes this PR gets a trial merge against origin/main, a checks read, and one re-queue.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit b91c351Sep 3, 2026
36 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-14337-flow-trigger-alias-guidance branch September 3, 2026 09:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FlowSchema's trigger alias prescribes a rename to type, and taking that advice cannot work — the binding moves to the START node's config

2 participants

@os-sam@os-project-manager