Skip to content

Commit c244d18

Browse files
committed
Prepare 0.2.0
1 parent fd07602 commit c244d18

13 files changed

Lines changed: 160 additions & 36 deletions

File tree

.accountability/surfaces.toml

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ description = "Shared Python tooling for scaffolding, validating, inspecting, ca
2424
# === CODEOWNERS Projection ===
2525

2626
[codeowners]
27-
informs = true
28-
requires_code_owner_review = false
27+
informs = true # REQ: An entry must be provided.
28+
requires_code_owner_review = false # REQ: An entry must be provided.
2929

3030
# === Local CODEOWNERS Role Resolution ===
3131
# These role identities are used only for CODEOWNERS owner resolution.
@@ -46,10 +46,13 @@ spec = "@structural-explainability/spec"
4646
security = "@structural-explainability/security"
4747
release = "@structural-explainability/release"
4848

49-
# === Local Oversight Roles ===
49+
# === Required Oversight Role ===
5050

5151
# Local oversight roles assign review ownership for this repository.
5252
# Each role maps local responsibility back to reusable accountable-surface roles.
53+
# Every repository MUST provide a local role responsible
54+
# for the authority manifest itself.
55+
# By convention this role is named "owner".
5356

5457
[oversight_roles.owner]
5558
label = "Owner"
@@ -59,6 +62,11 @@ requires_review = ["human_review"]
5962
requires_evidence = ["decision_note", "human_review_record"]
6063
ai_max_authority = "prohibited"
6164

65+
# === Additional Local Oversight Roles ===
66+
67+
# Local oversight roles assign review ownership for this repository.
68+
# Each role maps local responsibility back to reusable accountable-surface roles.
69+
6270
[oversight_roles.authority_projection]
6371
label = "Authority Projection"
6472
description = "Reviews surfaces that project accountable-surface declarations into GitHub CODEOWNERS enforcement artifacts."
@@ -129,6 +137,8 @@ ai_max_authority = "drafting"
129137

130138
# === Level 100: Broad source and test defaults ===
131139

140+
# --- Public Python API and implementation surfaces ---
141+
132142
[[surface]]
133143
id = "public-python-api"
134144
object_kind = "directory"
@@ -149,6 +159,7 @@ requires_review = ["human_review"]
149159
requires_evidence = ["decision_note", "verification_command_result"]
150160
ai_max_authority = "drafting"
151161

162+
# --- Tests and Fixtures ---
152163

153164
[[surface]]
154165
id = "tests-and-fixtures"
@@ -167,6 +178,8 @@ ai_max_authority = "drafting"
167178

168179
# === Level 200: Public command and projection implementation surfaces ===
169180

181+
# --- CLI command surface ---
182+
170183
[[surface]]
171184
id = "cli-command-surface"
172185
object_kind = "directory"
@@ -187,6 +200,7 @@ requires_review = ["human_review"]
187200
requires_evidence = ["decision_note", "verification_command_result"]
188201
ai_max_authority = "drafting"
189202

203+
# --- CODEOWNERS generation engine ---
190204

191205
[[surface]]
192206
id = "codeowners-generation-engine"
@@ -208,6 +222,7 @@ kind = "public_contract"
208222
[[surface.role]]
209223
kind = "validation_rule"
210224

225+
# --- Export and Catalog Boundary ---
211226

212227
[[surface]]
213228
id = "export-and-catalog-boundary"
@@ -232,6 +247,8 @@ ai_max_authority = "drafting"
232247

233248
# === Level 300: Spec-compatibility overrides ===
234249

250+
# --- Surface Spec Compatibility ---
251+
235252
[[surface]]
236253
id = "surface-spec-compatibility"
237254
object_kind = "directory"
@@ -266,6 +283,8 @@ ai_max_authority = "drafting"
266283

267284
# === Level 400: Project configuration and package contract ===
268285

286+
# --- Project Configuration ---
287+
269288
[[surface]]
270289
id = "project-configuration"
271290
object_kind = "file"
@@ -313,6 +332,8 @@ ai_max_authority = "drafting"
313332

314333
# === Level 500: Release and automation authority ===
315334

335+
# --- Release and Automation Authority ---
336+
316337
[[surface]]
317338
id = "release-and-automation-authority"
318339
object_kind = "directory"
@@ -341,6 +362,8 @@ ai_max_authority = "advisory"
341362

342363
# === Level 600: Repository governance and security documentation ===
343364

365+
# --- Repository Governance ---
366+
344367
[[surface]]
345368
id = "repository-governance"
346369
object_kind = "directory"
@@ -367,6 +390,7 @@ requires_review = ["human_review", "security_review"]
367390
requires_evidence = ["decision_note"]
368391
ai_max_authority = "advisory"
369392

393+
# --- Repository Security ---
370394

371395
[[surface]]
372396
id = "repository-security"
@@ -383,7 +407,29 @@ requires_evidence = ["decision_note"]
383407
ai_max_authority = "advisory"
384408

385409

386-
# === Level 700: Self-protecting authority manifest ===
410+
# === Level 700: Required Self-Protection ===
411+
#
412+
# Every accountable-surface manifest MUST declare itself as a protected surface.
413+
# This surface SHOULD be emitted after ordinary repository surfaces so that
414+
# CODEOWNERS projection gives the authority manifest the strongest applicable
415+
# ownership rule.
416+
#
417+
# The self-protection surface MUST:
418+
#
419+
# - use id = "authority-manifest"
420+
# - include paths = [".accountability/surfaces.toml"]
421+
# - declare oversight_role = "owner" or another local role mapped to
422+
# authority_boundary and authority_manifest_surface
423+
# - declare codeowners_order at the final/highest local projection level
424+
# - prohibit AI authority over the manifest
425+
# - require human review
426+
# - require evidence of review
427+
#
428+
# Rationale:
429+
# If an actor with technical capability can rewrite the authority declaration
430+
# without review, technical capability has silently become authority.
431+
432+
# --- Authority Manifest Self-Protection ---
387433

388434
[[surface]]
389435
id = "authority-manifest"

.github/CODEOWNERS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,17 @@
55
#
66
# Source: .accountability/surfaces.toml
77
# Generator: se-codeowners
8+
# Repository: se-codeowners
89
# Regenerate: se-codeowners generate --output .github/CODEOWNERS
910
#
11+
# [codeowners].informs = true means this surfaces manifest intentionally
12+
# informs the generated GitHub CODEOWNERS projection.
13+
#
14+
# [codeowners].requires_code_owner_review = false
15+
# records whether repository governance expects GitHub branch protection or
16+
# rulesets to require CODEOWNER review before protected changes merge.
17+
# Actual enforcement is configured in GitHub, not in this generated file.
18+
#
1019
# Each entry reflects the oversight_role assigned to an accountability
1120
# surface, resolved to a handle via [codeowners.role_handles]. GitHub
1221
# applies the LAST matching pattern for a given path, so order matters.

CHANGELOG.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@ and this project adheres to **[Semantic Versioning](https://semver.org/spec/v2.0
1313

1414
---
1515

16+
## [0.2.0] - 2026-06-04
17+
18+
## Added
19+
20+
- `tests/test_cli.py`
21+
- Additional comments on surface.toml
22+
23+
## Fixed
24+
25+
- `__main__.py` correctly named
26+
27+
---
28+
1629
## [0.1.0] - 2026-06-04
1730

1831
### Added
@@ -113,7 +126,8 @@ git push origin :refs/tags/vX.Z.Y
113126

114127
## Links
115128

116-
[Unreleased]: https://github.com/structural-explainability/se-codeowners/compare/v0.1.0...HEAD
129+
[Unreleased]: https://github.com/structural-explainability/se-codeowners/compare/v0.2.0...HEAD
130+
[0.2.0]: https://github.com/structural-explainability/se-codeowners/releases/tag/v0.2.0
117131
[0.1.0]: https://github.com/structural-explainability/se-codeowners/releases/tag/v0.1.0
118132

119133
<!-- markdownlint-enable MD024 -->

CITATION.cff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ cff-version: "1.2.0"
99
type: software
1010

1111
title: "SE CODEOWNERS"
12-
version: "0.1.0"
12+
version: "0.2.0"
1313
date-released: "2026-06-04"
1414

1515
authors:

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ typeCheckingMode = "strict" # CUSTOM: off | basic | standard | strict
8888
# WHY: Consistent test discovery and coverage visibility.
8989
minversion = "9.0"
9090
testpaths = ["tests"]
91-
addopts = "--cov=se_codeowners --cov-report=term-missing --cov-fail-under=50"
91+
addopts = "--cov=se_codeowners --cov-report=term-missing --cov-fail-under=60"
9292

9393
# === RUFF (PYTHON FORMATTING AND LINTING) ===
9494

@@ -181,5 +181,5 @@ packages = ["src/se_codeowners"]
181181
[tool.hatch.version]
182182
# WHY: Version derived from git tags at build time
183183
source = "vcs"
184-
fallback-version = "0.1.0" # Used when no git tags present (fresh clone, CI).
184+
fallback-version = "0.2.0" # Used when no git tags present (fresh clone, CI).
185185
tag-pattern = "^(?:v)?(?P<version>\\d+\\.\\d+\\.\\d+)$"

src/se_codeowners/generate.py

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,24 @@
66

77
_PLACEHOLDER_TOKENS = ("@OWNER", "OWNER_HANDLE")
88

9-
_HEADER = """\
10-
# ============================================================
11-
# .github/CODEOWNERS
12-
# ============================================================
13-
# GENERATED FILE -- do not edit by hand.
14-
#
15-
# Source: .accountability/surfaces.toml
16-
# Generator: se-codeowners
17-
# Regenerate: se-codeowners generate --output .github/CODEOWNERS
18-
#
19-
# Each entry reflects the oversight_role assigned to an accountability
20-
# surface, resolved to a handle via [codeowners.role_handles]. GitHub
21-
# applies the LAST matching pattern for a given path, so order matters.
22-
# Surfaces are emitted by codeowners_order, then surface id."""
23-
249

2510
def generate_codeowners_lines(
26-
grouped: list[tuple[Surface, list[tuple[str, str]]]], width: int
11+
doc: SurfacesDoc,
12+
grouped: list[tuple[Surface, list[tuple[str, str]]]],
13+
width: int,
2714
) -> str:
2815
"""Generate the lines of the CODEOWNERS file.
2916
3017
Args:
18+
doc (SurfacesDoc): The surfaces document being rendered.
3119
grouped (list[tuple[Surface, list[tuple[str, str]]]]): Grouped surfaces and their patterns.
3220
width (int): The width to pad the patterns to.
3321
3422
Returns:
3523
str: The generated CODEOWNERS file content.
3624
"""
3725
lines: list[str] = []
38-
lines.extend(_HEADER.splitlines())
26+
lines.extend(_header(doc).splitlines())
3927
for surface, rows in grouped:
4028
lines.append("")
4129
lines.append(f"# Surface: {surface.id} (role: {surface.oversight_role})")
@@ -73,7 +61,33 @@ def render_codeowners(doc: SurfacesDoc, *, strict: bool = False) -> str:
7361
if not all_patterns:
7462
raise SurfacesError("no surfaces with an oversight_role were found")
7563
width = max(len(pattern) for pattern in all_patterns)
76-
return generate_codeowners_lines(grouped, width)
64+
return generate_codeowners_lines(doc, grouped, width)
65+
66+
67+
def _header(doc: SurfacesDoc) -> str:
68+
return f"""\
69+
# ============================================================
70+
# .github/CODEOWNERS
71+
# ============================================================
72+
# GENERATED FILE -- do not edit by hand.
73+
#
74+
# Source: .accountability/surfaces.toml
75+
# Generator: se-codeowners
76+
# Repository: {doc.repository_name}
77+
# Regenerate: se-codeowners generate --output .github/CODEOWNERS
78+
#
79+
# [codeowners].informs = true means this surfaces manifest intentionally
80+
# informs the generated GitHub CODEOWNERS projection.
81+
#
82+
# [codeowners].requires_code_owner_review = {str(doc.requires_code_owner_review).lower()}
83+
# records whether repository governance expects GitHub branch protection or
84+
# rulesets to require CODEOWNER review before protected changes merge.
85+
# Actual enforcement is configured in GitHub, not in this generated file.
86+
#
87+
# Each entry reflects the oversight_role assigned to an accountability
88+
# surface, resolved to a handle via [codeowners.role_handles]. GitHub
89+
# applies the LAST matching pattern for a given path, so order matters.
90+
# Surfaces are emitted by codeowners_order, then surface id."""
7791

7892

7993
def _projected_surfaces(doc: SurfacesDoc) -> list[Surface]:

src/se_codeowners/load.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,28 +48,34 @@ def load_surfaces(path: Path) -> SurfacesDoc:
4848

4949
def _build_codeowners(
5050
parsed: dict[str, object],
51-
) -> tuple[dict[str, tuple[str, ...]], bool]:
51+
) -> tuple[dict[str, tuple[str, ...]], bool, bool]:
5252
raw = optional(parsed, "codeowners")
5353
if raw is None:
54-
return {}, False
54+
return {}, False, False
55+
5556
table = as_object_dict(raw, ctx="codeowners")
5657

57-
informs_value = optional(table, "informs")
58-
informs = (
59-
as_bool(informs_value, ctx="codeowners.informs")
60-
if informs_value is not None
61-
else False
58+
informs = as_bool(
59+
require(table, "informs", ctx="codeowners"),
60+
ctx="codeowners.informs",
61+
)
62+
63+
requires_code_owner_review = as_bool(
64+
require(table, "requires_code_owner_review", ctx="codeowners"),
65+
ctx="codeowners.requires_code_owner_review",
6266
)
6367

6468
handles_value = optional(table, "role_handles")
6569
if handles_value is None:
66-
return {}, informs
70+
return {}, informs, requires_code_owner_review
71+
6772
handles_table = as_object_dict(handles_value, ctx="codeowners.role_handles")
6873
role_handles = {
6974
role: _normalize_handles(value, ctx=f"codeowners.role_handles.{role}")
7075
for role, value in handles_table.items()
7176
}
72-
return role_handles, informs
77+
78+
return role_handles, informs, requires_code_owner_review
7379

7480

7581
def _build_doc(parsed: dict[str, object]) -> SurfacesDoc:
@@ -79,12 +85,13 @@ def _build_doc(parsed: dict[str, object]) -> SurfacesDoc:
7985
repo_name = as_str(
8086
require(repository, "name", ctx="repository"), ctx="repository.name"
8187
)
82-
role_handles, informs = _build_codeowners(parsed)
88+
role_handles, informs, requires_code_owner_review = _build_codeowners(parsed)
8389
return SurfacesDoc(
8490
repository_name=repo_name,
8591
surfaces=_build_surfaces(parsed),
8692
role_handles=role_handles,
8793
informs=informs,
94+
requires_code_owner_review=requires_code_owner_review,
8895
)
8996

9097

src/se_codeowners/model.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ class SurfacesDoc:
2626
surfaces: tuple[Surface, ...]
2727
role_handles: dict[str, tuple[str, ...]]
2828
informs: bool
29+
requires_code_owner_review: bool

tests/data/surfaces.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ prohibited_without_human_direction = ["Removing required assignment phases"]
6262

6363
[codeowners]
6464
informs = true
65+
requires_code_owner_review = false
6566

6667
[codeowners.role_handles]
6768
owner = "@denisecase"

0 commit comments

Comments
 (0)