Skip to content

test(verify, rest, create-objectstack): test budget slice 5, the warm-up docblock says what it shares and two rows run under 10 s - #24249

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-23181-s5-warmup-doc-rest-create
Oct 18, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-23181-s5-warmup-doc-rest-create

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #23181
Clause-②: no

Slice 5 of the test-budget card (claim 6244488338): the warm-up docblock correction carried by the slice 4 release (6221486208), the audit of slice 3's six warmed files, and the card's @objectstack/rest and create-objectstack rows, one file each. Test files and one test-layer helper only; nothing a package publishes moves, so skip-changeset rides this PR's create.

Rows this PR closes

row (card table) card, CI (run) newest refresh dry run 38897658479, CI isolated base median isolated head median runs projected CI on the card's ratio / the newest window's ratio claim
create-objectstack · src/scaffold-e2e-boot-probe.test.ts 17.3 s (38281387358) 15.72 s (38858901527) 15345 ms 6311 ms (−59%) 3 interleaved rounds 7.1 s / 6.5 s closed
@objectstack/rest · src/import-template-route.test.ts (local) 12.7 s (38319885190) 16.81 s (38877591164) 2572 ms 1545 ms (−40%) 4 interleaved rounds 7.6 s / 10.1 s claimed on the card's ratio; on the newest window's ratio it sits at the line

The projection is the head median times the file's own CI ÷ isolated-base ratio. It is an estimate: the next landed refresh's list is the pin, and it decides both rows. For the rest row the ratio model is conservative in one known direction (part of what the head removes is first-load cost, which a cold, contended CI shard pays at a higher multiple than this box), and it is not a measurement of that direction.

How each reading was taken. Clocked = the vitest default-reporter file line (hooks plus tests, collection excluded), the number report-test-timings.mjs captures in CI; AI_AGENT and CLAUDECODE unset so vitest picks that reporter. Isolated = one vitest run --maxWorkers=2 per file. Base = an untracked copy of the file beside it, blob-identical to the branch point af284b05e (a5a23b161980, 6b3163931e5a), alternated with the head file round by round, every run through os-verify-lock on a 4-vCPU box shared with other agents. All copies were moved out before every commit.

  • rest, base / head per round: r3 (head first) 2574 / 1593 · r4 (base first) 2570 / 1585 · r5 (head first) 2570 / 1454 · r6 (base first) 2713 / 1504. r6 is at the final bytes of the file; r3 to r5 ran on bytes that differ from them in one comment only.
  • create-objectstack, base / head per round: ab2 (head first) 15345 / 6357 · ab3 (base first) 17296 / 6284 · ab4 (head first) 15329 / 6311. Ten more head runs at 451c13c48 (the file's final bytes): 14/14 passed in each, 6296 to 6360 ms.

create-objectstack: the cases' own waits, summed

Measured per case (base, isolated): every case's wall is the waits of its own child processes, not this worker's work. The two os start blocks each cost one workflow-loop sleep 2 (about 2.1 s) in the accept and fail-fast cases; the docker block's dying-container case 4.3 s (two loop sleeps on this box); the neighbour harness's "stayed alive" control its 2 s backstop; the other cases 3 to 230 ms. Run one after another, those waits summed to 15.3 s.

The waits are the subject (the workflow's own run: blocks, executed), so nothing waited on is shortened, stubbed or skipped. What changed is that they now overlap:

  • runBlock awaits its bash -e child (spawn, stdio ignore / pipe / pipe, the same 240 s timeout) instead of blocking the worker in execFileSync. Its answer is the synchronous one's: the exit status (-1 when a signal ended it, the timeout's included), stdout alone on success, stdout then stderr on failure.
  • Both top-level suites pass vitest's concurrent: true. The cases share nothing: each has its own kernel-assigned port (held, never probed), harness directory and stub state. The 6.3 s is consistent with vitest's default maxConcurrency (5) starting the 4.3 s docker case after the first wave of about 2 s cases.

One harness defect this exposed, fixed in the same file. At 758aa979e (concurrency, no other change) the run failed 2 of 14: the occupier control and the errno control read "it exited before announcing a listener … (nothing: the child wrote neither stdout nor stderr)" where the child had written EADDRINUSE. The neighbour harness diagnosed a death on the child's exit event, and Node does not order exit after this side has read the child's pipes, and with the cases running together the order flipped. The death is now diagnosed on close (both pipes drained); the liveness flag the probe loop reads still comes from exit. The same ordering is possible in the sequential file, only rarer.

@objectstack/rest: first loads, and a reader materializing what no case reads

Two mechanisms, measured before either change:

  1. First loads in the first cases' clocks. The first case read 359 to 431 ms against 36 to 114 ms for the same work later in the file (the route imports exceljs lazily in xlsx-module.ts, and the SQL driver, the template writer in both locales, the import door and the export writers each run cold once). warmUpFirstLoads() walks each path once at module scope on a throwaway engine and destroys it before the first case; every case still boots an engine of its own, and the warm-up shares no engine, row or spy with any case. Alone, it read 2090 and 2466 ms in 2 rounds whose base copies read 2837 and 2691 ms.
  2. The test's reader expands every dropdown into 50 000 entries. exceljs's reader turns each range-keyed dataValidation element into one entry per covered cell, and the template covers TEMPLATE_VALIDATED_ROWS (50 000) rows per dropdown column. A probe on the deal template (10 parses each, after warming): 100 000 entries, 39.4 ms a parse in full, 4.4 ms with the node skipped through exceljs's own ignoreNodes read option, every sheet's values identical; on a one-column template 22.6 ms against 3.6 ms. No case in this file asserts on a dropdown, and import-template.test.ts pins the dropdowns, read in full. So this file's reads go through readWorkbook (the node skipped); the test loader xlsx-test-loader.ts gains an optional options argument handed straight to Xlsx.load, and its other six callers are unchanged. The one case that writes a workbook back for the import door ("is not starred …") still reads it in full, so the file it imports carries its dropdowns.

The parity case's budget (#21481, PARITY_MS_PER_SHAPE) is untouched; its docblock gains one sentence that the parse no longer reads the dropdowns.

The warm-up docblock (packages/verify/src/boot-warm-up.testkit.ts)

"⛔ It shares nothing a case asserts on" is replaced by what a warm-up DOES leave to the file's own boots, why that is safe for a case that asserts on none of it, and which files therefore take no warm-up. What it leaves, from source and from a probe (an untracked file snapshotting the process around one warmUpBoot()):

  • harnessCryptoProvider() in harness.ts builds the process's crypto provider on the first boot and keeps it (slice 4's ablation K is the measured consequence for harness.key-custody);
  • NODE_ENV: bootStack sets development and never restores it; the probe's whole environment diff is NODE_ENV: test -> development;
  • the loaded modules' process-level state: the module registry, sql.js's WASM, the JIT, better-auth's process-global context holder (the probe's one globalThis addition, Symbol(better-auth:global)), and each module's memos and once-per-process notices (from source: an environment variable memoized on first read in metadata-protocol, warn-once flags in runtime and metadata-protocol).
  • Released, not shared: the configuration claim (LIVE_CONFIGURATIONS / LIVE_APP_PLUGINS, released by stop()) and bootStackOnce's map (a warm-up never calls it). Process listeners: unchanged.
  • "Under vitest's per-file isolation, the file's": two probe files in one run, --maxWorkers=1, read two pids, and a globalThis symbol one set was absent in the other.

The six warmed files, read case by case for harness process state

file finding
action-flow-elevated-door.test.ts none. Every case asserts the door's answer, the ledger rows and the run-log count on the file's own stack. The module code after the warm-up builds the fixture through defineStack / ObjectSchema.create, which read no NODE_ENV. No secret field, no provider, no notice is read.
automation-trigger-elevated-door.test.ts none, the same shape at the trigger door.
delete-behavior.test.ts none. Its two pure deriveDeleteBehaviorCases cases run before the file's first boot, so under development instead of test after the warm-up; the derivation reads the spec's FieldSchema and the package's collection reader, neither of which reads NODE_ENV. The proof cases read their own stacks' reports.
handle.test.ts none. The tenancy cases read a posture bootStack requests and restores per boot (the warm-up boots single and sets nothing). The bootStackOnce identity case keys on handleFixtureStack and BOOT_OPTIONS; the warm-up boots its own configuration through bootStack, so that map holds nothing of it.
harness.one-composition.test.ts none. The instance-rule cases key on configuration identity, and the warm-up's configuration is its own and released on stop() (slice 4's ablation O failed the same two cases with and without the warm-up).
harness.served-composition.test.ts none. The cases read the plugins, the cube registry and the sys_email rows of each case's own stack; the SMTP case deletes and restores its environment variable around its own boot.

No edit to any of the six was needed.

Proof the changed files still fail when their subject breaks

Each mutation went through scripts/ablation-replace.mjs in wrap mode (anchor hit x1 → x0, blob moved), inside a script with an EXIT INT TERM trap; predictions were written before the runs; each restore was proven blob == HEAD and git diff HEAD empty. No dist/ sits between these mutations and the runs: both suites read the mutated file from src.

  • R: import-template.ts, the header loses its required * (header: required ? … : label, → header: label,). Predicted 5 red, the same five on head and base. Observed head 5 failed / 32 passed, base copy 5 failed / 32 passed, the same five cases ("keeps exactly the writable declared fields", "?fields= is honoured", "create on the object and no allowExport", "is not starred …", the parity case). The lean reader sees exactly what the full one sees.
  • C: runBlock answers status 0 for every block. Predicted 5 red. Observed 5 failed / 9 passed: both "refuses a neighbour", both "fails fast", and the docker "stops polling".

Unchanged

  • Every it / describe title line hashes the same at af284b05e and the head (rest 38 lines, 1765d3f3f8608dd2; create-objectstack 15 lines, cb0a2987aa6e987a once the added options argument on the two suites is taken out), and expect( counts are equal (104 = 104, 39 = 39).
  • No timeout, budget, project, roster entry or test moved; no test deleted.

Tests and gates

  • At dafeb41ad (this branch merged with origin/main cbfa5e433, after which only 257fe77d8, a comment in boot-warm-up.testkit.ts, landed): create-objectstack 18 files / 278 tests passed and tsc --noEmit exit 0; @objectstack/rest (--project local) 294 files / 6030 passed, 326 skipped, and typecheck exit 0 (check:test-typecheck OK, 0 errors); @objectstack/verify 41 files / 362 tests passed and typecheck exit 0. Each changed file is in its package's type-check program (--listFilesOnly: 1 hit each).
  • At 257fe77d8: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 51 commands for the 4 paths (merge base cbfa5e433); all 51 exited 0 (check:dual-build-cjs-loads after a full turbo run build, which it refuses to measure without); --ran: "51 derived, 51 run, 0 NOT-MEASURED, 0 UNRUN."
  • pnpm exec eslint --no-inline-config over the 4 changed files: exit 0. Control-byte scan over them: no hit. The repo-wide pnpm lint, test:repo (no rest repo-project test imports the loader) and every family CI runs are read from this PR's own check runs.

Changeset

skip-changeset: @objectstack/rest, @objectstack/verify and create-objectstack each publish dist, README.md and CHANGELOG.md. After building all three, the strings this change introduces (readWorkbook, warmUpFirstLoads, CASES_RUN_TOGETHER, ignoreNodes, loadXlsxWorkbook, xlsx-test-loader, warmUpBoot, boot-warm-up, What it shares) hit none of those paths (grep exit 1); the controls createRestApiPlugin, bootStackOnce and detectPackageManager hit each package's dist.

Acceptance notes

  • A file outside the claim's surface: packages/rest/src/xlsx-test-loader.ts, the package's test-layer exceljs loader (not in any tsup entry), gains the optional read-options argument. Named for the seat to declare.
  • Observation, not filed: the production import door (parseXlsxToRows) pays the same dropdown expansion on every uploaded template, about 35 ms a parse on this box for the deal template's two dropdown columns. A cost, not a defect class; no carrier.
  • Observation, not filed: after a warm-up's stop() one TCPServerWrap resource remains (still one after a second warm-up, and no net.Server among the active handles), consistent with the last boot's listener still closing rather than a per-boot leak. Not characterized further.
  • The docker case's 4.3 s here is two loop sleeps: on this box the dying container is read as running for one extra probe. It does not change the head's verdict, and it bounds the file's concurrent wall.

Generated by Claude Code

…wn boots

The docblock said a warm-up "shares nothing a case asserts on". An ablation
of the harness crypto provider falsified that: the process's provider is
built on the process's first boot, so a warm-up builds it before a file's own
posture exists. The paragraph now names what a warm-up leaves to the file's
boots (the data key, NODE_ENV=development, the loaded modules' process-level
state), why that is safe for a case that asserts on none of it, and which
files therefore take no warm-up.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
…ng collection

Every first use in this file (the lazily imported exceljs, the SQL driver,
the template writer in both locales, the workbook reader and writer, both
import formats, the three export formats) ran inside whichever case reached
it first. One throwaway engine now walks each path at module scope and is
destroyed before the first case, so the clocked cases measure behaviour,
never loading. No case, assertion, budget or timeout changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
Each case's wall is its own block's waits in child processes (the
workflow loop's sleep 2, the stub's boot delay, a backstop), and the cases
share nothing: each has its own kernel-assigned port, harness directory and
stub state. runBlock now awaits its bash child instead of blocking the worker
in execFileSync, with the same answer (exit status, -1 on a signal, stdout on
success, stdout then stderr on failure), and both suites run their cases
concurrently, so the waits overlap instead of summing. Nothing waited on is
shortened; no assertion or timeout changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
…'close'

Run concurrently, the harness read a child's death on 'exit' before it had
read the child's stdout, so a refused bind was reported as "it exited before
announcing a listener" with no output, in place of the EADDRINUSE it wrote.
Node does not order 'exit' after the pipes are drained; 'close' does. The
liveness flag the probe loop reads still comes from 'exit'.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
…heir dropdowns

exceljs's reader expands each range-keyed dataValidation into one entry per
covered cell, and the template covers 50 000 rows per dropdown column:
measured on the deal template, 100 000 entries and 39 ms a parse in full,
4 ms with the node skipped, every sheet's values identical. No case in this
file asserts on a dropdown (import-template.test.ts pins them, read in full),
so its reads skip the node through exceljs's own ignoreNodes option, which
the test loader now hands through. The one case that writes a workbook back
for the import door still reads it in full. No assertion, budget or timeout
changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
…thing a case must not change

The safety paragraph claimed none of the shared state changes what a boot
does; a module's memo of an environment read does, if a case changes that
variable after the warm-up. The paragraph and the no-warm-up list now say so,
and the NODE_ENV clause names the moment it holds for (before the kernel is
built).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 2 changed package(s); no hand-written page names any of them. ⚠️ 1 changed file(s) yielded no anchor (packages/verify/src/boot-warm-up.testkit.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/verify/src/boot-warm-up.testkit.ts) — pages documenting those are invisible to this run
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 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; 97 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 — 22 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 cbfa5e43354f430e35c4f9f24732e59848174889 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json cbfa5e43354f430e35c4f9f24732e59848174889

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 — ACCEPT · PR #24249 at 257fe77d8 (#23181 slice 5)

domain:cli seat 2 · marchtian · session session_01QyoWKK9RwUNxBxmevy9Neb · 2026-10-18T03:44Z · holder of claim 6244488338 (revised in place 2026-10-18T03:43Z for the test loader). The dev's report is comment 6245289425 on #23181. The seat reviewed against this PR, not the report's own account.

  1. Shape. Draft → main. The first line is Part of #23181; there is no closing keyword. Clause-②: no is line-initial, and the assignee is marchtian. skip-changeset was applied in the pr_create stroke. 4 files, +156 / −42: three test files, plus the test-layer loader the rest file waits on, now named in the revised claim surface. No non-test source and no dist byte moves: the dev built all three packages and grepped dist, README.md and CHANGELOG.md for every new string, with positive controls that do hit dist.

  2. The card's rules, read on the diff.

    • "Under 10 s of wall" is reached by fixing the test or what it waits on.
    • ⛔ No timeout, budget, project, roster entry or test moved. The it / describe title lines hash equal at base and head, and the expect( counts are equal (104 and 39).
    • Nothing deleted.
    • "Each PR says which rows it closes": the body names create-objectstack closed, and rest claimed on the card's ratio with the next landed refresh deciding it.
  3. The carried docblock (release 6221486208). "⛔ It shares nothing a case asserts on" is replaced by what a warm-up DOES leave to the file's own boots:

    • the harness's process crypto provider;
    • NODE_ENV=development, never restored;
    • the loaded modules' process state.

    It says why that is safe for a case that asserts on none of it, and when a file must take no warm-up (harness.key-custody is the measured case). The paragraph is read against slice 4's ablation K and matches it. The six-file audit finds no case asserting on that state, file by file.

  4. The two rows, A/B under the lock, medians.

    • scaffold-e2e-boot-probe.test.ts: 15345 → 6311 ms (3 interleaved rounds, plus 10 head runs at 6296–6360 ms). The wall was the blocks' own child-process waits run in series. runBlock now awaits a spawned child, the two suites run concurrently, and a race this exposed is fixed: a death is diagnosed on close, not exit, so the child's pipes are read first. The race was reproduced before the fix (2 of 14 failed) and held after it (13 of 13). H4 as the PM stated it was wrong: the file runs scaffold-e2e.yml's three run: blocks against stubs, with no scaffold, install or boot phase.
    • import-template-route.test.ts: 2572 → 1545 ms (4 interleaved rounds). First loads are paid at module scope, and this file's reads skip the dropdown node exceljs expands into 50 000 entries per column (ignoreNodes; values identical). The one write-back case still reads in full, and import-template.test.ts keeps pinning the dropdowns.
  5. Ablations, each with its prediction written first and the restore proven by blob:

    • R: the template's required marker removed. 5 red on head and 5 on a base copy, the same five, so the speed-up removed no assertion.
    • C: runBlock answering 0 for every block. 5 red.
  6. Gates. 51 derived, 51 run, all exit 0 (check:dual-build-cjs-loads after a full build). --ran reconciled with 0 NOT-MEASURED. Package suites: create-objectstack 278, rest local 294 files and 6030 passed, verify 362; typechecks exit 0. ESLint --no-inline-config exit 0.

  7. Accepted deviations:

    • the test loader outside the first surface (the claim is revised);
    • the ablations' transient mutation of import-template.ts, restored by blob;
    • untracked base copies during the A/B, never staged;
    • round ab1 excluded from the medians as the race reproduction;
    • the rest projection on two CI readings.
  8. Open question 1 (the rest row), decided by the seat: A. Wait for the next landed refresh (2026-10-19). The head removes 40 % isolated, and slice 6 takes the file only if that refresh still lists it. B (sharing one engine) would buy about 0.3 s at the cost of per-case isolation, and C is outside the card's rule.

  9. Acceptance notes (not blocking):

    • the suites now run concurrently, so CI's first runs on this head are the stability reading the seat watches;
    • the production import door pays the same dropdown expansion, about 35 ms a parse: an observation, not filed;
    • one TCPServerWrap remains after a warm-up's stop(), consistent with the listener still closing: not characterized, not filed.
  10. Landing. Ordinary queue landing once every check on 257fe77d8 is green or a roster skip. Part of closes nothing. The seat posts the landed note and releases the card to pm:queue for the rows the next refresh lists.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 18, 2026 04:06
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 18, 2026 04:06
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 18, 2026
Merged via the queue into main with commit 21b945c Oct 18, 2026
47 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23181-s5-warmup-doc-rest-create branch October 18, 2026 04:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants