Skip to content

feat(cli): the console static plugin takes a cloudflareWebAnalytics option that adds the beacon to the console document - #30961

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-5iqzi1
Nov 6, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-5iqzi1

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Elon · project thread

domain:server#5 · os-elon-musk · session_016WV4cxGr3fDvcDkCLUwZAo

Fixes #30936
Clause-②: yes (widening)
Metadata-protocol: none

维护者速读

  • 改了什么:控制台静态插件 createConsoleStaticPlugin 新增一个选项 cloudflareWebAnalytics: { token }。传了它,控制台页面就带上 Cloudflare Web Analytics 的统计脚本(带页面的 nonce),安全策略的 connect-src 多放行一个上报地址 https://cloudflareinsights.com。不传,页面和安全策略逐字节不变。
  • 为什么改:目前没人知道托管控制台有多少人打开、从哪里来。cloud#3590 要加免费、无 cookie 的 Cloudflare 访问统计,但控制台页面和它的安全策略归框架,cloud 只能等框架给一个选项。做成选项而不是环境变量,是为了 cloud 只在控制平面传它,租户平面永远不带。
  • 风险与回滚:默认关闭,不传选项的部署零变化(测试逐字节钉住)。script-src 没有放宽,统计脚本靠 nonce 加载。token 必须是 32 位十六进制,否则启动即拒绝,且报错不回显 token。回滚即 revert。
  • 先看:① packages/cli/src/utils/console.ts 的 readCloudflareWebAnalyticsToken 与 cloudflareWebAnalyticsBeaconScript(校验与转义);② 同文件 readIndexHtml 里写入脚本的位置(必须在打 nonce 之前);③ consoleContentSecurityPolicy 的 connect-src 一行。
  • 两分钟验证:pnpm --filter @objectstack/cli exec vitest run --project unit src/utils/console.content-security-policy.test.ts,预期 66 个测试全绿。
  • 需要你做的:无,席位落地。

What changes

createConsoleStaticPlugin(distPath, options?) takes one more option, cloudflareWebAnalytics?: { token: string }, also on the published @objectstack/cli/console subpath. All of it is in packages/cli/src/utils/console.ts:

  1. Refused at boot. start() reads the option once, beside readExtraConnectSources and readMapStyleOrigin. A token that is not exactly 32 hexadecimal digits (either case), or a missing or non-string token, throws Console static: the cloudflareWebAnalytics option's token is …, which is not a Cloudflare Web Analytics site token. …. The message gives the value's length and kind, never the value, so a credential passed there by mistake does not land in the boot log.
  2. The beacon tag. readIndexHtml writes, after the capability global and before stampScriptNonce, a deferred script element for https://static.cloudflareinsights.com/beacon.min.js whose data-cf-beacon attribute holds JSON.stringify({ token }), escaped with the file's existing escapeHtml (so the JSON's quotes are "). The nonce pass then stamps it like every other script in the document. The token is fixed at boot, so the comment's invariant (nothing read at request time is written before the stamp) still holds.
  3. connect-src. consoleContentSecurityPolicy takes cloudflareWebAnalytics: boolean and adds https://cloudflareinsights.com (CLOUDFLARE_WEB_ANALYTICS_ORIGIN) after the storage origin and before the operator's OS_CONSOLE_CSP_CONNECT_SRC sources. The policy jsdoc documents it the way TURNSTILE_ORIGIN is documented.
  4. script-src is unchanged. No host is added for the beacon (the card's open point; triage 6685354963). See Acceptance notes for what was and was not measured.

Without the option nothing is written: no tag, no origin, and the document and the header are byte-identical to main.

test/published-subpath-console.pin.test.ts pins the subpath's exact signatures, so its CreateConsoleStaticPluginKeepsItsRatifiedSignature fixture carries the widened options type.

Tests

New describe('the host\'s cloudflareWebAnalytics option') in packages/cli/src/utils/console.content-security-policy.test.ts, on the real plugin and a real HonoHttpServer, as the rest of that file. mount() gained a plugin field for the plugin's own options.

  • With the option: exactly one beacon tag, carrying the header's nonce, defer and the exact src; its data-cf-beacon, decoded as a browser decodes it, is { token }; five script tags, all nonced; connect-src is 'self', the default map style host and the report origin; script-src is exactly 'self', the nonce and 'wasm-unsafe-eval'; the header never names static.cloudflareinsights.com.
  • Beside OS_CONSOLE_CSP_CONNECT_SRC: the operator's source follows the report origin.
  • Byte-identity, three ways (no options, the other two options only, the key left undefined): the served document and the policy, nonce written as NONCE, equal literals read from main at f108e6d2 (captured with the unmodified module before the change; the literal was then checked equal to that capture).
  • Refusal: nine malformed tokens (empty, 31 and 33 hex digits, 32 non-hex characters, whitespace-padded, an attribute breakout, a JSON breakout, a number, null) and a missing token each reject the boot; the message never contains the token.
  • Breakout: the exact start tag, with the JSON's quotes as ", followed by its own end tag.

Acceptance notes

  • script-src and the beacon: NOT MEASURED from this container. The card asks whether the beacon loads anything further from static.cloudflareinsights.com. curl https://static.cloudflareinsights.com/beacon.min.js is refused by this environment's egress proxy (CONNECT tunnel failed, response 403), so the beacon's own code could not be read here. What holds without that reading: a nonce source admits the element that carries it, whatever its src and across redirects, so the beacon script itself loads under this policy. Whether it injects a further script element (which would carry no nonce and be refused) is the open point, and it stays with the seat's decision card, as triage 6685354963 and the claim direct. A browser load of a console served with the option, with the devtools console open for CSP reports, answers it in one step wherever that host is reachable.
  • Clause-②: yes (widening), against the claim's no. Re-derived from the diff, as the claim asks: the change widens the public surface of a published subpath, the options bag of createConsoleStaticPlugin, which published-subpath-console.pin.test.ts pins by shape. That is the widening the line asks about (「本卡放宽接受集或扩大公开面吗」); nothing existing is narrowed.
  • Changeset level patch. AGENTS.md Post-Task Checklist step 3: the level follows the metadata-protocol kind alone, and Metadata-protocol: none ships patch, widening included. src/console.ts's header and the pin's docblock say a widening of that subpath is a "minor-bumped act"; that text predates the level ruling and AGENTS.md wins where they disagree. Noted, not changed here.
  • Docs. No page under content/docs/ lists the console static plugin's options (rootRedirect, isDev and the plugin name have zero hits there), so the option is documented in the function's jsdoc and the changeset only.
  • Tag placement. The tag is written right after the head start tag, the same seam the base tag and the capability global use, so a shell without a head tag gets none of the three. defer keeps it from blocking the parse; its src is absolute, so its position before the base tag changes nothing.

Verification

All at cbef4f627b, the head this PR opens with (branched from main f108e6d2; origin/main has since moved 5 commits, none touching packages/cli/src/utils/console.ts or either test file here, so it is not merged in).

  • pnpm --filter @objectstack/cli exec vitest run --project unit src/utils/console.content-security-policy.test.ts: Tests 66 passed (66).
  • pnpm --filter @objectstack/cli typecheck: exit 0 (the lock's VERDICT command-exit 0; check:test-typecheck: OK).
  • pnpm --filter @objectstack/cli build, then vitest run --project unit test/published-subpath-console.pin.test.ts test/published-subpath-hook-body.pin.test.ts: Tests 29 passed (29); dist/utils/console.d.ts carries cloudflareWebAnalytics.
  • Whole unit project (vitest run --project unit --maxWorkers=2, before the CLI build): 387/389 files, 5469 tests passed; the two failures were the two published-subpath pins refusing on a missing packages/cli/dist ("packages/cli is not built"), both green after the build above. The integration layer is declared to CI.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 62 commands; all ran, plus pnpm lint, pnpm check:cli-command-ids and pnpm check:adr-symbol-anchors: 65 exit 0. check:dual-build-cjs-loads and check:i18n-coverage first exited 3 (PREREQUISITE NOT MET, no whole-tree dist) and exited 0 after turbo run build --concurrency=2 (lock VERDICT 0). dispatch-gates.mjs --ran: exit 0, "62 derived famil(ies) accounted for — 62 run, 0 NOT-MEASURED".
  • node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD: exit 0.
Ablations: each pin red once, then restored (scripts/ablation-replace.mjs; the CLI unit project imports src/, so no dist leg)

Each run: ablation-replace.mjs --file packages/cli/src/utils/console.ts --anchor … --replacement … -- vitest run --project unit src/utils/console.content-security-policy.test.ts; every mutation landed (anchor 1 → 0, blob 96f2c47898c3 → a new blob) and every restore read blob == HEAD (96f2c47898c3) and git diff HEAD empty.

  • A1, the option writes no tag (the head insertion replaced by void beaconTag;): Tests 2 failed | 64 passed.
  • A2, the report origin added without the option (the if (input.cloudflareWebAnalytics) guard dropped): Tests 16 failed | 50 passed (the three byte-identity cases and every exact connect-src assertion in the file).
  • A3, any token accepted (CLOUDFLARE_WEB_ANALYTICS_TOKEN replaced by a pattern that matches every string): Tests 7 failed | 59 passed (the seven string tokens; the number and null still refuse on type).
  • A4, the attribute unescaped (escapeHtml dropped): Tests 2 failed | 64 passed.
  • The pin's own control: published-subpath-console.pin.test.ts with the old options type restored reds TS2344: Type 'false' does not satisfy the constraint 'true' on the signature equality (Tests 1 failed | 13 passed), so the pin reads the rebuilt declaration. Restored: blob == HEAD.
65 commands, exit codes at cbef4f6
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/ci/snapshot-next.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:cli-test-child-env :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:i18n :: exit 0
pnpm check:i18n-walk-parity :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
pnpm lint :: exit 0
pnpm check:cli-command-ids :: exit 0
pnpm check:adr-symbol-anchors :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:i18n-coverage :: exit 0

Generated by Claude Code

…ption that adds the beacon to the console document

createConsoleStaticPlugin(distPath, { cloudflareWebAnalytics: { token } })
writes a deferred script tag for Cloudflare Web Analytics' beacon into the
document before the nonce pass, so it carries the document's nonce, with the
token as its escaped data-cf-beacon JSON, and adds https://cloudflareinsights.com
to connect-src. script-src is unchanged: the nonce admits the tag. A token that
is not 32 hexadecimal digits refuses the boot without echoing the value. Left
out, the document and the policy are byte-identical to before, pinned against
the output read at f108e6d. The published-subpath pin carries the widened
options type.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4cxGr3fDvcDkCLUwZAo
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 6, 2026
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

7 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 63 of 222 client-bound route-ledger rows — the other 159 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 159: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 104 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 35 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 6ddf827236902c0ccaee56b65431a4c8a9c7ac09 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from d6993f1a8df61e10f72ef1b934b8d8cd4098d523 — the merge of head cbef4f627b5af28c49e2e8f39844aec3f83068f4 into base 6ddf827236902c0ccaee56b65431a4c8a9c7ac09, 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 d6993f1a8df61e10f72ef1b934b8d8cd4098d523 && git checkout d6993f1a8df61e10f72ef1b934b8d8cd4098d523
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6ddf827236902c0ccaee56b65431a4c8a9c7ac09 cbef4f627b5af28c49e2e8f39844aec3f83068f4 && git checkout -B drift-repro 6ddf827236902c0ccaee56b65431a4c8a9c7ac09 && git merge --no-ff cbef4f627b5af28c49e2e8f39844aec3f83068f4

node scripts/docs-audit/affected-docs.mjs --json 6ddf827236902c0ccaee56b65431a4c8a9c7ac09

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#5 · os-elon-musk · session_016WV4cyvF7SP65frVFAYLv7

Contract review

Served-tier: TIER_DEFAULT
Head-sha: cbef4f627b5af28c49e2e8f39844aec3f83068f4
Local-runs: none

Owed by the Clause-②: yes (widening) line alone. The diff touches no published schema, no governed path, and no narrowing. Inputs: card #30936 (body, triage audit 6685354963, the seat's claims), the dev's os-dev report 6687148165, the PR body, and the net diff against the merge-base f108e6d2 (4 files, +236/-3).

① Derived judgments

  1. createConsoleStaticPlugin(distPath, options?) gains an optional cloudflareWebAnalytics?: { token: string } on the published @objectstack/cli/console subpath. This is additive: every existing call types and behaves as before. The subpath's signature pin (published-subpath-console.pin.test.ts) moves by exactly that key. Right.
  2. With the option, the served document gains one deferred script tag for https://static.cloudflareinsights.com/beacon.min.js, with data-cf-beacon set to the escaped { token } JSON. It is written after the head start tag before stampScriptNonce runs, so it carries the per-request nonce like the server's other tags. The policy adds https://cloudflareinsights.com to connect-src only, and script-src is unchanged (pinned: exact 'self', nonce, 'wasm-unsafe-eval', and the header never names static.cloudflareinsights.com). Right, and it is the card's Ask 1–3 exactly.
  3. Without the option, or with the key undefined, the document and the header are byte-identical to main (pinned against literals captured from the unmodified module at f108e6d2, three ways). Right.
  4. Boot refusal: a token outside /^[0-9a-f]{32}$/i, a missing token, null or a number throws at start. The message gives the value's length and kind, never the value. Right; it is the readExtraConnectSources precedent the card names.
  5. Breakout: the charset alone already excludes quotes and angle brackets, and the attribute is escaped as well (escapeHtml on the JSON). The pin asserts the exact start tag with &quot;, followed by its own end tag. Right.

② Semver level

One changeset, '@objectstack/cli': patch, with Clause-②: yes (widening) and Metadata-protocol: none line-initial in both the changeset and the PR body. patch is right: AGENTS.md Post-Task step 3 says the level follows the Metadata-protocol kind alone, and none ships patch, widening included. The dev notes that src/console.ts's header and the pin's docblock still call a widening of that subpath a "minor-bumped act". That prose predates the level ruling, and AGENTS.md wins. It is left unchanged here (see ③).

③ Boundary flags

  • Clause-② re-derived yes (widening) against the claim's provisional no. Accepted. The options bag of a published subpath is public surface, and nothing is narrowed.
  • script-src and the beacon's further loads: NOT MEASURED (this container's egress proxy refuses static.cloudflareinsights.com). The PR adds no host, which is what the card and triage asked for. A nonce admits the element that carries it, so the beacon itself loads. If the beacon injected a second script element, that element would carry no nonce and be refused. The cost is a console CSP report and a missed count, never a widened boundary. Accepted as declared. The first browser load on the cloud control plane is the measurement. A decision card is owed only if a CSP report names that host. The seat carries this on the card for the cloud follow-up (cloud#3590).
  • Docs: no content/docs page lists this plugin's options (0 hits for rootRedirect / isDev), so the jsdoc and the changeset carry it. Accepted.
  • Stale "minor-bumped act" prose in console.ts's header and the pin docblock: should-fix, carried, no card. Fix it on the next touch of either file. It does not mislead the gate, which reads Metadata-protocol.
  • Serial: no open PR touches packages/cli/src/utils/console.ts (read 13:22Z, and again at review).

No must-fix.

Implemented-by: claude/project-thread-5iqzi1
Reviewed-by: session_016WV4cyvF7SP65frVFAYLv7

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#5 · os-elon-musk · session_016WV4cyvF7SP65frVFAYLv7

席位核对 · ACCEPT

PR #30961 · head cbef4f62 · 2026-11-06T14:28Z · card #30936 (the platform half of cloud#3590; triage audit 6685354963) · claim re-posted with Branch claude/project-thread-5iqzi1 · os-dev report 6687148165 · review PASS 6687185923 (TIER_DEFAULT, cbef4f62)

维护者速读(席位校正)

The body's six lines stand as written, read against the diff.

席位核对

  • Card fit: Ask 1–3 met: the per-mount option, the nonced deferred beacon tag, https://cloudflareinsights.com in connect-src, and a boot refusal of a malformed token. Done-when met: with the option, the tag and the origin are present; without it, both are byte-identical; a malformed token refuses boot.
  • Open point, held as triage directed: script-src is not widened. The beacon's further loads were NOT MEASURED, because the container's proxy refuses the host. The measurement is the first browser load on the control plane. A decision card is owed only if a CSP report names static.cloudflareinsights.com.
  • Declarations: Fixes #30936. Clause-②: yes (widening) was re-derived from the claim's provisional no (the options bag of the published ./console subpath). Metadata-protocol: none. '@objectstack/cli': patch, which is right by the level rule; the review is owed and posted.
  • Carried, no card: the stale "minor-bumped act" prose in console.ts's header and the subpath pin docblock. Fix it on the next touch of either file.
  • Gates (report): 62 derived, all exit 0, 0 NOT-MEASURED. Lint, check:cli-command-ids and check:adr-symbol-anchors exit 0. CSP suite 66/66, and the published-subpath pins pass 29/29. Four ablations went red and were restored.
  • Landing: check-governed-merges --pr 30961 says NOT governed, +236/-3. The seat lands with pr_ready + automerge_enable once every check on cbef4f62 is green or expected-skip. After the merge, cloud#3590 can move its pin and pass the option on the control-plane mount only.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 6, 2026 14:48
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 6, 2026 14:48
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit 40ecdf9 Nov 6, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-5iqzi1 branch November 6, 2026 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

2 participants