Skip to content

chore(ci): derive the source-boundary guard from the policy manifest - #1069

Merged
abrichr merged 1 commit into
mainfrom
chore/derive-boundary-guard-from-manifest
Jul 28, 2026
Merged

chore(ci): derive the source-boundary guard from the policy manifest#1069
abrichr merged 1 commit into
mainfrom
chore/derive-boundary-guard-from-manifest

Conversation

@abrichr

@abrichr abrichr commented Jul 28, 2026

Copy link
Copy Markdown
Member

The problem

source-policy.yaml is described as the machine-readable manifest of what may
live where. No CI guard read it. This repository's check_source_boundary.py
kept 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 canonical
manifest (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 denylist
as a one-way ratchet:

  • every legacy path token still fails the path scan (planted file per token)
  • every legacy content pattern still fails the content scan
  • every token in the rendered policy is proven to be enforced, not just present
  • a clean tree still passes
  • each fail-closed path: missing file, invalid JSON, empty path_tokens,
    missing enforcement, unknown schema_version, unclassified repository

The 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.

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
@abrichr
abrichr merged commit 52a8a24 into main Jul 28, 2026
11 checks passed
@abrichr
abrichr deleted the chore/derive-boundary-guard-from-manifest branch July 28, 2026 03:08
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