Skip to content

Add a scope model and selector mechanism for carried rules (#368, PR-1) - #369

Merged
ptr727 merged 9 commits into
developfrom
rule-scope-mechanism-368
Jul 20, 2026
Merged

Add a scope model and selector mechanism for carried rules (#368, PR-1)#369
ptr727 merged 9 commits into
developfrom
rule-scope-mechanism-368

Conversation

@ptr727

@ptr727 ptr727 commented Jul 20, 2026

Copy link
Copy Markdown
Owner

What and why

First of four staged PRs for #368: give every governance rule a single explicit scope, so carried docs become granular single-scope pieces composed per repo rather than large pieces with internal carve-outs a reader must piece out. This PR is the inert foundation — it adds the mechanism and documents the model, with zero fleet-finding delta. Policy changes (turning on section-presence checking, the doc surgery, carrying Verification Discipline) follow in later PRs, each held before main.

Changes

  • spec/scope-model.md (new, hub-only, not in the carried baseline): the two axes — home (host vs repo) and reach (hub-only / all-downstream / type-specific) — and the appliesTo selector vocabulary: four disjoint namespaces (project types, workflowModel, releaseTrigger, consumerModel). States the load-bearing semantics: appliesTo is any-of (cross-axis AND is deliberately not expressible — that's the signal to split a piece), and entry-level AND section-level appliesTo compose. Records CODESTYLE.md's section→scope mapping (whole-carried, so documented not mechanized).
  • spec/files.schema.json: a sections element may be a bare string or a {name, appliesTo} object (oneOf). Backward-compatible — every existing entry stays valid.
  • spec/audit.py: match appliesTo against a repo's full selector set (repo_selectors), and replace the hardcoded operational develop.json path-swap with two data entries in files.json (appliesTo: ["release"] vs ["operational"]). This is the proof that the selector model subsumes an existing ad-hoc carve-out.
  • spec/validate.py: first pass over files.json — every appliesTo token must resolve to a known selector, no project type may collide with a reserved token (keeps the namespaces disjoint), and every cataloged repo must declare consumerModel so a push/pull-scoped section can't fail open.
  • AUDIT.md §3 + files.json note: one-line pointers to the scope model.

Inertness — verified

The four selector namespaces are disjoint and no current appliesTo token is non-*, so broadening the matcher changes no match. Proven by computing the required-file set for all 21 cataloged repos under old-code+old-data vs new-code+new-data: 0 differences. spec/validate.py stays green (and now also validates files.json). markdownlint, editorconfig-checker clean.

Held on develop — no develop → main promotion — per the standing instruction to observe what returns to develop first.

Foundational, deterministically inert step toward giving every governance rule
a single explicit scope so carried docs are granular single-scope pieces
composed per repo, not large pieces with internal carve-outs.

- spec/scope-model.md (new, hub-only, not carried): defines the two axes
  (host vs repo home; hub-only / all-downstream / type-specific reach) and the
  appliesTo selector vocabulary - four disjoint namespaces (project types,
  workflowModel, releaseTrigger, consumerModel) - with the any-of and
  entry-AND-section semantics. Records CODESTYLE's section->scope mapping.
- spec/files.schema.json: a sections element may now be a bare string or a
  {name, appliesTo} object (oneOf), so a section can be scoped, not just a file.
  Backward-compatible - every existing entry stays valid.
- spec/audit.py: match appliesTo against a repo's full selector set
  (repo_selectors: types + workflowModel + releaseTrigger + consumerModel), and
  replace the hardcoded operational develop.json path swap with two data
  entries in files.json (appliesTo release vs operational). The namespaces are
  disjoint and no current appliesTo token is non-star, so the required-file set
  is unchanged for all 21 cataloged repos (verified by diffing old vs new).
- spec/validate.py: first pass over files.json - every appliesTo token must
  resolve to a known selector, no project type may collide with a reserved
  token, and every cataloged repo must declare consumerModel (push/pull) so a
  push/pull-scoped section cannot fail open.
- AUDIT.md section 3 + files.json note: one-line pointers to the scope model.

No fleet-finding delta: this PR adds capability and codifies the current state.
The section-presence check, the doc surgery, and carrying Verification
Discipline follow in later PRs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 19:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Introduces a scope model and selector mechanism (appliesTo) for carried governance files/sections, enabling per-repo composition without changing current fleet findings.

Changes:

  • Add scope-selector vocabularies and files.json selector validation to spec/validate.py.
  • Add selector-aware matching to spec/audit.py, including moving the operational develop.json swap from code into spec/files.json data.
  • Add the appliesTo model documentation (spec/scope-model.md) and extend spec/files.schema.json to allow per-section selector objects.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
spec/validate.py Adds selector vocab + validates consumerModel presence and files.json appliesTo tokens.
spec/scope-model.md Documents the scope model, selector namespaces, and appliesTo semantics.
spec/files.schema.json Extends schema to allow sections entries as strings or { name, appliesTo } objects.
spec/files.json Adds selector-scoped baseline entries (notably release vs operational develop rulesets) and documents appliesTo.
spec/audit.py Computes repo selector sets and applies appliesTo matching when auditing baseline file presence.
AUDIT.md Points readers to the scope model when explaining applicability for carried files/sections.

Comment thread spec/scope-model.md Outdated
Comment thread AUDIT.md Outdated
…#369)

The repo doc convention (AGENTS.md) requires reference-style links in every
markdown file except the agent-instruction files. Convert spec/scope-model.md's
inline links to reference-style with a definitions block, and change the new
AUDIT.md pointer to the reference-style [scope-model] with its definition added
alphabetically to the Repo group. Both Copilot round-1 findings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 19:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

spec/files.schema.json:40

  • The top-level entry "appliesTo" schema is also unconstrained for array element types. Restrict it to "string" or "array of strings" for consistency with the section-level form and to prevent non-string tokens from reaching runtime matchers.
                    "intentRef": { "type": "string" },
                    "appliesTo": { "type": ["string", "array"] }
                }

Comment thread spec/scope-model.md Outdated
Comment thread spec/scope-model.md Outdated
Comment thread spec/files.schema.json
Comment thread spec/validate.py
Comment thread spec/audit.py
…ons (#369)

Copilot round-2 findings:
- files.schema.json: appliesTo array items are now constrained to strings at
  both the entry level and the new section-object level, so a non-string token
  cannot reach the runtime matchers.
- validate.py: check_selector guards a non-string token explicitly (CI runs no
  JSON-schema validation) instead of crashing on set membership of an
  unhashable value.
- scope-model.md, audit.py, AUDIT.md: replace clause-joining semicolons in the
  new prose and docstring with the house comma/two-sentence form.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 19:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Comment thread spec/scope-model.md Outdated
Comment thread spec/audit.py Outdated
Copilot round-3 findings: scope-model.md and the repo_selectors docstring
claimed every selector resolves repo -> defaults -> fleet default, but
consumerModel has no fleet default - validate.py requires it on every cataloged
repo instead. State each selector's resolution accurately: workflowModel and
releaseTrigger fall back to a fleet default (release, two-phase); consumerModel
is required per cataloged repo, so a cataloged repo always contributes one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 20:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Comment thread spec/validate.py
Copilot round-4 finding: check_selector accepted an empty appliesTo list, which
scopes an entry or section to no repo at all (it fails open - silently applies
nowhere). Reject [] in validate.py with a clear message, and add minItems:1 to
both appliesTo definitions in files.schema.json so an empty list is also a
schema violation. Use "*" for all repos, or list selectors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 20:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Comment thread spec/validate.py
Comment thread spec/validate.py
Copilot round-5 findings: releaseTrigger is now a scope selector, but validate
checked only workflowModel, so an invalid releaseTrigger would silently fail to
match any releaseTrigger-scoped section instead of erroring. Add per-repo and
defaults.releaseTrigger validation against RELEASE_TRIGGERS, mirroring the
workflowModel checks, and drive all three model/trigger/consumer checks off the
vocabulary constants so the messages and membership tests share one source.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 20:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Comment thread spec/scope-model.md Outdated
Comment thread spec/scope-model.md Outdated
Comment thread spec/scope-model.md Outdated
Comment thread spec/scope-model.md Outdated
…#369)

Copilot round-6 findings: three scope-model.md headings were sentence-case
(AGENTS.md title-case rule) and the CODESTYLE section-scope list used semicolons
between comma-free items, reading as clause-joining. Title-case the headings and
switch the list to commas.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 20:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

spec/audit.py:100

  • repo_selectors() falls back to defaults.get('consumerModel'), but registry/repos.schema.json does not allow defaults.consumerModel (defaults has additionalProperties:false and only defines groundTruthBranch/releaseTrigger/workflowModel). Keeping a defaults fallback here is dead code and can mislead future editors about supported registry shape; prefer to read only the per-repo consumerModel (which validate.py already requires for cataloged repos).
    sel.add(entry.get("workflowModel") or defaults.get("workflowModel") or "release")
    sel.add(entry.get("releaseTrigger") or defaults.get("releaseTrigger") or "two-phase")
    cm = entry.get("consumerModel") or defaults.get("consumerModel")
    if cm:
        sel.add(cm)

Copilot round-7 low-confidence finding (correct): repo_selectors fell back to
defaults.get("consumerModel"), but registry/repos.schema.json's defaults object
is additionalProperties:false with only groundTruthBranch/releaseTrigger/
workflowModel, so defaults.consumerModel can never exist - the fallback was dead
code that misrepresents the supported registry shape. Read only the per-repo
consumerModel (validate.py requires it on every cataloged repo); the guard stays
to shield a malformed non-cataloged entry. Still inert: required-file set
unchanged for all 21 repos.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 20:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Comment thread spec/validate.py
Copilot round-8 finding: the files.json validation loop exists because CI runs
no JSON-schema validation, but it assumed well-formed input - a non-array
baseline, a non-object entry, a non-array sections, or a non-string/non-object
section element would raise AttributeError or iterate a string instead of
reporting a clear validation error. Guard each shape and continue, mirroring the
existing secrets.json shape checks. Verified all four malformed shapes now exit
1 with a clear message and no traceback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 20:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 426591f into develop Jul 20, 2026
7 checks passed
@ptr727
ptr727 deleted the rule-scope-mechanism-368 branch July 20, 2026 21:32
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