diff --git a/docs/design/ingress.md b/docs/design/ingress.md index c8b4400..13a913e 100644 --- a/docs/design/ingress.md +++ b/docs/design/ingress.md @@ -236,6 +236,8 @@ Work-item targets use the bare tracker ID in the ontology namespace, matching WorkItem: its type, label, identifier and trust facts belong to tracker records and are not copied here. Readers join the records and provenance graphs. Code modules and commits retain their code namespace. +Each commit also carries `sha` and `in_repo`, taken from that git record and +repository name, so the shared Commit metadata constraint can judge it. Older output used `code/bead/` for work items; those historical aliases need explicit reconciliation, not a second mint or deletion of their provenance. diff --git a/scripts/ingest_git_provenance.py b/scripts/ingest_git_provenance.py index 4857fd5..dd11a19 100755 --- a/scripts/ingest_git_provenance.py +++ b/scripts/ingest_git_provenance.py @@ -171,6 +171,10 @@ def emit(repo_name: str, sha: str, items: list[str], paths: list[str], out: list out.append(f"<{commit_iri}> a <{ONTOLOGY}GitCommit> ;") out.append(f' rdfs:label {json.dumps(f"{repo_name}@{sha[:12]}")} ;') out.append(f' <{ONTOLOGY}sourceKind> "observed" ;') + # Shared Commit constraints use these names; take both values from the + # same git record, not the display label or an inferred repository. + out.append(f' <{ONTOLOGY}sha> {json.dumps(sha)} ;') + out.append(f' <{ONTOLOGY}in_repo> {json.dumps(repo_name)} ;') for item in items: out.append(f' <{ONTOLOGY}implements> <{work_item_iri(item)}> ;') for i, path in enumerate(paths): diff --git a/tests/test_ingest_git_provenance.py b/tests/test_ingest_git_provenance.py index b3183b6..cb527b2 100644 --- a/tests/test_ingest_git_provenance.py +++ b/tests/test_ingest_git_provenance.py @@ -95,6 +95,21 @@ class IriLaneTests(unittest.TestCase): ONTO = "http://aegis.gastown.local/ontology/" + def test_commit_required_metadata_comes_from_the_same_git_record(self): + sha = "0123456789abcdef0123456789abcdef01234567" + for paths in ([], ["src/main.py"]): + with self.subTest(paths=paths): + lines = [] + mod.emit('a"repo', sha, ["example-123"], paths, lines) + turtle = "\n".join(lines) + self.assertIn(f'<{mod.ONTOLOGY}sha> "{sha}"', turtle) + self.assertIn(f'<{mod.ONTOLOGY}in_repo> "a\\\"repo"', turtle) + commit_iri = mod.iri('a"repo', "commit", sha) + self.assertIn(f"<{commit_iri}>", turtle) + self.assertEqual(turtle.count(f'<{mod.ONTOLOGY}sha>'), 1) + self.assertEqual(turtle.count(f'<{mod.ONTOLOGY}in_repo>'), 1) + self.assertNotIn("aegis:sha", turtle) + def test_linked_work_items_are_references_not_copied_records(self): lines = [] mod.emit("example", "abc123", ["example-123"], ["src/main.py"], lines)