Skip to content

docs(architecture): one creation limiter, three refusal shapes — never key on its text - #4793

Merged
meshweaver-cloud[bot] merged 2 commits into
mainfrom
docs/creation-refusal-has-three-faces
Sep 19, 2026
Merged

meshweaver-cloud[bot] merged 2 commits into
mainfrom
docs/creation-refusal-has-three-faces

Conversation

@rbuergi

@rbuergi rbuergi commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

One limiter, three refusal shapes

#4790 (merged) documents the content-creation limit as a 403 whose body names the secondary limit. That is one of three faces, and the guidance is incomplete in the direction that costs a missed detection.

Measured 2026-09-19 while an agent answered a review thread:

what you called how it refuses
POST …/issues over REST 403, body names the secondary limit
POST …/pulls/{n}/comments/{id}/replies 422 {"resource":"PullRequestReview","code":"abuse","field":"base"} — not a 403, names no rate limit
gh issue create (GraphQL porcelain) exit 0, both streams empty, nothing created

One reply was refused four times over 43 minutes in the 422 shape and landed on the fifth attempt. So a watcher grepping for "secondary rate limit" sees nothing there, and one checking for 403 sees nothing either.

The rule that survives all three: key the decision on the absence of what you asked for. No id or number came back, so it did not happen, whatever the wrapper said.

The page also now records that the read limit is independent of the creation one and can land immediately after a successful write. It did, six seconds after that reply posted, delaying its byte-compare by eight minutes. So a failed read-back is not evidence of a failed write, and the verification read needs its own budget.

Verification

  • MeshWeaver.Documentation.Test: 601 succeeded, 0 failed, 0 skipped.
  • No internal links added, so nothing new for DocumentationLinkIntegrityTest to resolve.

Documentation only.

🤖 Generated with Claude Code

…r key on its text

#4790 documented the creation limit as a 403 whose body names the secondary limit. That is one of
three faces, and the guidance is incomplete in the direction that costs a missed detection.

Measured 2026-09-19 while answering a review thread: `POST …/pulls/{n}/comments/{id}/replies` is
refused as `422 {"resource":"PullRequestReview","code":"abuse","field":"base"}` — not a 403, and
naming no rate limit at all. One reply was refused four times over 43 minutes that way and landed on
the fifth attempt. So a watcher grepping for "secondary rate limit" sees nothing, and one checking
for 403 sees nothing either, while `gh issue create` reports the same condition as exit 0 with both
streams empty.

The rule that survives all three: key the decision on the ABSENCE of what was asked for. No `id` or
`number` came back, so it did not happen, whatever the wrapper said.

Also records that the read limit is independent and can land immediately after a successful write —
it did, six seconds later, delaying that reply's byte-compare by eight minutes. A failed read-back is
therefore not evidence of a failed write, and the verification read needs its own budget.

Verified: MeshWeaver.Documentation.Test 601 succeeded, 0 failed, 0 skipped. No internal links added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 19, 2026 10:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Missing response identifiers do not prove a write failed and could cause duplicate replies on retry.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
What changed in this PR

Documents GitHub content-creation refusal variants and independent read-limit behavior.

Changes:

  • Adds examples of 403, 422, and silent-success refusal shapes.
  • Advises verification reads with separate rate-limit budgeting.
File Description
ReadingCiSignals.md Expands guidance for creation-limit detection and verification.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +368 to +369
`403` sees nothing either. **Key the decision on the absence of what you asked for**: no `id` or
`number` came back, so it did not happen, whatever the wrapper said.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid, and it catches a contradiction inside the same section. Taking it.

I wrote that an absent id means "it did not happen", and two paragraphs later that a failed read-back is not evidence of a failed write. Both cannot be true: a response can be lost or suppressed after the server has committed, so the absent id licenses only "not confirmed". Acting on the stronger reading is how you get a duplicate reply — which on a review thread is worse than the original problem, because the stub case this section exists for is repaired by PATCH, and a second POST leaves the first standing beside it.

The revision says "not confirmed", and makes the retry conditional on an independent check rather than on the absent id: re-read the collection and look for your content before posting again. That is the same baseline rule the section already states for auditing somebody else's posts, now applied to your own retry, which is where it was missing.

Worth recording why the wrong version was tempting. Two of the three refusal shapes in the table genuinely do mean nothing was created — the 403 and the 422 are refusals at the API boundary. It was the third, the porcelain exiting 0 with empty streams, that I generalised from, and that one is ambiguous in exactly the way you name: silence from the wrapper says nothing about what the server did.

Fixed in the next push.

@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

0 tests   - 1 553   0 ✅  - 1 553   0s ⏱️ - 3m 11s
0 suites  -     2   0 💤 ±    0 
0 files    -     2   0 ❌ ±    0 

Results for commit e3da878. ± Comparison against base commit cc6818a.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

0 files   -     3  0 suites   - 3   0s ⏱️ - 5m 14s
0 tests  - 1 857  0 ✅  - 1 857  0 💤 ±0  0 ❌ ±0 
0 runs   - 1 858  0 ✅  - 1 858  0 💤 ±0  0 ❌ ±0 

Results for commit e3da878. ± Comparison against base commit cc6818a.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

0 files   -     5  0 suites   - 5   0s ⏱️ - 12m 43s
0 tests  - 3 679  0 ✅  - 3 677  0 💤  - 2  0 ❌ ±0 
0 runs   - 3 683  0 ✅  - 3 681  0 💤  - 2  0 ❌ ±0 

Results for commit e3da878. ± Comparison against base commit cc6818a.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

0 tests   - 443   0 ✅  - 443   0s ⏱️ -55s
0 suites  -   3   0 💤 ±  0 
0 files    -   3   0 ❌ ±  0 

Results for commit e3da878. ± Comparison against base commit cc6818a.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

601 tests   - 109   601 ✅ + 83   28s ⏱️ - 5m 14s
  1 suites  -   2     0 💤  - 192 
  1 files    -   2     0 ❌ ±  0 

Results for commit e3da878. ± Comparison against base commit cc6818a.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

265 tests  ±0   265 ✅ ±0   2m 52s ⏱️ +21s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit e3da878. ± Comparison against base commit cc6818a.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

  2 files   -    15    2 suites   - 15   3m 20s ⏱️ - 26m 59s
866 tests  - 7 641  866 ✅  - 7 447  0 💤  - 194  0 ❌ ±0 
866 runs   - 7 646  866 ✅  - 7 452  0 💤  - 194  0 ❌ ±0 

Results for commit e3da878. ± Comparison against base commit cc6818a.

♻️ This comment has been updated with latest results.

Copilot's finding is right and it caught a contradiction inside the same section. I wrote that an
absent `id` means the write did not happen, and two paragraphs later that a failed read-back is not
evidence of a failed write. Both cannot hold: a response can be lost or suppressed AFTER the server
commits, so the absent id licenses only "not confirmed".

The difference decides whether a retry is safe, and the stronger reading is how a duplicate gets
made — worst in the case this section exists for, since a stub reply is repaired with PATCH and a
second POST leaves the first standing beside it. The retry is now conditional on an independent
check: re-read the collection and look for your own content. That is the baseline rule already
stated below for auditing somebody else's posts, applied to your own retry, which is where it was
missing.

Also records why the wrong version was tempting: two of the three shapes, the 403 and the 422, are
refusals at the API boundary and genuinely created nothing. It is the porcelain's silent exit 0 that
is ambiguous, and I generalised from it.

Verified: MeshWeaver.Documentation.Test 601 succeeded, 0 failed, 0 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@meshweaver-cloud
meshweaver-cloud Bot added this pull request to the merge queue Sep 19, 2026
Merged via the queue into main with commit 18dc3d1 Sep 19, 2026
37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants