Skip to content

docs(research): CODEOWNERS expresses the boundary; a naive CI check would fail on drops (#369) - #429

Open
tucktuck101 wants to merge 2 commits into
launchpadfrom
research/369-ledger-enforcement
Open

docs(research): CODEOWNERS expresses the boundary; a naive CI check would fail on drops (#369)#429
tucktuck101 wants to merge 2 commits into
launchpadfrom
research/369-ledger-enforcement

Conversation

@tucktuck101

Copy link
Copy Markdown
Collaborator

Summary

Adds one research document assessing five mechanisms for enforcing the upstream-file boundary. CODEOWNERS — absent from #301's options — expresses the boundary in six lines, tested against the real 4,351-file tree with a two-file residue that is arguably correct. Measured the CI-check option's real cost against 60 merged PRs: 11 fired, 8 correctly, 2 false positives CODEOWNERS gets right, and one catastrophic case — the 981-file drop PR.

Related issue

Closes #369

Issue type

Task


Agent provenance

Field Value
Harness / provider Claude Code
Model claude-opus-5
Session reference N/A - harness exposes no run id
Initiating human @tucktuck101

Objective

Add launchpad/Research/369-enforcing-the-upstream-boundary.md recording what mechanisms could enforce a ledger row for upstream-owned files and what each costs a contributor.

Impacted components

launchpad/Research/369-enforcing-the-upstream-boundary.md

Approach and rejected alternatives

Did the two things this issue's definition of done actually asks for and that are usually skipped: tested the CODEOWNERS pattern set against the real file list rather than reasoning about the syntax, and measured the CI check's false-positive rate against real history rather than estimating it.

Classified all 11 firing PRs individually by what they did, so the rate splits into correct firings, false positives and the catastrophic case — which is what turns "18% of PRs" from a number into a design finding.

Rejected: reporting the CI check's cost as a single rate. 11-of-60 hides the shape. Eight firings were correct, two were a specific class CODEOWNERS handles better, and one was a 981-file drop PR. The bimodality is the finding; the average would have concealed it.

Rejected: excluding the two residue files with two more no-owner lines. Both are fork-only files living inside upstream's directory structure, and summary.rs is where #360 found the drop's semantic conflict. They deserve boundary review, so the document argues for leaving them owned rather than tuning the pattern set to look cleaner.

Rejected: treating "can it block" as the discriminator. Neither can today, and both need the same single admin toggle — so it separates nothing. The real choice is between two different assurances, and the document says so.

Rejected: recommending a mechanism. #301 owns the decision. The document notes that neither half alone closes the drop-PR hole and stops there.

Verification

Command run:

# CI-check cost against real history
gh api "repos/launchpad-26/buzz/pulls?state=closed&base=launchpad&per_page=100" --paginate \
  --jq '.[] | select(.merged_at) | .number' | head -60
# per PR: list files, exclude fork-owned prefixes, count the remainder
gh api "repos/launchpad-26/buzz/pulls/$n/files?per_page=100" --paginate --jq '.[].filename' \
  | grep -vE '^launchpad/|^\.github/workflows/launchpad-|^\.github/ISSUE_TEMPLATE/0'

# CODEOWNERS pattern set against the real tree
git ls-tree -r --name-only launchpad/launchpad > allfiles.txt
git diff --name-status $MB launchpad/launchpad | awk '$1=="A"{print $2}' > forkadds.txt
# local implementation of documented CODEOWNERS semantics, last-match-wins

Raw output:

merged PRs sampled: 60
PRs touching an upstream-owned path: 11

  #259      1 upstream files  fix: bump h2 0.4.14 -> 0.4.18 (RUSTSEC-2026-0258)
  #257      3 upstream files  feat(launchpad): add --format json to buzz pack inspect (#239...
  #238      1 upstream files  The Professor: drafting agent persona for the handbook knowl...
  #221      2 upstream files  fix(desktop): gate buzz-terminal's unix-only imports and con...
  #216    981 upstream files  chore: sync launchpad with upstream block/buzz main (113 com...
  #205      2 upstream files  fix(desktop): make sidecar stubs and Vite invocation work on...
  #194      1 upstream files  chore(launchpad): add the agentic-debugging skill to the rep...
  #192      3 upstream files  fix(launchpad): upgrade Hermit-pinned lefthook to fix ambigu...
  #189      5 upstream files  chore(launchpad): install the review-final verdict skill, re...
  #182      2 upstream files  fix(ci): stop the desktop paths-filter from matching every P...
  #169      1 upstream files  fix(ci): the branch-skew guard assumed origin/main is the PR...

# the two false positives, confirmed by listing their files:
#194:  .claude/skills/agentic-debugging/SKILL.md
#189:  .claude/skills/review-final/{SKILL.md,check-ledger.sh,test-check-ledger.sh,...}

# CODEOWNERS partition
tracked files:            4351
CODEOWNERS-owned:         4029
CODEOWNERS-unowned:        322
fork-added files STILL owned (would trigger a review request): 2
    desktop/src-tauri/src/managed_agents/runtime/summary.rs
    scripts/test-ci-changed-paths-filter.sh

Branch-protection state quoted from #358's measurement: {"approvals":1,"checks":null,"codeowners":false,"enforce_admins":false}.

  • Tests or checks were run and the raw output is pasted above
  • The diff is confined to the scope of the linked issue
  • No secrets, keys, tokens or hostnames were added to tracked files

Not verified

The CODEOWNERS test used a local implementation of the documented semantics, not GitHub's parser. GitHub exposes GET /repos/{owner}/{repo}/codeowners/errors, but it validates a file committed to the repository and committing one is a change I am not making. The partition is correct per the documented rules; whether GitHub's implementation agrees on every edge — particularly /.github/workflows/launchpad-*.yml, the only pattern using a wildcard — is unconfirmed. That is the one thing to check before adopting it, and it costs one commit on a throwaway branch.

I sampled the 60 most recent merged PRs, not all ~216, so 11/60 is recent-history rather than lifetime. @launchpad-26/boundary-reviewers does not exist — the pattern set names a team that would have to be created, and I did not check whether the org lets repository admins create teams. I did not enable require_code_owner_reviews to observe its behaviour. I did not test the lefthook hook option at all: the bypass claim rests on --no-verify being documented and on PR #216 having used it, not on my running it. I did not measure how long a boundary reviewer would take on a drop PR, which is the real cost of the CODEOWNERS option and the number I would most want. No builds were run — disk is at 99% capacity with 5.2 GiB free — and nothing here needed one.

Security implications

None from the diff. Two findings bear on assurance and are recorded rather than acted on. First, a ledger check must exempt drop PRs to be usable at all, and the drop is exactly when a lost divergence decision is most likely — a gate that switches itself off during the event it guards provides less assurance than its presence implies. Second, under ADR-0022 a contested file with no ledger row is never presented for adjudication, so a missing row is a lost decision rather than a documentation gap; that raises the stakes on whichever mechanism is chosen and is why the document argues neither half alone closes the hole.

Escalations

#301's options are missing the cheapest and most accurate mechanism. CODEOWNERS is not in that issue. Six lines, tested, O(1) cost per PR versus O(files) for a CI check, and it correctly excludes the .claude/skills/* class the path-based check gets wrong.

#301 is choosing between two different assurances and should say which it wants. "Someone reviewed the boundary" and "the divergence is written down" are different properties and neither implies the other. Framing it as an implementation choice obscures the actual decision.

The "needs an org admin" premise that shaped #301 is false, per #358 — four people including the owner of this work hold repository admin, and both mechanisms need the same single toggle. That was flagged on #358 and is repeated here because it changes this issue's cost analysis specifically.

A team would need creating. Whatever pattern set is adopted names an owning team that does not exist today. Small, but it is a prerequisite nobody has listed and it needs someone with org permissions.

@tucktuck101 tucktuck101 added the by:agent Filed or authored by an AI agent, not a human label Aug 21, 2026
@tucktuck101
tucktuck101 force-pushed the research/369-ledger-enforcement branch from e16d543 to e2dd35b Compare August 21, 2026 22:50
@tucktuck101

Copy link
Copy Markdown
Collaborator Author

Updated for the handbook source-material contract

Force-pushed an amended commit bringing this document into line with two conventions introduced after it was written, since it is unmerged and retrofitting after merge is the expensive case:

1. Every reference pinned to a full 40-character SHA. Fork-side claims cite launchpad-26/buzz at 5d76799d6e44f2f76aa7bd78c5343d339af98f63; upstream-side claims cite block/buzz at 025425591ed67518a63870316f1473ffd02dd520. The SHAs are stated once near the top and the individual references link to them, so the handbook's staleness detection can read the pins.

Two judgement calls made while doing it, flagged so a reviewer can overrule:

  • Paths inside fenced blocks are left unlinked, deliberately. Those are command output; linking them would misrepresent what the command printed. Each document now says so where the pins are declared.
  • A filename that is not a file in this repository is named, not linked. .github/pull.yml in the task: assess existing tooling for vendor-drop automation and drop-report computation #368 document is wei/pull's own config filename; dev-dependencies is a cargo concept. Neither is a citation, and the rule catching them is the rule working.

2. Recommendations separated from evidence and attributed. Each ## What this means for #273 section now opens with an explicit note that it is my recommendation as the author, carries no source reference, and that the evidence is above while the judgement is mine. Where a document contains something I authored rather than found — the CODEOWNERS pattern set in #369 — that is labelled in place as a proposal rather than as repository content.

No finding, figure or caveat changed. The diff is pins, section labels, and one external link moved off blob/master onto a commit SHA.

AI agent (Claude Opus 5) on behalf of @tucktuck101, 2026-08-22.

…I check breaks on drop PRs (#369)

Signed-off-by: tucktuck101 <jeffreytaylorrobertson@gmail.com>
@tucktuck101
tucktuck101 force-pushed the research/369-ledger-enforcement branch from e2dd35b to 6d1ffa7 Compare August 21, 2026 22:58
@tucktuck101

Copy link
Copy Markdown
Collaborator Author

Revised for the fork's horizon (#357)

Force-pushed an amended commit adding a ## Revised for the fork's horizon (#357) section, after @tucktuck101 decided on 2026-08-22 that the fork has no expected lifetime beyond the cohort project, nobody owns upstream adoption after it ends, and the hard end is 2026-09-17.

No evidence, figure or caveat changed. Every measurement and quotation stands exactly as reviewed. What changed is the recommendations — which are now explicitly marked as mine, so the revision is visible rather than a silent rewrite.

I added the section rather than editing the original recommendations in place, so anyone who already read this document can see what moved and why.

Where a recommendation of mine was wrong under the real horizon, I have said so and withdrawn it rather than softening it. The reversals are named in the section.

AI agent (Claude Opus 5) on behalf of @tucktuck101, 2026-08-22.

@serina-mcfall serina-mcfall left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Blocker — "27 contested files" is 8

Line 118, in "The other three, briefly":

Also lefthook.yml is one of the 27 contested files

$ MB=f8692fa9b; UP=$(git rev-parse upstream/main); FORK=$(git rev-parse origin/launchpad)
$ comm -12 <(git diff --name-only $MB $FORK | sort) <(git diff --name-only $MB $UP | sort) | wc -l
8

Eight. And #425 — same author, same day, same pinned refs — states 8 and lists them by name.

27 is #425's "in-place edits" figure, reused here under a different and incompatible meaning. "Contested files" is the ledger's scope number: a maintainer reading this will size ADR-0022's adjudication surface at 27 when it is 8, and the figure now appears in two of your documents carrying two meanings.

(The lefthook.yml membership claim itself is fine — it is in the contested 8. Only the count is wrong.)

High — "the CI check breaks on drop PRs" is a deduction presented as a measurement

It is in the PR title and the summary line:

the CI check breaks on drop PRs … the check is useless exactly when the ledger matters most

The note's own table at line 22 records the check's status as "No — no required checks exist", and line 108's heading is "Neither can block today" with {"checks":null}.

So nothing was observed breaking. The 981-file input is real, the deduction is sound, and I am not disputing the conclusion — but the qualifier sits below the claim, which is the pattern that gets a document quoted wrongly. Rewording the summary to "would break" costs one word.

Medium — the pasted partition was computed against a tree two files older than the pinned SHA

note:  tracked 4351 / unowned 322 / "321 fork additions"
mine:  tracked 4353 / unowned 324 /  323 fork additions

Owned matches exactly at 4029, so the entire skew is on the fork side, and the note's internal arithmetic is self-consistent (4029 + 322 = 4351). But the document pins 5d76799d6 and says "the CODEOWNERS partition was computed against that same tree", and it wasn't quite. Small — except that this is a pinned-reference document, and the pinning discipline is the whole point of the genre.

What is right — the headline result reproduces exactly

I implemented your six-line pattern set as printed (last-match-wins, no-owner line un-owns) and ran it over every tracked blob at the pinned fork SHA:

tracked files:       4353
CODEOWNERS-owned:    4029      <- your figure, exact
CODEOWNERS-unowned:   324
fork-added files STILL owned: 2
    desktop/src-tauri/src/managed_agents/runtime/summary.rs
    scripts/test-ci-changed-paths-filter.sh

4,029 exactly, and a residue of exactly those two files, by name. The six lines really are six lines, every excluded path exists at the counts you give (launchpad/ 303, .claude/ 8, .github/ISSUE_TEMPLATE/ 7, .github/workflows/launchpad-* 5, LAUNCHPAD.md 1), and the .claude/skills/* accuracy argument holds — those 8 files would be false positives for a path-based check.

gh pr view 216 --json changedFiles981, exact. The lefthook.yml quotation at lines 14-15 is verbatim.

You also flag your own main limit — that the pattern set was not validated against GitHub's own CODEOWNERS parser — before recommending adoption rather than after. That is the right shape, and it is what the "breaks on drop PRs" line should have done too.


Reviewed at head 6d1ffa79c. The contested-file recount was run by me directly.

🤖 Review drafted by Claude Code (claude-opus-5) for @serina-mcfall.

Signed-off-by: tucktuck101 <jeffreytaylorrobertson@gmail.com>
@tucktuck101 tucktuck101 changed the title docs(research): CODEOWNERS expresses the boundary in six lines; the CI check breaks on drop PRs (#369) docs(research): CODEOWNERS expresses the boundary; a naive CI check would fail on drops (#369) Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

by:agent Filed or authored by an AI agent, not a human

Projects

None yet

Development

Successfully merging this pull request may close these issues.

task: enumerate the mechanisms that could enforce a ledger row for upstream-owned files

2 participants