From 3f1ffd9fcc9e2c469ee3bdc3213f82c09e2f36cb Mon Sep 17 00:00:00 2001 From: Juan Cruz Fortunatti Date: Tue, 6 Oct 2026 17:30:33 +0200 Subject: [PATCH] fix(audit): read the catalogue registry from site/prepared and admit docs/evidence images --- .github/scripts/audits/check-documentation-links.mts | 4 +++- .../scripts/audits/check-documentation-links.test.mts | 9 ++++++++- .../audits/provenance-documentation-inventory.py | 2 +- .../audits/test_provenance_documentation_inventory.py | 10 ++++++++++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.github/scripts/audits/check-documentation-links.mts b/.github/scripts/audits/check-documentation-links.mts index 94ff13c5eb4..f3933fa925b 100644 --- a/.github/scripts/audits/check-documentation-links.mts +++ b/.github/scripts/audits/check-documentation-links.mts @@ -198,7 +198,9 @@ function audit(snapshot: Snapshot): Audit { 'docs/fixtures/telescope-projection/europa-oracle.json', 'docs/fixtures/telescope-projection/europa-registration.json']); // The site proposal is generated from these exact owner-requested inputs; arbitrary JSON stays forbidden. const sitePlanInputs = new Set(['docs/site-architecture/moves.json', 'docs/site-architecture/tiers.json', 'docs/site-architecture/edits.json', 'docs/site-architecture/references.json', 'docs/site-architecture/loader-options.json']); - for (const file of docs) if (!guides.has(file) && !illustrations.has(file) && !projectionEvidence.has(file) && !sitePlanInputs.has(file)) errors.push({file, reason: 'docs/ accepts Markdown guides and illustrations under docs/images/; move code, fixtures and raw output to their owner'}); + // Browser and figure evidence lives under docs/evidence/: its images, cited from body READMEs, and each capture's receipt. + const evidence = new Set(docs.filter(path => /^docs\/evidence\/.+(?:\.(?:png|jpg|jpeg|webp|avif|gif|svg)|\/receipt\.json)$/i.test(path))); + for (const file of docs) if (!guides.has(file) && !illustrations.has(file) && !evidence.has(file) && !projectionEvidence.has(file) && !sitePlanInputs.has(file)) errors.push({file, reason: 'docs/ accepts Markdown guides, illustrations under docs/images/ and evidence images under docs/evidence/; move code, fixtures and raw output to their owner'}); const index = 'docs/README.md'; if (!guides.has(index)) errors.push({file: index, reason: 'missing documentation index'}); const pending = guides.has(index) ? [index] : []; diff --git a/.github/scripts/audits/check-documentation-links.test.mts b/.github/scripts/audits/check-documentation-links.test.mts index 42572858671..a617c1430db 100644 --- a/.github/scripts/audits/check-documentation-links.test.mts +++ b/.github/scripts/audits/check-documentation-links.test.mts @@ -65,7 +65,7 @@ test('scoped mode checks non-Markdown additions and broken anchors', t => { f.write('docs/guide.md', '# Guide\n\n[Missing section](README.md#missing)\n\n![Comparison](images/comparison.svg)\n'); assert.deepEqual(new Set(f.check('HEAD').errors.map(error => error.reason)), new Set([ 'missing Markdown anchor', - 'docs/ accepts Markdown guides and illustrations under docs/images/; move code, fixtures and raw output to their owner', + 'docs/ accepts Markdown guides, illustrations under docs/images/ and evidence images under docs/evidence/; move code, fixtures and raw output to their owner', ])); }); @@ -222,3 +222,10 @@ test('only named executable site-plan inputs are admitted beside the guide', t = for (const name of ['moves', 'tiers', 'edits', 'references', 'loader-options', 'unexpected']) f.write(`docs/site-architecture/${name}.json`, '{}'); assert.deepEqual(f.check().errors.map(error => error.file), ['docs/site-architecture/unexpected.json']); }); + +test('evidence images and their capture receipt are admitted under docs/evidence/, other files are not', t => { + const f = fixture(t); + for (const path of ['docs/evidence/ui/capture/desktop.png', 'docs/evidence/ui/capture/receipt.json', 'docs/evidence/map/view.webp', + 'docs/evidence/ui/capture/raw.json', 'docs/evidence/ui/capture/capture.mts']) f.write(path, 'evidence'); + assert.deepEqual(f.check().errors.map(error => error.file), ['docs/evidence/ui/capture/capture.mts', 'docs/evidence/ui/capture/raw.json']); +}); diff --git a/.github/scripts/audits/provenance-documentation-inventory.py b/.github/scripts/audits/provenance-documentation-inventory.py index e11cbe5b039..2a5ec69b4d9 100644 --- a/.github/scripts/audits/provenance-documentation-inventory.py +++ b/.github/scripts/audits/provenance-documentation-inventory.py @@ -89,7 +89,7 @@ def summarize(label, entries): selected.add(registry) content = blobs(entries, selected) registry_source = content[registry].decode() - if re.search(r"from ['\"]\./prepared-(?:object-catalog\.m[jt]s|catalogue\.mjs)['\"]", registry_source): + if re.search(r"from ['\"]\.{1,2}/(?:[\w.-]+/)*prepared-(?:object-catalog\.m[jt]s|catalogue\.mjs)['\"]", registry_source): registry_basis = 'descriptor properties.catalog' registry_ids = [] for path, raw in content.items(): diff --git a/.github/scripts/audits/test_provenance_documentation_inventory.py b/.github/scripts/audits/test_provenance_documentation_inventory.py index 3e93c6790e1..ef7d645f8d7 100644 --- a/.github/scripts/audits/test_provenance_documentation_inventory.py +++ b/.github/scripts/audits/test_provenance_documentation_inventory.py @@ -69,6 +69,16 @@ def test_catalogue_reads_selected_snapshot_and_excludes_unregistered_folders(sel self.assertEqual(staged['missingRequiredFilesByRegisteredBody']['README.md'], ['example']) self.assertTrue(report['indexUnchangedDuringRead']) + def test_catalogue_import_from_the_prepared_folder_is_recognised(self): + self.write('site/directory/objects.mts', "import { CATALOGUE_ENTRIES } from '../prepared/prepared-catalogue.mjs';\n") + self.write('src/objects/example/object.json', {'id': 'example', 'properties': {'catalog': {}}}) + self.commit() + result, report = self.run_inventory() + self.assertEqual(result.returncode, 0, result.stderr) + snapshot = report['snapshots'][0] + self.assertEqual(snapshot['registryBasis'], 'descriptor properties.catalog') + self.assertEqual(snapshot['registeredBodies'], 1) + def test_empty_or_mismatched_catalogue_cannot_report_success(self): self.write('site/directory/objects.mts', "import { OBJECT_DESCRIPTORS } from './prepared-object-catalog.mts';\n") self.commit()