Skip to content

Commit 4df2810

Browse files
committed
feat(registry): version Notion projection policy
1 parent 51dd45d commit 4df2810

4 files changed

Lines changed: 52 additions & 2 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://local.invalid/github-repo-auditor/notion-projection-policy.v1.schema.json",
4+
"title": "Notion projection policy",
5+
"description": "Explains Local Portfolio rows that are aliases of, or intentional projections outside, GithubRepoAuditor portfolio truth.",
6+
"type": "object",
7+
"additionalProperties": false,
8+
"required": [
9+
"schema_version",
10+
"notion_title_aliases",
11+
"notion_projection_only_rows"
12+
],
13+
"properties": {
14+
"schema_version": {
15+
"const": "notion_projection_policy.v1"
16+
},
17+
"notion_title_aliases": {
18+
"type": "object",
19+
"additionalProperties": {
20+
"type": "string",
21+
"minLength": 1
22+
}
23+
},
24+
"notion_projection_only_rows": {
25+
"type": "object",
26+
"additionalProperties": {
27+
"type": "string",
28+
"minLength": 1
29+
}
30+
}
31+
}
32+
}

config/project-registry-overrides.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"_comment": "Operator-editable enrollment for the canonical project registry. 'overrides' pins hard normalization failures (drifted identifier -> canonical project_key). 'supplementary' enrolls real operator-OS projects the auditor does not track as git repos. 'memory_meta' maps ~/.claude memory note-slugs to their parent project (or '' for pure meta-notes). 'notion_title_aliases' and 'notion_projection_only_rows' explain Local Portfolio projection differences that are not distinct GithubRepoAuditor truth projects. Consumed by src/project_registry.py and cross-system smoke checks; falls back to built-in defaults if this file is absent.",
2+
"_comment": "Operator-editable enrollment for the canonical project registry. 'overrides' pins hard normalization failures (drifted identifier -> canonical project_key). 'supplementary' enrolls real operator-OS projects the auditor does not track as git repos. 'memory_meta' maps ~/.claude memory note-slugs to their parent project (or '' for pure meta-notes). 'notion_projection_policy_schema_version', 'notion_title_aliases', and 'notion_projection_only_rows' explain Local Portfolio projection differences that are not distinct GithubRepoAuditor truth projects. Consumed by src/project_registry.py and cross-system smoke checks; falls back to built-in defaults if this file is absent.",
33
"overrides": {
44
"jcc": "JobCommandCenter",
55
"jsm_export": "JSMTicketAnalyticsExport",
@@ -34,6 +34,7 @@
3434
"skill_library_port_2026-05": "",
3535
"skill_eval_harness_2026-05": ""
3636
},
37+
"notion_projection_policy_schema_version": "notion_projection_policy.v1",
3738
"notion_title_aliases": {
3839
"DesktopPEt-ready": "DesktopPEt",
3940
"EarthPulse-readiness": "EarthPulse",

src/project_registry.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from pathlib import Path
2727

2828
SCHEMA_VERSION = "1.0"
29+
NOTION_PROJECTION_POLICY_SCHEMA_VERSION = "notion_projection_policy.v1"
2930

3031
# Built-in fallbacks, mirrored by config/project-registry-overrides.json.
3132
# Hard normalization failures: drifted identifier -> canonical project_key.
@@ -124,20 +125,32 @@ def _strip_alias_prefix(alias: str) -> str:
124125

125126
def load_overrides_config(
126127
config_path: Path | None,
127-
) -> tuple[dict[str, str], list[dict], dict[str, str], dict[str, str], dict[str, str]]:
128+
) -> tuple[
129+
dict[str, str],
130+
list[dict],
131+
dict[str, str],
132+
str,
133+
dict[str, str],
134+
dict[str, str],
135+
]:
128136
"""Load overrides + supplementary + memory-meta, falling back to defaults."""
129137
if config_path is None or not config_path.exists():
130138
return (
131139
dict(DEFAULT_OVERRIDES),
132140
[dict(s) for s in DEFAULT_SUPPLEMENTARY],
133141
dict(DEFAULT_MEMORY_META),
142+
NOTION_PROJECTION_POLICY_SCHEMA_VERSION,
134143
dict(DEFAULT_NOTION_TITLE_ALIASES),
135144
dict(DEFAULT_NOTION_PROJECTION_ONLY_ROWS),
136145
)
137146
data = json.loads(config_path.read_text())
138147
overrides = data.get("overrides", DEFAULT_OVERRIDES)
139148
supplementary = data.get("supplementary", DEFAULT_SUPPLEMENTARY)
140149
memory_meta = data.get("memory_meta", DEFAULT_MEMORY_META)
150+
projection_policy_schema_version = data.get(
151+
"notion_projection_policy_schema_version",
152+
NOTION_PROJECTION_POLICY_SCHEMA_VERSION,
153+
)
141154
title_aliases = data.get("notion_title_aliases", DEFAULT_NOTION_TITLE_ALIASES)
142155
projection_only = data.get(
143156
"notion_projection_only_rows", DEFAULT_NOTION_PROJECTION_ONLY_ROWS
@@ -146,6 +159,7 @@ def load_overrides_config(
146159
dict(overrides),
147160
[dict(s) for s in supplementary],
148161
dict(memory_meta),
162+
str(projection_policy_schema_version),
149163
dict(title_aliases),
150164
dict(projection_only),
151165
)
@@ -314,6 +328,7 @@ def build_project_registry(
314328
overrides,
315329
supplementary,
316330
memory_meta,
331+
notion_projection_policy_schema_version,
317332
notion_title_aliases,
318333
notion_projection_only_rows,
319334
) = load_overrides_config(overrides_config_path)
@@ -463,6 +478,7 @@ def resolve_entry(raw: str) -> _Entry | None:
463478
"entry_count": len(entries),
464479
"resolution_overrides": overrides,
465480
"projection_policy": {
481+
"schema_version": notion_projection_policy_schema_version,
466482
"notion_title_aliases": notion_title_aliases,
467483
"notion_projection_only_rows": notion_projection_only_rows,
468484
},

tests/test_project_registry.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ def test_resolve_supplementary_from_each_spelling():
113113
def test_projection_policy_is_published_from_defaults():
114114
registry = build_project_registry(SNAPSHOT, overrides_config_path=None)
115115
policy = registry["projection_policy"]
116+
assert policy["schema_version"] == "notion_projection_policy.v1"
116117
assert policy["notion_title_aliases"]["DesktopPEt-ready"] == "DesktopPEt"
117118
assert "SecondBrain" in policy["notion_projection_only_rows"]
118119

0 commit comments

Comments
 (0)