Repository navigation
docs(upstream): account for the 2023 administrative sweep, and give the sweep tool a mode that could have caught it - #258
Conversation
Upstream ran two bulk clearouts before going quiet, and neither was a triage: 2023-06-15 `eddyv` closed 35 unmerged PRs with "Closing - Stale." (34 of them ours), and 2023-07-07 `johnbyrnejb` closed 28 issues with "Closing Issue" - every one marked COMPLETED rather than "not planned". That state reason is why the cohort was invisible. GitHub renders `completed` as resolved, so the issues read as done at a glance, and `upstream-sweep.sh` searches `updated:>=last_swept`, so anything last touched in 2023 can never appear in a sweep. None of the 28 were in this manifest; the mirroring that produced the other 78 fork mirrors only ever sampled *open* upstream issues. All 28 were re-read in full - bodies plus all 58 comments - and each verified against fork source rather than trusted from its thread. That mattered: four issue bodies point at PRs that "fix" them (#372->#390, #319->#270, #203->#345, #191->#346) and every one of those PRs was itself swept unmerged. Result: 2 already fixed (#41 offset-scan removal, #319 shutdown CME), 6 partly addressed, 20 fully open. Grouped into work items rather than 28 near-duplicate entries, since several want designing together - per-topic handlers with separate consume/produce types, seek with subscription changes, retry expiry with stall detection. #57 folded into the existing log-noise entry. Swept PR head commits were checked and are all still reachable, so the drafts are recoverable; entries cite branch and SHA, not just the PR number. Upstream-Issue: confluentinc#154 Forwarded: no Applied-Upstream: no
The point of mirroring is a clean, known cutoff - "every issue upstream closed administratively is accounted for here" - not a curated pick of the good ones. A set filtered by our own value judgement is not a cutoff: the next person cannot tell "not mirrored because it was junk" from "not mirrored because it was missed", and that ambiguity is worth far more than the cost of carrying a few items nobody will ever action. Drops the decline recommendations from #53, #199, #246 and #57 in favour of neutral statements of the trade-off, and renames sweep-2023-decline-candidates to sweep-2023-small-items (status wontfix -> none). The facts that informed those recommendations are kept - they are useful to whoever picks the issue up - but they are now framed as input to that person's decision rather than as our verdict delivered in advance. Fork issues can always be closed later on their merits, which leaves a visible record; pruning before mirroring does not. Records the principle in the section header so a later pass does not re-prune. Upstream-Issue: confluentinc#154 Forwarded: no Applied-Upstream: no
The 28 mirrors are live as #227-254 (label `upstream-admin-closed`), with confluentinc#41 -> #233 and confluentinc#319 -> #252 created and immediately closed as completed, carrying the code evidence that they are genuinely fixed. Records the fork numbers on each entry so the mapping is queryable rather than re-derived, which is the reason this file exists. Adds `fork.fork_issues` (the plural of the existing `fork_issue`) because several entries deliberately group upstream items that want designing together, and documents it in the schema block. Verified: all 28 mirrors are referenced, none missing, no strays. Also snapshots each upstream title verbatim into its mirror body header, dated 2026-08-07 - the fork titles may drift as these are worked, and the original wording is what makes an old thread findable. Upstream-Issue: confluentinc#154 Forwarded: no Applied-Upstream: no
…er saw The default sweep is a window search - `updated:>=$since` - so it structurally cannot see an item whose last activity predates the window, and `last_swept` only moves forward, so the blind spot grows. That is why upstream's 2023 administrative closures went unnoticed for three years: 28 issues closed with "Closing Issue" and 35 unmerged PRs closed as "Closing - Stale.", all marked COMPLETED, none of it triage, none of it visible to us. Adds `--audit`: no time window, asks "which closed upstream items are neither tracked in the manifest nor mirrored in the fork?", and flags days where an implausible number of things closed at once. Run against the real repo it rediscovers both sweeps from scratch and reports zero unaccounted, which is also an end-to-end check that the new fork_issues linkage is correct. Bots are excluded from the PR analysis. Dependabot self-closes superseded bumps in batches that look exactly like a sweep - 2022-08-16, 2022-10-20, 2023-11-03 and 2024-01-25 are all dependabot - and unfiltered they buried the two real sweeps in noise. Filtering them surfaced two human PRs that had been sitting inside those batches (confluentinc#508, confluentinc#650), now recorded for review. The report says outright that a bulk day is not proof of a sweep, because a release triage looks identical from here, and that stateReason COMPLETED is not evidence of a fix - that assumption is what made this cohort invisible. Known remaining hole, recorded not fixed: a PR closed alone on a quiet day is still invisible, since detection keys on bulk. Upstream-Issue: confluentinc#154 Forwarded: no Applied-Upstream: no
…ver checked Discussions were invisible to everything: not in the manifest, not mirrored, not in any sweep mode. 74 of them upstream, and nothing we run would ever have mentioned one. They were NOT swept - no bulk closure, and the largest day (2024-04-02, six threads) is all answered=true housekeeping. So the failure mode is different from the 2023 cohort: not administrative closure, just questions nobody answered, in a place no issue search reaches. Handling differs accordingly - these want answering or converting, not mirroring wholesale. The audit now lists zero-reply discussions, excluding release-announcement threads by title: those legitimately have no replies, and counting them as neglect would put 9 false positives at the top of the report. Two finds justify the check on their own. Discussion 542 (zero replies) is a field report of a transactional consumer stuck in rebalance because it cannot acquire the produce lock, with the reporter suspecting the revoke-time flush - the same lock lifecycle as bug-producing-lock-double-release, and they raised the timeout without effect, which is evidence about the mechanism rather than the duration. Discussion 883 (zero replies) is the same complaint as fork mirror #187 but with a runnable reproducer, which #187 lacks. Upstream-Issue: confluentinc#154 Forwarded: no Applied-Upstream: no
…eam item Two notes, both about what is NOT done. Discussions are not a queue to be converted. There is no pipeline turning threads into issues, and most should never become one. The rule is a judgement: if reading a discussion makes us think there is an issue, we raise one - a normal fork issue on its own merits, citing the discussion as where it came from. It exists because we believe the problem is real, not because a thread existed. That also keeps `upstream-mirror` meaning exactly "an upstream issue we carry", which is what makes the 2023 cutoff verifiable. The wider obligation is now written down: every upstream issue, PR and discussion must be accounted for - carried, declined, or genuinely resolved upstream - not sampled. The 2023 cohort was found only because it was a *bulk* event; `--audit` keys on bulk closures and zero-reply threads, and both are proxies that miss whole shapes of problem (a PR closed alone on a quiet day, a discussion with one dismissive reply, an issue marked COMPLETED with no linked PR). The audit narrows the field; only reading discharges the obligation. Also records what has been ruled out, so it is not re-investigated: wiki disabled, no advisories, all open-milestone issues already mirrored, the orphan branches accounted for (v0.6.x-dev is the lambda-actor work already captured via swept PRs; 0.5.3.x's regression fix IS on master as a908e16), and "upstream pushed today" being a pushed_at artefact rather than new activity. Left open: project boards need a read:project scope we do not have, and 169 forks are unexamined.
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
|
Claude finished @astubbs's task in 6m 40s —— View job Claude finished reviewing
SummaryThis is careful, well-cross-checked work — I traced the manifest's own bookkeeping (the 28 upstream issue numbers, the 28 fork mirror numbers 227–254, and the "2 already fixed / 6 partially addressed / 20 fully open" split) and every count reconciles exactly against the individual One concrete bug found by static analysis (I could not execute anything in this sandbox — see note at the end): 🔴 Two new manifest entries use a
|
✅ Duplicate Code ReportTwo engines run in parallel for cross-validation. Each has its own thresholds tuned to its baseline - the real safety net is the per-engine "max increase vs base" check. ✅ PMD CPD
No new clones introduced by this PR. ✅ jscpd (language-agnostic)
No new clones introduced by this PR. Powered by astubbs/duplicate-code-cross-check |
✅ SpotBugs ReportNo bugs found (new bugs only — baseline from base branch excluded). |
Two new entries used `group: batching-ordering`, which is not in the GROUPS allow-list in scripts/upstream-map.py, so `upstream-map.py validate` failed on them. Caught in review on #258. My mistake was validating the wrong way: I checked the file with an ad-hoc yaml.safe_load plus a duplicate-id check, which passes happily on a group the schema rejects, instead of running the project's own validator that AGENTS.md tells contributors to run before committing manifest changes. Reassigned both to `features` rather than widening GROUPS. The rest of this cohort's feature entries already use `features`, so this keeps the cohort internally consistent, and the ordering/batching distinction is not lost - it lives where it belongs, on the issues themselves via the `area/batching-ordering` label (#244, #236). Adding a vocabulary term to a shared schema as a side effect of a mirroring PR is the larger, less reversible change. Also drops the redundant 345 from sweep-2023-broker-disconnect-commit's `related` list, where it already appears in `prs`. Verified: `upstream-map.py validate` reports OK on 27 entries, and `refs` and `table` both still render.
Correct, and confirmed by running it — thank you. This was a real failure on my side: I validated the manifest with an ad-hoc Fixed in 7072a4d. I took the second of your two options — reassigning both entries to
Agreed it was harmless, but I was editing the file anyway so I dropped the duplicate — it reads as if 345 is two different things otherwise. On the three items you flagged as unverified: |
|
Claude finished @astubbs's task in 1m 30s —— View job Claude finished re-reviewing after fix commit
SummaryCommit
The chosen fix (reassign to Duplicate Not independently re-verifiedSame sandbox constraint as before: VerdictNo outstanding issues. The fix is scoped exactly to what was flagged, doesn't touch unrelated content, and the commit message accurately documents both the root cause (wrong validation method used pre-push) and the reasoning behind the Branch: |
🧪🔒 Quarantine Lane Report
🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its |
… inflight The three inflight notes stay - they hold undischarged work - but they were also carrying permanent content, and inflight is transient by its own charter. Moved to durable homes, each citing the manifest rather than restating it: - docs/upstream.md gains the upstream-admin-closed cohort note (with the "do not trust 2023-era closure states" rule), the discussions non-mirroring policy decided 2026-08-07, documentation of --audit and its known blind spots, and the ruled-out upstream surfaces (wiki, advisories, milestones, orphan branches, pushed_at). - docs/solutions/ gains the transferable lesson: a closure state is a rendering choice not a triage, "fixed by #N" must be checked against the merge bit, windowed watchers need a no-window audit, and bots must be filtered before hunting bulk events. - upstream-pr-analysis.adoc is corrected: it claimed issues were never bulk-closed, which the 2023-07-07 sweep disproves, and the PR sweep is now confirmed rather than suspected. The inflight notes now hold only open work, pointing at the new homes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nifest (the state) docs/upstream.md's opening enumerated everything it owns except the editorial analysis, and neither source-of-truth reference was a clickable link. Now the two roles are named explicitly: the .adoc is the plan, the manifest is the state tracker. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@codex review this |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d08c5d3c21
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…nd correct the sweep cohort Address PR review feedback (#258). - upstream-sweep.sh: `gh --paginate` only substitutes a variable named `endCursor`; calling it `$cursor` meant the cursor was never fed back and gh re-requested page 1 forever (reproduced: 469 identical pages at first:2). It only looked healthy because 74 discussions fit in one page of 100. The failure was also swallowed by `2>/dev/null || true`, which would have printed a clean "(none)" for a broken query - the one answer this mode must never invent. Fail loudly instead. - upstream-map.yaml: the sweep cohort listed 36 PRs against a documented count of 35. Upstream closed exactly 35 unmerged PRs on 2023-06-15; confluentinc#66 (closed 2022-10-19) was carried in by mistake. Listing a ref here marks it accounted for, so it was hiding an unrelated PR from every future audit. Recorded PR 258 and moved the entry to `pr-open` per the lifecycle rule. - next-upstream-coverage-completeness.md: dropped the cached counts - inflight notes must not record what a command can answer, and the "~100 partially mirrored" line already contradicted docs/upstream.md, which says all 78 open upstream issues are mirrored with backlinks. Kept only what the audit cannot say: what nobody has read yet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
… say why The rule said "the manifest tracks upstream PRs only", so mirroring the 2023 sweep cohort into it read as a violation. The rule is what is out of date. An archived upstream's closed issue numbers and closure events are frozen. Caching them locally is a read-path optimisation, not a second tracker: grepping one file is instant, while the same answer from the mirrors costs dozens of API round-trips and burns rate limit shared across agents. The usual objection to duplication - the copy silently diverges - needs a source that can still move, and this one cannot. So the boundary is redrawn where it actually bites: the mirror owns an issue's *live state* and remains the only place you update it; the manifest may cache what is frozen (number, cohort, owning mirror). Cache what is frozen; never mirror what is moving. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
The previous commit wrote the rule into AGENTS.md, which is the one place it does not belong. AGENTS.md's own preamble calls itself a router - "if it only matters once you are already in a topic... it goes in that topic's doc" - and docs/upstream.md line 7 already claims ownership explicitly: "AGENTS.md carries only the pointer and the one-line rule that manifest upkeep is the agent's job." The rule was in fact already stated twice before this branch (AGENTS.md:442 and docs/upstream.md:23), so growing the AGENTS.md copy deepened a fragmentation that was already there. Now stated once, where an agent editing the manifest will actually be looking, with AGENTS.md left holding the pointer and the one binding rule whose failure is silent (keep the fork side in sync). Net effect on the router: one line shorter than master, despite covering more. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…nearby
Diagnosed from a live miss. A reviewer cited AGENTS.md's "the manifest tracks
upstream PRs only", so that section was read and treated as authoritative - while
docs/upstream.md had been stating the same rule, and claiming ownership of it, the
whole time. The duplicate was then grown in the router rather than fixed at source.
Nothing in the reading path revealed the mistake. AGENTS.md stated a complete-looking
rule and followed it with a pointer listing four adjacent mechanics ("the manifest
schema, the mirrors, the commit trailers and the upstream sweep"), which reads as
*further detail*, not as *this is a summary and that doc wins*. The contract was
written down - docs/upstream.md:7 - but only on the side nobody enters from, since
AGENTS.md is what loads every session.
The anti-duplication rule already existed here ("Never state a fact twice"). What was
missing is the half that makes it discoverable: a cross-reference must name the owner
and say the owner wins, so a reader can tell a stub from the whole rule before editing
the copy. Applied to the rule itself and to the upstream pointer that failed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
#291) AGENTS.md is a router: it holds what binds every session and points at the doc that owns the rest. That only works if a reader can tell which side is authoritative from the side they entered on, and most pointers did not say. It failed that way on #258. A reviewer cited AGENTS.md's "the manifest tracks upstream PRs only", so that copy was read as authoritative and amended there - while docs/upstream.md had been stating the same rule, and claiming ownership of the topic in its own opening, the whole time. Both copies read as complete; neither said who won. Two pointers already did it right - docs/ci.md ("stated in one place only... Do not restate the rule here; it has drifted before") and the refactoring pointer ("see the table above for what it owns"). The rest listed adjacent topics, which reads as further detail rather than a hand-off. Converted those: testing, releasing, issue-references, copyright, citations now name the owner and say it wins on conflict. Three routed docs never stated their half, so entering from the doc gave no signal either: bin/AGENTS.md, docs/refactoring.md and docs/inflight/AGENTS.md. They do now, matching the eight that already did. Left alone: pointers already carrying the signal, and plain citations, where the phrasing would be noise. The goal is that a reader can find the owner, not that a template appears everywhere. Rejected alternative: a bin/check-doc-ownership.sh gate enforcing the reciprocal half in CI. Reviewing it found the guard silently skipped a routed doc (bin/AGENTS.md - fixed by hand here instead), and that its self-test could not detect the guard checking only one doc of nine. Enforcement machinery for a documentation convention was not worth its own maintenance burden, let alone its own bugs. The convention is a review call, which is what the rule now says. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…hat insists Six entries in `src/docs/development/upstream-map.yaml` were stale or about to be. #204 merged while its entry still said `status: pr-open`, which is what prompted this. Checking the rest found two more already wrong - #31 (merged the same morning) and #258 (merged 2026-08-12, stale for a week) - and three belonging to this PR that would have gone the same way at merge. **The rule is now: write `merged` in the branch and push it before you merge.** That reads like claiming something untrue and is not - branch content is visible to nobody until it lands, and the moment it lands the entry is correct. There is no observable instant where the manifest is wrong, and nothing to clean up afterwards. Doing it in the other order is what costs: the branch is gone, the correction becomes a commit straight to master, and until someone remembers, the manifest lies. An earlier draft of this took the framing that `merged` is impossible to write honestly before the merge, and built a `bin/check-upstream-map.sh` plus a scheduled workflow to detect the resulting staleness on master. That premise was wrong, and everything built on it was solving a problem that does not need to exist. Deleted in favour of the hook. `.claude/hooks/check-upstream-map-merged.sh` refuses a `gh pr merge <N>` while an entry naming that PR still says `pr-open`, and says what to do instead. It gates on the status rather than on mere mention, so it is silent once the entry is right - a guard that fires on correct behaviour teaches people to route around it. It fails open on every uncertainty (no PyYAML, unparseable manifest, no PR number on the command line), because a hook that blocks on its own bug jams the tool shut. It is deliberately small and deliberately disposable: all of this exists only until the last upstream link is closed out, and then it is one file to delete with nothing else to unpick. The manifest's schema is already validated by `scripts/upstream-map.py validate`, so this adds no second copy of any rule. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012J2KjzpiUKg2tGFT7B2D2e
Description
Upstream ran two bulk clearouts before going quiet, and neither was a triage:
eddyvjohnbyrnejbEvery one of the 28 was marked COMPLETED rather than "not planned". That is why nobody noticed
for three years: GitHub renders
completedas resolved, so they read as done at a glance, and nosymptom search surfaces them.
scripts/upstream-sweep.shsearchesupdated:>=last_swept, soanything last touched in 2023 could never appear in a sweep either - the cohort was structurally
invisible to our own tooling, and none of the 28 were in the manifest.
What this PR contains
The manifest section (
upstream-map.yaml) - 13 grouped work-item entries plus the sweep history.Grouped rather than 28 near-duplicate entries, because several want designing together: per-topic
handlers with separate consume/produce types, seek with subscription changes, retry expiry with
stall detection.
--audit, a mode that can see what the window search cannot. No time window. Asks "which closedupstream items are neither tracked in the manifest nor mirrored in the fork?", and flags days where
an implausible number of things closed at once. Run against the real repo it rediscovers both sweeps
from scratch and reports zero unaccounted, which is also an end-to-end check that the new
fork_issueslinkage is correct.Three
docs/inflight/notes recording what is not done - see "Deliberately not in scope".The 28 mirrors themselves are already live as #227 - #254
under the new
upstream-admin-closedlabel; this PR is the tracking and tooling half.Verification, not inference
All 28 issues were re-read in full - bodies plus all 58 comments - and each checked against fork
source rather than trusted from its thread. That mattered: four issue bodies point at PRs that
"fix" them, and every one of those PRs was itself swept unmerged.
Outcome: 2 already fixed (confluentinc#41, confluentinc#319 -
mirrored and immediately closed, carrying the evidence), 6 partially addressed, 20 fully open.
Strongest evidence for the "still open" verdicts: confluentinc#29 still blocks on
futureSend.get(...)beside a source comment naming the unmerged PR; confluentinc#144'sretry loop is bounded by a constant named
arbitrarilyChosenLimitForArbitraryErrorSituation; andconfluentinc#183's
ShardKey.KeyWithEquals.equalsreturnstruefor two null keys,collapsing every null-key record into one shard - exactly the live defect that reporter described and
never got a reply to.
All swept PR head commits were checked and are still reachable, so the drafts are recoverable;
entries cite branch and SHA rather than just a PR number.
Bots are excluded from the PR analysis
Dependabot self-closes superseded bumps in batches that look identical to a sweep - 2022-08-16,
2022-10-20, 2023-11-03 and 2024-01-25 are all dependabot - and unfiltered they buried the two real
sweeps. Filtering them surfaced two human PRs sitting inside those batches:
confluentinc#508 (our own docs work, closed with a CLA-not-signed comment) and
confluentinc#650 (closed with no comment at all). Both recorded for review.
Discussions
A third content type, invisible to every mode here and to the manifest. Not swept - 74 of them,
no bulk closure, and the largest day is all
answered=truehousekeeping. The failure mode isdifferent: questions nobody answered, somewhere no issue search reaches.
--auditnow listszero-reply discussions, excluding release-announcement threads by title, which would otherwise put 9
false positives at the top of the report.
Two finds justify the check on their own. Discussion 542 (zero replies since 2023) is a field report
of a transactional consumer evicted from the group after failing to acquire the produce lock
during rebalance, the reporter suspecting the revoke-time flush - the same lock lifecycle as
docs/inflight/bug-producing-lock-double-release.md, and they raised the timeout without effect,which is evidence about the mechanism rather than the duration. Discussion 883 (zero replies) is the
same complaint as #187 but with a runnable reproducer, which that issue lacks.
We do not mirror discussions. Where reading one makes us think there is an issue, we raise a
normal fork issue on its merits, citing the discussion as its source. That keeps
upstream-mirrormeaning exactly "an upstream issue we carry", which is what makes the cutoff verifiable.
Mirror the whole cohort, including the items we rate as worthless
An earlier revision of these notes recommended declining three of the 28. That was optimising for
the wrong thing and has been removed. The point is a clean, known cutoff - "every issue upstream
closed administratively is accounted for" - not a curated pick. A set filtered by our own judgement
is not a cutoff: the next reader cannot tell "not mirrored because it was junk" from "not mirrored
because it was missed". Carrying a few items nobody actions costs almost nothing. Fork issues can
always be closed later on their merits, which leaves a visible record; pruning before mirroring does
not. The reasoning is recorded in the manifest section header so a later tidy-up does not re-prune.
Deliberately not in scope, and recorded rather than fixed
docs/inflight/next-upstream-coverage-completeness.mdstatesthat every upstream issue, PR and discussion must be accounted for, and that this has not been
done: ~139 closed issues unread, ~100 open issues never completeness-checked, 68 of 74 discussions
unread.
--auditkeys on bulk closures and zero-reply threads; both are proxies that miss wholeshapes of problem (a PR closed alone on a quiet day, a discussion with one dismissive reply). The
audit narrows the field - only reading discharges it.
has_projects: trueupstream, but our token lacksread:project.Feature: Transactional mode for individual consume producer loop instead of periodic batch confluentinc/parallel-consumer#40, Feature: Run mode where transition to plain offset committing (no encoded offsets needed) confluentinc/parallel-consumer#49 and
Integrate into a Proxy so can support any language and a server side queue implementation confluentinc/parallel-consumer#154, where absence was easy to establish and little else was checked.
Ruled out and written down so they are not re-investigated: wiki disabled, no security advisories,
all open-milestone issues already mirrored, orphan branches accounted for (
v0.6.x-devis thelambda-actor work already captured via swept PRs;
0.5.3.x's regression fix is on master asa908e1663), and "upstream pushed today" being apushed_atartefact rather than new activity.Checklist
fork.fork_issueskey), and threedocs/inflight/notes.docs/inflight/AGENTS.mdneeded no change.N/Afor unit tests: this is a manifest edit plus a read-only reportingmode with no assertable product behaviour. Verified by running it against the live repo, where
it rediscovers both sweeps and reports zero unaccounted, and by
python3 scripts/upstream-map.py validate(OK: 27 entries, no schema errors) plusrefs/tablestill rendering. Running thatvalidator is what the first review round caught me not doing - an ad-hoc
yaml.safe_loadcheckpassed a
group:value the schema rejects. Three real bugs were found and fixed this way:gh api --slurpalready wraps pages, so.[][]iterated a level too deep and thediscussions section silently printed "(none)" - it failed closed, not loud - and unfiltered bot
batches drowned the signal.
bash -nandshellcheck -S warningare clean; the onlyshellcheck hit is pre-existing, on the untouched drift block.
N/A. No workflow or runner changes.--auditis read-only, posts nothing, and adds no credentials; it reads more of the upstreamAPI than the default mode, so it is not wired into any scheduled job.