chore(ci): derive the source-boundary guard from the policy manifest - #1069
Merged
Conversation
The open-core boundary is described by a machine-readable manifest (source-policy.yaml, canonical in the private openadapt-internal repo), but nothing read it. This guard kept its own hand-copied denylist of the same crown-jewel categories, so the manifest advised while a separate copy enforced, and the two could drift silently. The rules now come from source-policy.public.json: a generated file rendered from the canonical manifest, carrying only the publishable subset (category names, public repository classifications, and the match rules). A public CI job cannot read a private manifest and must never be given a way to, so the rules travel outward as a generated artifact instead. The private repository re-renders and compares this copy daily. The guard fails closed. A missing, unparseable, incomplete, or unknown-schema policy exits 2 and stops the build; a guard that passes with no rules is worse than the hardcoded list it replaced. An unclassified repository also exits 2, which makes the manifest's coverage rule mechanical. Nothing got weaker. tests/test_source_boundary.py pins the exact previous denylist as a one-way ratchet and proves each entry still blocks, proves every policy token is actually enforced, and proves each fail-closed path. The guard additionally gained the private path segments and the private-artifact content banner, which it did not check before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
source-policy.yamlis described as the machine-readable manifest of what maylive where. No CI guard read it. This repository's
check_source_boundary.pykept its own hand-copied denylist of the same crown-jewel categories, so the
manifest advised humans and agents while a separate copy did the enforcing. The
two could drift silently, and the manifest is the thing people trust.
The change
The guard now derives every token, pattern, and signature from
source-policy.public.json, a generated file rendered from the canonicalmanifest (
OpenAdaptAI/openadapt-internal:source-policy.yaml).The canonical manifest is private and this CI job is public. A public job cannot
read a private file and must never be given a way to, so the rules travel
outward as a generated artifact rather than the guard reaching inward. The
private repository re-renders and compares this copy against the canonical
manifest daily; the renderer refuses to emit prose, private repository entries,
or any undisclosed private repository name.
Fails closed
A missing, unparseable, incomplete, or unknown-schema policy exits 2 and
stops the build. A guard that silently passes when its rules are absent is worse
than the hardcoded list it replaced, because everyone believes it ran. An
unclassified repository also exits 2, which makes the manifest's coverage rule
("every active repository must have an entry") mechanical instead of a comment.
Proof it did not get weaker
tests/test_source_boundary.py(66 cases) pins the exact previous denylistas a one-way ratchet:
path_tokens,missing
enforcement, unknownschema_version, unclassified repositoryThe guard also gained two checks it never had: private path segments
(
private/,.private/) and the private-artifact content banner.The workflow runs those tests on every PR and push.