diff --git a/.github/workflows/cross-repo-issue-closer.yml b/.github/workflows/cross-repo-issue-closer.yml index b82103bcb04..d6a4c4a5137 100644 --- a/.github/workflows/cross-repo-issue-closer.yml +++ b/.github/workflows/cross-repo-issue-closer.yml @@ -162,10 +162,21 @@ jobs: # to fail instead of one. It still fails, and the loop still names the # target it failed on. # - # 400/401/404/422 stay exempt: a malformed request, a wrong target, or a - # body past GitHub's 65536-character comment limit is this repo's own - # bug. It is answered correctly on the first try and asking again only - # spends runner minutes. + # 400/401/404/422 stay exempt: a malformed request, a wrong target, or + # an oversized body is this repo's own bug. It is answered correctly on + # the first try and asking again only spends runner minutes. + # + # Both caps this step can hit are MEASURED, in UTF-8 BYTES, and they are + # the same number — ⛔ neither is the 65,536 characters this comment used + # to name. A COMMENT (`issues.createComment` below) stores 262,144 bytes + # and refuses 262,145 with a real 422: objectstack#18826, bisected to one + # byte. An ISSUE BODY (`issues.update` below) stores 262,144 and refuses + # 262,145 SILENTLY — 200, old body kept, nothing reported: + # objectstack#18793, bisected to one byte, restated as + # `ISSUE_BODY_LIMIT` in scripts/pm/check-half-states.mjs. ⛔ Do not + # re-derive 65,536 from a refusal message: GitHub's own 422 text still + # says "maximum is 65536 characters" and #18826 falsified it in both unit + # and value by storing a 262,144-character comment. retries: 3 retry-exempt-status-codes: 400,401,404,422 script: | diff --git a/.github/workflows/docs-drift-check.yml b/.github/workflows/docs-drift-check.yml index 87f929eec9c..47175bbc9d3 100644 --- a/.github/workflows/docs-drift-check.yml +++ b/.github/workflows/docs-drift-check.yml @@ -554,9 +554,17 @@ jobs: // separates it from a genuine permission denial. // Everything else stays fatal on purpose: 401 / plain 403 (the `permissions:` // block above is wrong), 404 (wrong target), 422 (the body this workflow built - // is not postable — e.g. past GitHub's 65536-character comment limit), and any - // non-HTTP error such as a TypeError in the code above. Those are this repo's - // own bugs and must keep failing the job. + // is not postable — e.g. past the COMMENT cap), and any non-HTTP error such as + // a TypeError in the code above. Those are this repo's own bugs and must keep + // failing the job. + // + // That cap is MEASURED, in UTF-8 BYTES, ⛔ not the 65,536 characters this + // comment used to name: a comment stores 262,144 bytes and refuses 262,145 + // with a real 422, on BOTH `issues.createComment` and `issues.updateComment` + // below — objectstack#18826, bisected to one byte on each endpoint. ⛔ Do not + // re-derive 65,536 from the refusal itself: GitHub's own 422 text still reads + // "maximum is 65536 characters" and #18826 falsified it in both unit and + // value, storing a 262,144-CHARACTER comment. const TRANSIENT_NETWORK_CODES = new Set([ 'ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'EAI_AGAIN', 'ENOTFOUND', 'EPIPE', 'EHOSTUNREACH', 'ENETUNREACH', 'UND_ERR_SOCKET', 'UND_ERR_CONNECT_TIMEOUT', diff --git a/.github/workflows/merge-queue-triage.yml b/.github/workflows/merge-queue-triage.yml index d649bb99613..60d87161a49 100644 --- a/.github/workflows/merge-queue-triage.yml +++ b/.github/workflows/merge-queue-triage.yml @@ -174,9 +174,22 @@ jobs: # wrong `permissions:` block) now takes four attempts to fail instead of # one. It still fails. # - # 400/401/404/422 stay exempt: a malformed request, a wrong target, or a - # body past GitHub's 65536-character comment limit is this repo's own bug, - # answered on the first try and not improved by asking again. + # 400/401/404/422 stay exempt: a malformed request, a wrong target, or + # an oversized body is this repo's own bug, answered on the first try and + # not improved by asking again. + # + # Both caps this workflow can hit are MEASURED, in UTF-8 BYTES, and they + # are the same number — ⛔ neither is the 65,536 characters this comment + # used to name. A COMMENT (`issues.createComment` below) stores 262,144 + # bytes and refuses 262,145 with a real 422: objectstack#18826, bisected + # to one byte. An ISSUE BODY (`issues.update` / `issues.create` below) + # stores 262,144 and refuses 262,145 — on `create` with a 422 (#18826), + # on `update` SILENTLY, 200 with the old body kept and nothing reported: + # objectstack#18793, bisected to one byte, restated as + # `ISSUE_BODY_LIMIT` in scripts/pm/check-half-states.mjs. ⛔ Do not + # re-derive 65,536 from a refusal message: GitHub's own 422 text still + # says "maximum is 65536 characters" and #18826 falsified it in both unit + # and value by storing a 262,144-character comment. retries: 3 retry-exempt-status-codes: 400,401,404,422 script: | diff --git a/.github/workflows/scheduled-full-run-card.yml b/.github/workflows/scheduled-full-run-card.yml index af34c81d8d2..1e3b9bfac60 100644 --- a/.github/workflows/scheduled-full-run-card.yml +++ b/.github/workflows/scheduled-full-run-card.yml @@ -121,9 +121,22 @@ jobs: # Same transient-retry posture as merge-queue-triage.yml, and for the # same reason: 403 is REMOVED from the exempt list because GitHub # answers a secondary rate limit with 403 as well as 429, and this job - # pages issues. 400/401/404/422 stay exempt — a malformed request or a - # body past the 65536-character limit is this repo's own bug and is - # not improved by asking again. + # pages issues. 400/401/404/422 stay exempt — a malformed request or + # an oversized body is this repo's own bug and is not improved by + # asking again. The size that makes a body oversized is MEASURED, in + # UTF-8 BYTES: this job writes an ISSUE BODY, which stores 262,144 + # bytes and refuses 262,145 (objectstack#18793, bisected to one byte; + # restated as `ISSUE_BODY_LIMIT` in scripts/pm/check-half-states.mjs). + # ⛔ Not the 65,536 characters this comment used to name, and ⛔ not + # the number in GitHub's own refusal text either — that string still + # reads "maximum is 65536 characters" and objectstack#18826 falsified + # it in both unit and value by storing a 262,144-character comment. + # ⚠️ On `issues.update` an oversized body is not a 422 at all: the + # platform answers 200, keeps the OLD body and reports nothing + # (#18793). The 422 exemption stays right for the malformed request it + # also covers, and `issues.create` DOES answer 422 on an oversized + # body (#18826) — but ⛔ a refresh that outgrows the cap never reaches + # it. retries: 3 retry-exempt-status-codes: 400,401,404,422 script: | diff --git a/.github/workflows/test-nightly-tiers.yml b/.github/workflows/test-nightly-tiers.yml index 2eb37dc1b65..75a166b715f 100644 --- a/.github/workflows/test-nightly-tiers.yml +++ b/.github/workflows/test-nightly-tiers.yml @@ -433,8 +433,19 @@ jobs: const failing = read('failing-files.txt').split('\n').map((s) => s.trim()).filter(Boolean); const extractorExit = process.env.FAILING_FILES_EXIT; - // The log tails, bounded: a GitHub issue body caps at 65536 chars, - // and the run link carries the whole log anyway. + // The log tails, bounded. ⛔ The bound below is NOT the platform + // cap, and the platform cap is not 65,536 of anything. This step + // posts an ISSUE BODY, and that surface is MEASURED, in UTF-8 + // BYTES: 262,144 bytes stored, 262,145 refused — bisected to one + // byte on objectstack#18793, and restated as `ISSUE_BODY_LIMIT` in + // scripts/pm/check-half-states.mjs. ⛔ Do not re-derive 65,536 from + // a refusal message: GitHub's own 422 text still says "maximum is + // 65536 characters" and is false in both unit and value — + // objectstack#18826 stored a 262,144-CHARACTER comment. ⚠️ And on + // this surface the refusal is not a 422 at all: an oversized issue + // body is answered 200 with the OLD body kept and nothing reported + // (#18793), so a body that outgrows the cap vanishes silently. + // The run link carries the whole log anyway. const tails = []; const reportsDir = path.join(process.env.RUNNER_TEMP, 'nightly-reports'); const walk = (dir) => { @@ -447,6 +458,16 @@ jobs: } }; walk(reportsDir); + // BUDGET is a READING budget, ⛔ not a platform bound, and it is + // deliberately NOT re-sized now that the cap above is known to be + // four times what this file used to assert. 40,000 is about as much + // log tail as a person reads off a card before clicking through, + // and the run link carries every byte elided here — so this number + // is sized against the reader, and a correction to the cap moves a + // reading, never a licence to print more. It is counted in the unit + // the slice below uses (JS string length, i.e. UTF-16 code units), + // because nothing judges it against the byte cap: even as bytes of + // CJK prose it stays far under 262,144. const BUDGET = 40000; const perTail = Math.max(2000, Math.floor(BUDGET / Math.max(1, tails.length))); const tailBlocks = tails.sort((a, b) => a.shard.localeCompare(b.shard)).flatMap(({ shard, text }) => [