Skip to content

Commit 627eeab

Browse files
fix(stack): require explicit alternate research source identities
1 parent 2415ed2 commit 627eeab

1 file changed

Lines changed: 38 additions & 21 deletions

File tree

tools/check_stack_consistency.py

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@
3232
# class: evidence
3333
# since: 2026-09-12
3434
#
35+
# id: alternate_research_source_is_explicit
36+
# given: a research BASE.json source commit differs from its repository's manifest-pinned libs commit
37+
# then: the workspace carries an explicit matching research_participants source identity rather than silently rebasing the libs pin
38+
# class: boundary
39+
# since: 2026-09-12
40+
#
3541
# id: separated_stack_component_has_graph_identity
3642
# given: a research BASE.json declares a project name distinct from its source repository name
3743
# then: the project has an explicit research_participants record and the former source owner does not retain a known superseded authority claim
@@ -128,25 +134,34 @@ def check_repository_projection(manifest: dict[str, Any], human: str, findings:
128134
return index
129135

130136

131-
def check_research_participants(manifest: dict[str, Any], human: str, findings: list[str]) -> set[tuple[str, str]]:
132-
seen: set[tuple[str, str]] = set()
137+
def check_research_participants(
138+
manifest: dict[str, Any],
139+
human: str,
140+
findings: list[str],
141+
) -> tuple[set[tuple[str, str]], set[tuple[str, str, str]]]:
142+
keys: set[tuple[str, str]] = set()
143+
sources: set[tuple[str, str, str]] = set()
133144
for entry in manifest.get("research_participants", []):
134-
key = (entry.get("workspace", ""), entry.get("participant_id", ""))
135-
if key in seen:
145+
workspace = str(entry.get("workspace", ""))
146+
participant_id = str(entry.get("participant_id", ""))
147+
repository = str(entry.get("repository", ""))
148+
commit = str(entry.get("commit", ""))
149+
key = (workspace, participant_id)
150+
if key in keys:
136151
error(findings, "research.duplicate", f"duplicate research participant {key!r}")
137-
seen.add(key)
138-
workspace = entry.get("workspace", "")
139-
commit = entry.get("commit", "")
152+
keys.add(key)
153+
sources.add((workspace, repository, commit))
140154
if workspace and workspace not in human:
141155
error(findings, "research.human_drift", f"STACK_MANIFEST.md does not mention workspace {workspace!r}")
142156
if commit and commit not in human:
143157
error(findings, "research.human_drift", f"STACK_MANIFEST.md does not mention research commit {commit!r}")
144-
return seen
158+
return keys, sources
145159

146160

147161
def check_base_records(
148162
repositories: dict[str, dict[str, Any]],
149-
research_participants: set[tuple[str, str]],
163+
research_participant_keys: set[tuple[str, str]],
164+
research_source_identities: set[tuple[str, str, str]],
150165
readme: str,
151166
findings: list[str],
152167
) -> None:
@@ -165,15 +180,17 @@ def check_base_records(
165180
source_entry = repositories.get(source_repository)
166181

167182
if source_entry and source_commit != source_entry.get("commit"):
168-
error(
169-
findings,
170-
"base.source_drift",
171-
f"{base_path.relative_to(ROOT)} pins {source_repository}@{source_commit}, manifest pins {source_entry.get('commit')}",
172-
)
183+
explicit_source = (workspace, source_repository, source_commit)
184+
if explicit_source not in research_source_identities:
185+
error(
186+
findings,
187+
"base.source_drift",
188+
f"{base_path.relative_to(ROOT)} pins {source_repository}@{source_commit}, manifest libs pin is {source_entry.get('commit')}, and no matching research_participants source identity exists",
189+
)
173190

174191
source_name = source_repository.rsplit("/", 1)[-1] if source_repository else ""
175192
if project and source_name and project != source_name:
176-
if (workspace, project) not in research_participants:
193+
if (workspace, project) not in research_participant_keys:
177194
error(
178195
findings,
179196
"base.separated_unrepresented",
@@ -185,7 +202,7 @@ def check_base_records(
185202

186203
def check_english_gonol_regression(
187204
repositories: dict[str, dict[str, Any]],
188-
research_participants: set[tuple[str, str]],
205+
research_participant_keys: set[tuple[str, str]],
189206
human: str,
190207
readme: str,
191208
findings: list[str],
@@ -204,7 +221,7 @@ def check_english_gonol_regression(
204221
error(findings, "english_gonol.stale_edcm_authority", "EDCM still claims English/text gonol construction authority after separation")
205222

206223
expected = ("research/english-gonol/", "english-gonol")
207-
if expected not in research_participants:
224+
if expected not in research_participant_keys:
208225
error(findings, "english_gonol.graph_missing", "English Gonol is missing its explicit stack research-participant identity")
209226

210227
for surface_name, surface in (("STACK_MANIFEST.md", human), ("README.md", readme)):
@@ -225,17 +242,17 @@ def main() -> int:
225242
check_manifest_shape(manifest, findings)
226243
check_digest(manifest, human, findings)
227244
repositories = check_repository_projection(manifest, human, findings)
228-
research_participants = check_research_participants(manifest, human, findings)
229-
check_base_records(repositories, research_participants, readme, findings)
230-
check_english_gonol_regression(repositories, research_participants, human, readme, findings)
245+
research_participant_keys, research_source_identities = check_research_participants(manifest, human, findings)
246+
check_base_records(repositories, research_participant_keys, research_source_identities, readme, findings)
247+
check_english_gonol_regression(repositories, research_participant_keys, human, readme, findings)
231248

232249
if findings:
233250
for finding in findings:
234251
print(f"error: {finding}")
235252
print(f"stack consistency: fail ({len(findings)} error(s))")
236253
return 1
237254

238-
print(f"stack consistency: pass ({len(repositories)} repositories, {len(research_participants)} research participant identities)")
255+
print(f"stack consistency: pass ({len(repositories)} repositories, {len(research_participant_keys)} research participant identities)")
239256
return 0
240257

241258

0 commit comments

Comments
 (0)