From 58ba9d4a18553b9c1358b23b57fb6f1e3daf9e36 Mon Sep 17 00:00:00 2001 From: Thorsten Hindermann Date: Sun, 13 Sep 2026 02:51:50 +0200 Subject: [PATCH 1/2] fix: accept absent empty active intake collection --- .../intake-authoring-governance/README.md | 10 +++++--- .../man/validate-intake-governance-config.1 | 4 +++- .../validate-intake-governance-config.py | 23 +++++++++++++------ .../tests/test-intake-governance-config.ps1 | 6 +++++ .../man/validate-intake-governance-config.1 | 4 +++- .../validate-intake-governance-config.py | 23 +++++++++++++------ .../tests/test-intake-governance-config.ps1 | 6 +++++ .../intake-sequencing-governance/README.md | 11 ++++++--- .../man/validate-intake-governance-config.1 | 4 +++- .../validate-intake-governance-config.py | 23 +++++++++++++------ .../tests/test-intake-governance-config.ps1 | 6 +++++ 11 files changed, 90 insertions(+), 30 deletions(-) diff --git a/.specify/presets/intake-authoring-governance/README.md b/.specify/presets/intake-authoring-governance/README.md index 147349fa..08a512cf 100644 --- a/.specify/presets/intake-authoring-governance/README.md +++ b/.specify/presets/intake-authoring-governance/README.md @@ -331,7 +331,9 @@ Eine aktive Lieferserie hat genau ein `Eligible`-Ziel. Eine `Completed`-Serie bewahrt ausschließlich abgeschlossene Mitglieder in der konfigurierten Archiv-Collection und hat kein `Eligible`-Ziel. `SeriesManifest` erlaubt einer laufenden Serie, aktive Ziele und bereits archivierte Vorgänger gemeinsam zu -referenzieren. +referenzieren. Weil Git leere Verzeichnisse nicht speichert, gilt eine fehlende +aktive Collection in diesem Modus als Bestand null; `DirectoryStrict` verlangt +das konfigurierte Verzeichnis weiterhin. *Version 0.3.0 describes requirements collections with schema 2.0. Documentation language is explicit and independent from implementation @@ -339,8 +341,10 @@ language and locale. Portable roles resolve localized names. Migration is authority-bound, hash-bound, and atomic. `Eligible` selects order but grants no implementation or remote authority. `DirectoryStrict` validates a dedicated active directory; `SeriesManifest` supports established flat or mixed layouts -without trusting manual counts. A completed Series preserves its members in -the configured archive collection without inventing an eligible candidate.* +without trusting manual counts. Because Git does not preserve empty directories, +an absent active collection counts as empty in this mode; `DirectoryStrict` +still requires the directory. A completed Series preserves its members in the +configured archive collection without inventing an eligible candidate.* ## Status / Statuspruefung diff --git a/.specify/presets/intake-authoring-governance/docs/man/validate-intake-governance-config.1 b/.specify/presets/intake-authoring-governance/docs/man/validate-intake-governance-config.1 index e896895c..0c6a0c1f 100644 --- a/.specify/presets/intake-authoring-governance/docs/man/validate-intake-governance-config.1 +++ b/.specify/presets/intake-authoring-governance/docs/man/validate-intake-governance-config.1 @@ -20,7 +20,9 @@ or Blocked. Eligibility is an ordering result and grants no delivery authority. A completed non-empty series is Aligned only when every retained target is Completed, stored in the configured archive collection, and no target is Eligible. SeriesManifest permits an active series to reference active targets -and already archived predecessors together. +and already archived predecessors together. Because Git does not preserve empty +directories, an absent active collection is counted as empty in SeriesManifest +mode; DirectoryStrict still requires the configured directory. .SH EXIT STATUS Zero means Aligned or a valid journal. One means migration or clarification is required. Two means blocked or invalid input. diff --git a/.specify/presets/intake-authoring-governance/scripts/validate-intake-governance-config.py b/.specify/presets/intake-authoring-governance/scripts/validate-intake-governance-config.py index 28e0ceb5..017e8a26 100644 --- a/.specify/presets/intake-authoring-governance/scripts/validate-intake-governance-config.py +++ b/.specify/presets/intake-authoring-governance/scripts/validate-intake-governance-config.py @@ -122,11 +122,15 @@ def validate_series_manifest( fail("RIG014", "series manifest must contain non-empty orderedTargets") series_status = required_text(manifest, "status", "RIG017") - active_paths = { - item.relative_to(repo).as_posix() - for item in active_dir.iterdir() - if item.is_file() and intake_name_matches(item.name, pattern) - } + active_paths = ( + { + item.relative_to(repo).as_posix() + for item in active_dir.iterdir() + if item.is_file() and intake_name_matches(item.name, pattern) + } + if active_dir.is_dir() + else set() + ) active_prefix = f"{active_dir.relative_to(repo).as_posix().rstrip('/')}/" archive_prefix = f"{archive_dir.relative_to(repo).as_posix().rstrip('/')}/" target_paths: list[str] = [] @@ -281,8 +285,13 @@ def validate_config(data: dict, repo: Path) -> dict: if not (repo / roles[key]).is_file(): missing.append(roles[key]) for key in ("requirements-intake", "requirements-baseline"): - if not (repo / roles[key]).is_dir(): - missing.append(roles[key]) + if (repo / roles[key]).is_dir(): + continue + if key == "requirements-intake" and inventory_mode == "SeriesManifest": + # Git speichert keine leeren Verzeichnisse; SeriesManifest beweist den Bestand. + # Git does not store empty directories; SeriesManifest proves the inventory. + continue + missing.append(roles[key]) if not (repo / collections["seriesManifest"]).is_file(): missing.append(collections["seriesManifest"]) diff --git a/.specify/presets/intake-authoring-governance/tests/test-intake-governance-config.ps1 b/.specify/presets/intake-authoring-governance/tests/test-intake-governance-config.ps1 index 812091c0..49b610ca 100644 --- a/.specify/presets/intake-authoring-governance/tests/test-intake-governance-config.ps1 +++ b/.specify/presets/intake-authoring-governance/tests/test-intake-governance-config.ps1 @@ -146,6 +146,12 @@ try { Set-Content -LiteralPath $ManifestPath -Encoding utf8NoBOM Invoke-Fixture (Write-JsonFixture 'completed-series.json' $ManifestInventory) 0 '"eligibleCandidate": "N/A"' + $ActiveCollection = Join-Path $Root 'requirements/intakes/active' + Remove-Item -LiteralPath $ActiveCollection -Recurse -Force + Invoke-Fixture (Write-JsonFixture 'completed-series-without-active-directory.json' $ManifestInventory) 0 '"activeIntakeCount": 0' + New-Item -ItemType Directory -Path $ActiveCollection | Out-Null + Set-Content -LiteralPath $Target -Value '# Beispiel' -Encoding utf8NoBOM + $CompletedInActive = $Manifest.Clone() $CompletedInActive.status = 'Completed' $CompletedInActive.orderedTargets = @($Manifest.orderedTargets[0].Clone()) diff --git a/.specify/presets/intake-review-governance/docs/man/validate-intake-governance-config.1 b/.specify/presets/intake-review-governance/docs/man/validate-intake-governance-config.1 index 3145e050..71ff5575 100644 --- a/.specify/presets/intake-review-governance/docs/man/validate-intake-governance-config.1 +++ b/.specify/presets/intake-review-governance/docs/man/validate-intake-governance-config.1 @@ -20,7 +20,9 @@ or Blocked. Eligibility is an ordering result and grants no delivery authority. A completed non-empty series is Aligned only when every retained target is Completed, stored in the configured archive collection, and no target is Eligible. SeriesManifest permits an active series to reference active targets -and already archived predecessors together. +and already archived predecessors together. Because Git does not preserve empty +directories, an absent active collection is counted as empty in SeriesManifest +mode; DirectoryStrict still requires the configured directory. .SH EXIT STATUS Zero means Aligned or a valid journal. One means migration or clarification is required. Two means blocked or invalid input. diff --git a/.specify/presets/intake-review-governance/scripts/validate-intake-governance-config.py b/.specify/presets/intake-review-governance/scripts/validate-intake-governance-config.py index 28e0ceb5..017e8a26 100644 --- a/.specify/presets/intake-review-governance/scripts/validate-intake-governance-config.py +++ b/.specify/presets/intake-review-governance/scripts/validate-intake-governance-config.py @@ -122,11 +122,15 @@ def validate_series_manifest( fail("RIG014", "series manifest must contain non-empty orderedTargets") series_status = required_text(manifest, "status", "RIG017") - active_paths = { - item.relative_to(repo).as_posix() - for item in active_dir.iterdir() - if item.is_file() and intake_name_matches(item.name, pattern) - } + active_paths = ( + { + item.relative_to(repo).as_posix() + for item in active_dir.iterdir() + if item.is_file() and intake_name_matches(item.name, pattern) + } + if active_dir.is_dir() + else set() + ) active_prefix = f"{active_dir.relative_to(repo).as_posix().rstrip('/')}/" archive_prefix = f"{archive_dir.relative_to(repo).as_posix().rstrip('/')}/" target_paths: list[str] = [] @@ -281,8 +285,13 @@ def validate_config(data: dict, repo: Path) -> dict: if not (repo / roles[key]).is_file(): missing.append(roles[key]) for key in ("requirements-intake", "requirements-baseline"): - if not (repo / roles[key]).is_dir(): - missing.append(roles[key]) + if (repo / roles[key]).is_dir(): + continue + if key == "requirements-intake" and inventory_mode == "SeriesManifest": + # Git speichert keine leeren Verzeichnisse; SeriesManifest beweist den Bestand. + # Git does not store empty directories; SeriesManifest proves the inventory. + continue + missing.append(roles[key]) if not (repo / collections["seriesManifest"]).is_file(): missing.append(collections["seriesManifest"]) diff --git a/.specify/presets/intake-review-governance/tests/test-intake-governance-config.ps1 b/.specify/presets/intake-review-governance/tests/test-intake-governance-config.ps1 index 812091c0..49b610ca 100644 --- a/.specify/presets/intake-review-governance/tests/test-intake-governance-config.ps1 +++ b/.specify/presets/intake-review-governance/tests/test-intake-governance-config.ps1 @@ -146,6 +146,12 @@ try { Set-Content -LiteralPath $ManifestPath -Encoding utf8NoBOM Invoke-Fixture (Write-JsonFixture 'completed-series.json' $ManifestInventory) 0 '"eligibleCandidate": "N/A"' + $ActiveCollection = Join-Path $Root 'requirements/intakes/active' + Remove-Item -LiteralPath $ActiveCollection -Recurse -Force + Invoke-Fixture (Write-JsonFixture 'completed-series-without-active-directory.json' $ManifestInventory) 0 '"activeIntakeCount": 0' + New-Item -ItemType Directory -Path $ActiveCollection | Out-Null + Set-Content -LiteralPath $Target -Value '# Beispiel' -Encoding utf8NoBOM + $CompletedInActive = $Manifest.Clone() $CompletedInActive.status = 'Completed' $CompletedInActive.orderedTargets = @($Manifest.orderedTargets[0].Clone()) diff --git a/.specify/presets/intake-sequencing-governance/README.md b/.specify/presets/intake-sequencing-governance/README.md index f2eee668..2c202e22 100644 --- a/.specify/presets/intake-sequencing-governance/README.md +++ b/.specify/presets/intake-sequencing-governance/README.md @@ -101,10 +101,15 @@ schema 2.0, target paths are resolved from portable roles and collection paths. An active delivery series has exactly one explicit `Eligible` target; that state selects order only and grants no implementation or remote authority. A `Completed` series preserves its members in the configured archive collection -and has no eligible target. +and has no eligible target. In `SeriesManifest` mode, a missing active +collection counts as empty because Git does not preserve empty directories; +`DirectoryStrict` still requires the configured directory. A valid `Idle` series has neither members nor an eligible target. *Eine aktive Lieferserie hat genau ein `Eligible`-Ziel. Eine abgeschlossene Serie bewahrt ihre abgeschlossenen Mitglieder in der konfigurierten -Archiv-Collection, hat aber kein ausführbares Ziel. `Idle` bleibt ausschließlich -der leere Zustand. Aus `Eligible` entstehen keine Lieferrechte.* +Archiv-Collection, hat aber kein ausführbares Ziel. Im Modus `SeriesManifest` +gilt eine fehlende aktive Collection als leer, weil Git leere Verzeichnisse +nicht speichert; `DirectoryStrict` verlangt das Verzeichnis weiterhin. `Idle` +bleibt ausschließlich der leere Zustand. Aus `Eligible` entstehen keine +Lieferrechte.* diff --git a/.specify/presets/intake-sequencing-governance/docs/man/validate-intake-governance-config.1 b/.specify/presets/intake-sequencing-governance/docs/man/validate-intake-governance-config.1 index 457a74b8..21c01b87 100644 --- a/.specify/presets/intake-sequencing-governance/docs/man/validate-intake-governance-config.1 +++ b/.specify/presets/intake-sequencing-governance/docs/man/validate-intake-governance-config.1 @@ -23,7 +23,9 @@ dependencies and therefore has no eligible candidate. A completed non-empty series is Aligned only when every retained target is Completed, stored in the configured archive collection, and no target is Eligible. SeriesManifest permits an active series to reference active targets -and already archived predecessors together. +and already archived predecessors together. Because Git does not preserve empty +directories, an absent active collection is counted as empty in SeriesManifest +mode; DirectoryStrict still requires the configured directory. .PP Canonical-index uniqueness follows Git repository boundaries. An index in a nested Git checkout belongs to that checkout; an ordinary duplicate index diff --git a/.specify/presets/intake-sequencing-governance/scripts/validate-intake-governance-config.py b/.specify/presets/intake-sequencing-governance/scripts/validate-intake-governance-config.py index 1a84b28f..f5d4b85c 100644 --- a/.specify/presets/intake-sequencing-governance/scripts/validate-intake-governance-config.py +++ b/.specify/presets/intake-sequencing-governance/scripts/validate-intake-governance-config.py @@ -145,11 +145,15 @@ def validate_series_manifest( if not targets: fail("RIG014", "series manifest must contain non-empty orderedTargets") - active_paths = { - item.relative_to(repo).as_posix() - for item in active_dir.iterdir() - if item.is_file() and intake_name_matches(item.name, pattern) - } + active_paths = ( + { + item.relative_to(repo).as_posix() + for item in active_dir.iterdir() + if item.is_file() and intake_name_matches(item.name, pattern) + } + if active_dir.is_dir() + else set() + ) active_prefix = f"{active_dir.relative_to(repo).as_posix().rstrip('/')}/" archive_prefix = f"{archive_dir.relative_to(repo).as_posix().rstrip('/')}/" target_paths: list[str] = [] @@ -304,8 +308,13 @@ def validate_config(data: dict, repo: Path) -> dict: if not (repo / roles[key]).is_file(): missing.append(roles[key]) for key in ("requirements-intake", "requirements-baseline"): - if not (repo / roles[key]).is_dir(): - missing.append(roles[key]) + if (repo / roles[key]).is_dir(): + continue + if key == "requirements-intake" and inventory_mode == "SeriesManifest": + # Git speichert keine leeren Verzeichnisse; SeriesManifest beweist den Bestand. + # Git does not store empty directories; SeriesManifest proves the inventory. + continue + missing.append(roles[key]) if not (repo / collections["seriesManifest"]).is_file(): missing.append(collections["seriesManifest"]) diff --git a/.specify/presets/intake-sequencing-governance/tests/test-intake-governance-config.ps1 b/.specify/presets/intake-sequencing-governance/tests/test-intake-governance-config.ps1 index 6febccf9..41a6350c 100644 --- a/.specify/presets/intake-sequencing-governance/tests/test-intake-governance-config.ps1 +++ b/.specify/presets/intake-sequencing-governance/tests/test-intake-governance-config.ps1 @@ -186,6 +186,12 @@ try { Set-Content -LiteralPath $ManifestPath -Encoding utf8NoBOM Invoke-Fixture (Write-JsonFixture 'completed-series.json' $ManifestInventory) 0 '"eligibleCandidate": "N/A"' + $ActiveCollection = Join-Path $Root 'requirements/intakes/active' + Remove-Item -LiteralPath $ActiveCollection -Recurse -Force + Invoke-Fixture (Write-JsonFixture 'completed-series-without-active-directory.json' $ManifestInventory) 0 '"activeIntakeCount": 0' + New-Item -ItemType Directory -Path $ActiveCollection | Out-Null + Set-Content -LiteralPath $Target -Value '# Beispiel' -Encoding utf8NoBOM + $CompletedInActive = $Manifest.Clone() $CompletedInActive.status = 'Completed' $CompletedInActive.orderedTargets = @($Manifest.orderedTargets[0].Clone()) From 47283e9d2aa4dab50d50fba07fcc3bd8e18d734a Mon Sep 17 00:00:00 2001 From: Thorsten Hindermann Date: Sun, 13 Sep 2026 02:52:12 +0200 Subject: [PATCH 2/2] docs: refresh project statistics --- docs/project-statistics.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/project-statistics.md b/docs/project-statistics.md index 751f4ffa..c03e726a 100644 --- a/docs/project-statistics.md +++ b/docs/project-statistics.md @@ -1144,25 +1144,25 @@ Profil 2 verwendet Git-getrackte Textdateien und sichtbare Git-Aktivitaet. Die W | Kennzahl / Metric | Wert / Value | |---|---:| -| Textbasis / Text base | 726505 lines | +| Textbasis / Text base | 726565 lines | | Textdateien / Text files | 3385 | | Beobachtbarer Zeitraum / Observable period | 2025-09-21..2026-09-13 | | Aktivtage / Active days | 97 | -| Relevante Commits / Relevant commits | 595 | -| Zeilen je Aktivtag / Lines per active day | 7489.7 | +| Relevante Commits / Relevant commits | 596 | +| Zeilen je Aktivtag / Lines per active day | 7490.4 | | Peak-Tag im Fenster / Peak day in window | 2026-03-22 / 321183 | | Peak-Woche im Fenster / Peak week in window | 2026-03-22 / 373576 | | Laengste Serie / Longest streak | 17 days | | Speedup vs. 80 lines/day | 93.6x | | Speedup vs. 125 lines/day | 59.9x | -| Methodik / Methodology | v2; source `98b0f28e1dfb` | +| Methodik / Methodology | v2; source `58ba9d4a1855` | ### Artefaktmix / Artifact Mix ```text Produktiv / Production [####................] 19.2% | 139704 Tests [#...................] 7.5% | 54341 -Dokumentation / Documentation [##########..........] 50.5% | 366697 +Dokumentation / Documentation [##########..........] 50.5% | 366757 Skripte / Scripts [#...................] 3.2% | 23414 Konfiguration / Configuration [#...................] 5.8% | 42483 Daten und Medien / Data and media [#...................] 0.0% | 1 @@ -1188,7 +1188,7 @@ Sa/Sa 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 ```text Wochen / Weeks 27..52 | 2026-03-22..2026-09-19 -So/Su 4 4 0 4 0 0 4 4 0 0 4 0 4 0 0 0 4 4 4 1 4 4 0 4 0 3 +So/Su 4 4 0 4 0 0 4 4 0 0 4 0 4 0 0 0 4 4 4 1 4 4 0 4 0 4 Mo/Mo 4 4 0 1 4 0 4 0 0 0 1 0 2 0 3 1 4 4 0 0 0 2 0 0 4 - Di/Tu 3 4 0 0 0 0 3 4 0 3 2 0 0 0 2 0 4 4 4 0 0 0 0 0 4 - Mi/We 4 0 0 0 3 0 4 1 0 0 2 0 4 0 2 0 4 2 4 0 0 0 0 0 0 - @@ -1381,7 +1381,7 @@ Die Faktoren vergleichen sichtbare Lieferdichte mit den dokumentierten manuellen Scale: 0..10000 lines/day Experienced manual [#...................] 80 Thorsten solo [#...................] 125 -Visible repository [###############.....] 7489.7 +Visible repository [###############.....] 7490.4 ``` Die gemeinsame Skala vergleicht Referenzen und sichtbare Lieferdichte. Sie schreibt die Git-Aktivitaet keiner Person oder KI pauschal zu. @@ -1407,6 +1407,6 @@ DE: Das Fenster beginnt am 2025-09-21 und endet am 2026-09-13. Es enthaelt 97 ak | 2026-06 | 42176 | | 2026-07 | 205615 | | 2026-08 | 122136 | -| 2026-09 | 25370 | +| 2026-09 | 25490 |