Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 192 additions & 1 deletion governance/owners.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,201 @@
"entity_type": "P",
"characterization": "synthetic",
"roles": ["portfolio-owner", "governance-authority"],
"ty": {
"given": [
{
"term": "canon",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "ontology",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "governance",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "portfolio-hardening",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
}
],
"earned": []
},
"vy": {
"given": [
{
"term": "github-actions",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "cloudflare-workers",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
}
],
"earned": []
},
"ry": {
"given": [
{
"term": "portfolio-policy",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "trust-anchor",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
}
],
"earned": []
}
},
{
"github": "@nickbianchi",
"chittyid": "PENDING-MINT",
"entity_type": "P",
"characterization": "natural",
"roles": ["service-owner", "governance-authority"],
"ty": {
"given": [
{
"term": "canon",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "ontology",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "governance",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "identity",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "crypto",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
}
],
"earned": []
},
"vy": {
"given": [
{
"term": "github-actions",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "cloudflare-workers",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
}
],
"earned": []
},
"ry": {
"given": [
{
"term": "portfolio-policy",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "trust-anchor",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "mint",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "sign",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
},
{
"term": "audit",
"authority": "chittycanon://core/services/chitty-canon",
"decision": "chittycanon://docs/ops/policy/chitty-canon-charter",
"granted_at": "2026-04-17",
"expires_at": "2026-07-17"
}
],
"earned": []
}
},
{
"github": "@CHITTYOS/cicd-admins",
"chittyid": "PENDING-MINT",
"entity_type": "A",
"roles": ["cicd-admins", "workflow-owners"],
"ty": { "given": [], "earned": [] },
"vy": { "given": [], "earned": [] },
"ry": { "given": [], "earned": [] }
},
{
"github": "@CHITTYOS/developers",
"chittyid": "PENDING-MINT",
"entity_type": "A",
"roles": ["default-owners"],
"ty": { "given": [], "earned": [] },
"vy": { "given": [], "earned": [] },
"ry": { "given": [], "earned": [] }
}
Comment on lines +189 to 206
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm whether anything else (e.g., a tools/script) populates team grants at runtime
# or whether owners.json is the sole source of truth for eligibility.
rg -nP -C3 'cicd-admins|chittyos-runtime|service-ops|product-delivery'
fd -t f 'owners.json|repo_requirements.json'

Repository: chittyfoundation/.github

Length of output: 1617


🏁 Script executed:

cat -n governance/owners.json | sed -n '197,210p'

Repository: chittyfoundation/.github

Length of output: 439


🏁 Script executed:

cat -n governance/rules.json | head -50

Repository: chittyfoundation/.github

Length of output: 1891


🏁 Script executed:

rg -l 'grant|owner|chittyid' --type py --type js --type ts | head -20

Repository: chittyfoundation/.github

Length of output: 216


🏁 Script executed:

rg -i 'populate|bootstrap|grant|eligible' --type-list | head -5
fd -t f '\.(sh|py|js|ts|yaml|yml|json)$' | xargs rg -l 'populate.*grant|grant.*populate|bootstrap' 2>/dev/null | head -10

Repository: chittyfoundation/.github

Length of output: 457


🏁 Script executed:

cat -n governance/rules.json | sed -n '40,100p'

Repository: chittyfoundation/.github

Length of output: 2310


🏁 Script executed:

rg -i 'bootstrap|pending|mint|rollout|phase' governance/ --type json | head -20

Repository: chittyfoundation/.github

Length of output: 420


🏁 Script executed:

fd . -type f \( -name '*.sh' -o -name '*.py' -o -name '*.js' -o -name '*.ts' \) | head -20

Repository: chittyfoundation/.github

Length of output: 238


🏁 Script executed:

rg 'populate|grant|eligible|bootstrap' --type json governance/ -A2 -B2

Repository: chittyfoundation/.github

Length of output: 7599


🏁 Script executed:

cat governance/owners.json | jq '.principals[] | select(.github == "@nickbianchi" or .github == "@chitcommit") | {github, ty, vy, ry}' 2>/dev/null

Repository: chittyfoundation/.github

Length of output: 5598


Eligibility gaps deadlock PRs in CHITTYOS/ and CHITTYAPPS/ repos; SPOF risk on @nickbianchi for chittyid.**

Cross-checking against governance/repo_requirements.json:

Repo pattern Requires (ty / vy / ry) Eligible principal?
CHITTYFOUNDATION/chittycanon canon, ontology, governance / cloudflare-workers, github-actions / trust-anchor, portfolio-policy @chitcommit, @nickbianchi
CHITTYFOUNDATION/chittyid identity, crypto, governance / cloudflare-workers / mint, sign, audit ⚠️ Only @nickbianchi (SPOF)
CHITTYOS/* chittyos-runtime / cloudflare-workers / service-ops ❌ None — both team entries have empty ty/vy/ry
CHITTYAPPS/* app / cloudflare-workers / product-delivery ❌ None

With governance/rules.json set to codeowners_required.enforcement: "block" and require_code_owner_reviews: true across all scoped organizations (including CHITTYOS and CHITTYAPPS), no PR can merge in CHITTYOS/CHITTYAPPS repos: no CODEOWNER principal satisfies the eligibility requirements, so the block cannot be cleared.

Option 1 (immediate fix): Populate @CHITTYOS/cicd-admins and @CHITTYOS/developers grants with the required chittyos-runtime / cloudflare-workers / service-ops terms.

Option 2 (intentional bootstrap scope): Narrow governance/rules.json scope.organizations and/or governance/repo_requirements.json to CHITTYFOUNDATION only for now, with documented rollout sequence for tier 2/3 repos.

Also address the chittyid SPOF: @nickbianchi is the sole eligible approver. Grant @chitcommit (or another principal) the identity, crypto, mint, sign, audit terms to avoid approval bottleneck.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@governance/owners.json` around lines 189 - 206, The two `@CHITTYOS` principals
("@CHITTYOS/cicd-admins" and "@CHITTYOS/developers") have empty ty/vy/ry claims
(and chittyid set to "PENDING-MINT"), causing eligibility gaps for CHITTYOS/*
and CHITTYAPPS/* repos and a SPOF on chittyid for "@nickbianchi"; either (A)
immediately populate the ty/vy/ry arrays for "@CHITTYOS/cicd-admins" and/or
"@CHITTYOS/developers" to include the required terms (e.g., add
"chittyos-runtime" to ty, "cloudflare-workers" to vy, and "service-ops" to ry)
and mint the chittyid (replace "PENDING-MINT" with the real id) or (B) narrow
the enforcement scope (the rule "codeowners_required.enforcement" and/or the org
list in repo requirements) to exclude CHITTYOS/CHITTYAPPS until those grants are
populated; also remove the single-approver SPOF by granting "@chitcommit" (or
another principal) the missing identity/crypto/mint/sign/audit terms so chittyid
repos no longer rely solely on "@nickbianchi".

]
}

38 changes: 32 additions & 6 deletions governance/protected-paths.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
# Authoritative protected paths (regex-style) for governance enforcement.
# These are checked to prevent AI/bot bypasses on sensitive governance/config files.
# ChittyCanon Protected Paths
# These patterns are fetched by hooks at runtime from GitHub
# AI agents CANNOT modify this file (requires PR + human approval)
#
# Format: regex patterns, one per line
# Lines starting with # are comments

^\.github/workflows/
^\.github/CODEOWNERS$
^CODEOWNERS$
^governance/
# Governance core files
governance/sign-rules\.sh$
governance/deploy-governance\.sh$
governance/rules\.json$
governance/rules\.json\.sig$
governance/schema\.json$
governance/CANON_PUBLIC_KEY$
governance/caller-workflow\.yml$
governance/KEY_ISOLATION\.md$
governance/README\.md$
governance/protected-paths\.txt$

# Any file in governance directory
/governance/[^/]+$
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Match governance files with repo-relative path regex

The regex /governance/[^/]+$ does not match common repo-relative paths like governance/new-rule.json (no leading slash) and only matches a single path segment, despite the comment saying it should cover any file in the governance directory. This can leave newly added governance files unprotected unless they are manually listed.

Useful? React with 👍 / 👎.

Comment on lines +20 to +21
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm how protected-paths.txt is consumed and what string the regex is matched against
# (path is typically a relative repo path with no leading slash).
rg -nP -C3 'protected[-_]paths' --type=py --type=ts --type=js --type=sh
rg -nP -C3 -g 'block-governance-edits.*' '.'

Repository: chittyfoundation/.github

Length of output: 365


🏁 Script executed:

#!/bin/bash
# First, let's see the actual file and its structure
echo "=== Content of protected-paths.txt ==="
find . -name "protected-paths.txt" -type f

Repository: chittyfoundation/.github

Length of output: 139


🏁 Script executed:

#!/bin/bash
# Search for all references to 'protected-paths' or 'block-governance' without file type restrictions
echo "=== Searching for protected-paths references ==="
rg -i 'protected.?paths' -l
echo ""
echo "=== Searching for block-governance references ==="
rg -i 'block.?governance' -l

Repository: chittyfoundation/.github

Length of output: 229


🏁 Script executed:

#!/bin/bash
# Look for any files in governance directory and GitHub workflows that might reference protected paths
echo "=== Files in governance and .github directories ==="
find . -path "./governance/*" -type f | head -20
find . -path "./.github/*" -type f | head -20

Repository: chittyfoundation/.github

Length of output: 511


🏁 Script executed:

#!/bin/bash
# Read the protected-paths.txt file with line numbers to see lines 20-21
echo "=== governance/protected-paths.txt ==="
cat -n ./governance/protected-paths.txt

Repository: chittyfoundation/.github

Length of output: 1239


🏁 Script executed:

#!/bin/bash
# Read the pr-governance.yml workflow which likely uses this file
echo "=== .github/workflows/pr-governance.yml ==="
cat ./.github/workflows/pr-governance.yml

Repository: chittyfoundation/.github

Length of output: 5759


Leading slash makes the "any file in governance directory" pattern unmatchable.

Every other entry in this file is written relative to the repository root without a leading slash (e.g., governance/sign-rules\.sh$, \.claude/hooks/). This entry uses /governance/[^/]+$, which against a relative path like governance/new-file.json will never match (no / precedes governance). The result is that the broad "catch any new top-level file under governance/" protection silently does nothing — only the explicitly enumerated files on Lines 9–18 are covered, leaving any newly added governance file unprotected until someone updates this list.

The workflow uses git diff --name-only to generate relative paths, then matches them with grep -qE against these patterns. The leading slash in the pattern prevents it from ever matching.

🛡️ Proposed fix
-# Any file in governance directory
-/governance/[^/]+$
+# Any file in governance directory
+governance/[^/]+$
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Any file in governance directory
/governance/[^/]+$
# Any file in governance directory
governance/[^/]+$
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@governance/protected-paths.txt` around lines 20 - 21, The pattern
"/governance/[^/]+$" is unmatchable against relative paths; replace it with
"governance/[^/]+$" (no leading slash) so git diff --name-only results like
"governance/new-file.json" will match; update the entry in the protected-paths
list to match the style of the other entries (e.g., governance/sign-rules\.sh$)
to ensure the broad catch-all for top-level files under governance is enforced.


# Hook self-protection
\.claude/hooks/block-governance-edits\.py$
\.claude/hooks/

# GitHub workflow protection
\.github/workflows/pr-governance\.yml$
\.github/workflows/governance
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Protect all workflow files in protected-path rules

The protected-path list now only targets pr-governance.yml and workflow names containing governance, while rules.json still declares a blanket workflow restriction (cannot_modify_workflow_files and .github/workflows/**). This mismatch means non-governance workflow files can bypass hook-based protected-path enforcement even though policy says they should be blocked.

Useful? React with 👍 / 👎.

\.github/CODEOWNERS$
CODEOWNERS$

# Settings protection (prevents disabling hooks)
\.claude/settings.*\.json$
Comment on lines +33 to +34
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Unescaped . in settings glob can over-match.

\.claude/settings.*\.json$ leaves the . between settings and * unescaped, so the literal-looking settings.*.json actually matches paths such as .claude/settingsXYZanything.json and even nested paths like .claude/settings/foo/bar.json (because . matches /). The intent appears to be “files like .claude/settings.local.json, .claude/settings.ci.json, etc.” — anchor the variant explicitly.

♻️ Proposed fix
-\.claude/settings.*\.json$
+\.claude/settings(\.[^/]+)?\.json$
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Settings protection (prevents disabling hooks)
\.claude/settings.*\.json$
# Settings protection (prevents disabling hooks)
\.claude/settings(\.[^/]+)?\.json$
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@governance/protected-paths.txt` around lines 33 - 34, The glob/regex pattern
string `\.claude/settings.*\.json$` overmatches because the dot before the
variant is unescaped and `.*` allows slashes; update the pattern so the dot is
literal and the variant is restricted to a filename segment (e.g. replace
`\.claude/settings.*\.json$` with a pattern that uses `\.` and a non-slash class
like `\.[^/]+\.json$`) to match only files like `.claude/settings.local.json`
and not nested paths or concatenated names.

35 changes: 33 additions & 2 deletions governance/repo_requirements.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,40 @@
"notes": "Repo-level ownership gating. A CODEOWNERS principal is eligible only if its TY/VY/RY satisfies the repo requirement. Use globs for org/repo.",
"repos": [
{
"repo": "*/*",
"repo": "CHITTYFOUNDATION/chittycanon",
"tier": 0,
"requires": {
"ty": ["canon", "ontology", "governance"],
"vy": ["cloudflare-workers", "github-actions"],
"ry": ["trust-anchor", "portfolio-policy"]
}
},
{
"repo": "CHITTYFOUNDATION/chittyid",
"tier": 0,
"requires": {
"ty": ["identity", "crypto", "governance"],
"vy": ["cloudflare-workers"],
"ry": ["mint", "sign", "audit"]
}
},
{
"repo": "CHITTYOS/*",
"tier": 2,
"requires": {
"ty": ["chittyos-runtime"],
"vy": ["cloudflare-workers"],
"ry": ["service-ops"]
Comment on lines +28 to +30
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Make org repo requirements satisfiable by current principals

repo_requirements.json now mandates TY/RY terms like chittyos-runtime, service-ops, app, and product-delivery for CHITTYOS/* and CHITTYAPPS/*, but no principal in governance/owners.json currently has those grants. Because this file explicitly states CODEOWNERS eligibility depends on satisfying these requirements, these org patterns can end up with zero eligible approvers, making protected PRs effectively unmergeable until matching grants are added or requirements are relaxed.

Useful? React with 👍 / 👎.

}
},
{
"repo": "CHITTYAPPS/*",
"tier": 3,
"requires": { "ty": [], "vy": ["cloudflare-workers"], "ry": [] }
"requires": {
"ty": ["app"],
"vy": ["cloudflare-workers"],
"ry": ["product-delivery"]
}
}
]
}
Comment on lines 5 to 43
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the consumer that resolves repo -> requires to learn its non-match behavior
# and precedence handling.
rg -nP -C3 'repo_requirements|requires\.(ty|vy|ry)' --type=py --type=ts --type=js --type=sh

Repository: chittyfoundation/.github

Length of output: 216


🏁 Script executed:

# Search for files mentioning repo_requirements without restrictive type filters
rg -l 'repo_requirements' --max-count=20

Repository: chittyfoundation/.github

Length of output: 50


🏁 Script executed:

# Explore governance directory structure
ls -la governance/ 2>/dev/null || echo "governance dir not found"

Repository: chittyfoundation/.github

Length of output: 502


🏁 Script executed:

# Find all code files that might consume config files
fd -e ts -e js -e py -e sh . --max-depth=3 | head -20

Repository: chittyfoundation/.github

Length of output: 50


🏁 Script executed:

# Check rules.json to validate organization scope mentioned in review
cat governance/rules.json 2>/dev/null | head -100

Repository: chittyfoundation/.github

Length of output: 2986


🏁 Script executed:

# Read the complete repo_requirements.json file
cat governance/repo_requirements.json

Repository: chittyfoundation/.github

Length of output: 1164


🏁 Script executed:

# Read the schema.json to understand tier definitions and resolver semantics
cat governance/schema.json

Repository: chittyfoundation/.github

Length of output: 2961


🏁 Script executed:

# Search for any code that might parse/consume repo_requirements (broader search)
rg 'repo_requirements|tier.*requires|requires.*tier' -i --max-count=30

Repository: chittyfoundation/.github

Length of output: 50


Unmatched repos and tier 1 have no defined behavior; precedence for glob patterns is undocumented.

Three issues to address before merging:

  1. Unmatched repo fallback is unspecified. When a repository doesn't match any of the four entries (CHITTYFOUNDATION/chittycanon, CHITTYFOUNDATION/chittyid, CHITTYOS/*, CHITTYAPPS/*), the file provides no requires and the notes don't specify what happens next. This leaves the resolver in ambiguity: fail-open (any CODEOWNER passes, weakening governance) or fail-closed (no one eligible, deadlocking PRs). Either restore a */* catch-all with explicit defaults, or update notes to document the fallback behavior.

  2. Tier 1 is missing. Tiers jump 0 → 2 → 3; tier 1 is never defined. If this gap is intentional, add a notes: clarification. If it's an oversight, add a tier 1 entry or document why it's reserved.

  3. No documented precedence for glob patterns. Once a more-specific entry like CHITTYOS/critical-service coexists with CHITTYOS/*, the resolver must know whether most-specific wins or first-match wins. Add a precedence rule to the schema or notes:.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@governance/repo_requirements.json` around lines 5 - 43, The JSON lacks a
fallback, a tier-1 definition, and a documented glob precedence: add either a
catch-all entry (repo "*/*") with explicit default "requires" to define
fail-open vs fail-closed behavior or add a clear "notes" section that documents
the fallback behavior; add an explicit tier 1 entry (or a "notes" line
explaining why tier 1 is intentionally reserved) so tiers are contiguous or
purposely gapped; and add a glob precedence rule in the schema/notes (e.g.,
"most-specific wins" or "first-match wins" and whether explicit repo entries
override wildcards) so resolution between entries like
"CHITTYOS/critical-service" and "CHITTYOS/*" is deterministic.

Expand Down
Loading
Loading