Skip to content

docs(workflows): five 65536 restatements re-worded to the measured caps, each naming its record - #18841

Merged
os-justin merged 1 commit into
mainfrom
claude/issue-18806-workflow-65536-bindings
Sep 18, 2026
Merged

os-justin merged 1 commit into
mainfrom
claude/issue-18806-workflow-65536-bindings

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

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 = 65536 from scripts/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 a github-script step cannot import ISSUE_BODY_LIMIT, so these five are restatements by construction and the only defence against the next drift is that grep finds them. Comments only: no behaviour changes, and retry-exempt-status-codes: 400,401,404,422 is untouched on all four files that carry it.

① The COMMENT surface, bisected — objectstack#18826

Probe issue objectstack#18826, opened for this measurement and closed completed with the record as its last comment. Bodies of chosen byte sizes written through scripts/pm/post-stamped.mjs --comment=18826 (writes 1–7, each read back byte-exact by the tool) and direct REST curl (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.

# endpoint sent bytes sent chars answer class / exit verdict
1 comment create 200 200 201 footer-appended, exit 0 (stored 258) landed
2 comment create 65,536 65,536 201 identical, exit 0 landed
3 comment create 65,537 65,537 201 identical, exit 0 landed
4 comment create 262,144 262,144 201 identical, exit 0 landed
5 comment create 262,145 262,145 422 exit 3, nothing written refused
6 comment create 262,145 222,145 422 exit 3 refused (multi-byte)
7 comment create 262,144 222,144 201 identical, exit 0 landed (multi-byte)
8 comment create 262,145 262,145 422 raw REST refused — captured the platform's text
9 comment UPDATE 262,144 262,144 200 stored byte-identical landed
10 comment UPDATE 262,145 262,145 422 previous body kept refused
11 comment UPDATE 290 290 200 stored 348 landed — restores write 1
12 issue CREATE 262,145 262,145 422 nothing created refused

Twelve write attempts, plus the probe issue and the record comment: 14 writes in total on the probe.

  • The bracket the card asked for, at the folklore number: writes 2 and 3 (65,536 and 65,537 bytes) both landed, so 65,536 is not a boundary on this surface in either unit. Writes 4 and 5 are the boundary, one byte apart, taken on this run.
  • The unit is BYTES. Write 6 sends 262,145 bytes carrying only 222,145 characters (a 20,000-character run of 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.
  • Both comment endpoints agree: issues.createComment (writes 4/5) and issues.updateComment (writes 9/10) are bracketed at the same value.
  • The refusal SHAPE differs from the issue-body surface, and that half is new. On issues.update an 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 on issues.create it 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}/comments at 262,145 bytes:

{"resource":"IssueComment","code":"unprocessable","field":"data",
 "message":"Body is too long (maximum is 65536 characters)"}

PATCH /issues/comments/{id} and POST /issues say 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/main ad1f94e8ec (the branch point). After this PR, grep -rn 18793 .github/workflows and grep -rn 18826 .github/workflows each find the files that restate the corresponding measurement; grep -rn 65536 still finds all five, now attached to the sentence that says the number is false.

file:line surface before after — the number, the unit, and the record
test-nightly-tiers.yml:436 ISSUE body (issues.update :541 / issues.create :547) 「a GitHub issue body caps at 65536 chars」 262,144 UTF-8 bytes stored / 262,145 refused — objectstack#18793, restated as ISSUE_BODY_LIMIT; plus the silent-refusal shape (#18793) and the false 422 text (#18826)
scheduled-full-run-card.yml:125 ISSUE body (issues.update :214 / issues.create :219) 「a body past the 65536-character limit is this repo's own bug」 same cap, objectstack#18793; and that on issues.update this never arrives as a 422 at all, while issues.create does answer 422 (#18826)
cross-repo-issue-closer.yml:166 COMMENT (createComment :282 / :477 / :488) and issue body (issues.update :492) 「a body past GitHub's 65536-character comment limit is this repo's own bug」 both caps, both 262,144 bytes: comment from objectstack#18826 (loud 422), issue body from objectstack#18793 (silent)
merge-queue-triage.yml:178 COMMENT (createComment :879) and issue body (issues.update :733, issues.create :771) same sentence as :166 both caps with both records, and the per-endpoint refusal shape: create 422 (#18826), update silent (#18793)
docs-drift-check.yml:557 COMMENT (createComment :665 / updateComment :670) 「past GitHub's 65536-character comment limit」 262,144 bytes stored / 262,145 refused with a real 422 on both endpoints — objectstack#18826, bisected on each

No retry-exemption code changed. retry-exempt-status-codes: 400,401,404,422 is 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 stated

Kept 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.mjs took when it left MARKDOWN_BODY_BUDGET at 60,000 bytes while ISSUE_BODY_LIMIT moved four times up. The comment also names the budget's unit (JS string length, i.e. UTF-16 code units — what the slice below 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.

workflow runs seen failed runs read what the failures were a real 422 on a body/comment write?
test-nightly-tiers.yml 12 (all, 2026-09-07 → 09-17) 6 (all) every one at Run this shard's nightly tiers; the card-filing job File or refresh the red card concluded success in all six no
scheduled-full-run-card.yml 5,923 total 0 — the workflow has never failed no
cross-repo-issue-closer.yml 5,175 total 4 (all) 1 deliberate (a closing keyword named a PR, not an issue); 2 SyntaxError: Identifier 'octokit' has already been declared; 1 Set up job no
merge-queue-triage.yml 18,415 total 22 (all) 17 startup failures with no job; 4 Set up job; 1 HttpError: We couldn't respond to your request in time (a platform timeout) no
docs-drift-check.yml 9,916 total 100 most recent (2026-08-17 → 09-05) exactly 1 job-level failure, at Self-test the change → docs mapper; the rest are startup failures with no job no in that window

Instrument 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 own recentRelayFailures shows another seat hitting the same host today). So failure causes were read from check-run annotations instead, which carry the github-script step'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.

  1. A real 422 on a body or comment write: none found in any of the windows above. The only 422s this card produced are the ones this PR's own probe manufactured on a throwaway issue.
  2. A comment cap BELOW a budget some workflow relies on: the measured comment cap is 262,144 bytes. The largest budget any of the five relies on is test-nightly-tiers.yml's BUDGET = 400006.5× under it, and that one is on the issue-body surface anyway. check-half-states.mjs's MARKDOWN_BODY_BUDGET = 60000 bytes 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_PARSE_EXIT=0
OK   .github/workflows/test-nightly-tiers.yml       name="Nightly Tiers"             jobs=2 [tiers,report]
OK   .github/workflows/scheduled-full-run-card.yml  name="Scheduled Full Run Card"   jobs=1 [file]
OK   .github/workflows/cross-repo-issue-closer.yml  name="Cross-repo Issue Closer"   jobs=1 [close-foreign-issues]
OK   .github/workflows/merge-queue-triage.yml       name="Merge Queue Triage"        jobs=1 [triage]
OK   .github/workflows/docs-drift-check.yml         name="Docs Drift Check"          jobs=1 [docs-drift]

yaml@2.9.0 from the workspace, YAML.parse on 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-script script: | block, the JS in every one of those blocks was compiled too (new AsyncFunction(...) over the extracted source — github-script's own wrapper shape):

JS_PARSE_EXIT=0
JS OK  test-nightly-tiers.yml       :: report               :: File or refresh the card
JS OK  scheduled-full-run-card.yml  :: file                 :: File or refresh the card
JS OK  cross-repo-issue-closer.yml  :: close-foreign-issues :: Close (or report) cross-repo closing keywords
JS OK  merge-queue-triage.yml       :: triage               :: Post the triage comment
JS OK  docs-drift-check.yml         :: docs-drift           :: Comment on PR
github-script blocks checked: 5

skip-changeset, measured

Nothing published moves. Read mechanically rather than recalled: 70 package.json files in the tree declare a files[] array, and zero of their entries name .github or workflow (positive control: the reader really does see entries — packages/spec returns dist, 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/objectstack from 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-$?.

dispatch-gates --ran: 41 derived famil(ies) accounted for — 41 run, 0 NOT-MEASURED, 0 UNRUN
  (a DERIVED zero — all 41 recorded an exit code and none of them is 3)
exit command
0 node scripts/check-aggregator-roster.mjs (+ --self-test)
0 node scripts/check-closing-keyword-parity.mjs (+ --self-test)
0 node scripts/check-comment-mask-corpus.mjs
0 node scripts/check-cross-repo-closer-outcome.mjs (+ --self-test)
0 node scripts/check-merge-queue-triage-outcome.mjs (+ --self-test)
0 node scripts/check-position-name-fold-loaders.mjs (+ --self-test)
0 node scripts/check-self-test-wired.mjs (+ --self-test)
0 node scripts/check-self-test-workflow-commands.mjs (+ --self-test)
0 node scripts/check-step-collectors.mjs (+ --self-test)
0 node scripts/check-whole-set-label-write.mjs (+ --self-test)
0 node scripts/docs-audit/check-affected-docs.mjs · node scripts/docs-audit/check-drift-comment.mjs
0 node scripts/nightly-tiers.mjs --self-test · node scripts/pm/ci-failure.mjs --self-test
0 pnpm check:agent-test-spelling · check:declared-population-live · check:driver-memory-census · check:node-version · check:nul-bytes
0 pnpm check:pm-dispatch-gates · check:pm-expected-skips · check:pm-widening-tells
0 pnpm check:pnpm-acquisition · check:pnpm-filter-targets · check:refd-timer-probe
0 pnpm check:required-contexts · check:shard-attestation · check:stall-guard-budget · check:stall-guard-headroom
0 pnpm check:watch-hint-literal · check:workflow-status-functions · check:workflow-step-name-quoting

The derivation was re-taken after git fetch origin main (tree then 5 commits behind; the newer origin/main is a84da6096a) 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:

exit command reading
0 node scripts/check-platform-checklist-watchdog.mjs (+ --self-test) green
0 node scripts/check-ci-filter-parity.mjs (+ --self-test) green
0 node scripts/ci/scheduled-full-run.mjs --self-test green
0 node scripts/ci/select-shard-timings-run.mjs --self-test green
0 node scripts/measure-test-shard-timings.mjs --self-test green
0 node scripts/pr-labels.mjs --self-test green
0 node scripts/check-single-claim-paths.mjs --self-test green
2 node scripts/check-single-claim-paths.mjs NOT MEASURED, not red — it refuses with 「NOT WIRED — PR_NUMBER is not set … This is a wiring or usage failure, NOT a verdict」. It needs the PR context single-claim-path-guard.yml supplies; CI reads it.

A control-character self-scan over the five edited files (grep -naP for the C0 set minus tab/newline, and DEL) returns nothing, beside check:nul-bytes green above.

What this PR deliberately does NOT do

  • scripts/pm/check-half-states.mjs and its ISSUE_BODY_LIMIT are read, not touched. The workflows restate that measurement; they do not re-declare it.
  • ⛔ No retry-exempt-status-codes value moved, and no other file is in the diff.
  • BUDGET was not raised. The decision is stated in ③ above rather than taken silently in either direction.

Generated by Claude Code

…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>
@os-justin os-justin added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 17, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing 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
  • 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 — 0 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 a84da6096a580764a946f028206a89d44b528339packageMentionDocs.

This was referenced Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

CI note from the seat — Nightly tiers (2/2) is red on the BASE, ⛔ not this PR's

2026-09-18T00:12Z. The one red check-run on ded59ceec9 (run 35288055722, job 105424671812) fails two packages/cli nightly-tier pins — validate-json-failure-warnings.e2e.test.ts :500 and validate-json-failure-conversions.e2e.test.ts :415, both 「the emitJson exit count moved … expected 6 but got 7」. packages/cli/src/commands/validate.ts on origin/main 42f8df1723 has SEVEN await emitJson( exits, the seventh (:517) from PR #18769 095c7f60ae (merged 2026-09-17T20:44:55Z), and every one of the seven carries both warnings: and conversions: — the contracts hold, the integer pins are stale. This PR's diff is five workflow files and nothing under packages/cli/**; the tier ran here only because the diff touches test-nightly-tiers.yml (its pull_request path filter), and it is ⛔ not one of main's seven required contexts (/rules/branches/main), so the queue takes its verdict from the required set, all green here. The newest run on main (35186437174, 2026-09-17T05:37Z, e46ac0fb65) predates #18769 — the base has been red on this tier since 20:44Z with no run to say so. Seat's reproduction on the base: the tests' own payloadLiterals (extracted verbatim from both test files, types stripped) run over validate.ts at 42f8df1723 at 2026-09-18T00:11Z: 7 literals · valid: false 6 · valid: true 1 · carrying the key 7 of 7 — the pinned 6 / 5 fails by construction in both files; control at 095c7f60ae^: 6 exits, the pin held.

No fix PR exists yet: filed as #18848 for the cli lane, with the two-line patch named there; ⛔ not ported into this PR (a packages/cli test edit would widen a workflow-comment PR). No re-run: the assertion is a deterministic source count. The seat lands this PR on the required set; the landing record on #18806 names this note.


Generated by Claude Code

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

Labels

ci/cd size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants