docs(workflows): five 65536 restatements re-worded to the measured caps, each naming its record - #18841
Conversation
…ps, each naming its record The five lines asserted "GitHub's 65536-character limit" as fact. The ISSUE-body half was falsified by bisection (objectstack#18793: 262,144 UTF-8 bytes stored, 262,145 refused); the COMMENT half is bisected here (objectstack#18826: the same boundary, on both createComment and updateComment, with a real 422 where the issue-body surface is silent). A github-script step cannot import ISSUE_BODY_LIMIT, so these five are restatements by construction — so each now NAMES the record it was read from, and the next falsification finds all five by grep. GitHub's own 422 text still claims "maximum is 65536 characters" and is false in both unit and value; every restatement says so, because that string is where the folklore comes back from. test-nightly-tiers.yml's BUDGET = 40000 is left at 40,000 and stated as what it is: a reading budget sized against the reader, not a platform bound inherited from the false cap. No behaviour changes: comments only. retry-exempt-status-codes is untouched on all four files that carry it. Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
CI note from the seat —
|
Fixes #18806
Clause-②: no
The defect
Five workflow files asserted 「GitHub's 65536-character limit」 as a fact, and none of them cited a measurement. PR #18796 falsified that number on the ISSUE-body surface by bisection (objectstack#18793: 262,144 UTF-8 bytes stored, 262,145 refused — a 262,145-byte body carrying only 222,145 characters was refused, so the unit is bytes), and removed the matching
ISSUE_BODY_LIMIT = 65536fromscripts/pm/check-half-states.mjs; these five lines did not follow. Two of them (test-nightly-tiers.yml:436,scheduled-full-run-card.yml:125) post ISSUE bodies against the falsified number, and one of those two SIZES a live bound from it (BUDGET = 40000). The other three named a COMMENT limit that nobody in this fleet had measured. This PR measures the comment surface, re-words all five to the measured unit and value on each surface, and makes every restatement name the record it was read from — because agithub-scriptstep cannot importISSUE_BODY_LIMIT, so these five are restatements by construction and the only defence against the next drift is thatgrepfinds them. Comments only: no behaviour changes, andretry-exempt-status-codes: 400,401,404,422is untouched on all four files that carry it.① The COMMENT surface, bisected — objectstack#18826
Probe issue objectstack#18826, opened for this measurement and closed
completedwith the record as its last comment. Bodies of chosen byte sizes written throughscripts/pm/post-stamped.mjs --comment=18826(writes 1–7, each read back byte-exact by the tool) and direct RESTcurl(writes 8–12, read back and compared here).Result: 262,144 bytes stored, 262,145 refused — the same value as the issue-body surface, and the same unit.
footer-appended, exit 0 (stored 258)identical, exit 0identical, exit 0identical, exit 0identical, exit 0Twelve write attempts, plus the probe issue and the record comment: 14 writes in total on the probe.
U+4E2D, 3 bytes each, plus ASCII padding). A character cap — or a UTF-16 code-unit cap, the same number here — would have taken it with 40,000 to spare. It was refused. Write 7 is the positive control: the same multi-byte shape at 262,144 bytes / 222,144 characters landed, so what refused write 6 is the one extra byte and not the content.issues.createComment(writes 4/5) andissues.updateComment(writes 9/10) are bracketed at the same value.issues.updatean oversized body is SILENT — 200, the old body kept, nothing reported ([probe] #18664 — bisecting GitHub's real issue-body refusal boundary, in BYTES #18793). On both comment endpoints and onissues.createit is a real HTTP 422 that writes nothing. Write 12 measures the issue-CREATE refusal; ⛔ its landing side was not taken, because that write would leave a 256 KiB issue on the board — so the create surface is bracketed from above only, and the refusal merely agrees with [probe] #18664 — bisecting GitHub's real issue-body refusal boundary, in BYTES #18793's bisected boundary.⛔ GitHub's own 422 text names 65,536 characters, and it is false
Captured verbatim from
POST /issues/{n}/commentsat 262,145 bytes:{"resource":"IssueComment","code":"unprocessable","field":"data", "message":"Body is too long (maximum is 65536 characters)"}PATCH /issues/comments/{id}andPOST /issuessay the same thing. Write 4 stored a 262,144-CHARACTER comment — four times the maximum that string claims. This is the most likely provenance of the folklore, it is still served today, and a reader who meets a 422 and re-derives the cap from its text re-derives the false number. Every one of the five restatements now says so by name; that sentence is the actual inoculation, more than the number is.② The five lines, before and after — each names the record it was read from
Line numbers are on
origin/mainad1f94e8ec(the branch point). After this PR,grep -rn 18793 .github/workflowsandgrep -rn 18826 .github/workflowseach find the files that restate the corresponding measurement;grep -rn 65536still finds all five, now attached to the sentence that says the number is false.test-nightly-tiers.yml:436issues.update:541 /issues.create:547)ISSUE_BODY_LIMIT; plus the silent-refusal shape (#18793) and the false 422 text (#18826)scheduled-full-run-card.yml:125issues.update:214 /issues.create:219)issues.updatethis never arrives as a 422 at all, whileissues.createdoes answer 422 (#18826)cross-repo-issue-closer.yml:166createComment:282 / :477 / :488) and issue body (issues.update:492)merge-queue-triage.yml:178createComment:879) and issue body (issues.update:733,issues.create:771)create422 (#18826),updatesilent (#18793)docs-drift-check.yml:557createComment:665 /updateComment:670)⛔ No retry-exemption code changed.
retry-exempt-status-codes: 400,401,404,422is byte-identical in all four files that carry it; a 422 from a malformed request is still answered correctly on the first try. What moved is the example the rationale gives for 「this repo's own bug」 — and on the two issue-body files, the additional fact that an oversized body is not the 422 the sentence implies.③
BUDGET = 40000— the fold decision, taken and statedKept at 40,000, and the comment above it now says what it is sized against. It is a READING budget, not a platform bound: 40,000 is about as much log tail as a person reads off a card before clicking through, and the run link in the same body carries every byte elided here. A cap that was mis-measured is a correction to a reading, never a licence to print more — the same posture
check-half-states.mjstook when it leftMARKDOWN_BODY_BUDGETat 60,000 bytes whileISSUE_BODY_LIMITmoved four times up. The comment also names the budget's unit (JS string length, i.e. UTF-16 code units — what theslicebelow it actually counts) and says why that is safe here: nothing judges it against the byte cap, and even read as bytes of CJK prose 40,000 stays far under 262,144. ⛔ It is no longer inheriting the false 65,536 silently.④ The run-history search — report only, zero state writes
The cheapest search the triage named. Windows are stated because they are what was read, not what exists.
test-nightly-tiers.ymlRun this shard's nightly tiers; the card-filing jobFile or refresh the red cardconcluded success in all sixscheduled-full-run-card.ymlcross-repo-issue-closer.ymlSyntaxError: Identifier 'octokit' has already been declared; 1Set up jobmerge-queue-triage.ymlSet up job; 1HttpError: We couldn't respond to your request in time(a platform timeout)docs-drift-check.ymlSelf-test the change → docs mapper; the rest are startup failures with no jobInstrument note: the raw job logs are not reachable from this container — the log endpoint 302s to
*.blob.core.windows.net, which the agent proxy denies at CONNECT (403; the proxy's ownrecentRelayFailuresshows another seat hitting the same host today). So failure causes were read from check-run annotations instead, which carry thegithub-scriptstep's thrown error verbatim. That is why the docs-drift window is the 100 most recent failures rather than all 2,017.The escalation condition, answered
⛔ Neither leg fires; this stays p2 on these readings.
test-nightly-tiers.yml'sBUDGET = 40000— 6.5× under it, and that one is on the issue-body surface anyway.check-half-states.mjs'sMARKDOWN_BODY_BUDGET = 60000bytes is likewise 4.4× under. Nothing in the fleet budgets anywhere near the cap on either surface.⭐ One reading that is worth the seat's eye even though it does not trip the condition: the measured cap is four times what four of these files assumed, so the fleet has been budgeting against a number that was too strict, never too loose. That is the harmless direction, and it is why 「nothing breaks today」 in the card is still true after measurement.
The YAML parse readings — the loud failure for a workflow edit
A YAML edit that breaks a workflow is the failure mode that costs the most, so both layers were parsed after the edit, from the worktree at the commit under review:
yaml@2.9.0from the workspace,YAML.parseon each file, job names read back off the parsed document so the parse is not merely "did not throw".And because four of the five edits sit inside a
github-scriptscript: |block, the JS in every one of those blocks was compiled too (new AsyncFunction(...)over the extracted source — github-script's own wrapper shape):skip-changeset, measuredNothing published moves. Read mechanically rather than recalled: 70
package.jsonfiles in the tree declare afiles[]array, and zero of their entries name.githuborworkflow(positive control: the reader really does see entries —packages/specreturnsdist,json-schema,liveness,prompts,llms.txt,README.md,src/**/*.zod.ts,CHANGELOG.md,api-surface,spec-changes.json)..github/workflows/**is CI configuration and is not shipped by any package.Gates — derived, not hand-fed
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackfrom the worktree (no path list passed; the script took the change set from the merge base itself: 5 committed paths, 0 working-tree, 0 untracked). 41 commands derived, 41 run, every one exit 0, each captured redirect-then-$?.node scripts/check-aggregator-roster.mjs(+--self-test)node scripts/check-closing-keyword-parity.mjs(+--self-test)node scripts/check-comment-mask-corpus.mjsnode scripts/check-cross-repo-closer-outcome.mjs(+--self-test)node scripts/check-merge-queue-triage-outcome.mjs(+--self-test)node scripts/check-position-name-fold-loaders.mjs(+--self-test)node scripts/check-self-test-wired.mjs(+--self-test)node scripts/check-self-test-workflow-commands.mjs(+--self-test)node scripts/check-step-collectors.mjs(+--self-test)node scripts/check-whole-set-label-write.mjs(+--self-test)node scripts/docs-audit/check-affected-docs.mjs·node scripts/docs-audit/check-drift-comment.mjsnode scripts/nightly-tiers.mjs --self-test·node scripts/pm/ci-failure.mjs --self-testpnpm check:agent-test-spelling·check:declared-population-live·check:driver-memory-census·check:node-version·check:nul-bytespnpm check:pm-dispatch-gates·check:pm-expected-skips·check:pm-widening-tellspnpm check:pnpm-acquisition·check:pnpm-filter-targets·check:refd-timer-probepnpm check:required-contexts·check:shard-attestation·check:stall-guard-budget·check:stall-guard-headroompnpm check:watch-hint-literal·check:workflow-status-functions·check:workflow-step-name-quotingThe derivation was re-taken after
git fetch origin main(tree then 5 commits behind; the newerorigin/mainisa84da6096a) and the 41-command list came back byte-identical, so nothing that landed in the meantime added a family to this diff. The run's own stale-tree note names one file it derives from that moved upstream —scripts/check-sdui-manifest.mjs— which sits in the artifact-roster block and is silent for these paths in either direction.Repo-wide
pnpm lint(eslint . --no-inline-config): exit 0.Beyond the derived 41, the derivation warns that an artifact roster's silence is not a clearance when the roster lives in a directory one of the changed paths is in. Those were run rather than assumed:
node scripts/check-platform-checklist-watchdog.mjs(+--self-test)node scripts/check-ci-filter-parity.mjs(+--self-test)node scripts/ci/scheduled-full-run.mjs --self-testnode scripts/ci/select-shard-timings-run.mjs --self-testnode scripts/measure-test-shard-timings.mjs --self-testnode scripts/pr-labels.mjs --self-testnode scripts/check-single-claim-paths.mjs --self-testnode scripts/check-single-claim-paths.mjssingle-claim-path-guard.ymlsupplies; CI reads it.A control-character self-scan over the five edited files (
grep -naPfor the C0 set minus tab/newline, and DEL) returns nothing, besidecheck:nul-bytesgreen above.What this PR deliberately does NOT do
scripts/pm/check-half-states.mjsand itsISSUE_BODY_LIMITare read, not touched. The workflows restate that measurement; they do not re-declare it.retry-exempt-status-codesvalue moved, and no other file is in the diff.BUDGETwas not raised. The decision is stated in ③ above rather than taken silently in either direction.Generated by Claude Code