Skip to content

docs(plan): split Step 3b-2b into the REST sampler and verify; settle verify's edges (D36) - #27

Merged
ChrisHuie merged 6 commits into
mainfrom
feature/plan-step3b2b-verify
Oct 22, 2026
Merged

ChrisHuie merged 6 commits into
mainfrom
feature/plan-step3b2b-verify

Conversation

@ChrisHuie

Copy link
Copy Markdown
Contributor

Splits Step 3b-2b into two pull requests and settles what plan 5.4 left open about verify, recorded in the plan of record (docs/redesign-plan.md) as D36 and gaps 94-96.

Steps (section 6 and the status table)

  • 3b-2b-1, REST sampler: tests, scripts/, and docs/testing.md; nothing under src/ changes, since Transport.rest_get takes any path and query. It keeps the old scope's sampler clauses and adds:

    • each REST list read 100 entries per page until a page holds fewer (rest_get returns no Link header);
    • REST units stamped like the other kinds, written with the sampled PRs' GraphQL units by a recorder mode of their own into its own directory beside recorded/, replacing that directory alone, as the first-sync mode does; the offline tests replay that directory, and the default mode and --only documents leave it as it is;
    • the recorder's PRIVATE refusal before any request, then the public-only resolve before the first REST request;
    • --repo, refusing prebid-agent-skills before any request (D33);
    • offline tests that compare REST bodies with rows written from that run's GraphQL units, or with seeded rows.

    Acceptance: an adversarial review and CI on the exact head.

  • 3b-2b-2, verify: ghactivity verify, its head check, the timeline lookup and its recordings, the scenario model's event history, and the live acceptance, which runs 3b-2b-1's sampler.

  • Every scope, test, and acceptance clause of the old 3b-2b is in one of the two parts. One acceptance clause is removed: prebid-server-java joining when none of the three repositories holds an in-coverage dismissed review, since all three do (section 7).

Decisions (D36, 5.4)

  • verify writes nothing and claims no run. Before any request it reads sync_runs and exits 1, naming the run, under the claim's rule: a null deadline_at, a future one, or one less than 10 minutes past.

  • Before each repository's first index request, verify reads that repository's repo_sync_state and sync_pending rows in one statement, then its items with their closing links and sync_failures rows in one statement.

  • --repo limits verify as it limits a sync (D27). Without it, the untracked set is the one 5.3 step 1 marks, which requires every configured entry to resolve without failing step 1.

  • Unverified:

    • every repository error the source raises at resolve or index;
    • step 1's own checks: a stored github_id mismatch, gap 92, and a full_name held by another tracked repository;
    • a repository error during a lookup by number or a timeline lookup, which keeps the classes already given and leaves unchecked the items no lookup answered.

    A verify in which every repository it attempts is unverified, with no repository stopped by the deadline, is fatal.

  • Report:

    • a verify that never starts writes none and leaves PATH as it was;
    • once it starts, every exit its own code handles writes one;
    • a report that cannot be written turns 0 or 2 into 1, and an interrupt stays 130;
    • points and REST calls come from the source, and seconds run from start to the report's write.
  • Link totals alone (a PR with more than the 10 links the index carries): drift when the connected events at or after the boundary number at least the increase, or the disconnected events at least the decrease. With an earlier updatedAt, each differing issue still needs an event naming it. The report names each link difference's issues and totals.

  • A timeline lookup that still fails after one solo retry, a filteredCount mismatch included, leaves its item unchecked, which fails only --exact.

Design

  • 5.6: snapshot exits 1 on a missing, unreadable, or stale (last_run_id) verify report, and the manifest records the unverified repositories. Step 6a's tests gain the refusal.
  • 5.8: the lookup reads each PR through repository(owner:, name:), passes since one second before the boundary, and keeps entries at or after it. Its document takes no tests/query_versions.json entry. The recorder's default mode and --only documents record the timeline units.
  • 5.4 child counts: the issue_labels check moves to Step 4a, beside the issue comment and assignee checks 5.1 already assigns there. Step 4a's tests gain all three.
  • 3b-2b-2 acceptance: the start floor is 2,500 points. verify --exact exits 0, or exits 2 with only closing-link errors, each backed by REST evidence.

Evidence (section 7, measured 2026-10-21)

  • Timeline since: on eight events in Prebid.js and prebid-server, since excluded an event created at it and included it 1 ms earlier; filteredCount applied since; every createdAt was a whole second. Each read cost 1 point.
  • Step 3b-2a-2c acceptance, read-only on that run's file:
    • run costs, and per-run failures of 0;
    • 59 PRs above 250 commits, and 247 compare-linked accounts stored by lookups that answered with the requested node id;
    • one compare link answered NOT_FOUND (github_id 1708813), which GraphQL also leaves unlinked;
    • no Copilot row among 2,580 review requests;
    • DISMISSED reviews in all three repositories (25, 622, 3).

Gaps and open items

  • Gap 94: verify's unstated edges. Gap 95: since excludes its own instant. Gap 96: the sampler's paging and the recorder's modes.
  • Section 8:
    • The dismissed-review and node-id-form items are removed, resolved by the acceptance file.
    • The commit-cost item names Step 5 alone, and so does the Copilot item.
    • The disconnected-event and keyword-link items name 3b-2b-2.
    • The keyword-link item notes that events in the window can cover a change in the totals alone.

References

  • The Step 3 overview and section 6 list nine pull requests, and "Step 3b-2b" means both parts.
  • 5.7, 5.8, gap 74, and the scenario-model step name 3b-2b-2 where only verify is meant.
  • Bare references that mean both parts are unchanged (line 14).
  • CLAUDE.md's scope lines name both parts.

Head: cfc040e3181e159959e93f560a4390015bebb322

Ran: uv run --no-sync pre-commit run --files docs/redesign-plan.md CLAUDE.md (passed); live GraphQL reads on prebid/Prebid.js and prebid/prebid-server; read-only sqlite3 on Step 3b-2a-2c's acceptance file.
Not run: pytest, alembic check, and the wheel build (no code changed); REST paging on the sampler's endpoints; a DisconnectedEvent (none found).

… verify's edges (D36)

Step 3b-2b becomes 3b-2b-1 (the REST sampler and its recorder mode) and
3b-2b-2 (verify, its timeline lookup, and the live acceptance).

5.4 states verify's lock check, repository set under --repo, report on
each exit, unverified repositories, link-total differences, and cost
fields (D36, gap 94). 5.8 passes the timeline lookup's since one second
before the boundary, as measured (gap 95). 3b-2b-1 gains a paging rule
and recorder-mode rules (gap 96). Section 7 records the timeline
measurements and Step 3b-2a-2c's acceptance file; section 8 drops the
items that file resolved.
5.4: verify's untracked set follows 5.3 step 1's every-entry-resolved
rule; the lock rule names a future deadline; stored rows are read per
repository before its index; the unverified list names every resolve and
index failure and gap 92; a verify with no repository verified is fatal;
a totals-only link difference needs as many connected or disconnected
events as the change; an interrupt keeps 130 when the report cannot be
written; elapsed seconds are defined.

5.6: snapshot refuses a missing or stale verify report. 5.8: the lookup
keeps entries at or after the boundary, and a count mismatch leaves its
item unchecked. 3b-2b-1: the sampler's REST units live in their own
directory, the sampler takes --repo, and the offline tests pair REST and
GraphQL units from one recorder run. 3b-2b-2: matching tests, a timeline
re-recording rule, a 2,500-point floor, and the exit the acceptance
requires. Step 4a: issue child-count tests. Section 7: per-run failure
counts, code versions, and the compare-linked accounts.
5.4: verify reads each repository's state and pending rows in one
statement, then its items with their links and failures in one
statement; the untracked set requires every entry to resolve without
failing 5.3 step 1; the unverified list adds the full_name check; a
repository error in either lookup leaves the items no lookup answered
unchecked; the per-issue link rule applies only to an earlier updatedAt,
and the report names each link difference's issues and totals.

5.8: the timeline lookup reads through repository(owner:, name:).
3b-2b-1: the sampler's recorder mode writes every unit of its run into
its own directory. D36, gap 96, 5.6, section 8, and the 3b-2b-2 and 6a
test lists follow.
An unchecked item and an unverified repository now fail verify in both
modes, not only under --exact, so the scheduled verify cannot exit 0 on a
database it did not check and D25 does not publish it. A timeline lookup
whose entries still differ from filteredCount after its retry, or whose
response the typed models reject, is an error. D37 records the rule:
an error over a silent failure or silently wrong data. Gap 97, D25's
wording, 5.8, and the 3b-2b-2 tests follow.
@ChrisHuie

Copy link
Copy Markdown
Contributor Author

Head is now 9c2b80326a783f7c87f1786ae1464c5491e65d93. One commit since cfc040e (9c2b803), which changes only docs/redesign-plan.md.

The description's Decisions section says a timeline lookup that still fails after one solo retry leaves its item unchecked, and that unchecked fails only --exact. That is superseded:

  • D37 (new): an error is better than a silent failure or silently wrong data. Where the code cannot check something, or cannot accept what GitHub returned, it reports a failure that stops publication or turns the run red, never a pass.
  • 5.4: an unchecked item and an unverified repository fail verify in both modes. They no longer fail only under --exact. The scheduled verify --max-minutes 40 (5.6) therefore exits 2 when verify could not check something, and D25 does not publish.
  • 5.4 classes:
    • unchecked: a lookup whose request GitHub did not answer after the retries (transport, server, timeout, or rate limit), or one a repository error stopped.
    • error: a lookup whose answer verify cannot accept, meaning entries that still differ from filteredCount after the retry, or a value the typed models reject.
  • 5.8: a filteredCount difference that remains after the retry makes the item an error.
  • D25: "verify exited 0 (no error, 5.4)" now reads "(no failing class, 5.4)".
  • Gap 97 records the gap. 3b-2b-2's tests gain:
    • an unchecked item, and an unverified repository, each making a verify without --exact exit 2;
    • a filteredCount difference after the retry, and a typed-model rejection, each classed as an error.

Ran on this head:

  • uv run --no-sync pre-commit run --files docs/redesign-plan.md (passed);
  • CI on 9c2b803: 4 of 4 checks and 1 of 1 workflow run passed.

Not run: pytest locally (docs only).

…nted inputs

Verify's child-count checks and Step 4a's issue child-count checks are
tested on their passing side, each reporting the rows it compared, equal to
the counts the scenario model or the stored totals give. The timeline
lookup's entries before the boundary are events the scenario model renders.
ChrisHuie added a commit that referenced this pull request Oct 22, 2026
- Step 3b-2a-2d names every test file that plants by file (the engine's
  patched writer and prune, user_version and alembic_version edits, dropped
  indexes, the checkers' planted files, the wheel copy, edited query
  documents), states which inputs stay as D39 inputs, tests env.py's
  file-held violation refusal on its passing side, and accepts on a list of
  every test that writes or patches, with a word search over tests/**/*.py.
- D39 and its four copies except configuration as well as stubs.
- 6a tests verify --db-only's unknown-revision refusal on a file from a
  flawless test-only revision past head.
- 3b-2a-3: the foreign_keys read-back through a fake DBAPI connection, and
  entries for an entry that resolves to another github_id.
- D25 and 5.4: an unverified repository makes the exit 2; a rate limit only
  waits; association drift is a deliberate exception (D38); the timeline
  test's half-second entry states the model's comparison order.
- 3b-2a-2b-2's history, 5.7, and the Step 3 overview follow.
@ChrisHuie
ChrisHuie merged commit 543b03d into main Oct 22, 2026
4 checks passed
ChrisHuie added a commit that referenced this pull request Oct 22, 2026
… inputs (D39) (#28)

* docs(plan): split Step 3b-2b into the REST sampler and verify; settle verify's edges (D36)

Step 3b-2b becomes 3b-2b-1 (the REST sampler and its recorder mode) and
3b-2b-2 (verify, its timeline lookup, and the live acceptance).

5.4 states verify's lock check, repository set under --repo, report on
each exit, unverified repositories, link-total differences, and cost
fields (D36, gap 94). 5.8 passes the timeline lookup's since one second
before the boundary, as measured (gap 95). 3b-2b-1 gains a paging rule
and recorder-mode rules (gap 96). Section 7 records the timeline
measurements and Step 3b-2a-2c's acceptance file; section 8 drops the
items that file resolved.

* docs(plan): tighten Step 3b-2b's rules after review

5.4: verify's untracked set follows 5.3 step 1's every-entry-resolved
rule; the lock rule names a future deadline; stored rows are read per
repository before its index; the unverified list names every resolve and
index failure and gap 92; a verify with no repository verified is fatal;
a totals-only link difference needs as many connected or disconnected
events as the change; an interrupt keeps 130 when the report cannot be
written; elapsed seconds are defined.

5.6: snapshot refuses a missing or stale verify report. 5.8: the lookup
keeps entries at or after the boundary, and a count mismatch leaves its
item unchecked. 3b-2b-1: the sampler's REST units live in their own
directory, the sampler takes --repo, and the offline tests pair REST and
GraphQL units from one recorder run. 3b-2b-2: matching tests, a timeline
re-recording rule, a 2,500-point floor, and the exit the acceptance
requires. Step 4a: issue child-count tests. Section 7: per-run failure
counts, code versions, and the compare-linked accounts.

* docs(plan): close the gaps the convergence review found in Step 3b-2b

5.4: verify reads each repository's state and pending rows in one
statement, then its items with their links and failures in one
statement; the untracked set requires every entry to resolve without
failing 5.3 step 1; the unverified list adds the full_name check; a
repository error in either lookup leaves the items no lookup answered
unchecked; the per-issue link rule applies only to an earlier updatedAt,
and the report names each link difference's issues and totals.

5.8: the timeline lookup reads through repository(owner:, name:).
3b-2b-1: the sampler's recorder mode writes every unit of its run into
its own directory. D36, gap 96, 5.6, section 8, and the 3b-2b-2 and 6a
test lists follow.

* docs(plan): fail verify on anything it could not check (D37)

An unchecked item and an unverified repository now fail verify in both
modes, not only under --exact, so the scheduled verify cannot exit 0 on a
database it did not check and D25 does not publish it. A timeline lookup
whose entries still differ from filteredCount after its retry, or whose
response the typed models reject, is an error. D37 records the rule:
an error over a silent failure or silently wrong data. Gap 97, D25's
wording, 5.8, and the 3b-2b-2 tests follow.

* docs(plan): apply D37 to the sync and to Steps 4b-6 (D38)

Records the decisions on the silent-failure audit as D38 and gaps 98-110: Step 3b-2a-3 makes a failed account refresh, a time-limited run with unreached items, and a hydration that contradicts the run's own index each exit 2, scopes accepted failures to an error class, re-hydrates PRs with more links than the index carries, stores every node id GitHub reports, and closes the source and run-record holes the audit found. Steps 4b, 5, and 6 and 5.6 gain the completeness, permission, staleness, and workflow rules.

* docs(plan): close the gaps the review of D38 found

A hydration that contradicts the run's index gets one engine retry, then
fails. Any deadline makes the exit 2, and the failures a batch made final
before it are recorded. The migration refuses a populated file. A close
that cannot write an outcome raises before writing. code_version names
which git answer it trusts. Commit-link node ids are stored; accepted
failures match the class exactly and can name an account. verify
--db-only writes and digests the working file, snapshot checks the job's
own sync through --after-run, and 6b compares App and token reads before
any App run publishes. The stopped-schedule case is stated as visible
only to a consumer.

* docs(plan): settle the remaining gaps in D38's rules

Accepted failures match class and message exactly and can name an
account by github_id or node_id; a refresh failure an account entry
accepts leaves the exit code alone, and verify --exact matches entries as
5.5 does. code_version decides a checkout by a .git entry before reading
git's exit codes. A stamp is refused by on_version_apply inside the
transaction. Gap 110 refuses only for rows no later run reads. snapshot
takes --after-run/--sync-exit or --sync-run, checks the stored row of
every configured entry, and names its manifest keys. 6b's App-and-token
comparison names its runs and script. Tests follow each rule.

* docs: no planted, seeded, or tampered inputs, and no mutation testing (D39)

D39 records the maintainer's rule: no test, review, or acceptance changes a
copy of the code, a test, the plan, review material, a database, a snapshot,
or a manifest to see whether something catches the change, and none measures
how many such changes are found. Test inputs are recorded GitHub responses,
stubs of what GitHub or the environment can send, calls and configuration the
code must refuse, and files this project's own code wrote; a check only a
defect could fail is tested on its passing side with the exact count it
compared.

- Step 3's evidence rule, the sampler, verify, Step 4a, Step 4b, and Step 6
  no longer prescribe planted mismatches, planted row deletions, tampered
  copies, or a corrupted db-test release.
- Section 7 drops the mutation result from the SQLite pragmas row.
- Step 7's audit replaces merged tests that change a copy of a database or a
  file.
- docs/testing.md, CONTRIBUTING.md, and CLAUDE.md state the same rule.

* docs(plan): close the findings of the last review of D38

- 5.3 step 5: GraphQL's commit connection holds the newest 250, so above 250
  commits its first page must equal compare's positions total-250..total-151
  in order (Prebid.js #8858: positions 4-103); the oldest commits are checked
  by count alone.
- 5.5: an accepted_failures entry matches the class and the cause, the first
  line of the failure's message, which carries no request id, count, or
  typed-model text; tracking.py keeps the recorded class list, held equal to
  the engine's classes by a test; index contradictions take the class
  IndexContradiction; Step 4b adds a compare entry form.
- 5.6 and Step 6: a validated `repo` dispatch input (bootstrap only) for 6b's
  App-and-token comparison, which covers every table the sync writes from
  GitHub, names the cases its sample must hold, and lists App nulls; the
  manifest carries repo_filter, links_not_found, and Step 4b's counts; 6a
  tests compare_reads and lists each exit-code output for the sync.yml test.
- Step 4b: completeness against an ascending read's first event, files from
  the first page, per-compare transactions, a stored error for a compare that
  still fails, deletion counters, verify's own read as unverified, halted vs
  not-synced classes, and merge-brought commits.
- Step 5: the updatedAt measurement's instrument per kind and its counting
  rule, contradiction rates, the link NOT_FOUND share, and the bootstrap's
  projected time; the conditional pull request is 5b.
- 5.1: a read version after Step 6b instead of a refusal, account_node_ids'
  keys and indexes, trigger `unknown`, untracked files outside -dirty, and
  `stamp --purge` at head.
- Step 3b-2a-3: scope and tests follow the above, plus re-recording and
  docs/development.md.

* docs(plan): close the findings of the review of 12b2452

- D39 says "no flaw is seeded" (valid seed.py rows stay allowed), lists one
  set of allowed inputs everywhere it is restated, and widens Step 7's
  replacement to tests that write a planted file, revision, call, connection,
  or decoy; 5.7's planted import files are marked for that replacement.
- The scenario model renders a PR above 250 commits as GitHub does (newest
  250), and the commit-page rule is measured live on the acceptance
  repositories' large PRs before it is fixed.
- A 2xx REST response that is not JSON or lacks a Date is a server error
  whatever its headers; only a readable one without rate-limit headers is
  not understood.
- sync_runs gains link_lookups (the NOT_FOUND limit's denominator, without
  accounts an earlier lookup answered NOT_FOUND) and entries (each configured
  entry's github_id), which snapshot --after-run checks.
- D25 and publish's condition exclude a run with `repo` from release db; the
  sync.yml test checks publish's and report's exact values and snapshot's
  --out and --run-id.
- verify --db-only's digest and manifest refusals are tested on snapshots
  the code wrote; compare_reads maps row ids to natural keys; db-test runs
  go in a stated order.
- Step 4b: a halt exits 2, an empty log is complete when the ascending read
  is empty, a commit-count mismatch or later-page 404 is a compare error, the
  refresh runs first, verify reads stored rows before its reads, and a
  compare entry's not-failed rule.
- 5b covers a recurring index contradiction's retry delay; the report lists
  each contradiction and whether the retry cleared it.

* docs(plan): one D39 text everywhere it is restated; settle the last gaps

- D39's rule is one text, copied word for word into Step 3, CLAUDE.md,
  docs/testing.md, and CONTRIBUTING.md; it allows stubs built in memory from a
  recorded unit, valid rows written in SQL, and configuration naming a state
  GitHub can produce, for tests and acceptances.
- Step 3b-2a-3: the readable-2xx REST rule in its summaries, and how each
  refusing shape is built (replay.modify or the scenario model's set).
- sync_runs.entries records the matched stored row's github_id, else the
  resolved one; snapshot skips an id with no stored row; a snapshot's T is
  the time snapshot writes it.
- 6a states why verify --db-only's unknown-revision refusal is tested on its
  passing side; the acceptance names the refusals the 6a tests cover.
- D38 and 6a state the commit-link limit as 5.2 does; Step 7 names only the
  view-alter revision that leaves a view in place.

* docs(plan): state verify's child-count and timeline tests without planted inputs

Verify's child-count checks and Step 4a's issue child-count checks are
tested on their passing side, each reporting the rows it compared, equal to
the counts the scenario model or the stored totals give. The timeline
lookup's entries before the boundary are events the scenario model renders.

* docs(plan): take planting out of every step; replace the planted tests next (3b-2a-2d)

- New Step 3b-2a-2d, ahead of Step 3b-2a-3: replace every merged test that
  changes a file or a copy of one, or writes a file, revision, call,
  connection, or decoy holding a flaw, to see whether a check catches it;
  a check GitHub, the environment, or a caller can fail keeps a refusing
  side built from D39's inputs, and one only a defect could fail is tested
  on its passing side with the exact count it compared. Step 7's item for
  this is removed, and D39 and its copies name 3b-2a-2d.
- The history of Steps 1, 2, 3a, 3b-1, 3b-2a-2a, and 3b-2a-2c, gaps 52, 68,
  and 80, and 5.7 no longer describe planted, decoy, changed-copy, or
  edited-entry tests, and Steps 1 and 2 no longer accept on code removed or
  a document changed to see a test fail.
- CLAUDE.md lists Step 3b-2a-2d.

* docs(plan): close the review of a2fed22 and f55ae74, and of #27's D37

- Step 3b-2a-2d names every test file that plants by file (the engine's
  patched writer and prune, user_version and alembic_version edits, dropped
  indexes, the checkers' planted files, the wheel copy, edited query
  documents), states which inputs stay as D39 inputs, tests env.py's
  file-held violation refusal on its passing side, and accepts on a list of
  every test that writes or patches, with a word search over tests/**/*.py.
- D39 and its four copies except configuration as well as stubs.
- 6a tests verify --db-only's unknown-revision refusal on a file from a
  flawless test-only revision past head.
- 3b-2a-3: the foreign_keys read-back through a fake DBAPI connection, and
  entries for an entry that resolves to another github_id.
- D25 and 5.4: an unverified repository makes the exit 2; a rate limit only
  waits; association drift is a deliberate exception (D38); the timeline
  test's half-second entry states the model's comparison order.
- 3b-2a-2b-2's history, 5.7, and the Step 3 overview follow.

* docs(plan): close the convergence check of 6dbd284

- 3b-2a-3: SQLite built without foreign-key support answers
  `PRAGMA foreign_keys` with no row, not 0; the listener refuses a missing
  read-back, tested with a fake DBAPI connection that returns no row.
- 3b-2a-2d names test_snapshot.py's half-read cursor control, test_views.py's
  row-id projection control, the authorizer and ATTACH revisions, and the
  revision-rule checker's planted files; the acceptance lists every test that
  runs a call or query holding a flaw, and its review finds no test that
  holds one; the patched size limit becomes an oversized in-memory unit; the
  patched prune's interrupt case stays as an interrupt at a set point.
- 3b-2b-2: no model entry at half a second; under the measured since and
  whole-second times verify's createdAt filter is tested on its passing side.
- 5.4: without --max-minutes a rate-limit wait over the named constant is
  fatal.
@ChrisHuie
ChrisHuie deleted the feature/plan-step3b2b-verify branch October 22, 2026 20:25
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.

1 participant