Repository navigation
Description
Restart-when: npx tsx scripts/bench/runtime-publish-gate.bench.mts on a then-current main reports the real-shape N=420 flow-publish median materially below this band (⇒ some later change reached this door; correct the body and close against a number), OR objectstack cloud has real tenants whose Studio publish latency is a reported symptom
⛔ Ruled 2026-08-20: record it, do not fix it. Maintainer verbatim: 「C 只记录,不修(推荐)」
This card is a live perf record, not a work item. Its number was re-measured on 2026-08-20 at 482.78 ms and is inside the band below. ⛔ Do not scope a narrowing off this card without a new ruling — the measured reason is in "Why the obvious fixes do not reach this door".
Recording a measured fact that has lived only inside a card comment (#4716 comment 5309105113, measured on origin/main @ 66beee0f3 by a throwaway harness) so it stops being invisible to every perf listing. Filed unassigned during the 2026-08-18 adjudication of that card (maintainer directed the split package: 「简化开发」→「同意」).
The measurement
Today's shipped gate costs 33.4 ms at 21 objects → 506 ms at 420 objects per flow publish. The companion per-rule profile shows the bill concentrates in at-gate rules being handed the whole stack: validateStackExpressions ≈ 70% of the option-(a) bill, validateReferenceIntegrity ≈ 11% — both already on the runtime-publish surface today.
✅ Re-confirmed twice since, on two later refs and two different machines:
- 2026-08-19 (PR test(scripts): reconcile the two published runtime publish-gate measurements, and leave a re-runnable benchmark (#9851) #9853's reconciliation): 29.6–30.6 ms @21 and 461–485 ms @420 — within ~9%.
- 2026-08-20 (post-runtime gate: full runtime-safe rule snapshot for the publish door — the expensive half split out of the object-gating card #9612): 32.20 ms @21 and 482.78 ms @420 (min 441.98 / max 559.38), via the merged harness. Landing runtime gate: full runtime-safe rule snapshot for the publish door — the expensive half split out of the object-gating card #9612 did not move this door.
validateStackExpressions walks — not its object count. Count is a proxy whose constant is set by what the objects contain. At identical N a real-shape stack and a synthetic expression-free one are ~21–26× apart (measured 2026-08-20: real/stub 26.4× @21, 21.4× @420). Any number quoted from this family must name its stack shape; not doing so is what once made this card's figure look 16–25× wrong against another correct measurement of a different workload.
Why it may matter
Flow publish is a user-facing interactive action (Studio designer). Half a second of gate latency at 420 objects is already perceptible, and it grows with exactly the number that grows as a tenant succeeds.
validateRuleCompilability at 81.3%, a rule that appears in no profile on this card. ⛔ Every concentration figure here describes flow publishing only, and the object door remains unmeasured. The better-characterised half is the less important half.
⭐ Why the obvious fixes do not reach this door — measured 2026-08-20
Package-narrowing (#9612) requires two things, and a tenant-authored overlay stack has neither:
- A
packageIdon the write. REST derives it solely from?package=(packages/rest/src/rest-server.ts, where'all'/empty is commented as "env-local overlay"), so a Studio publish yieldsundefinedandresolveWritePackageScopereturns at its first guard. _packageIdprovenance on the stored objects.narrowObjectsToPackageClosure's limb 2 —if (typeof owner !== 'string' || owner === '' || owner === OVERLAY_PROVENANCE_SENTINEL) return true;— keeps any object with no provenance, and the real seeded corpus carries zero.
⇒ 📌 Fixing the missing packageId (#10063) would not, on its own, buy the saving. That is the counter-intuitive part and the reason this card stays open: without it, the plumbing fix reads like a latency win and is not one on an unpackaged corpus. Cross-noted on #10063.
The counterfactual proves the mechanism is sound rather than broken: on a provenance-stamped seed the shipped deriver narrows 421 objects to 45 (10.7%) and saves 89.5% (468.10 → 49.36 ms), with the differential verdict semantically unchanged and both positive controls live. The narrowing works; it just cannot reach this corpus.
⛔ Fences, carried and still binding
- The size-threshold option (skip the gate when N is large) is a deliberate fail-open at scale — the gate stops judging the largest tenants. Same shape as The #4630 unscoped multi-write refusals on sys_comment never fire through the wired engine — per-row dispatch bypasses resolveTargetRows on both verbs #9798 / probeInstallOrganizations answers a failed sys_organization read with a memoised empty list, so system-insert organization resolution fails open #9261 / ADR-0110 D3. ⛔ Not a dev's pick.
- Caching is refuted on measurement: runtime gate: full runtime-safe rule snapshot for the publish door — the expensive half split out of the object-gating card #9612 measured snapshot construction at 0.001–0.011 ms. 86% of the bill is rules running.
- The binding direction if a fix is ever scoped remains the maintainer's input-narrowing ruling (runtime gate: full runtime-safe rule snapshot for the publish door — the expensive half split out of the object-gating card #9612 comment
5339305885) — which, per the section above, does not reach this door as literally implemented.
The instrument
npx tsx scripts/bench/runtime-publish-gate.bench.mts # both shapes
npx tsx scripts/bench/runtime-publish-gate.bench.mts --mode package # the closure counterfactual
Re-runnable, states its own timed span, warmup policy and seeded stack shape, prints both shapes, and names the rules it dispatched — so a stale build shows up as a changed rule list rather than as a silently wrong number. It replaces the throwaway harness this card's original reading came from, which was the real problem all along: the number was right, and nobody could re-run it to find that out.
Activity
os-support-ai commentedon Aug 18, 2026
Triage — first-touch grading (triage seat, session
session_01KTguWpd7WNGYTGmSNY31RL): promoted topm:queue+pm:blocked,domain:metadata, type Task.Blocked-by: #4716
Rationale: real measured pull — flow publish is an interactive Studio action and 506 ms at 420 objects grows with exactly the number a successful tenant grows; the landing surface (what the publish door hands the at-gate rules,
validateStackExpressions/validateReferenceIntegrity) is the same gate file family as #9612, whose parent #4716 is in flight — same serialization applies, so this waits behind it rather than colliding. On unlock, re-verify the per-rule profile on the merged ref before scoping the fix (the #4716 work may already change what the gate is handed).Generated by Claude Code
os-warren commentedon Aug 19, 2026
Unlock scan (triage seat, 2026-08-19 ~09:1xZ round): the last declared blockers (#9851, #4716) are closed and the stated parking gate — PR #9920 — merged today at 08:51Z, so the parked condition is discharged →
pm:blockedremoved, back to queue. Engine seat: re-scope together with sibling #9612 on the post-#9920 ref (the maintainer's input-narrowing ruling of 08:08Z is the binding direction); stale-premise check on the measured 506 ms figure is expected — re-measure on the merged benchmark harness.Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
Closed
not_planned: a performance baseline with no product landing waiting on itTriage seat (objectstack-wide, seat post #6015) ·
session_01W89enF2dYV7K4N2Fbfj33f· 2026-09-27T16:14Z. ⛔ Not a claim. The measurements here (506 ms per flow publish at 420 objects, and the bench script) stay valid as the baseline.Provenance: who — the maintainer; verbatim — 「同意」, answering the triage seat's on-hold cleanup plan (which named this card and this disposition); where — the maintainer's chat with the triage session
session_01W89enF2dYV7K4N2Fbfj33f, 2026-09-27.scripts/bench/runtime-publish-gate.bench.mtsand finds the band moved. No landing depends on it, and no seat is scheduled to run it.pm:queuecardsnot_planned#19458) closes no-pull tooling and measurement cards. The maintainer's own words on the cheaper form of deferral (Stock cleanup under ruling #202 B (maintainer 「B(荐)A + 清理存量」, amended to close): the triage seat closes the 90 listed toolingpm:queuecardsnot_planned#19458): 「an open card is read by every sweep, a closed one costs nothing and stays searchable」.Reopen is free when a publish-time latency blocks a product landing or a customer-visible flow (name it), or when the bench is re-run and shows the cost grew.
docs(agents): a claim pre-check hit is a hint, not a claim (objectsta…