fix(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init - #14649

Open
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe
Open

fix(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init#14649
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe

Conversation

@os-musk

Copy link
Copy Markdown
Collaborator

Fixes#14527

MetadataPlugin.init() carried a bare console.log immediately after
ctx.registerService('metadata', this.manager), printing
typeof this.manager.getRegisteredTypes. Nothing gated it on NODE_ENV, a debug flag
or a logger level, so every kernel boot that installs MetadataPlugin wrote it to
stdout — the CLI, the dev server and any embedding host alike.

What changed

One line deleted from packages/metadata/src/plugin.ts, plus a changeset. The whole
contribution is 2 files changed, 30 insertions(+), 1 deletion(-), and the single
source hunk is:

 // Register Metadata Manager as the primary metadata service provider.
ctx.registerService('metadata', this.manager);
- console.log('[MetadataPlugin] Registered metadata service, has getRegisteredTypes:', typeof this.manager.getRegisteredTypes);

Deleted rather than demoted to ctx.logger.debug

The triage allowed either. Deletion was chosen on two measurements:

  • Nothing reads it. A repo-wide search for the printed string (has getRegisteredTypes,
    and separately Registered metadata service) returns exactly one hit: the emitting line
    itself. No test, script, doc or workflow consumes it.
  • What it reported is statically known.getRegisteredTypes is a method the class
    declares, so the typeof it printed cannot vary in a way an operator could act on.
    Demoting would preserve a probe whose answer is fixed at compile time.

The ctx.logger.info('Initializing Metadata Manager', ...) call three lines above already
announces the same lifecycle step through the plugin's own logger, carrying the fields that
are actually actionable (root, watch, artifactSource).

Severity is bounded, per the triage: on the two paths where a stray stdout line would break
a parser — --json payloads and the os serve protocol channel — packages/cli already
reserves stdout by redirecting to stderr, so nothing downstream depended on this line being
present or absent.

Deliberately not touched

  • The file's five other console.* calls (:627:653 on this tree, the dev HMR path).
    Each carries // eslint-disable-next-line no-console, and the one that prints on every
    non-development boot carries a written rationale for doing so. That distinction is what
    makes this a one-line change and not a six-line one. The file's console.* count went
    6 -> 5 and its eslint-disable count stayed at 5.
  • eslint.config.mjs and the no-console question. No inline disable was added, none
    was removed, and the rule was not configured. That question, and the inert-suppression
    population behind it, is the separate card 134 eslint-disable comments across 45 source files are structurally inert — pnpm lint runs --no-inline-config #14529.

Why no gate moves in either direction here

Re-measured on this tree, with eslint's own resolved config rather than a text search:

$ eslint --print-config packages/metadata/src/plugin.ts
resolved rules for this file: 6
no-console entry: null
any rule name containing "console": []
$ grep -n '"lint"' package.json
32: "lint": "node --stack-size=4000 node_modules/eslint/bin/eslint.js . --no-inline-config"

no-console is not configured, and the repo's only lint invocation passes
--no-inline-config, so the sibling disable comments are inert. Repo-wide pnpm lint is
green (exit 0, no output, 118s) while the file still contains five console.* calls — which
is the positive control that the rule really is off. This change therefore turns no gate
green and no gate red.

Measurements

Everything below was run at head 9d4221244, after merging origin/main.

The shipped artefact moved. No assertion exists to ablate, so no ablation is owed; the
proof is the built output instead. grep -rn 'has getRegisteredTypes' dist/ in
packages/metadata:

beforeafter
probe string in dist/8 hits (index.js, index.cjs, node.js, node.cjs + 4 sourcemaps)0
control: sibling HMR print8 files8 files
control: the logger.info above it8 files8 files

Both controls still hold, so the zero is a real absence rather than a broken grep or an
unbuilt dist/. The probe is likewise absent from the 677-test run log, in which the
sibling console.log still appears 17 times.

Tests.pnpm --filter @objectstack/metadata exec vitest run --maxWorkers=2
Test Files 43 passed (43), Tests 677 passed (677).

Type check.@objectstack/metadata has no typecheck script on this tree (PR #14627,
which adds it, has not landed), so the substitute was
pnpm --filter @objectstack/metadata exec tsc --noEmit -p tsconfig.json. It reports 89
errors — all 89 in *.test.ts files, zero in plugin.ts and zero in any non-test file. A
reverse check confirms they are pre-existing rather than mine: restoring the pre-change
plugin.ts from the merge base and re-running gives the same 89, also with zero in
plugin.ts. The restore was verified by blob hash (8e4d1e3c... on disk equals the HEAD
blob) with an empty git diff HEAD. --listFiles confirms plugin.ts is in the program
that was checked.

Gates. The family union was re-derived from the actual change set on this tree
(node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, 2 paths vs merge
base ca48cf377) and gives 30 families — identical before and after the merge. 27 are
green. Three answer exit 3, which each script's own verdict text defines as NOT MEASURED
rather than a failure, and none for a reason this change can influence:

  • check-test-completeness.mjs — needs a saved turbo run test log as an argument; the
    derived family names it with none, which the script documents as the unreachable branch
    ("record this gate as NOT MEASURED; it is not a red").
  • scripts/pm/check-half-states.mjs — needs repo-scoped REST egress, which this container
    refuses (a direct REST probe answers 403). It sweeps the PM board, not this diff. Its
    --self-test wrapper, pnpm check:pm-half-states, is green at 2062 cases.
  • check:dual-build-cjs-loadsPREREQUISITE NOT MET: it reads built output and 57
    packages have no dist/ in this worktree. Its --self-test is green at 93 cases, and the
    property it asserts was checked directly for the package this PR touches: all four
    published require entries of @objectstack/metadata (index.cjs, node.cjs,
    errors.cjs, migrations/index.cjs) load, with MetadataPlugin exported as a function.

Always-runs: pnpm lint green (full repo, not narrowed), pnpm check:nul-bytes green
(7991 files), pnpm check:error-status-conformance green.

Changeset.@objectstack/metadata: patch. The file is in the published entry closure,
measured three ways: src/index.ts:14 re-exports MetadataPlugin from ./plugin.js; the
manifest ships "files": ["dist"] with exports["."] resolving to ./dist/index.js and
publishConfig.access: public; and pnpm check:published-files counts this package among
the 69 publishable members with a files whitelist and an exports map. Most directly, the
probe string was physically present in the shipped bundles before this change. One fewer
line on boot is observable, so skip-changeset would have been wrong.

This PR touches no package.json, so Validate Package Dependencies should not run on it.
That job is red on main for four lockfile advisories tracked in #14639, and no lockfile
change is carried here.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…Plugin.init` (#14527)
`init()` printed a bare `console.log` immediately after
`ctx.registerService('metadata', this.manager)`, reporting
`typeof this.manager.getRegisteredTypes`. Nothing gated it, so every kernel
boot that installs `MetadataPlugin` wrote it to stdout.
It reported a statically known fact and a repo-wide search found no reader of
the string, so it is deleted rather than demoted to `ctx.logger.debug`. The
`ctx.logger.info('Initializing Metadata Manager', ...)` call three lines above
already announces the step through the plugin's own logger.
The file's five other `console.*` calls (the dev HMR path) are untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/client-sdk.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/api/index.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/automation/flows.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/kernel/services-checklist.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/protocol/kernel/metadata-service.mdx(via MetadataPlugin (symbol, a top-level class))

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

  • content/docs/releases/implementation-status.mdx(via MetadataPlugin (symbol, a top-level class))

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
  • 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 — 12 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 20b883918aa787e65299e72b85c2b3396aa2006fpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 — the merge of head 9d422124467501b60af0f1885d7a78b5c77d5bb9 into base 20b883918aa787e65299e72b85c2b3396aa2006f, 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 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 && git checkout 8bd18a6f4e2db0764aa87b42e38e373abcd5f266
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 20b883918aa787e65299e72b85c2b3396aa2006f 9d422124467501b60af0f1885d7a78b5c77d5bb9 && git checkout -B drift-repro 20b883918aa787e65299e72b85c2b3396aa2006f && git merge --no-ff 9d422124467501b60af0f1885d7a78b5c77d5bb9
node scripts/docs-audit/affected-docs.mjs --json 20b883918aa787e65299e72b85c2b3396aa2006f

⚠️ 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 20b883918aa787e65299e72b85c2b3396aa2006f → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 17:50Z and armed auto-merge (squash) at 17:51:32Z on head 9d4221244. The echo read method: MERGE; the queue build ref is the arming reading, not the echo.

  • Review: ACCEPT on the card. Clause-② no, self-read from the final diff — one deleted console.log, nothing published changes shape.
  • Governed-surface test on the two changed paths: 0 of 2 governed — ordinary queue landing.
  • Enqueue bar: all 33 check runs on 9d4221244 completed success or skipped, read with perPage: 50.

The red on this head was diagnosed, not waited out.Test Core (1/6) failed at 17:15:22Z on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:317 (AssertionError: expected 'SIGKILL' to be null). Established as not this PR's before the one permitted re-run was spent:

The re-run on the identical head, with no push and no rebase, passed at 17:45:27Z. Red then green on a byte-identical tree is the controlled A/B, and it is now recorded on the queue-flake anchor #14648 (comment 5513952826) together with the shape of a real fix. That anchor is domain:cli; this lane contributed the measurement and ⛔ did not claim it, relabel it, or weaken the test.

⚠️ Known exposure, stated rather than discovered later: the queue build runs the full suite, so this PR meets that same packages/cli test again on its queue build. #14629 was ejected on it at 17:45Z. If this PR is ejected the same way, the disposition is the same — ⛔ no second re-run, ⛔ no re-queue while #14648 is open and unfixed, and the failure goes on the record rather than into another full-queue round.

Owed by the seat at MERGED: verify by content on origin/main that the probe line is gone from packages/metadata/src/plugin.ts and that the five sibling console.* calls remain, strip pm:dispatched from #14527, landing record on the card, inventory reconciliation.


Generated by Claude Code

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

Copy link
Copy Markdown
Contributor

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

队列构建 33671786568 红了。队列跑的是全量套件(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 无队列失败记录(首次)。
  • 过去 24h 队列共有 15 个失败构建(不含本次)。

分诊清单:

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

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

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Ejected from the merge queue at 19:35:44Z — not this PR's failure, and deliberately NOT re-queued

Engine execution seat (session session_0112hMx9hjJ9BgB28X97DS68), reading queue build 33671786568.

What failed:Test Core (1/6) on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.tsAssertionError: expected 'SIGKILL' to be null. The same signature that ejected #14629 at 17:45Z.

Verified by content that nothing landed:git grep -c "has getRegisteredTypes" origin/main -- packages/metadata/src/plugin.ts still returns 1, against a control of 14 console occurrences in the same file. The probe line this PR deletes is still on main, so this was an ejection and not a quiet landing.

Why it is not this PR's. The diff is two files in packages/metadata and cannot reach packages/cligit grep -c "has getRegisteredTypes" origin/main -- packages/cli = 0. Removing stdout output makes an unread-pipe scenario strictly less likely to block, so the change points away from the failure.

Why it is not a regression at all. The assertion is a wall-clock claim, not a behavioural one: UNREAD_HARD_CAP_MS = 40_000 at :153 and setTimeout(() => child.kill('SIGKILL'), …) at :186 mean signal === 'SIGKILL' reports only that the harness's own 40-second cap beat the child's exit. The decisive evidence is on this very PR: the same test, same assertion, failed on the PR side at 17:15:22Z and passed on a re-run of the identical head — no push, no rebase — at 17:45:27Z. A test red and green on a byte-identical tree is not measuring the tree.

⭐ The class has escalated since that first stand-down. The triage workflow's own count is now 8 independent PRs in 24 hours (#14499, #14593, #14617, #14629, #14631, #14649, #14665, #14675) across 15 failed queue builds, and it states that figure as a lower bound. Anchor: #14648 (domain:cli, priority:p1, pm:dispatched, assigned) — the card with an owner — plus the auto-filed duplicate #14706 which carries the current count. This lane's full diagnosis and the shape of a real fix are on both (5513952826 and 5515382957): the only option that closes the class is to assert the child's own exit reason instead of the absence of a signal, taking the wall clock out of the oracle.

Why this PR is not being re-queued. The triage workflow's checklist, item 2: once an aggregation issue exists, re-queuing before the fix only burns another full-queue round for every PR behind it — and with 15 failed builds in 24h that cost is now measurably superlinear. ⛔ No re-queue while the anchor is open and unfixed. ⛔ The test is not being skipped, quarantined, relaxed or re-baselined by this lane, and no second re-run has been spent — weakening a gate stays a human act and none of it is ours to do.

State: PR stays open, ready, mergeable_state: clean; auto-merge not re-armed. Card #14527 moved to pm:blocked with body lines Blocked-by: #14648 and Unlock-action: re-check PR #14649 — that second line is what stops the unlock scan re-dispatching finished, reviewed, green work when the anchor closes.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

2 participants

@os-musk@claude
, '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(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init - #14649

Open
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe
Open

fix(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init#14649
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe

Conversation

@os-musk

Copy link
Copy Markdown
Collaborator

Fixes#14527

MetadataPlugin.init() carried a bare console.log immediately after
ctx.registerService('metadata', this.manager), printing
typeof this.manager.getRegisteredTypes. Nothing gated it on NODE_ENV, a debug flag
or a logger level, so every kernel boot that installs MetadataPlugin wrote it to
stdout — the CLI, the dev server and any embedding host alike.

What changed

One line deleted from packages/metadata/src/plugin.ts, plus a changeset. The whole
contribution is 2 files changed, 30 insertions(+), 1 deletion(-), and the single
source hunk is:

 // Register Metadata Manager as the primary metadata service provider.
ctx.registerService('metadata', this.manager);
- console.log('[MetadataPlugin] Registered metadata service, has getRegisteredTypes:', typeof this.manager.getRegisteredTypes);

Deleted rather than demoted to ctx.logger.debug

The triage allowed either. Deletion was chosen on two measurements:

  • Nothing reads it. A repo-wide search for the printed string (has getRegisteredTypes,
    and separately Registered metadata service) returns exactly one hit: the emitting line
    itself. No test, script, doc or workflow consumes it.
  • What it reported is statically known.getRegisteredTypes is a method the class
    declares, so the typeof it printed cannot vary in a way an operator could act on.
    Demoting would preserve a probe whose answer is fixed at compile time.

The ctx.logger.info('Initializing Metadata Manager', ...) call three lines above already
announces the same lifecycle step through the plugin's own logger, carrying the fields that
are actually actionable (root, watch, artifactSource).

Severity is bounded, per the triage: on the two paths where a stray stdout line would break
a parser — --json payloads and the os serve protocol channel — packages/cli already
reserves stdout by redirecting to stderr, so nothing downstream depended on this line being
present or absent.

Deliberately not touched

  • The file's five other console.* calls (:627:653 on this tree, the dev HMR path).
    Each carries // eslint-disable-next-line no-console, and the one that prints on every
    non-development boot carries a written rationale for doing so. That distinction is what
    makes this a one-line change and not a six-line one. The file's console.* count went
    6 -> 5 and its eslint-disable count stayed at 5.
  • eslint.config.mjs and the no-console question. No inline disable was added, none
    was removed, and the rule was not configured. That question, and the inert-suppression
    population behind it, is the separate card 134 eslint-disable comments across 45 source files are structurally inert — pnpm lint runs --no-inline-config #14529.

Why no gate moves in either direction here

Re-measured on this tree, with eslint's own resolved config rather than a text search:

$ eslint --print-config packages/metadata/src/plugin.ts
resolved rules for this file: 6
no-console entry: null
any rule name containing "console": []
$ grep -n '"lint"' package.json
32: "lint": "node --stack-size=4000 node_modules/eslint/bin/eslint.js . --no-inline-config"

no-console is not configured, and the repo's only lint invocation passes
--no-inline-config, so the sibling disable comments are inert. Repo-wide pnpm lint is
green (exit 0, no output, 118s) while the file still contains five console.* calls — which
is the positive control that the rule really is off. This change therefore turns no gate
green and no gate red.

Measurements

Everything below was run at head 9d4221244, after merging origin/main.

The shipped artefact moved. No assertion exists to ablate, so no ablation is owed; the
proof is the built output instead. grep -rn 'has getRegisteredTypes' dist/ in
packages/metadata:

beforeafter
probe string in dist/8 hits (index.js, index.cjs, node.js, node.cjs + 4 sourcemaps)0
control: sibling HMR print8 files8 files
control: the logger.info above it8 files8 files

Both controls still hold, so the zero is a real absence rather than a broken grep or an
unbuilt dist/. The probe is likewise absent from the 677-test run log, in which the
sibling console.log still appears 17 times.

Tests.pnpm --filter @objectstack/metadata exec vitest run --maxWorkers=2
Test Files 43 passed (43), Tests 677 passed (677).

Type check.@objectstack/metadata has no typecheck script on this tree (PR #14627,
which adds it, has not landed), so the substitute was
pnpm --filter @objectstack/metadata exec tsc --noEmit -p tsconfig.json. It reports 89
errors — all 89 in *.test.ts files, zero in plugin.ts and zero in any non-test file. A
reverse check confirms they are pre-existing rather than mine: restoring the pre-change
plugin.ts from the merge base and re-running gives the same 89, also with zero in
plugin.ts. The restore was verified by blob hash (8e4d1e3c... on disk equals the HEAD
blob) with an empty git diff HEAD. --listFiles confirms plugin.ts is in the program
that was checked.

Gates. The family union was re-derived from the actual change set on this tree
(node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, 2 paths vs merge
base ca48cf377) and gives 30 families — identical before and after the merge. 27 are
green. Three answer exit 3, which each script's own verdict text defines as NOT MEASURED
rather than a failure, and none for a reason this change can influence:

  • check-test-completeness.mjs — needs a saved turbo run test log as an argument; the
    derived family names it with none, which the script documents as the unreachable branch
    ("record this gate as NOT MEASURED; it is not a red").
  • scripts/pm/check-half-states.mjs — needs repo-scoped REST egress, which this container
    refuses (a direct REST probe answers 403). It sweeps the PM board, not this diff. Its
    --self-test wrapper, pnpm check:pm-half-states, is green at 2062 cases.
  • check:dual-build-cjs-loadsPREREQUISITE NOT MET: it reads built output and 57
    packages have no dist/ in this worktree. Its --self-test is green at 93 cases, and the
    property it asserts was checked directly for the package this PR touches: all four
    published require entries of @objectstack/metadata (index.cjs, node.cjs,
    errors.cjs, migrations/index.cjs) load, with MetadataPlugin exported as a function.

Always-runs: pnpm lint green (full repo, not narrowed), pnpm check:nul-bytes green
(7991 files), pnpm check:error-status-conformance green.

Changeset.@objectstack/metadata: patch. The file is in the published entry closure,
measured three ways: src/index.ts:14 re-exports MetadataPlugin from ./plugin.js; the
manifest ships "files": ["dist"] with exports["."] resolving to ./dist/index.js and
publishConfig.access: public; and pnpm check:published-files counts this package among
the 69 publishable members with a files whitelist and an exports map. Most directly, the
probe string was physically present in the shipped bundles before this change. One fewer
line on boot is observable, so skip-changeset would have been wrong.

This PR touches no package.json, so Validate Package Dependencies should not run on it.
That job is red on main for four lockfile advisories tracked in #14639, and no lockfile
change is carried here.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…Plugin.init` (#14527)
`init()` printed a bare `console.log` immediately after
`ctx.registerService('metadata', this.manager)`, reporting
`typeof this.manager.getRegisteredTypes`. Nothing gated it, so every kernel
boot that installs `MetadataPlugin` wrote it to stdout.
It reported a statically known fact and a repo-wide search found no reader of
the string, so it is deleted rather than demoted to `ctx.logger.debug`. The
`ctx.logger.info('Initializing Metadata Manager', ...)` call three lines above
already announces the step through the plugin's own logger.
The file's five other `console.*` calls (the dev HMR path) are untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/client-sdk.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/api/index.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/automation/flows.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/kernel/services-checklist.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/protocol/kernel/metadata-service.mdx(via MetadataPlugin (symbol, a top-level class))

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

  • content/docs/releases/implementation-status.mdx(via MetadataPlugin (symbol, a top-level class))

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
  • 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 — 12 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 20b883918aa787e65299e72b85c2b3396aa2006fpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 — the merge of head 9d422124467501b60af0f1885d7a78b5c77d5bb9 into base 20b883918aa787e65299e72b85c2b3396aa2006f, 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 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 && git checkout 8bd18a6f4e2db0764aa87b42e38e373abcd5f266
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 20b883918aa787e65299e72b85c2b3396aa2006f 9d422124467501b60af0f1885d7a78b5c77d5bb9 && git checkout -B drift-repro 20b883918aa787e65299e72b85c2b3396aa2006f && git merge --no-ff 9d422124467501b60af0f1885d7a78b5c77d5bb9
node scripts/docs-audit/affected-docs.mjs --json 20b883918aa787e65299e72b85c2b3396aa2006f

⚠️ 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 20b883918aa787e65299e72b85c2b3396aa2006f → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 17:50Z and armed auto-merge (squash) at 17:51:32Z on head 9d4221244. The echo read method: MERGE; the queue build ref is the arming reading, not the echo.

  • Review: ACCEPT on the card. Clause-② no, self-read from the final diff — one deleted console.log, nothing published changes shape.
  • Governed-surface test on the two changed paths: 0 of 2 governed — ordinary queue landing.
  • Enqueue bar: all 33 check runs on 9d4221244 completed success or skipped, read with perPage: 50.

The red on this head was diagnosed, not waited out.Test Core (1/6) failed at 17:15:22Z on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:317 (AssertionError: expected 'SIGKILL' to be null). Established as not this PR's before the one permitted re-run was spent:

The re-run on the identical head, with no push and no rebase, passed at 17:45:27Z. Red then green on a byte-identical tree is the controlled A/B, and it is now recorded on the queue-flake anchor #14648 (comment 5513952826) together with the shape of a real fix. That anchor is domain:cli; this lane contributed the measurement and ⛔ did not claim it, relabel it, or weaken the test.

⚠️ Known exposure, stated rather than discovered later: the queue build runs the full suite, so this PR meets that same packages/cli test again on its queue build. #14629 was ejected on it at 17:45Z. If this PR is ejected the same way, the disposition is the same — ⛔ no second re-run, ⛔ no re-queue while #14648 is open and unfixed, and the failure goes on the record rather than into another full-queue round.

Owed by the seat at MERGED: verify by content on origin/main that the probe line is gone from packages/metadata/src/plugin.ts and that the five sibling console.* calls remain, strip pm:dispatched from #14527, landing record on the card, inventory reconciliation.


Generated by Claude Code

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

Copy link
Copy Markdown
Contributor

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

队列构建 33671786568 红了。队列跑的是全量套件(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 无队列失败记录(首次)。
  • 过去 24h 队列共有 15 个失败构建(不含本次)。

分诊清单:

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

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

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Ejected from the merge queue at 19:35:44Z — not this PR's failure, and deliberately NOT re-queued

Engine execution seat (session session_0112hMx9hjJ9BgB28X97DS68), reading queue build 33671786568.

What failed:Test Core (1/6) on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.tsAssertionError: expected 'SIGKILL' to be null. The same signature that ejected #14629 at 17:45Z.

Verified by content that nothing landed:git grep -c "has getRegisteredTypes" origin/main -- packages/metadata/src/plugin.ts still returns 1, against a control of 14 console occurrences in the same file. The probe line this PR deletes is still on main, so this was an ejection and not a quiet landing.

Why it is not this PR's. The diff is two files in packages/metadata and cannot reach packages/cligit grep -c "has getRegisteredTypes" origin/main -- packages/cli = 0. Removing stdout output makes an unread-pipe scenario strictly less likely to block, so the change points away from the failure.

Why it is not a regression at all. The assertion is a wall-clock claim, not a behavioural one: UNREAD_HARD_CAP_MS = 40_000 at :153 and setTimeout(() => child.kill('SIGKILL'), …) at :186 mean signal === 'SIGKILL' reports only that the harness's own 40-second cap beat the child's exit. The decisive evidence is on this very PR: the same test, same assertion, failed on the PR side at 17:15:22Z and passed on a re-run of the identical head — no push, no rebase — at 17:45:27Z. A test red and green on a byte-identical tree is not measuring the tree.

⭐ The class has escalated since that first stand-down. The triage workflow's own count is now 8 independent PRs in 24 hours (#14499, #14593, #14617, #14629, #14631, #14649, #14665, #14675) across 15 failed queue builds, and it states that figure as a lower bound. Anchor: #14648 (domain:cli, priority:p1, pm:dispatched, assigned) — the card with an owner — plus the auto-filed duplicate #14706 which carries the current count. This lane's full diagnosis and the shape of a real fix are on both (5513952826 and 5515382957): the only option that closes the class is to assert the child's own exit reason instead of the absence of a signal, taking the wall clock out of the oracle.

Why this PR is not being re-queued. The triage workflow's checklist, item 2: once an aggregation issue exists, re-queuing before the fix only burns another full-queue round for every PR behind it — and with 15 failed builds in 24h that cost is now measurably superlinear. ⛔ No re-queue while the anchor is open and unfixed. ⛔ The test is not being skipped, quarantined, relaxed or re-baselined by this lane, and no second re-run has been spent — weakening a gate stays a human act and none of it is ours to do.

State: PR stays open, ready, mergeable_state: clean; auto-merge not re-armed. Card #14527 moved to pm:blocked with body lines Blocked-by: #14648 and Unlock-action: re-check PR #14649 — that second line is what stops the unlock scan re-dispatching finished, reviewed, green work when the anchor closes.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

2 participants

@os-musk@claude
, '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(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init - #14649

Open
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe
Open

fix(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init#14649
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe

Conversation

@os-musk

Copy link
Copy Markdown
Collaborator

Fixes#14527

MetadataPlugin.init() carried a bare console.log immediately after
ctx.registerService('metadata', this.manager), printing
typeof this.manager.getRegisteredTypes. Nothing gated it on NODE_ENV, a debug flag
or a logger level, so every kernel boot that installs MetadataPlugin wrote it to
stdout — the CLI, the dev server and any embedding host alike.

What changed

One line deleted from packages/metadata/src/plugin.ts, plus a changeset. The whole
contribution is 2 files changed, 30 insertions(+), 1 deletion(-), and the single
source hunk is:

 // Register Metadata Manager as the primary metadata service provider.
ctx.registerService('metadata', this.manager);
- console.log('[MetadataPlugin] Registered metadata service, has getRegisteredTypes:', typeof this.manager.getRegisteredTypes);

Deleted rather than demoted to ctx.logger.debug

The triage allowed either. Deletion was chosen on two measurements:

  • Nothing reads it. A repo-wide search for the printed string (has getRegisteredTypes,
    and separately Registered metadata service) returns exactly one hit: the emitting line
    itself. No test, script, doc or workflow consumes it.
  • What it reported is statically known.getRegisteredTypes is a method the class
    declares, so the typeof it printed cannot vary in a way an operator could act on.
    Demoting would preserve a probe whose answer is fixed at compile time.

The ctx.logger.info('Initializing Metadata Manager', ...) call three lines above already
announces the same lifecycle step through the plugin's own logger, carrying the fields that
are actually actionable (root, watch, artifactSource).

Severity is bounded, per the triage: on the two paths where a stray stdout line would break
a parser — --json payloads and the os serve protocol channel — packages/cli already
reserves stdout by redirecting to stderr, so nothing downstream depended on this line being
present or absent.

Deliberately not touched

  • The file's five other console.* calls (:627:653 on this tree, the dev HMR path).
    Each carries // eslint-disable-next-line no-console, and the one that prints on every
    non-development boot carries a written rationale for doing so. That distinction is what
    makes this a one-line change and not a six-line one. The file's console.* count went
    6 -> 5 and its eslint-disable count stayed at 5.
  • eslint.config.mjs and the no-console question. No inline disable was added, none
    was removed, and the rule was not configured. That question, and the inert-suppression
    population behind it, is the separate card 134 eslint-disable comments across 45 source files are structurally inert — pnpm lint runs --no-inline-config #14529.

Why no gate moves in either direction here

Re-measured on this tree, with eslint's own resolved config rather than a text search:

$ eslint --print-config packages/metadata/src/plugin.ts
resolved rules for this file: 6
no-console entry: null
any rule name containing "console": []
$ grep -n '"lint"' package.json
32: "lint": "node --stack-size=4000 node_modules/eslint/bin/eslint.js . --no-inline-config"

no-console is not configured, and the repo's only lint invocation passes
--no-inline-config, so the sibling disable comments are inert. Repo-wide pnpm lint is
green (exit 0, no output, 118s) while the file still contains five console.* calls — which
is the positive control that the rule really is off. This change therefore turns no gate
green and no gate red.

Measurements

Everything below was run at head 9d4221244, after merging origin/main.

The shipped artefact moved. No assertion exists to ablate, so no ablation is owed; the
proof is the built output instead. grep -rn 'has getRegisteredTypes' dist/ in
packages/metadata:

beforeafter
probe string in dist/8 hits (index.js, index.cjs, node.js, node.cjs + 4 sourcemaps)0
control: sibling HMR print8 files8 files
control: the logger.info above it8 files8 files

Both controls still hold, so the zero is a real absence rather than a broken grep or an
unbuilt dist/. The probe is likewise absent from the 677-test run log, in which the
sibling console.log still appears 17 times.

Tests.pnpm --filter @objectstack/metadata exec vitest run --maxWorkers=2
Test Files 43 passed (43), Tests 677 passed (677).

Type check.@objectstack/metadata has no typecheck script on this tree (PR #14627,
which adds it, has not landed), so the substitute was
pnpm --filter @objectstack/metadata exec tsc --noEmit -p tsconfig.json. It reports 89
errors — all 89 in *.test.ts files, zero in plugin.ts and zero in any non-test file. A
reverse check confirms they are pre-existing rather than mine: restoring the pre-change
plugin.ts from the merge base and re-running gives the same 89, also with zero in
plugin.ts. The restore was verified by blob hash (8e4d1e3c... on disk equals the HEAD
blob) with an empty git diff HEAD. --listFiles confirms plugin.ts is in the program
that was checked.

Gates. The family union was re-derived from the actual change set on this tree
(node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, 2 paths vs merge
base ca48cf377) and gives 30 families — identical before and after the merge. 27 are
green. Three answer exit 3, which each script's own verdict text defines as NOT MEASURED
rather than a failure, and none for a reason this change can influence:

  • check-test-completeness.mjs — needs a saved turbo run test log as an argument; the
    derived family names it with none, which the script documents as the unreachable branch
    ("record this gate as NOT MEASURED; it is not a red").
  • scripts/pm/check-half-states.mjs — needs repo-scoped REST egress, which this container
    refuses (a direct REST probe answers 403). It sweeps the PM board, not this diff. Its
    --self-test wrapper, pnpm check:pm-half-states, is green at 2062 cases.
  • check:dual-build-cjs-loadsPREREQUISITE NOT MET: it reads built output and 57
    packages have no dist/ in this worktree. Its --self-test is green at 93 cases, and the
    property it asserts was checked directly for the package this PR touches: all four
    published require entries of @objectstack/metadata (index.cjs, node.cjs,
    errors.cjs, migrations/index.cjs) load, with MetadataPlugin exported as a function.

Always-runs: pnpm lint green (full repo, not narrowed), pnpm check:nul-bytes green
(7991 files), pnpm check:error-status-conformance green.

Changeset.@objectstack/metadata: patch. The file is in the published entry closure,
measured three ways: src/index.ts:14 re-exports MetadataPlugin from ./plugin.js; the
manifest ships "files": ["dist"] with exports["."] resolving to ./dist/index.js and
publishConfig.access: public; and pnpm check:published-files counts this package among
the 69 publishable members with a files whitelist and an exports map. Most directly, the
probe string was physically present in the shipped bundles before this change. One fewer
line on boot is observable, so skip-changeset would have been wrong.

This PR touches no package.json, so Validate Package Dependencies should not run on it.
That job is red on main for four lockfile advisories tracked in #14639, and no lockfile
change is carried here.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…Plugin.init` (#14527)
`init()` printed a bare `console.log` immediately after
`ctx.registerService('metadata', this.manager)`, reporting
`typeof this.manager.getRegisteredTypes`. Nothing gated it, so every kernel
boot that installs `MetadataPlugin` wrote it to stdout.
It reported a statically known fact and a repo-wide search found no reader of
the string, so it is deleted rather than demoted to `ctx.logger.debug`. The
`ctx.logger.info('Initializing Metadata Manager', ...)` call three lines above
already announces the step through the plugin's own logger.
The file's five other `console.*` calls (the dev HMR path) are untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/client-sdk.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/api/index.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/automation/flows.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/kernel/services-checklist.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/protocol/kernel/metadata-service.mdx(via MetadataPlugin (symbol, a top-level class))

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

  • content/docs/releases/implementation-status.mdx(via MetadataPlugin (symbol, a top-level class))

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
  • 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 — 12 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 20b883918aa787e65299e72b85c2b3396aa2006fpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 — the merge of head 9d422124467501b60af0f1885d7a78b5c77d5bb9 into base 20b883918aa787e65299e72b85c2b3396aa2006f, 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 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 && git checkout 8bd18a6f4e2db0764aa87b42e38e373abcd5f266
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 20b883918aa787e65299e72b85c2b3396aa2006f 9d422124467501b60af0f1885d7a78b5c77d5bb9 && git checkout -B drift-repro 20b883918aa787e65299e72b85c2b3396aa2006f && git merge --no-ff 9d422124467501b60af0f1885d7a78b5c77d5bb9
node scripts/docs-audit/affected-docs.mjs --json 20b883918aa787e65299e72b85c2b3396aa2006f

⚠️ 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 20b883918aa787e65299e72b85c2b3396aa2006f → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 17:50Z and armed auto-merge (squash) at 17:51:32Z on head 9d4221244. The echo read method: MERGE; the queue build ref is the arming reading, not the echo.

  • Review: ACCEPT on the card. Clause-② no, self-read from the final diff — one deleted console.log, nothing published changes shape.
  • Governed-surface test on the two changed paths: 0 of 2 governed — ordinary queue landing.
  • Enqueue bar: all 33 check runs on 9d4221244 completed success or skipped, read with perPage: 50.

The red on this head was diagnosed, not waited out.Test Core (1/6) failed at 17:15:22Z on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:317 (AssertionError: expected 'SIGKILL' to be null). Established as not this PR's before the one permitted re-run was spent:

The re-run on the identical head, with no push and no rebase, passed at 17:45:27Z. Red then green on a byte-identical tree is the controlled A/B, and it is now recorded on the queue-flake anchor #14648 (comment 5513952826) together with the shape of a real fix. That anchor is domain:cli; this lane contributed the measurement and ⛔ did not claim it, relabel it, or weaken the test.

⚠️ Known exposure, stated rather than discovered later: the queue build runs the full suite, so this PR meets that same packages/cli test again on its queue build. #14629 was ejected on it at 17:45Z. If this PR is ejected the same way, the disposition is the same — ⛔ no second re-run, ⛔ no re-queue while #14648 is open and unfixed, and the failure goes on the record rather than into another full-queue round.

Owed by the seat at MERGED: verify by content on origin/main that the probe line is gone from packages/metadata/src/plugin.ts and that the five sibling console.* calls remain, strip pm:dispatched from #14527, landing record on the card, inventory reconciliation.


Generated by Claude Code

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

Copy link
Copy Markdown
Contributor

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

队列构建 33671786568 红了。队列跑的是全量套件(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 无队列失败记录(首次)。
  • 过去 24h 队列共有 15 个失败构建(不含本次)。

分诊清单:

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

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

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Ejected from the merge queue at 19:35:44Z — not this PR's failure, and deliberately NOT re-queued

Engine execution seat (session session_0112hMx9hjJ9BgB28X97DS68), reading queue build 33671786568.

What failed:Test Core (1/6) on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.tsAssertionError: expected 'SIGKILL' to be null. The same signature that ejected #14629 at 17:45Z.

Verified by content that nothing landed:git grep -c "has getRegisteredTypes" origin/main -- packages/metadata/src/plugin.ts still returns 1, against a control of 14 console occurrences in the same file. The probe line this PR deletes is still on main, so this was an ejection and not a quiet landing.

Why it is not this PR's. The diff is two files in packages/metadata and cannot reach packages/cligit grep -c "has getRegisteredTypes" origin/main -- packages/cli = 0. Removing stdout output makes an unread-pipe scenario strictly less likely to block, so the change points away from the failure.

Why it is not a regression at all. The assertion is a wall-clock claim, not a behavioural one: UNREAD_HARD_CAP_MS = 40_000 at :153 and setTimeout(() => child.kill('SIGKILL'), …) at :186 mean signal === 'SIGKILL' reports only that the harness's own 40-second cap beat the child's exit. The decisive evidence is on this very PR: the same test, same assertion, failed on the PR side at 17:15:22Z and passed on a re-run of the identical head — no push, no rebase — at 17:45:27Z. A test red and green on a byte-identical tree is not measuring the tree.

⭐ The class has escalated since that first stand-down. The triage workflow's own count is now 8 independent PRs in 24 hours (#14499, #14593, #14617, #14629, #14631, #14649, #14665, #14675) across 15 failed queue builds, and it states that figure as a lower bound. Anchor: #14648 (domain:cli, priority:p1, pm:dispatched, assigned) — the card with an owner — plus the auto-filed duplicate #14706 which carries the current count. This lane's full diagnosis and the shape of a real fix are on both (5513952826 and 5515382957): the only option that closes the class is to assert the child's own exit reason instead of the absence of a signal, taking the wall clock out of the oracle.

Why this PR is not being re-queued. The triage workflow's checklist, item 2: once an aggregation issue exists, re-queuing before the fix only burns another full-queue round for every PR behind it — and with 15 failed builds in 24h that cost is now measurably superlinear. ⛔ No re-queue while the anchor is open and unfixed. ⛔ The test is not being skipped, quarantined, relaxed or re-baselined by this lane, and no second re-run has been spent — weakening a gate stays a human act and none of it is ours to do.

State: PR stays open, ready, mergeable_state: clean; auto-merge not re-armed. Card #14527 moved to pm:blocked with body lines Blocked-by: #14648 and Unlock-action: re-check PR #14649 — that second line is what stops the unlock scan re-dispatching finished, reviewed, green work when the anchor closes.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

2 participants

@os-musk@claude
, '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(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init - #14649

Open
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe
Open

fix(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init#14649
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe

Conversation

@os-musk

Copy link
Copy Markdown
Collaborator

Fixes#14527

MetadataPlugin.init() carried a bare console.log immediately after
ctx.registerService('metadata', this.manager), printing
typeof this.manager.getRegisteredTypes. Nothing gated it on NODE_ENV, a debug flag
or a logger level, so every kernel boot that installs MetadataPlugin wrote it to
stdout — the CLI, the dev server and any embedding host alike.

What changed

One line deleted from packages/metadata/src/plugin.ts, plus a changeset. The whole
contribution is 2 files changed, 30 insertions(+), 1 deletion(-), and the single
source hunk is:

 // Register Metadata Manager as the primary metadata service provider.
ctx.registerService('metadata', this.manager);
- console.log('[MetadataPlugin] Registered metadata service, has getRegisteredTypes:', typeof this.manager.getRegisteredTypes);

Deleted rather than demoted to ctx.logger.debug

The triage allowed either. Deletion was chosen on two measurements:

  • Nothing reads it. A repo-wide search for the printed string (has getRegisteredTypes,
    and separately Registered metadata service) returns exactly one hit: the emitting line
    itself. No test, script, doc or workflow consumes it.
  • What it reported is statically known.getRegisteredTypes is a method the class
    declares, so the typeof it printed cannot vary in a way an operator could act on.
    Demoting would preserve a probe whose answer is fixed at compile time.

The ctx.logger.info('Initializing Metadata Manager', ...) call three lines above already
announces the same lifecycle step through the plugin's own logger, carrying the fields that
are actually actionable (root, watch, artifactSource).

Severity is bounded, per the triage: on the two paths where a stray stdout line would break
a parser — --json payloads and the os serve protocol channel — packages/cli already
reserves stdout by redirecting to stderr, so nothing downstream depended on this line being
present or absent.

Deliberately not touched

  • The file's five other console.* calls (:627:653 on this tree, the dev HMR path).
    Each carries // eslint-disable-next-line no-console, and the one that prints on every
    non-development boot carries a written rationale for doing so. That distinction is what
    makes this a one-line change and not a six-line one. The file's console.* count went
    6 -> 5 and its eslint-disable count stayed at 5.
  • eslint.config.mjs and the no-console question. No inline disable was added, none
    was removed, and the rule was not configured. That question, and the inert-suppression
    population behind it, is the separate card 134 eslint-disable comments across 45 source files are structurally inert — pnpm lint runs --no-inline-config #14529.

Why no gate moves in either direction here

Re-measured on this tree, with eslint's own resolved config rather than a text search:

$ eslint --print-config packages/metadata/src/plugin.ts
resolved rules for this file: 6
no-console entry: null
any rule name containing "console": []
$ grep -n '"lint"' package.json
32: "lint": "node --stack-size=4000 node_modules/eslint/bin/eslint.js . --no-inline-config"

no-console is not configured, and the repo's only lint invocation passes
--no-inline-config, so the sibling disable comments are inert. Repo-wide pnpm lint is
green (exit 0, no output, 118s) while the file still contains five console.* calls — which
is the positive control that the rule really is off. This change therefore turns no gate
green and no gate red.

Measurements

Everything below was run at head 9d4221244, after merging origin/main.

The shipped artefact moved. No assertion exists to ablate, so no ablation is owed; the
proof is the built output instead. grep -rn 'has getRegisteredTypes' dist/ in
packages/metadata:

beforeafter
probe string in dist/8 hits (index.js, index.cjs, node.js, node.cjs + 4 sourcemaps)0
control: sibling HMR print8 files8 files
control: the logger.info above it8 files8 files

Both controls still hold, so the zero is a real absence rather than a broken grep or an
unbuilt dist/. The probe is likewise absent from the 677-test run log, in which the
sibling console.log still appears 17 times.

Tests.pnpm --filter @objectstack/metadata exec vitest run --maxWorkers=2
Test Files 43 passed (43), Tests 677 passed (677).

Type check.@objectstack/metadata has no typecheck script on this tree (PR #14627,
which adds it, has not landed), so the substitute was
pnpm --filter @objectstack/metadata exec tsc --noEmit -p tsconfig.json. It reports 89
errors — all 89 in *.test.ts files, zero in plugin.ts and zero in any non-test file. A
reverse check confirms they are pre-existing rather than mine: restoring the pre-change
plugin.ts from the merge base and re-running gives the same 89, also with zero in
plugin.ts. The restore was verified by blob hash (8e4d1e3c... on disk equals the HEAD
blob) with an empty git diff HEAD. --listFiles confirms plugin.ts is in the program
that was checked.

Gates. The family union was re-derived from the actual change set on this tree
(node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, 2 paths vs merge
base ca48cf377) and gives 30 families — identical before and after the merge. 27 are
green. Three answer exit 3, which each script's own verdict text defines as NOT MEASURED
rather than a failure, and none for a reason this change can influence:

  • check-test-completeness.mjs — needs a saved turbo run test log as an argument; the
    derived family names it with none, which the script documents as the unreachable branch
    ("record this gate as NOT MEASURED; it is not a red").
  • scripts/pm/check-half-states.mjs — needs repo-scoped REST egress, which this container
    refuses (a direct REST probe answers 403). It sweeps the PM board, not this diff. Its
    --self-test wrapper, pnpm check:pm-half-states, is green at 2062 cases.
  • check:dual-build-cjs-loadsPREREQUISITE NOT MET: it reads built output and 57
    packages have no dist/ in this worktree. Its --self-test is green at 93 cases, and the
    property it asserts was checked directly for the package this PR touches: all four
    published require entries of @objectstack/metadata (index.cjs, node.cjs,
    errors.cjs, migrations/index.cjs) load, with MetadataPlugin exported as a function.

Always-runs: pnpm lint green (full repo, not narrowed), pnpm check:nul-bytes green
(7991 files), pnpm check:error-status-conformance green.

Changeset.@objectstack/metadata: patch. The file is in the published entry closure,
measured three ways: src/index.ts:14 re-exports MetadataPlugin from ./plugin.js; the
manifest ships "files": ["dist"] with exports["."] resolving to ./dist/index.js and
publishConfig.access: public; and pnpm check:published-files counts this package among
the 69 publishable members with a files whitelist and an exports map. Most directly, the
probe string was physically present in the shipped bundles before this change. One fewer
line on boot is observable, so skip-changeset would have been wrong.

This PR touches no package.json, so Validate Package Dependencies should not run on it.
That job is red on main for four lockfile advisories tracked in #14639, and no lockfile
change is carried here.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…Plugin.init` (#14527)
`init()` printed a bare `console.log` immediately after
`ctx.registerService('metadata', this.manager)`, reporting
`typeof this.manager.getRegisteredTypes`. Nothing gated it, so every kernel
boot that installs `MetadataPlugin` wrote it to stdout.
It reported a statically known fact and a repo-wide search found no reader of
the string, so it is deleted rather than demoted to `ctx.logger.debug`. The
`ctx.logger.info('Initializing Metadata Manager', ...)` call three lines above
already announces the step through the plugin's own logger.
The file's five other `console.*` calls (the dev HMR path) are untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/client-sdk.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/api/index.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/automation/flows.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/kernel/services-checklist.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/protocol/kernel/metadata-service.mdx(via MetadataPlugin (symbol, a top-level class))

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

  • content/docs/releases/implementation-status.mdx(via MetadataPlugin (symbol, a top-level class))

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
  • 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 — 12 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 20b883918aa787e65299e72b85c2b3396aa2006fpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 — the merge of head 9d422124467501b60af0f1885d7a78b5c77d5bb9 into base 20b883918aa787e65299e72b85c2b3396aa2006f, 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 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 && git checkout 8bd18a6f4e2db0764aa87b42e38e373abcd5f266
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 20b883918aa787e65299e72b85c2b3396aa2006f 9d422124467501b60af0f1885d7a78b5c77d5bb9 && git checkout -B drift-repro 20b883918aa787e65299e72b85c2b3396aa2006f && git merge --no-ff 9d422124467501b60af0f1885d7a78b5c77d5bb9
node scripts/docs-audit/affected-docs.mjs --json 20b883918aa787e65299e72b85c2b3396aa2006f

⚠️ 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 20b883918aa787e65299e72b85c2b3396aa2006f → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 17:50Z and armed auto-merge (squash) at 17:51:32Z on head 9d4221244. The echo read method: MERGE; the queue build ref is the arming reading, not the echo.

  • Review: ACCEPT on the card. Clause-② no, self-read from the final diff — one deleted console.log, nothing published changes shape.
  • Governed-surface test on the two changed paths: 0 of 2 governed — ordinary queue landing.
  • Enqueue bar: all 33 check runs on 9d4221244 completed success or skipped, read with perPage: 50.

The red on this head was diagnosed, not waited out.Test Core (1/6) failed at 17:15:22Z on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:317 (AssertionError: expected 'SIGKILL' to be null). Established as not this PR's before the one permitted re-run was spent:

The re-run on the identical head, with no push and no rebase, passed at 17:45:27Z. Red then green on a byte-identical tree is the controlled A/B, and it is now recorded on the queue-flake anchor #14648 (comment 5513952826) together with the shape of a real fix. That anchor is domain:cli; this lane contributed the measurement and ⛔ did not claim it, relabel it, or weaken the test.

⚠️ Known exposure, stated rather than discovered later: the queue build runs the full suite, so this PR meets that same packages/cli test again on its queue build. #14629 was ejected on it at 17:45Z. If this PR is ejected the same way, the disposition is the same — ⛔ no second re-run, ⛔ no re-queue while #14648 is open and unfixed, and the failure goes on the record rather than into another full-queue round.

Owed by the seat at MERGED: verify by content on origin/main that the probe line is gone from packages/metadata/src/plugin.ts and that the five sibling console.* calls remain, strip pm:dispatched from #14527, landing record on the card, inventory reconciliation.


Generated by Claude Code

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

Copy link
Copy Markdown
Contributor

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

队列构建 33671786568 红了。队列跑的是全量套件(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 无队列失败记录(首次)。
  • 过去 24h 队列共有 15 个失败构建(不含本次)。

分诊清单:

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

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

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Ejected from the merge queue at 19:35:44Z — not this PR's failure, and deliberately NOT re-queued

Engine execution seat (session session_0112hMx9hjJ9BgB28X97DS68), reading queue build 33671786568.

What failed:Test Core (1/6) on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.tsAssertionError: expected 'SIGKILL' to be null. The same signature that ejected #14629 at 17:45Z.

Verified by content that nothing landed:git grep -c "has getRegisteredTypes" origin/main -- packages/metadata/src/plugin.ts still returns 1, against a control of 14 console occurrences in the same file. The probe line this PR deletes is still on main, so this was an ejection and not a quiet landing.

Why it is not this PR's. The diff is two files in packages/metadata and cannot reach packages/cligit grep -c "has getRegisteredTypes" origin/main -- packages/cli = 0. Removing stdout output makes an unread-pipe scenario strictly less likely to block, so the change points away from the failure.

Why it is not a regression at all. The assertion is a wall-clock claim, not a behavioural one: UNREAD_HARD_CAP_MS = 40_000 at :153 and setTimeout(() => child.kill('SIGKILL'), …) at :186 mean signal === 'SIGKILL' reports only that the harness's own 40-second cap beat the child's exit. The decisive evidence is on this very PR: the same test, same assertion, failed on the PR side at 17:15:22Z and passed on a re-run of the identical head — no push, no rebase — at 17:45:27Z. A test red and green on a byte-identical tree is not measuring the tree.

⭐ The class has escalated since that first stand-down. The triage workflow's own count is now 8 independent PRs in 24 hours (#14499, #14593, #14617, #14629, #14631, #14649, #14665, #14675) across 15 failed queue builds, and it states that figure as a lower bound. Anchor: #14648 (domain:cli, priority:p1, pm:dispatched, assigned) — the card with an owner — plus the auto-filed duplicate #14706 which carries the current count. This lane's full diagnosis and the shape of a real fix are on both (5513952826 and 5515382957): the only option that closes the class is to assert the child's own exit reason instead of the absence of a signal, taking the wall clock out of the oracle.

Why this PR is not being re-queued. The triage workflow's checklist, item 2: once an aggregation issue exists, re-queuing before the fix only burns another full-queue round for every PR behind it — and with 15 failed builds in 24h that cost is now measurably superlinear. ⛔ No re-queue while the anchor is open and unfixed. ⛔ The test is not being skipped, quarantined, relaxed or re-baselined by this lane, and no second re-run has been spent — weakening a gate stays a human act and none of it is ours to do.

State: PR stays open, ready, mergeable_state: clean; auto-merge not re-armed. Card #14527 moved to pm:blocked with body lines Blocked-by: #14648 and Unlock-action: re-check PR #14649 — that second line is what stops the unlock scan re-dispatching finished, reviewed, green work when the anchor closes.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

2 participants

@os-musk@claude
, '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(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init - #14649

Open
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe
Open

fix(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init#14649
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe

Conversation

@os-musk

Copy link
Copy Markdown
Collaborator

Fixes#14527

MetadataPlugin.init() carried a bare console.log immediately after
ctx.registerService('metadata', this.manager), printing
typeof this.manager.getRegisteredTypes. Nothing gated it on NODE_ENV, a debug flag
or a logger level, so every kernel boot that installs MetadataPlugin wrote it to
stdout — the CLI, the dev server and any embedding host alike.

What changed

One line deleted from packages/metadata/src/plugin.ts, plus a changeset. The whole
contribution is 2 files changed, 30 insertions(+), 1 deletion(-), and the single
source hunk is:

 // Register Metadata Manager as the primary metadata service provider.
ctx.registerService('metadata', this.manager);
- console.log('[MetadataPlugin] Registered metadata service, has getRegisteredTypes:', typeof this.manager.getRegisteredTypes);

Deleted rather than demoted to ctx.logger.debug

The triage allowed either. Deletion was chosen on two measurements:

  • Nothing reads it. A repo-wide search for the printed string (has getRegisteredTypes,
    and separately Registered metadata service) returns exactly one hit: the emitting line
    itself. No test, script, doc or workflow consumes it.
  • What it reported is statically known.getRegisteredTypes is a method the class
    declares, so the typeof it printed cannot vary in a way an operator could act on.
    Demoting would preserve a probe whose answer is fixed at compile time.

The ctx.logger.info('Initializing Metadata Manager', ...) call three lines above already
announces the same lifecycle step through the plugin's own logger, carrying the fields that
are actually actionable (root, watch, artifactSource).

Severity is bounded, per the triage: on the two paths where a stray stdout line would break
a parser — --json payloads and the os serve protocol channel — packages/cli already
reserves stdout by redirecting to stderr, so nothing downstream depended on this line being
present or absent.

Deliberately not touched

  • The file's five other console.* calls (:627:653 on this tree, the dev HMR path).
    Each carries // eslint-disable-next-line no-console, and the one that prints on every
    non-development boot carries a written rationale for doing so. That distinction is what
    makes this a one-line change and not a six-line one. The file's console.* count went
    6 -> 5 and its eslint-disable count stayed at 5.
  • eslint.config.mjs and the no-console question. No inline disable was added, none
    was removed, and the rule was not configured. That question, and the inert-suppression
    population behind it, is the separate card 134 eslint-disable comments across 45 source files are structurally inert — pnpm lint runs --no-inline-config #14529.

Why no gate moves in either direction here

Re-measured on this tree, with eslint's own resolved config rather than a text search:

$ eslint --print-config packages/metadata/src/plugin.ts
resolved rules for this file: 6
no-console entry: null
any rule name containing "console": []
$ grep -n '"lint"' package.json
32: "lint": "node --stack-size=4000 node_modules/eslint/bin/eslint.js . --no-inline-config"

no-console is not configured, and the repo's only lint invocation passes
--no-inline-config, so the sibling disable comments are inert. Repo-wide pnpm lint is
green (exit 0, no output, 118s) while the file still contains five console.* calls — which
is the positive control that the rule really is off. This change therefore turns no gate
green and no gate red.

Measurements

Everything below was run at head 9d4221244, after merging origin/main.

The shipped artefact moved. No assertion exists to ablate, so no ablation is owed; the
proof is the built output instead. grep -rn 'has getRegisteredTypes' dist/ in
packages/metadata:

beforeafter
probe string in dist/8 hits (index.js, index.cjs, node.js, node.cjs + 4 sourcemaps)0
control: sibling HMR print8 files8 files
control: the logger.info above it8 files8 files

Both controls still hold, so the zero is a real absence rather than a broken grep or an
unbuilt dist/. The probe is likewise absent from the 677-test run log, in which the
sibling console.log still appears 17 times.

Tests.pnpm --filter @objectstack/metadata exec vitest run --maxWorkers=2
Test Files 43 passed (43), Tests 677 passed (677).

Type check.@objectstack/metadata has no typecheck script on this tree (PR #14627,
which adds it, has not landed), so the substitute was
pnpm --filter @objectstack/metadata exec tsc --noEmit -p tsconfig.json. It reports 89
errors — all 89 in *.test.ts files, zero in plugin.ts and zero in any non-test file. A
reverse check confirms they are pre-existing rather than mine: restoring the pre-change
plugin.ts from the merge base and re-running gives the same 89, also with zero in
plugin.ts. The restore was verified by blob hash (8e4d1e3c... on disk equals the HEAD
blob) with an empty git diff HEAD. --listFiles confirms plugin.ts is in the program
that was checked.

Gates. The family union was re-derived from the actual change set on this tree
(node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, 2 paths vs merge
base ca48cf377) and gives 30 families — identical before and after the merge. 27 are
green. Three answer exit 3, which each script's own verdict text defines as NOT MEASURED
rather than a failure, and none for a reason this change can influence:

  • check-test-completeness.mjs — needs a saved turbo run test log as an argument; the
    derived family names it with none, which the script documents as the unreachable branch
    ("record this gate as NOT MEASURED; it is not a red").
  • scripts/pm/check-half-states.mjs — needs repo-scoped REST egress, which this container
    refuses (a direct REST probe answers 403). It sweeps the PM board, not this diff. Its
    --self-test wrapper, pnpm check:pm-half-states, is green at 2062 cases.
  • check:dual-build-cjs-loadsPREREQUISITE NOT MET: it reads built output and 57
    packages have no dist/ in this worktree. Its --self-test is green at 93 cases, and the
    property it asserts was checked directly for the package this PR touches: all four
    published require entries of @objectstack/metadata (index.cjs, node.cjs,
    errors.cjs, migrations/index.cjs) load, with MetadataPlugin exported as a function.

Always-runs: pnpm lint green (full repo, not narrowed), pnpm check:nul-bytes green
(7991 files), pnpm check:error-status-conformance green.

Changeset.@objectstack/metadata: patch. The file is in the published entry closure,
measured three ways: src/index.ts:14 re-exports MetadataPlugin from ./plugin.js; the
manifest ships "files": ["dist"] with exports["."] resolving to ./dist/index.js and
publishConfig.access: public; and pnpm check:published-files counts this package among
the 69 publishable members with a files whitelist and an exports map. Most directly, the
probe string was physically present in the shipped bundles before this change. One fewer
line on boot is observable, so skip-changeset would have been wrong.

This PR touches no package.json, so Validate Package Dependencies should not run on it.
That job is red on main for four lockfile advisories tracked in #14639, and no lockfile
change is carried here.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…Plugin.init` (#14527)
`init()` printed a bare `console.log` immediately after
`ctx.registerService('metadata', this.manager)`, reporting
`typeof this.manager.getRegisteredTypes`. Nothing gated it, so every kernel
boot that installs `MetadataPlugin` wrote it to stdout.
It reported a statically known fact and a repo-wide search found no reader of
the string, so it is deleted rather than demoted to `ctx.logger.debug`. The
`ctx.logger.info('Initializing Metadata Manager', ...)` call three lines above
already announces the step through the plugin's own logger.
The file's five other `console.*` calls (the dev HMR path) are untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/client-sdk.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/api/index.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/automation/flows.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/kernel/services-checklist.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/protocol/kernel/metadata-service.mdx(via MetadataPlugin (symbol, a top-level class))

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

  • content/docs/releases/implementation-status.mdx(via MetadataPlugin (symbol, a top-level class))

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
  • 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 — 12 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 20b883918aa787e65299e72b85c2b3396aa2006fpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 — the merge of head 9d422124467501b60af0f1885d7a78b5c77d5bb9 into base 20b883918aa787e65299e72b85c2b3396aa2006f, 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 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 && git checkout 8bd18a6f4e2db0764aa87b42e38e373abcd5f266
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 20b883918aa787e65299e72b85c2b3396aa2006f 9d422124467501b60af0f1885d7a78b5c77d5bb9 && git checkout -B drift-repro 20b883918aa787e65299e72b85c2b3396aa2006f && git merge --no-ff 9d422124467501b60af0f1885d7a78b5c77d5bb9
node scripts/docs-audit/affected-docs.mjs --json 20b883918aa787e65299e72b85c2b3396aa2006f

⚠️ 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 20b883918aa787e65299e72b85c2b3396aa2006f → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 17:50Z and armed auto-merge (squash) at 17:51:32Z on head 9d4221244. The echo read method: MERGE; the queue build ref is the arming reading, not the echo.

  • Review: ACCEPT on the card. Clause-② no, self-read from the final diff — one deleted console.log, nothing published changes shape.
  • Governed-surface test on the two changed paths: 0 of 2 governed — ordinary queue landing.
  • Enqueue bar: all 33 check runs on 9d4221244 completed success or skipped, read with perPage: 50.

The red on this head was diagnosed, not waited out.Test Core (1/6) failed at 17:15:22Z on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:317 (AssertionError: expected 'SIGKILL' to be null). Established as not this PR's before the one permitted re-run was spent:

The re-run on the identical head, with no push and no rebase, passed at 17:45:27Z. Red then green on a byte-identical tree is the controlled A/B, and it is now recorded on the queue-flake anchor #14648 (comment 5513952826) together with the shape of a real fix. That anchor is domain:cli; this lane contributed the measurement and ⛔ did not claim it, relabel it, or weaken the test.

⚠️ Known exposure, stated rather than discovered later: the queue build runs the full suite, so this PR meets that same packages/cli test again on its queue build. #14629 was ejected on it at 17:45Z. If this PR is ejected the same way, the disposition is the same — ⛔ no second re-run, ⛔ no re-queue while #14648 is open and unfixed, and the failure goes on the record rather than into another full-queue round.

Owed by the seat at MERGED: verify by content on origin/main that the probe line is gone from packages/metadata/src/plugin.ts and that the five sibling console.* calls remain, strip pm:dispatched from #14527, landing record on the card, inventory reconciliation.


Generated by Claude Code

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

Copy link
Copy Markdown
Contributor

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

队列构建 33671786568 红了。队列跑的是全量套件(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 无队列失败记录(首次)。
  • 过去 24h 队列共有 15 个失败构建(不含本次)。

分诊清单:

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

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

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Ejected from the merge queue at 19:35:44Z — not this PR's failure, and deliberately NOT re-queued

Engine execution seat (session session_0112hMx9hjJ9BgB28X97DS68), reading queue build 33671786568.

What failed:Test Core (1/6) on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.tsAssertionError: expected 'SIGKILL' to be null. The same signature that ejected #14629 at 17:45Z.

Verified by content that nothing landed:git grep -c "has getRegisteredTypes" origin/main -- packages/metadata/src/plugin.ts still returns 1, against a control of 14 console occurrences in the same file. The probe line this PR deletes is still on main, so this was an ejection and not a quiet landing.

Why it is not this PR's. The diff is two files in packages/metadata and cannot reach packages/cligit grep -c "has getRegisteredTypes" origin/main -- packages/cli = 0. Removing stdout output makes an unread-pipe scenario strictly less likely to block, so the change points away from the failure.

Why it is not a regression at all. The assertion is a wall-clock claim, not a behavioural one: UNREAD_HARD_CAP_MS = 40_000 at :153 and setTimeout(() => child.kill('SIGKILL'), …) at :186 mean signal === 'SIGKILL' reports only that the harness's own 40-second cap beat the child's exit. The decisive evidence is on this very PR: the same test, same assertion, failed on the PR side at 17:15:22Z and passed on a re-run of the identical head — no push, no rebase — at 17:45:27Z. A test red and green on a byte-identical tree is not measuring the tree.

⭐ The class has escalated since that first stand-down. The triage workflow's own count is now 8 independent PRs in 24 hours (#14499, #14593, #14617, #14629, #14631, #14649, #14665, #14675) across 15 failed queue builds, and it states that figure as a lower bound. Anchor: #14648 (domain:cli, priority:p1, pm:dispatched, assigned) — the card with an owner — plus the auto-filed duplicate #14706 which carries the current count. This lane's full diagnosis and the shape of a real fix are on both (5513952826 and 5515382957): the only option that closes the class is to assert the child's own exit reason instead of the absence of a signal, taking the wall clock out of the oracle.

Why this PR is not being re-queued. The triage workflow's checklist, item 2: once an aggregation issue exists, re-queuing before the fix only burns another full-queue round for every PR behind it — and with 15 failed builds in 24h that cost is now measurably superlinear. ⛔ No re-queue while the anchor is open and unfixed. ⛔ The test is not being skipped, quarantined, relaxed or re-baselined by this lane, and no second re-run has been spent — weakening a gate stays a human act and none of it is ours to do.

State: PR stays open, ready, mergeable_state: clean; auto-merge not re-armed. Card #14527 moved to pm:blocked with body lines Blocked-by: #14648 and Unlock-action: re-check PR #14649 — that second line is what stops the unlock scan re-dispatching finished, reviewed, green work when the anchor closes.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

2 participants

@os-musk@claude
, '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(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init - #14649

Open
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe
Open

fix(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init#14649
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe

Conversation

@os-musk

Copy link
Copy Markdown
Collaborator

Fixes#14527

MetadataPlugin.init() carried a bare console.log immediately after
ctx.registerService('metadata', this.manager), printing
typeof this.manager.getRegisteredTypes. Nothing gated it on NODE_ENV, a debug flag
or a logger level, so every kernel boot that installs MetadataPlugin wrote it to
stdout — the CLI, the dev server and any embedding host alike.

What changed

One line deleted from packages/metadata/src/plugin.ts, plus a changeset. The whole
contribution is 2 files changed, 30 insertions(+), 1 deletion(-), and the single
source hunk is:

 // Register Metadata Manager as the primary metadata service provider.
ctx.registerService('metadata', this.manager);
- console.log('[MetadataPlugin] Registered metadata service, has getRegisteredTypes:', typeof this.manager.getRegisteredTypes);

Deleted rather than demoted to ctx.logger.debug

The triage allowed either. Deletion was chosen on two measurements:

  • Nothing reads it. A repo-wide search for the printed string (has getRegisteredTypes,
    and separately Registered metadata service) returns exactly one hit: the emitting line
    itself. No test, script, doc or workflow consumes it.
  • What it reported is statically known.getRegisteredTypes is a method the class
    declares, so the typeof it printed cannot vary in a way an operator could act on.
    Demoting would preserve a probe whose answer is fixed at compile time.

The ctx.logger.info('Initializing Metadata Manager', ...) call three lines above already
announces the same lifecycle step through the plugin's own logger, carrying the fields that
are actually actionable (root, watch, artifactSource).

Severity is bounded, per the triage: on the two paths where a stray stdout line would break
a parser — --json payloads and the os serve protocol channel — packages/cli already
reserves stdout by redirecting to stderr, so nothing downstream depended on this line being
present or absent.

Deliberately not touched

  • The file's five other console.* calls (:627:653 on this tree, the dev HMR path).
    Each carries // eslint-disable-next-line no-console, and the one that prints on every
    non-development boot carries a written rationale for doing so. That distinction is what
    makes this a one-line change and not a six-line one. The file's console.* count went
    6 -> 5 and its eslint-disable count stayed at 5.
  • eslint.config.mjs and the no-console question. No inline disable was added, none
    was removed, and the rule was not configured. That question, and the inert-suppression
    population behind it, is the separate card 134 eslint-disable comments across 45 source files are structurally inert — pnpm lint runs --no-inline-config #14529.

Why no gate moves in either direction here

Re-measured on this tree, with eslint's own resolved config rather than a text search:

$ eslint --print-config packages/metadata/src/plugin.ts
resolved rules for this file: 6
no-console entry: null
any rule name containing "console": []
$ grep -n '"lint"' package.json
32: "lint": "node --stack-size=4000 node_modules/eslint/bin/eslint.js . --no-inline-config"

no-console is not configured, and the repo's only lint invocation passes
--no-inline-config, so the sibling disable comments are inert. Repo-wide pnpm lint is
green (exit 0, no output, 118s) while the file still contains five console.* calls — which
is the positive control that the rule really is off. This change therefore turns no gate
green and no gate red.

Measurements

Everything below was run at head 9d4221244, after merging origin/main.

The shipped artefact moved. No assertion exists to ablate, so no ablation is owed; the
proof is the built output instead. grep -rn 'has getRegisteredTypes' dist/ in
packages/metadata:

beforeafter
probe string in dist/8 hits (index.js, index.cjs, node.js, node.cjs + 4 sourcemaps)0
control: sibling HMR print8 files8 files
control: the logger.info above it8 files8 files

Both controls still hold, so the zero is a real absence rather than a broken grep or an
unbuilt dist/. The probe is likewise absent from the 677-test run log, in which the
sibling console.log still appears 17 times.

Tests.pnpm --filter @objectstack/metadata exec vitest run --maxWorkers=2
Test Files 43 passed (43), Tests 677 passed (677).

Type check.@objectstack/metadata has no typecheck script on this tree (PR #14627,
which adds it, has not landed), so the substitute was
pnpm --filter @objectstack/metadata exec tsc --noEmit -p tsconfig.json. It reports 89
errors — all 89 in *.test.ts files, zero in plugin.ts and zero in any non-test file. A
reverse check confirms they are pre-existing rather than mine: restoring the pre-change
plugin.ts from the merge base and re-running gives the same 89, also with zero in
plugin.ts. The restore was verified by blob hash (8e4d1e3c... on disk equals the HEAD
blob) with an empty git diff HEAD. --listFiles confirms plugin.ts is in the program
that was checked.

Gates. The family union was re-derived from the actual change set on this tree
(node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, 2 paths vs merge
base ca48cf377) and gives 30 families — identical before and after the merge. 27 are
green. Three answer exit 3, which each script's own verdict text defines as NOT MEASURED
rather than a failure, and none for a reason this change can influence:

  • check-test-completeness.mjs — needs a saved turbo run test log as an argument; the
    derived family names it with none, which the script documents as the unreachable branch
    ("record this gate as NOT MEASURED; it is not a red").
  • scripts/pm/check-half-states.mjs — needs repo-scoped REST egress, which this container
    refuses (a direct REST probe answers 403). It sweeps the PM board, not this diff. Its
    --self-test wrapper, pnpm check:pm-half-states, is green at 2062 cases.
  • check:dual-build-cjs-loadsPREREQUISITE NOT MET: it reads built output and 57
    packages have no dist/ in this worktree. Its --self-test is green at 93 cases, and the
    property it asserts was checked directly for the package this PR touches: all four
    published require entries of @objectstack/metadata (index.cjs, node.cjs,
    errors.cjs, migrations/index.cjs) load, with MetadataPlugin exported as a function.

Always-runs: pnpm lint green (full repo, not narrowed), pnpm check:nul-bytes green
(7991 files), pnpm check:error-status-conformance green.

Changeset.@objectstack/metadata: patch. The file is in the published entry closure,
measured three ways: src/index.ts:14 re-exports MetadataPlugin from ./plugin.js; the
manifest ships "files": ["dist"] with exports["."] resolving to ./dist/index.js and
publishConfig.access: public; and pnpm check:published-files counts this package among
the 69 publishable members with a files whitelist and an exports map. Most directly, the
probe string was physically present in the shipped bundles before this change. One fewer
line on boot is observable, so skip-changeset would have been wrong.

This PR touches no package.json, so Validate Package Dependencies should not run on it.
That job is red on main for four lockfile advisories tracked in #14639, and no lockfile
change is carried here.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…Plugin.init` (#14527)
`init()` printed a bare `console.log` immediately after
`ctx.registerService('metadata', this.manager)`, reporting
`typeof this.manager.getRegisteredTypes`. Nothing gated it, so every kernel
boot that installs `MetadataPlugin` wrote it to stdout.
It reported a statically known fact and a repo-wide search found no reader of
the string, so it is deleted rather than demoted to `ctx.logger.debug`. The
`ctx.logger.info('Initializing Metadata Manager', ...)` call three lines above
already announces the step through the plugin's own logger.
The file's five other `console.*` calls (the dev HMR path) are untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/client-sdk.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/api/index.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/automation/flows.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/kernel/services-checklist.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/protocol/kernel/metadata-service.mdx(via MetadataPlugin (symbol, a top-level class))

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

  • content/docs/releases/implementation-status.mdx(via MetadataPlugin (symbol, a top-level class))

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
  • 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 — 12 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 20b883918aa787e65299e72b85c2b3396aa2006fpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 — the merge of head 9d422124467501b60af0f1885d7a78b5c77d5bb9 into base 20b883918aa787e65299e72b85c2b3396aa2006f, 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 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 && git checkout 8bd18a6f4e2db0764aa87b42e38e373abcd5f266
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 20b883918aa787e65299e72b85c2b3396aa2006f 9d422124467501b60af0f1885d7a78b5c77d5bb9 && git checkout -B drift-repro 20b883918aa787e65299e72b85c2b3396aa2006f && git merge --no-ff 9d422124467501b60af0f1885d7a78b5c77d5bb9
node scripts/docs-audit/affected-docs.mjs --json 20b883918aa787e65299e72b85c2b3396aa2006f

⚠️ 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 20b883918aa787e65299e72b85c2b3396aa2006f → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 17:50Z and armed auto-merge (squash) at 17:51:32Z on head 9d4221244. The echo read method: MERGE; the queue build ref is the arming reading, not the echo.

  • Review: ACCEPT on the card. Clause-② no, self-read from the final diff — one deleted console.log, nothing published changes shape.
  • Governed-surface test on the two changed paths: 0 of 2 governed — ordinary queue landing.
  • Enqueue bar: all 33 check runs on 9d4221244 completed success or skipped, read with perPage: 50.

The red on this head was diagnosed, not waited out.Test Core (1/6) failed at 17:15:22Z on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:317 (AssertionError: expected 'SIGKILL' to be null). Established as not this PR's before the one permitted re-run was spent:

The re-run on the identical head, with no push and no rebase, passed at 17:45:27Z. Red then green on a byte-identical tree is the controlled A/B, and it is now recorded on the queue-flake anchor #14648 (comment 5513952826) together with the shape of a real fix. That anchor is domain:cli; this lane contributed the measurement and ⛔ did not claim it, relabel it, or weaken the test.

⚠️ Known exposure, stated rather than discovered later: the queue build runs the full suite, so this PR meets that same packages/cli test again on its queue build. #14629 was ejected on it at 17:45Z. If this PR is ejected the same way, the disposition is the same — ⛔ no second re-run, ⛔ no re-queue while #14648 is open and unfixed, and the failure goes on the record rather than into another full-queue round.

Owed by the seat at MERGED: verify by content on origin/main that the probe line is gone from packages/metadata/src/plugin.ts and that the five sibling console.* calls remain, strip pm:dispatched from #14527, landing record on the card, inventory reconciliation.


Generated by Claude Code

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

Copy link
Copy Markdown
Contributor

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

队列构建 33671786568 红了。队列跑的是全量套件(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 无队列失败记录(首次)。
  • 过去 24h 队列共有 15 个失败构建(不含本次)。

分诊清单:

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

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

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Ejected from the merge queue at 19:35:44Z — not this PR's failure, and deliberately NOT re-queued

Engine execution seat (session session_0112hMx9hjJ9BgB28X97DS68), reading queue build 33671786568.

What failed:Test Core (1/6) on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.tsAssertionError: expected 'SIGKILL' to be null. The same signature that ejected #14629 at 17:45Z.

Verified by content that nothing landed:git grep -c "has getRegisteredTypes" origin/main -- packages/metadata/src/plugin.ts still returns 1, against a control of 14 console occurrences in the same file. The probe line this PR deletes is still on main, so this was an ejection and not a quiet landing.

Why it is not this PR's. The diff is two files in packages/metadata and cannot reach packages/cligit grep -c "has getRegisteredTypes" origin/main -- packages/cli = 0. Removing stdout output makes an unread-pipe scenario strictly less likely to block, so the change points away from the failure.

Why it is not a regression at all. The assertion is a wall-clock claim, not a behavioural one: UNREAD_HARD_CAP_MS = 40_000 at :153 and setTimeout(() => child.kill('SIGKILL'), …) at :186 mean signal === 'SIGKILL' reports only that the harness's own 40-second cap beat the child's exit. The decisive evidence is on this very PR: the same test, same assertion, failed on the PR side at 17:15:22Z and passed on a re-run of the identical head — no push, no rebase — at 17:45:27Z. A test red and green on a byte-identical tree is not measuring the tree.

⭐ The class has escalated since that first stand-down. The triage workflow's own count is now 8 independent PRs in 24 hours (#14499, #14593, #14617, #14629, #14631, #14649, #14665, #14675) across 15 failed queue builds, and it states that figure as a lower bound. Anchor: #14648 (domain:cli, priority:p1, pm:dispatched, assigned) — the card with an owner — plus the auto-filed duplicate #14706 which carries the current count. This lane's full diagnosis and the shape of a real fix are on both (5513952826 and 5515382957): the only option that closes the class is to assert the child's own exit reason instead of the absence of a signal, taking the wall clock out of the oracle.

Why this PR is not being re-queued. The triage workflow's checklist, item 2: once an aggregation issue exists, re-queuing before the fix only burns another full-queue round for every PR behind it — and with 15 failed builds in 24h that cost is now measurably superlinear. ⛔ No re-queue while the anchor is open and unfixed. ⛔ The test is not being skipped, quarantined, relaxed or re-baselined by this lane, and no second re-run has been spent — weakening a gate stays a human act and none of it is ours to do.

State: PR stays open, ready, mergeable_state: clean; auto-merge not re-armed. Card #14527 moved to pm:blocked with body lines Blocked-by: #14648 and Unlock-action: re-check PR #14649 — that second line is what stops the unlock scan re-dispatching finished, reviewed, green work when the anchor closes.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

2 participants

@os-musk@claude
, '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(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init - #14649

Open
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe
Open

fix(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init#14649
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe

Conversation

@os-musk

Copy link
Copy Markdown
Collaborator

Fixes#14527

MetadataPlugin.init() carried a bare console.log immediately after
ctx.registerService('metadata', this.manager), printing
typeof this.manager.getRegisteredTypes. Nothing gated it on NODE_ENV, a debug flag
or a logger level, so every kernel boot that installs MetadataPlugin wrote it to
stdout — the CLI, the dev server and any embedding host alike.

What changed

One line deleted from packages/metadata/src/plugin.ts, plus a changeset. The whole
contribution is 2 files changed, 30 insertions(+), 1 deletion(-), and the single
source hunk is:

 // Register Metadata Manager as the primary metadata service provider.
ctx.registerService('metadata', this.manager);
- console.log('[MetadataPlugin] Registered metadata service, has getRegisteredTypes:', typeof this.manager.getRegisteredTypes);

Deleted rather than demoted to ctx.logger.debug

The triage allowed either. Deletion was chosen on two measurements:

  • Nothing reads it. A repo-wide search for the printed string (has getRegisteredTypes,
    and separately Registered metadata service) returns exactly one hit: the emitting line
    itself. No test, script, doc or workflow consumes it.
  • What it reported is statically known.getRegisteredTypes is a method the class
    declares, so the typeof it printed cannot vary in a way an operator could act on.
    Demoting would preserve a probe whose answer is fixed at compile time.

The ctx.logger.info('Initializing Metadata Manager', ...) call three lines above already
announces the same lifecycle step through the plugin's own logger, carrying the fields that
are actually actionable (root, watch, artifactSource).

Severity is bounded, per the triage: on the two paths where a stray stdout line would break
a parser — --json payloads and the os serve protocol channel — packages/cli already
reserves stdout by redirecting to stderr, so nothing downstream depended on this line being
present or absent.

Deliberately not touched

  • The file's five other console.* calls (:627:653 on this tree, the dev HMR path).
    Each carries // eslint-disable-next-line no-console, and the one that prints on every
    non-development boot carries a written rationale for doing so. That distinction is what
    makes this a one-line change and not a six-line one. The file's console.* count went
    6 -> 5 and its eslint-disable count stayed at 5.
  • eslint.config.mjs and the no-console question. No inline disable was added, none
    was removed, and the rule was not configured. That question, and the inert-suppression
    population behind it, is the separate card 134 eslint-disable comments across 45 source files are structurally inert — pnpm lint runs --no-inline-config #14529.

Why no gate moves in either direction here

Re-measured on this tree, with eslint's own resolved config rather than a text search:

$ eslint --print-config packages/metadata/src/plugin.ts
resolved rules for this file: 6
no-console entry: null
any rule name containing "console": []
$ grep -n '"lint"' package.json
32: "lint": "node --stack-size=4000 node_modules/eslint/bin/eslint.js . --no-inline-config"

no-console is not configured, and the repo's only lint invocation passes
--no-inline-config, so the sibling disable comments are inert. Repo-wide pnpm lint is
green (exit 0, no output, 118s) while the file still contains five console.* calls — which
is the positive control that the rule really is off. This change therefore turns no gate
green and no gate red.

Measurements

Everything below was run at head 9d4221244, after merging origin/main.

The shipped artefact moved. No assertion exists to ablate, so no ablation is owed; the
proof is the built output instead. grep -rn 'has getRegisteredTypes' dist/ in
packages/metadata:

beforeafter
probe string in dist/8 hits (index.js, index.cjs, node.js, node.cjs + 4 sourcemaps)0
control: sibling HMR print8 files8 files
control: the logger.info above it8 files8 files

Both controls still hold, so the zero is a real absence rather than a broken grep or an
unbuilt dist/. The probe is likewise absent from the 677-test run log, in which the
sibling console.log still appears 17 times.

Tests.pnpm --filter @objectstack/metadata exec vitest run --maxWorkers=2
Test Files 43 passed (43), Tests 677 passed (677).

Type check.@objectstack/metadata has no typecheck script on this tree (PR #14627,
which adds it, has not landed), so the substitute was
pnpm --filter @objectstack/metadata exec tsc --noEmit -p tsconfig.json. It reports 89
errors — all 89 in *.test.ts files, zero in plugin.ts and zero in any non-test file. A
reverse check confirms they are pre-existing rather than mine: restoring the pre-change
plugin.ts from the merge base and re-running gives the same 89, also with zero in
plugin.ts. The restore was verified by blob hash (8e4d1e3c... on disk equals the HEAD
blob) with an empty git diff HEAD. --listFiles confirms plugin.ts is in the program
that was checked.

Gates. The family union was re-derived from the actual change set on this tree
(node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, 2 paths vs merge
base ca48cf377) and gives 30 families — identical before and after the merge. 27 are
green. Three answer exit 3, which each script's own verdict text defines as NOT MEASURED
rather than a failure, and none for a reason this change can influence:

  • check-test-completeness.mjs — needs a saved turbo run test log as an argument; the
    derived family names it with none, which the script documents as the unreachable branch
    ("record this gate as NOT MEASURED; it is not a red").
  • scripts/pm/check-half-states.mjs — needs repo-scoped REST egress, which this container
    refuses (a direct REST probe answers 403). It sweeps the PM board, not this diff. Its
    --self-test wrapper, pnpm check:pm-half-states, is green at 2062 cases.
  • check:dual-build-cjs-loadsPREREQUISITE NOT MET: it reads built output and 57
    packages have no dist/ in this worktree. Its --self-test is green at 93 cases, and the
    property it asserts was checked directly for the package this PR touches: all four
    published require entries of @objectstack/metadata (index.cjs, node.cjs,
    errors.cjs, migrations/index.cjs) load, with MetadataPlugin exported as a function.

Always-runs: pnpm lint green (full repo, not narrowed), pnpm check:nul-bytes green
(7991 files), pnpm check:error-status-conformance green.

Changeset.@objectstack/metadata: patch. The file is in the published entry closure,
measured three ways: src/index.ts:14 re-exports MetadataPlugin from ./plugin.js; the
manifest ships "files": ["dist"] with exports["."] resolving to ./dist/index.js and
publishConfig.access: public; and pnpm check:published-files counts this package among
the 69 publishable members with a files whitelist and an exports map. Most directly, the
probe string was physically present in the shipped bundles before this change. One fewer
line on boot is observable, so skip-changeset would have been wrong.

This PR touches no package.json, so Validate Package Dependencies should not run on it.
That job is red on main for four lockfile advisories tracked in #14639, and no lockfile
change is carried here.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…Plugin.init` (#14527)
`init()` printed a bare `console.log` immediately after
`ctx.registerService('metadata', this.manager)`, reporting
`typeof this.manager.getRegisteredTypes`. Nothing gated it, so every kernel
boot that installs `MetadataPlugin` wrote it to stdout.
It reported a statically known fact and a repo-wide search found no reader of
the string, so it is deleted rather than demoted to `ctx.logger.debug`. The
`ctx.logger.info('Initializing Metadata Manager', ...)` call three lines above
already announces the step through the plugin's own logger.
The file's five other `console.*` calls (the dev HMR path) are untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/client-sdk.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/api/index.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/automation/flows.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/kernel/services-checklist.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/protocol/kernel/metadata-service.mdx(via MetadataPlugin (symbol, a top-level class))

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

  • content/docs/releases/implementation-status.mdx(via MetadataPlugin (symbol, a top-level class))

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
  • 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 — 12 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 20b883918aa787e65299e72b85c2b3396aa2006fpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 — the merge of head 9d422124467501b60af0f1885d7a78b5c77d5bb9 into base 20b883918aa787e65299e72b85c2b3396aa2006f, 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 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 && git checkout 8bd18a6f4e2db0764aa87b42e38e373abcd5f266
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 20b883918aa787e65299e72b85c2b3396aa2006f 9d422124467501b60af0f1885d7a78b5c77d5bb9 && git checkout -B drift-repro 20b883918aa787e65299e72b85c2b3396aa2006f && git merge --no-ff 9d422124467501b60af0f1885d7a78b5c77d5bb9
node scripts/docs-audit/affected-docs.mjs --json 20b883918aa787e65299e72b85c2b3396aa2006f

⚠️ 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 20b883918aa787e65299e72b85c2b3396aa2006f → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 17:50Z and armed auto-merge (squash) at 17:51:32Z on head 9d4221244. The echo read method: MERGE; the queue build ref is the arming reading, not the echo.

  • Review: ACCEPT on the card. Clause-② no, self-read from the final diff — one deleted console.log, nothing published changes shape.
  • Governed-surface test on the two changed paths: 0 of 2 governed — ordinary queue landing.
  • Enqueue bar: all 33 check runs on 9d4221244 completed success or skipped, read with perPage: 50.

The red on this head was diagnosed, not waited out.Test Core (1/6) failed at 17:15:22Z on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:317 (AssertionError: expected 'SIGKILL' to be null). Established as not this PR's before the one permitted re-run was spent:

The re-run on the identical head, with no push and no rebase, passed at 17:45:27Z. Red then green on a byte-identical tree is the controlled A/B, and it is now recorded on the queue-flake anchor #14648 (comment 5513952826) together with the shape of a real fix. That anchor is domain:cli; this lane contributed the measurement and ⛔ did not claim it, relabel it, or weaken the test.

⚠️ Known exposure, stated rather than discovered later: the queue build runs the full suite, so this PR meets that same packages/cli test again on its queue build. #14629 was ejected on it at 17:45Z. If this PR is ejected the same way, the disposition is the same — ⛔ no second re-run, ⛔ no re-queue while #14648 is open and unfixed, and the failure goes on the record rather than into another full-queue round.

Owed by the seat at MERGED: verify by content on origin/main that the probe line is gone from packages/metadata/src/plugin.ts and that the five sibling console.* calls remain, strip pm:dispatched from #14527, landing record on the card, inventory reconciliation.


Generated by Claude Code

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

Copy link
Copy Markdown
Contributor

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

队列构建 33671786568 红了。队列跑的是全量套件(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 无队列失败记录(首次)。
  • 过去 24h 队列共有 15 个失败构建(不含本次)。

分诊清单:

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

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

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Ejected from the merge queue at 19:35:44Z — not this PR's failure, and deliberately NOT re-queued

Engine execution seat (session session_0112hMx9hjJ9BgB28X97DS68), reading queue build 33671786568.

What failed:Test Core (1/6) on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.tsAssertionError: expected 'SIGKILL' to be null. The same signature that ejected #14629 at 17:45Z.

Verified by content that nothing landed:git grep -c "has getRegisteredTypes" origin/main -- packages/metadata/src/plugin.ts still returns 1, against a control of 14 console occurrences in the same file. The probe line this PR deletes is still on main, so this was an ejection and not a quiet landing.

Why it is not this PR's. The diff is two files in packages/metadata and cannot reach packages/cligit grep -c "has getRegisteredTypes" origin/main -- packages/cli = 0. Removing stdout output makes an unread-pipe scenario strictly less likely to block, so the change points away from the failure.

Why it is not a regression at all. The assertion is a wall-clock claim, not a behavioural one: UNREAD_HARD_CAP_MS = 40_000 at :153 and setTimeout(() => child.kill('SIGKILL'), …) at :186 mean signal === 'SIGKILL' reports only that the harness's own 40-second cap beat the child's exit. The decisive evidence is on this very PR: the same test, same assertion, failed on the PR side at 17:15:22Z and passed on a re-run of the identical head — no push, no rebase — at 17:45:27Z. A test red and green on a byte-identical tree is not measuring the tree.

⭐ The class has escalated since that first stand-down. The triage workflow's own count is now 8 independent PRs in 24 hours (#14499, #14593, #14617, #14629, #14631, #14649, #14665, #14675) across 15 failed queue builds, and it states that figure as a lower bound. Anchor: #14648 (domain:cli, priority:p1, pm:dispatched, assigned) — the card with an owner — plus the auto-filed duplicate #14706 which carries the current count. This lane's full diagnosis and the shape of a real fix are on both (5513952826 and 5515382957): the only option that closes the class is to assert the child's own exit reason instead of the absence of a signal, taking the wall clock out of the oracle.

Why this PR is not being re-queued. The triage workflow's checklist, item 2: once an aggregation issue exists, re-queuing before the fix only burns another full-queue round for every PR behind it — and with 15 failed builds in 24h that cost is now measurably superlinear. ⛔ No re-queue while the anchor is open and unfixed. ⛔ The test is not being skipped, quarantined, relaxed or re-baselined by this lane, and no second re-run has been spent — weakening a gate stays a human act and none of it is ours to do.

State: PR stays open, ready, mergeable_state: clean; auto-merge not re-armed. Card #14527 moved to pm:blocked with body lines Blocked-by: #14648 and Unlock-action: re-check PR #14649 — that second line is what stops the unlock scan re-dispatching finished, reviewed, green work when the anchor closes.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

2 participants

@os-musk@claude
, '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(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init - #14649

Open
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe
Open

fix(metadata): drop the leftover boot-time debug probe from MetadataPlugin.init#14649
os-musk wants to merge 2 commits into
mainfrom
claude/issue-14527-metadata-plugin-debug-probe

Conversation

@os-musk

Copy link
Copy Markdown
Collaborator

Fixes#14527

MetadataPlugin.init() carried a bare console.log immediately after
ctx.registerService('metadata', this.manager), printing
typeof this.manager.getRegisteredTypes. Nothing gated it on NODE_ENV, a debug flag
or a logger level, so every kernel boot that installs MetadataPlugin wrote it to
stdout — the CLI, the dev server and any embedding host alike.

What changed

One line deleted from packages/metadata/src/plugin.ts, plus a changeset. The whole
contribution is 2 files changed, 30 insertions(+), 1 deletion(-), and the single
source hunk is:

 // Register Metadata Manager as the primary metadata service provider.
ctx.registerService('metadata', this.manager);
- console.log('[MetadataPlugin] Registered metadata service, has getRegisteredTypes:', typeof this.manager.getRegisteredTypes);

Deleted rather than demoted to ctx.logger.debug

The triage allowed either. Deletion was chosen on two measurements:

  • Nothing reads it. A repo-wide search for the printed string (has getRegisteredTypes,
    and separately Registered metadata service) returns exactly one hit: the emitting line
    itself. No test, script, doc or workflow consumes it.
  • What it reported is statically known.getRegisteredTypes is a method the class
    declares, so the typeof it printed cannot vary in a way an operator could act on.
    Demoting would preserve a probe whose answer is fixed at compile time.

The ctx.logger.info('Initializing Metadata Manager', ...) call three lines above already
announces the same lifecycle step through the plugin's own logger, carrying the fields that
are actually actionable (root, watch, artifactSource).

Severity is bounded, per the triage: on the two paths where a stray stdout line would break
a parser — --json payloads and the os serve protocol channel — packages/cli already
reserves stdout by redirecting to stderr, so nothing downstream depended on this line being
present or absent.

Deliberately not touched

  • The file's five other console.* calls (:627:653 on this tree, the dev HMR path).
    Each carries // eslint-disable-next-line no-console, and the one that prints on every
    non-development boot carries a written rationale for doing so. That distinction is what
    makes this a one-line change and not a six-line one. The file's console.* count went
    6 -> 5 and its eslint-disable count stayed at 5.
  • eslint.config.mjs and the no-console question. No inline disable was added, none
    was removed, and the rule was not configured. That question, and the inert-suppression
    population behind it, is the separate card 134 eslint-disable comments across 45 source files are structurally inert — pnpm lint runs --no-inline-config #14529.

Why no gate moves in either direction here

Re-measured on this tree, with eslint's own resolved config rather than a text search:

$ eslint --print-config packages/metadata/src/plugin.ts
resolved rules for this file: 6
no-console entry: null
any rule name containing "console": []
$ grep -n '"lint"' package.json
32: "lint": "node --stack-size=4000 node_modules/eslint/bin/eslint.js . --no-inline-config"

no-console is not configured, and the repo's only lint invocation passes
--no-inline-config, so the sibling disable comments are inert. Repo-wide pnpm lint is
green (exit 0, no output, 118s) while the file still contains five console.* calls — which
is the positive control that the rule really is off. This change therefore turns no gate
green and no gate red.

Measurements

Everything below was run at head 9d4221244, after merging origin/main.

The shipped artefact moved. No assertion exists to ablate, so no ablation is owed; the
proof is the built output instead. grep -rn 'has getRegisteredTypes' dist/ in
packages/metadata:

beforeafter
probe string in dist/8 hits (index.js, index.cjs, node.js, node.cjs + 4 sourcemaps)0
control: sibling HMR print8 files8 files
control: the logger.info above it8 files8 files

Both controls still hold, so the zero is a real absence rather than a broken grep or an
unbuilt dist/. The probe is likewise absent from the 677-test run log, in which the
sibling console.log still appears 17 times.

Tests.pnpm --filter @objectstack/metadata exec vitest run --maxWorkers=2
Test Files 43 passed (43), Tests 677 passed (677).

Type check.@objectstack/metadata has no typecheck script on this tree (PR #14627,
which adds it, has not landed), so the substitute was
pnpm --filter @objectstack/metadata exec tsc --noEmit -p tsconfig.json. It reports 89
errors — all 89 in *.test.ts files, zero in plugin.ts and zero in any non-test file. A
reverse check confirms they are pre-existing rather than mine: restoring the pre-change
plugin.ts from the merge base and re-running gives the same 89, also with zero in
plugin.ts. The restore was verified by blob hash (8e4d1e3c... on disk equals the HEAD
blob) with an empty git diff HEAD. --listFiles confirms plugin.ts is in the program
that was checked.

Gates. The family union was re-derived from the actual change set on this tree
(node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, 2 paths vs merge
base ca48cf377) and gives 30 families — identical before and after the merge. 27 are
green. Three answer exit 3, which each script's own verdict text defines as NOT MEASURED
rather than a failure, and none for a reason this change can influence:

  • check-test-completeness.mjs — needs a saved turbo run test log as an argument; the
    derived family names it with none, which the script documents as the unreachable branch
    ("record this gate as NOT MEASURED; it is not a red").
  • scripts/pm/check-half-states.mjs — needs repo-scoped REST egress, which this container
    refuses (a direct REST probe answers 403). It sweeps the PM board, not this diff. Its
    --self-test wrapper, pnpm check:pm-half-states, is green at 2062 cases.
  • check:dual-build-cjs-loadsPREREQUISITE NOT MET: it reads built output and 57
    packages have no dist/ in this worktree. Its --self-test is green at 93 cases, and the
    property it asserts was checked directly for the package this PR touches: all four
    published require entries of @objectstack/metadata (index.cjs, node.cjs,
    errors.cjs, migrations/index.cjs) load, with MetadataPlugin exported as a function.

Always-runs: pnpm lint green (full repo, not narrowed), pnpm check:nul-bytes green
(7991 files), pnpm check:error-status-conformance green.

Changeset.@objectstack/metadata: patch. The file is in the published entry closure,
measured three ways: src/index.ts:14 re-exports MetadataPlugin from ./plugin.js; the
manifest ships "files": ["dist"] with exports["."] resolving to ./dist/index.js and
publishConfig.access: public; and pnpm check:published-files counts this package among
the 69 publishable members with a files whitelist and an exports map. Most directly, the
probe string was physically present in the shipped bundles before this change. One fewer
line on boot is observable, so skip-changeset would have been wrong.

This PR touches no package.json, so Validate Package Dependencies should not run on it.
That job is red on main for four lockfile advisories tracked in #14639, and no lockfile
change is carried here.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…Plugin.init` (#14527)
`init()` printed a bare `console.log` immediately after
`ctx.registerService('metadata', this.manager)`, reporting
`typeof this.manager.getRegisteredTypes`. Nothing gated it, so every kernel
boot that installs `MetadataPlugin` wrote it to stdout.
It reported a statically known fact and a repo-wide search found no reader of
the string, so it is deleted rather than demoted to `ctx.logger.debug`. The
`ctx.logger.info('Initializing Metadata Manager', ...)` call three lines above
already announces the step through the plugin's own logger.
The file's five other `console.*` calls (the dev HMR path) are untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/client-sdk.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/api/index.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/automation/flows.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/kernel/services-checklist.mdx(via MetadataPlugin (symbol, a top-level class))
  • content/docs/protocol/kernel/metadata-service.mdx(via MetadataPlugin (symbol, a top-level class))

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

  • content/docs/releases/implementation-status.mdx(via MetadataPlugin (symbol, a top-level class))

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
  • 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 — 12 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 20b883918aa787e65299e72b85c2b3396aa2006fpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 — the merge of head 9d422124467501b60af0f1885d7a78b5c77d5bb9 into base 20b883918aa787e65299e72b85c2b3396aa2006f, 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 8bd18a6f4e2db0764aa87b42e38e373abcd5f266 && git checkout 8bd18a6f4e2db0764aa87b42e38e373abcd5f266
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 20b883918aa787e65299e72b85c2b3396aa2006f 9d422124467501b60af0f1885d7a78b5c77d5bb9 && git checkout -B drift-repro 20b883918aa787e65299e72b85c2b3396aa2006f && git merge --no-ff 9d422124467501b60af0f1885d7a78b5c77d5bb9
node scripts/docs-audit/affected-docs.mjs --json 20b883918aa787e65299e72b85c2b3396aa2006f

⚠️ 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 20b883918aa787e65299e72b85c2b3396aa2006f → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 17:50Z and armed auto-merge (squash) at 17:51:32Z on head 9d4221244. The echo read method: MERGE; the queue build ref is the arming reading, not the echo.

  • Review: ACCEPT on the card. Clause-② no, self-read from the final diff — one deleted console.log, nothing published changes shape.
  • Governed-surface test on the two changed paths: 0 of 2 governed — ordinary queue landing.
  • Enqueue bar: all 33 check runs on 9d4221244 completed success or skipped, read with perPage: 50.

The red on this head was diagnosed, not waited out.Test Core (1/6) failed at 17:15:22Z on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts:317 (AssertionError: expected 'SIGKILL' to be null). Established as not this PR's before the one permitted re-run was spent:

The re-run on the identical head, with no push and no rebase, passed at 17:45:27Z. Red then green on a byte-identical tree is the controlled A/B, and it is now recorded on the queue-flake anchor #14648 (comment 5513952826) together with the shape of a real fix. That anchor is domain:cli; this lane contributed the measurement and ⛔ did not claim it, relabel it, or weaken the test.

⚠️ Known exposure, stated rather than discovered later: the queue build runs the full suite, so this PR meets that same packages/cli test again on its queue build. #14629 was ejected on it at 17:45Z. If this PR is ejected the same way, the disposition is the same — ⛔ no second re-run, ⛔ no re-queue while #14648 is open and unfixed, and the failure goes on the record rather than into another full-queue round.

Owed by the seat at MERGED: verify by content on origin/main that the probe line is gone from packages/metadata/src/plugin.ts and that the five sibling console.* calls remain, strip pm:dispatched from #14527, landing record on the card, inventory reconciliation.


Generated by Claude Code

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

Copy link
Copy Markdown
Contributor

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

队列构建 33671786568 红了。队列跑的是全量套件(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 无队列失败记录(首次)。
  • 过去 24h 队列共有 15 个失败构建(不含本次)。

分诊清单:

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

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

@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Ejected from the merge queue at 19:35:44Z — not this PR's failure, and deliberately NOT re-queued

Engine execution seat (session session_0112hMx9hjJ9BgB28X97DS68), reading queue build 33671786568.

What failed:Test Core (1/6) on packages/cli/test/run-dev-unbuilt-workspace.e2e.test.tsAssertionError: expected 'SIGKILL' to be null. The same signature that ejected #14629 at 17:45Z.

Verified by content that nothing landed:git grep -c "has getRegisteredTypes" origin/main -- packages/metadata/src/plugin.ts still returns 1, against a control of 14 console occurrences in the same file. The probe line this PR deletes is still on main, so this was an ejection and not a quiet landing.

Why it is not this PR's. The diff is two files in packages/metadata and cannot reach packages/cligit grep -c "has getRegisteredTypes" origin/main -- packages/cli = 0. Removing stdout output makes an unread-pipe scenario strictly less likely to block, so the change points away from the failure.

Why it is not a regression at all. The assertion is a wall-clock claim, not a behavioural one: UNREAD_HARD_CAP_MS = 40_000 at :153 and setTimeout(() => child.kill('SIGKILL'), …) at :186 mean signal === 'SIGKILL' reports only that the harness's own 40-second cap beat the child's exit. The decisive evidence is on this very PR: the same test, same assertion, failed on the PR side at 17:15:22Z and passed on a re-run of the identical head — no push, no rebase — at 17:45:27Z. A test red and green on a byte-identical tree is not measuring the tree.

⭐ The class has escalated since that first stand-down. The triage workflow's own count is now 8 independent PRs in 24 hours (#14499, #14593, #14617, #14629, #14631, #14649, #14665, #14675) across 15 failed queue builds, and it states that figure as a lower bound. Anchor: #14648 (domain:cli, priority:p1, pm:dispatched, assigned) — the card with an owner — plus the auto-filed duplicate #14706 which carries the current count. This lane's full diagnosis and the shape of a real fix are on both (5513952826 and 5515382957): the only option that closes the class is to assert the child's own exit reason instead of the absence of a signal, taking the wall clock out of the oracle.

Why this PR is not being re-queued. The triage workflow's checklist, item 2: once an aggregation issue exists, re-queuing before the fix only burns another full-queue round for every PR behind it — and with 15 failed builds in 24h that cost is now measurably superlinear. ⛔ No re-queue while the anchor is open and unfixed. ⛔ The test is not being skipped, quarantined, relaxed or re-baselined by this lane, and no second re-run has been spent — weakening a gate stays a human act and none of it is ours to do.

State: PR stays open, ready, mergeable_state: clean; auto-merge not re-armed. Card #14527 moved to pm:blocked with body lines Blocked-by: #14648 and Unlock-action: re-check PR #14649 — that second line is what stops the unlock scan re-dispatching finished, reviewed, green work when the anchor closes.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

2 participants

@os-musk@claude