Skip to content

Make the runtime boundary check blame only a PR's own increase - #15153

Merged
cderv merged 14 commits into
mainfrom
rt/02c-ratchet-pr-delta
Oct 22, 2026
Merged

cderv merged 14 commits into
mainfrom
rt/02c-ratchet-pr-delta

Conversation

@cderv

@cderv cderv commented Oct 22, 2026

Copy link
Copy Markdown
Member

Follow-up to #15135, whose absolute comparison failed every open PR after a merge race: #15134 merged 20 minutes after the ratchet, its checks had run on a base without it, and its six new Deno.* calls in two test files went unchecked.
main went red (run) and, because pull_request runs check out the merge commit, #15136 failed for an increase it did not make (run).
The baseline was raised on main in 8599166 to recover.

The error text had a second problem: it always says "Application code must use src/deno_ral/", but src/deno_ral has no readTextFile, writeTextFile, makeTempDir or env yet, and only a thin call() for processes.
A bug fix that adds Deno.readTextFileSync in src failed CI with nothing to change.

Plan: 02c-ratchet-pr-delta-and-guidance.md

Fix

Pull request runs compare the merge commit with its first parent (--against HEAD^1, fetch-depth: 2) and judge only the PR's own change.
Pushes to main and manual runs keep the absolute baseline gate.

An increase fails a PR only for a Deno.* API listed in kRalDenoReplacements: an exact key whose src/deno_ral replacement is equivalent today (build.os and build.arch, in the scopes that can import deno_ral; not in resources, src-js or src/core/lib, which the browser-side YAML intelligence imports).
Every other increase is a warning on the added line plus a job summary with the per-scope delta and a maintainer step: update the branch with main, run --update-baseline --accept-increase, push the baseline change to the PR.
The summary prints the expected counts after merge.

Increases net out per Deno API and per std module before any file is blamed, so a renamed file or a moved call is not a finding.
std imports are never errors: whether a deno_ral wrapper replaces an import depends on the symbols used, on submodule semantics (path/posix) and on type exports, which a name check cannot establish.
They get a suggested module from kStdSuggestions in the warning.

--accept-increase replaces --accept-tests-increase and accepts any scope, since an uncovered increase in src needs a bump as well.
A PR that adds a deno_ral replacement adds its keys to kRalDenoReplacements in the same change (rule added to _TEMPLATE.md), which turns later increases of that API into errors.

PR comments were not used: fork PRs have a read-only token, and pull_request_target is not worth its security surface for text the job summary already carries.

Replaying the two incidents on Windows 11:

Checklist

I have (if applicable):

  • referenced the GitHub issue this PR closes
  • updated the appropriate changelog in the PR
  • ensured the present test suite passes
  • added new tests
  • created a separate documentation PR in Quarto's website repo and linked it to this PR
AI-assisted PR
  • AI tool used: Claude Code (Sonnet 5.5 for the implementation, Opus 5.5 for advice and a whole-branch review)
  • Codebase grounding: local clone
  • Human review: I have reviewed, tested, and verified the AI-generated content before submitting.

Note: autonomous AI agents submitting PRs without human oversight are not permitted — see the Code of Conduct.

@posit-snyk-bot

posit-snyk-bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

cderv added 14 commits October 22, 2026 15:57
… import

The ratchet needs to know which file and which API raised a count before it can say what to change. countText returns the Deno API keys and the counted std specifiers of one file, and countScope sums it, so the absolute totals are unchanged (checked on main: same five rows).

scopesOf shares one predicate with countScope, so a changed-file list can be mapped to scopes with the same exts, exclude, include and skip rules.

The new tests are characterisation tests of new pure functions: their first run failed at module load, which is not a RED step.
… replacements

diffChanges turns a list of changed files into per-scope deltas and findings. Increases net out per Deno API and per std module before any file is blamed, so a renamed file or a moved call is not a finding. Only an exact API key in kRalDenoReplacements (build.os, build.arch: the ones src/deno_ral covers equivalently today) carries a replacement; std imports only ever carry a suggestion, because a name check cannot show that a wrapper exports what the file uses.

The new tests are characterisation tests of new pure functions: their first run failed at module load, which is not a RED step.
A covered Deno API is the only error; everything else is a warning that says nothing needs to change in the PR and a maintainer accepts the count. The job summary carries the per-scope delta, the replacement to use, and the maintainer block with the expected post-merge counts, so the reviewer sees at review time that a baseline bump is needed. When main is already above the baseline and the PR adds nothing, the result is a notice, not a failure.

The new tests are characterisation tests of a new pure function: their first run failed at module load, which is not a RED step.
A comment or string such as Deno.constructor made the per-API count read the inherited Object.prototype.constructor, so the scope total became a string and the delta NaN: the positive-delta guard then skipped every finding, including a covered Deno.build.os increase, and the PR passed. Deno.__proto__ was dropped entirely by the inherited setter.

countText and the empty counts of diffChanges now use Object.create(null), so any identifier is an ordinary key.
Pull request runs check out the merge commit, so comparing absolute counts with the baseline blamed every open PR for an increase merged on main by another PR (#15136 after #15134). With --against HEAD^1 the PR is compared with the base branch tip and judged only on its own change; push runs keep the absolute baseline gate.

gitChanges reads NUL-delimited git output through execProcess, splits renames into delete + add and checks every git call. --accept-increase replaces --accept-tests-increase and accepts any scope, since an uncovered increase in src needs a bump too.

The new tests are characterisation tests of new functions: their first run failed at module load, which is not a RED step.
Plans that raise a count tell the executor to run --accept-increase now that the flag covers every scope, and the README lands 02c before any count-changing PR. The template carries the rule that a PR adding an equivalent deno_ral replacement adds its exact keys to kRalDenoReplacements in the same change, because a key added before the replacement works would fail contributor PRs with a hint that does not compile.
…tree

A comparison with a long history changes enough files that the path arguments exceed the Windows command-line limit (os error 206 on a 1000-commit comparison), so delta mode failed before attributing anything. The existing set membership check already decides which paths exist at the base, so ls-tree no longer takes a pathspec.

The regression test fails only on Windows: the Linux argument limit is about 2 MB, so there it passes with or without the change.
…line notice

The notice compares the base (current minus the PR delta) with the baseline. Only a zero-delta test asserted notices, so computing the base as the current counts would have put a false "main is above" notice on every PR that raises a count while the suite stayed green. The raising-count renderDelta test now asserts no notice; with the base computed from current alone it fails.
… own

The "main is above the baseline" notice reconstructs the base counts as current minus the PR delta. Comparing them with the PR's working-tree baseline gave a false notice for every PR that lowers a count and commits the lowered baseline (src 994 > 993). Delta mode now reads the baseline committed at the --against revision; a revision without one (before the ratchet) falls back to the working-tree baseline.
src/core/lib is imported by the browser-side YAML intelligence, so it cannot import src/deno_ral/platform.ts (it probes Deno in a try block instead). A new Deno.build.os there got the blocking error with a deno_ral hint that cannot compile in that code, and --accept-increase cannot clear a finding. Findings under src/core/lib/ now use the uncovered warning text.
…ort deno_ral

The map covered both APIs in every scope, so a Deno.build.os added to the engine extension template (scope resources, user code) or to a plain JavaScript file (src-js) failed CI and asked for src/deno_ral/platform.ts, which neither can import. They are now errors only in src, package/src and tests; elsewhere the finding is the uncovered warning.
…eck can fail

Verification 4 appended Deno.build.os to src/core/lib/text.ts and expected exit 1, but findings under src/core/lib/ are warning-only since that code is browser-shared. The recipe now uses src/command/utils.ts, where the covered error and the exit code still show.
@cderv
cderv force-pushed the rt/02c-ratchet-pr-delta branch from 9bfc775 to 861e72a Compare October 22, 2026 13:58
@cderv
cderv merged commit 67893c6 into main Oct 22, 2026
52 checks passed
@cderv
cderv deleted the rt/02c-ratchet-pr-delta branch October 22, 2026 15:27
cderv added a commit that referenced this pull request Oct 22, 2026
The README asks whoever merges a plan's PR to move its row in "The
sequence" from `planned` to `merged`. PR 02c landed in #15153, so the
row now says so.

This matters beyond bookkeeping: Phase 0 states that every PR that
changes a Deno or std count lands after 02c, and the next plans
(00a onwards) check their dependencies against this table at step 0.
cderv added a commit that referenced this pull request Oct 22, 2026
The plans themselves merged to main in #15133, so HANDOFF.md no longer
points readers at the fork branch they were drafted on, and "How to
resume" starts from main.

PR 02 merged in #15135 and PR 02c in #15153. The README sequence row
for 02 still said "in progress", and HANDOFF.md still named PR 02 as
the first plan to open. Plans check their dependencies against that
table at step 0, so it now says merged, and the handoff names 00a
then 00b as the next plans in README order.

docs: runtime migration, wrap-up checklist for agents

The README already says who moves a row in "The sequence" and when,
but a merge on GitHub runs no session, so the status column and
HANDOFF.md drifted: PR 02 still read "in progress" after it merged.
Findings made while executing one plan that change another plan's
prerequisites were also left only in PR threads.

An AGENTS.md in this directory is read by any agent working on the
plans, the implementer included, so the reminder sits where the work
happens. Findings go to README "Open decisions with an owner" rather
than into the affected plan, which its step 0 re-reads anyway. The
file is removed with the directory when the migration ends.
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