From 05eeb6b688dd87e577b565f9555b8f39e05b4d99 Mon Sep 17 00:00:00 2001 From: Thorsten Hindermann Date: Sun, 20 Sep 2026 14:10:02 +0200 Subject: [PATCH 1/3] chore: synchronize verified maintenance package --- docs/man/maintain-agentic-workspace.1.md | 21 +++++++ scripts/lib/maintenance_container_worker.py | 44 ++++++++++++++ scripts/lib/maintenance_execution_context.py | 9 ++- .../test_maintenance_execution_context.py | 58 +++++++++++++++++++ 4 files changed, 130 insertions(+), 2 deletions(-) diff --git a/docs/man/maintain-agentic-workspace.1.md b/docs/man/maintain-agentic-workspace.1.md index ff9d93d7..c8fea2a2 100644 --- a/docs/man/maintain-agentic-workspace.1.md +++ b/docs/man/maintain-agentic-workspace.1.md @@ -42,6 +42,27 @@ is insufficient. For `SandboxPreflightBlocked`, inspect the report, explicitly publish the reviewed package and pin it in the sandbox image. Maintenance never rebuilds or replaces the image automatically.* +Nach bestandener Vorprüfung gilt Git-Vertrauen nur für den jeweiligen +Wartungsprozess und die exakt deklarierten Repository-Pfade. Eine leere +Vertrauensliste setzt geerbte pauschale Freigaben zurück, bevor die geprüften +Pfade ergänzt werden. Es wird keine `.gitconfig` geschrieben. Damit bleibt +`--check-only` schreibfrei und die Wartung benötigt nach einer +Container-Neuerzeugung keine manuelle `safe.directory`-Liste. Interaktive +Git-Aufrufe außerhalb der Wartung erhalten dadurch keine Freigabe. +Auf anderen Systemen müssen deren Mounts und Zielpfade im freigegebenen +Ausführungs- und Flottenvertrag übereinstimmen; es gibt keine Pfadsuche oder +Wildcard-Freigabe. Details und Aktivierungsstand: +[Prozessgebundenes Git-Vertrauen](https://github.com/hindermath/home-baseline/blob/4459e744d126d51b832986b09ac1a73cf88c1607/docs/maintenance/container-git-trust.md). + +*After successful preflight, Git trust applies only to the maintenance process +and exact declared repository paths. An empty trust entry resets inherited +broad permissions before validated paths are added. No `.gitconfig` is written; +check-only stays read-only and recreated containers need no manual trust list +for maintenance. Interactive Git outside maintenance receives no additional +trust. Other systems must match their approved execution and fleet contracts; +there is no path discovery or wildcard permission. See the linked operating +note for activation status.* + Ohne Optionen öffnet ein vollständig interaktives Terminal zuerst die Wartungs-TUI. TUI bedeutet Terminal User Interface, also eine textbasierte Benutzungsoberfläche im Terminal. Die Vorauswahl ist diff --git a/scripts/lib/maintenance_container_worker.py b/scripts/lib/maintenance_container_worker.py index 682361b5..3bdfd20c 100644 --- a/scripts/lib/maintenance_container_worker.py +++ b/scripts/lib/maintenance_container_worker.py @@ -5,9 +5,53 @@ import pathlib import subprocess import tempfile +import os +import contextlib + + +@contextlib.contextmanager +def repository_trust(entries): + """Scope Git trust to validated targets and restore the caller even on error.""" + paths = [] + for entry in entries: + repo = pathlib.Path(entry["containerPath"]) + root = pathlib.Path(entry["containerRoot"]) + if not root.is_absolute() or repo.resolve() != repo or root.resolve() != root: + raise ValueError("Delegated path has symlink components or is not absolute") + repo.relative_to(root) + if any(char in str(repo) for char in ("*", "\n", "\r")): + raise ValueError("Wildcard or control character in delegated Git target") + if (repo / ".git").is_symlink() or not (repo / ".git").is_dir(): + raise ValueError("Missing delegated Git target") + paths.append(str(repo)) + # The leaf worker is a dedicated, single-threaded process. Its children + # (including Bash/PowerShell and storage helpers) inherit the same exact + # list. Empty first value resets broader system/global safe.directory lists. + keys = {key for key in os.environ if key == "GIT_CONFIG_PARAMETERS" or + key == "GIT_CONFIG_COUNT" or key.startswith(("GIT_CONFIG_KEY_", "GIT_CONFIG_VALUE_"))} + previous = {key: os.environ[key] for key in keys} + values = ["", *sorted(set(paths))] + managed = {"GIT_CONFIG_COUNT": str(len(values))} + for index, value in enumerate(values): + managed[f"GIT_CONFIG_KEY_{index}"] = "safe.directory" + managed[f"GIT_CONFIG_VALUE_{index}"] = value + try: + for key in keys: + del os.environ[key] + os.environ.update(managed) + yield + finally: + for key in managed: + os.environ.pop(key, None) + os.environ.update(previous) def execute(payload: dict) -> dict: + with repository_trust(payload["entries"]): + return _execute(payload) + + +def _execute(payload: dict) -> dict: source = pathlib.Path(payload["source"]) mode = payload["mode"] if mode not in ("check-only", "dry-run", "update"): diff --git a/scripts/lib/maintenance_execution_context.py b/scripts/lib/maintenance_execution_context.py index 793626c5..70dc9711 100644 --- a/scripts/lib/maintenance_execution_context.py +++ b/scripts/lib/maintenance_execution_context.py @@ -79,9 +79,11 @@ def validate_contract(data: dict, today: datetime.date | None = None, *, check_e p = json.load(sys.stdin) root = pathlib.Path(p["root"]) repo = pathlib.Path(p["repository"]) -if root.resolve() != root or repo.resolve() != repo: +if not root.is_absolute() or root.resolve() != root or repo.resolve() != repo: raise SystemExit("Symlink boundary rejected") repo.relative_to(root) +if any(char in str(repo) for char in ("*", "\n", "\r")): + raise SystemExit("Wildcard or control character in Git target") if (repo / ".git").is_symlink(): raise SystemExit("Symlink Git directory rejected") if p["action"] == "probe": @@ -103,7 +105,10 @@ def validate_contract(data: dict, today: datetime.date | None = None, *, check_e raise SystemExit("Symbolic-ref mutation is not allowed") if effective[0] == "merge" and (len(effective) != 3 or effective[1] != "--ff-only" or not re.fullmatch(r"[0-9a-f]{40,64}", effective[2])): raise SystemExit("Only bounded fast-forward merge is allowed") - result = subprocess.run(["git", "-C", str(repo), *args], capture_output=True, text=True) + # Reset inherited trust (including '*') and trust only this validated repo. + # Command-scope configuration survives recreation without writing .gitconfig. + result = subprocess.run(["git", "-c", "safe.directory=", "-c", "safe.directory=" + str(repo), + "-C", str(repo), *args], capture_output=True, text=True) print(json.dumps({"returncode":result.returncode,"stdout":result.stdout,"stderr":result.stderr})) else: raise SystemExit("Unknown maintenance action") diff --git a/scripts/tests/test_maintenance_execution_context.py b/scripts/tests/test_maintenance_execution_context.py index 95f0bbb5..5511ae4b 100644 --- a/scripts/tests/test_maintenance_execution_context.py +++ b/scripts/tests/test_maintenance_execution_context.py @@ -35,6 +35,64 @@ def contract(): class ExecutionContextTests(unittest.TestCase): + def test_command_trust_without_global_config_after_recreation(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory).resolve() + repo = root / "repo with spaces" + subprocess.run(["git", "init", "-q", str(repo)], check=True) + payload = {"root": str(root), "repository": str(repo), "action": "git", + "arguments": ["status", "--porcelain=v1"]} + # A new HOME models a recreated container with no user Git config. + for generation in ("first", "recreated"): + home = root / generation + home.mkdir() + env = {**os.environ, "HOME": str(home), "GIT_CONFIG_NOSYSTEM": "1", + "GIT_CONFIG_GLOBAL": str(home / ".gitconfig"), + "GIT_TEST_ASSUME_DIFFERENT_OWNER": "1"} + denied = subprocess.run(["git", "-C", str(repo), "status"], env=env, capture_output=True) + self.assertNotEqual(denied.returncode, 0) + result = subprocess.run([sys.executable, "-c", context.REMOTE_PROGRAM], + input=json.dumps(payload), text=True, capture_output=True, env=env) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(json.loads(result.stdout)["returncode"], 0, result.stdout) + self.assertFalse((home / ".gitconfig").exists()) + + @unittest.skipIf(os.name == "nt", "Linux container environment: Windows drops empty env values; Windows hosts delegate to Linux") + def test_worker_trust_exact_inherited_and_restored_on_error(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory).resolve() + payload = self.worker_payload(root) + repo = Path(payload["entries"][0]["containerPath"]) + subprocess.run(["git", "init", "-q", str(repo)], check=True) + other = root / "not-declared" + subprocess.run(["git", "init", "-q", str(other)], check=True) + with patch.dict(os.environ, {"GIT_TEST_ASSUME_DIFFERENT_OWNER": "1", + "GIT_CONFIG_COUNT": "1", "GIT_CONFIG_KEY_0": "safe.directory", + "GIT_CONFIG_VALUE_0": "*"}): + before = dict(os.environ) + with self.assertRaisesRegex(RuntimeError, "fixture"): + with worker.repository_trust(payload["entries"]): + for _ in range(2): + child = ["bash", "-c", 'git -C "$1" status --porcelain=v1', "bash", str(repo)] + good = subprocess.run(child, capture_output=True) + bad = subprocess.run(["git", "-C", str(other), "status"], capture_output=True) + self.assertEqual(good.returncode, 0, good.stderr) + self.assertNotEqual(bad.returncode, 0) + raise RuntimeError("fixture") + self.assertEqual(dict(os.environ), before) + + def test_worker_trust_rejects_wildcard_and_escape_before_environment_change(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory).resolve() + payload = self.worker_payload(root) + before = dict(os.environ) + for path in (str(root / "*"), str(root.parent)): + entry = {**payload["entries"][0], "containerPath": path} + with self.assertRaises(ValueError): + with worker.repository_trust([entry]): + self.fail("Unsafe target accepted") + self.assertEqual(dict(os.environ), before) + def test_strict_contract_and_expiry(self): self.assertEqual(context.validate_contract(contract()), contract()) for changes in ({"schemaVersion": True}, {"schemaVersion": 2}, {"expiresOn": "2000-01-01"}, From fd649912387a16634d3232ba4450af551fee6a50 Mon Sep 17 00:00:00 2001 From: Thorsten Hindermann Date: Sun, 20 Sep 2026 14:10:16 +0200 Subject: [PATCH 2/3] docs: refresh project transparency snapshot --- docs/project-statistics/report.md | 12 ++++++------ docs/project-statistics/snapshot.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/project-statistics/report.md b/docs/project-statistics/report.md index 5dd6441b..9bd41b18 100644 --- a/docs/project-statistics/report.md +++ b/docs/project-statistics/report.md @@ -10,23 +10,23 @@ Git-bound inventory and activity; not a measure of quality, learning performance | Kennzahl / Metric | Wert / Value | | --- | --- | | Textdateien / Text files | 3487 | -| Textzeilen / Text lines | 751471 | +| Textzeilen / Text lines | 751599 | | Aktivtage / Active days | 98 | | Stichtag / As of | 2026-09-20 | | Fensterbeginn / Window start | 2025-09-28 | | Zeitzone / Time zone | UTC | Quellrevision / Source revision: -b918a1463b6e6bd97d74e8d76e414603e2a9e1f9 +05eeb6b688dd87e577b565f9555b8f39e05b4d99 ### Artefakte / Artifacts | Kategorie / Category | Dateien / Files | Zeilen / Lines | | --- | ---: | ---: | | Production | 854 | 140068 | -| Tests | 429 | 55081 | -| Documentation | 1600 | 384417 | -| Scripts | 136 | 28874 | +| Tests | 429 | 55139 | +| Documentation | 1600 | 384438 | +| Scripts | 136 | 28923 | | Configuration | 139 | 43165 | | DataMedia | 1 | 1 | | Other | 328 | 99865 | @@ -158,7 +158,7 @@ Sa/Sa 0 4 0 0 4 4 0 0 4 0 4 3 0 4 4 2 4 2 4 4 0 4 0 4 4 - | 2026-09-12 | 8815 | 558 | | 2026-09-13 | 18458 | 1438 | | 2026-09-19 | 7250 | 162 | -| 2026-09-20 | 1244 | 29 | +| 2026-09-20 | 1374 | 31 | ### Abdeckung / Coverage diff --git a/docs/project-statistics/snapshot.json b/docs/project-statistics/snapshot.json index e0d9ab44..8d85b4df 100644 --- a/docs/project-statistics/snapshot.json +++ b/docs/project-statistics/snapshot.json @@ -1 +1 @@ -{"schemaVersion":1,"presetVersion":"0.1.0","methodology":"project-transparency/1","configSha256":"35ecec870453206fe9c0c92c2c0a87c4fb9ef6a6457bad4903d7d5b5f2ee1130","rendererSha256":"a40f565e2f7451d5688103b69e40a7a39e226507cf3ee43144a60af386d65d30","generatedSha256":"8dacf2e0a1f6b75e42ff91f7180419216a9f4a776aa9fd7ef7a046981d62fc75","measurement":{"sourceRevision":"b918a1463b6e6bd97d74e8d76e414603e2a9e1f9","asOf":"2026-09-20","timeZone":"UTC","windowStart":"2025-09-28","windowWeeks":52,"inputsSha256":"894b0a9b19cfad6ab68b3a467b032e6b1a3abf7806ffd9371fd9443cc95fa47d","totalTextFiles":3487,"totalTextLines":751471,"activeDays":98,"categories":{"Production":{"files":854,"lines":140068},"Tests":{"files":429,"lines":55081},"Documentation":{"files":1600,"lines":384417},"Scripts":{"files":136,"lines":28874},"Configuration":{"files":139,"lines":43165},"DataMedia":{"files":1,"lines":1},"Other":{"files":328,"lines":99865}},"daily":{"2026-02-08":{"added":139076,"removed":29},"2026-03-01":{"added":4960,"removed":39},"2026-03-06":{"added":36824,"removed":149},"2026-03-08":{"added":1616,"removed":1},"2026-03-16":{"added":1345,"removed":356},"2026-03-17":{"added":1150,"removed":86},"2026-03-18":{"added":46,"removed":32},"2026-03-20":{"added":6565,"removed":709},"2026-03-21":{"added":63301,"removed":1705},"2026-03-22":{"added":316337,"removed":4336},"2026-03-23":{"added":8488,"removed":1785},"2026-03-24":{"added":471,"removed":61},"2026-03-25":{"added":1670,"removed":210},"2026-03-27":{"added":31948,"removed":1963},"2026-03-28":{"added":6037,"removed":223},"2026-03-29":{"added":15537,"removed":2397},"2026-03-30":{"added":17062,"removed":4797},"2026-03-31":{"added":277,"removed":0},"2026-04-02":{"added":1902,"removed":2},"2026-04-11":{"added":0,"removed":1651},"2026-04-12":{"added":189,"removed":6},"2026-04-13":{"added":19,"removed":17},"2026-04-17":{"added":84,"removed":14},"2026-04-20":{"added":7912,"removed":309},"2026-04-22":{"added":455,"removed":433},"2026-04-24":{"added":2868,"removed":125},"2026-04-30":{"added":2708,"removed":259},"2026-05-02":{"added":31880,"removed":2176},"2026-05-04":{"added":5558,"removed":2070},"2026-05-05":{"added":648,"removed":22},"2026-05-06":{"added":2374,"removed":184},"2026-05-08":{"added":4835,"removed":204408},"2026-05-09":{"added":2274,"removed":65},"2026-05-10":{"added":3276,"removed":303},"2026-05-12":{"added":2467,"removed":436},"2026-05-13":{"added":1,"removed":1},"2026-05-15":{"added":3,"removed":0},"2026-05-21":{"added":420,"removed":1},"2026-05-22":{"added":2578,"removed":1193},"2026-05-26":{"added":459,"removed":77},"2026-05-28":{"added":952,"removed":10},"2026-05-29":{"added":573,"removed":61},"2026-05-30":{"added":2845,"removed":270},"2026-05-31":{"added":1377,"removed":56},"2026-06-01":{"added":2,"removed":0},"2026-06-02":{"added":96,"removed":47},"2026-06-03":{"added":85,"removed":21},"2026-06-04":{"added":1097,"removed":159},"2026-06-11":{"added":1473,"removed":2171},"2026-06-13":{"added":3620,"removed":308},"2026-06-14":{"added":2109,"removed":130},"2026-06-15":{"added":51,"removed":53},"2026-06-17":{"added":27068,"removed":4},"2026-06-18":{"added":102,"removed":100},"2026-06-19":{"added":1895,"removed":90},"2026-06-20":{"added":420,"removed":5},"2026-06-26":{"added":105,"removed":11},"2026-06-29":{"added":534,"removed":253},"2026-06-30":{"added":79,"removed":88},"2026-07-01":{"added":108,"removed":5},"2026-07-03":{"added":431,"removed":26},"2026-07-04":{"added":1950,"removed":1448},"2026-07-06":{"added":48,"removed":0},"2026-07-10":{"added":6283,"removed":2420},"2026-07-11":{"added":11966,"removed":3631},"2026-07-12":{"added":34047,"removed":945},"2026-07-13":{"added":15096,"removed":498},"2026-07-14":{"added":5748,"removed":204},"2026-07-15":{"added":3837,"removed":427},"2026-07-16":{"added":34780,"removed":851},"2026-07-17":{"added":20769,"removed":1499},"2026-07-18":{"added":45,"removed":45},"2026-07-19":{"added":16222,"removed":3963},"2026-07-21":{"added":5843,"removed":267},"2026-07-22":{"added":93,"removed":5},"2026-07-23":{"added":2140,"removed":186},"2026-07-24":{"added":2606,"removed":297},"2026-07-25":{"added":2627,"removed":212},"2026-07-26":{"added":8851,"removed":1251},"2026-07-28":{"added":3054,"removed":488},"2026-07-29":{"added":10199,"removed":188},"2026-07-31":{"added":11,"removed":5},"2026-08-01":{"added":133,"removed":25},"2026-08-02":{"added":36,"removed":4},"2026-08-08":{"added":4363,"removed":241},"2026-08-09":{"added":11987,"removed":812},"2026-08-13":{"added":4008,"removed":35},"2026-08-15":{"added":1318,"removed":767},"2026-08-17":{"added":76,"removed":23},"2026-08-28":{"added":8319,"removed":317},"2026-08-29":{"added":4628,"removed":290},"2026-08-30":{"added":84289,"removed":465},"2026-09-07":{"added":8253,"removed":44},"2026-09-08":{"added":4141,"removed":2516},"2026-09-12":{"added":8815,"removed":558},"2026-09-13":{"added":18458,"removed":1438},"2026-09-19":{"added":7250,"removed":162},"2026-09-20":{"added":1244,"removed":29}},"omitted":{"binary":68,"symlinks":0,"submodules":0,"excluded":3}}} +{"schemaVersion":1,"presetVersion":"0.1.0","methodology":"project-transparency/1","configSha256":"35ecec870453206fe9c0c92c2c0a87c4fb9ef6a6457bad4903d7d5b5f2ee1130","rendererSha256":"a40f565e2f7451d5688103b69e40a7a39e226507cf3ee43144a60af386d65d30","generatedSha256":"d4db0cfd68d8a31cdeed2cc4d9e5d64be7cee2b800084c66c56699cbfa2c61dc","measurement":{"sourceRevision":"05eeb6b688dd87e577b565f9555b8f39e05b4d99","asOf":"2026-09-20","timeZone":"UTC","windowStart":"2025-09-28","windowWeeks":52,"inputsSha256":"b1198109a4899bc2b3ec754f67328c639159309300db2c3f588ad0b0df3e2573","totalTextFiles":3487,"totalTextLines":751599,"activeDays":98,"categories":{"Production":{"files":854,"lines":140068},"Tests":{"files":429,"lines":55139},"Documentation":{"files":1600,"lines":384438},"Scripts":{"files":136,"lines":28923},"Configuration":{"files":139,"lines":43165},"DataMedia":{"files":1,"lines":1},"Other":{"files":328,"lines":99865}},"daily":{"2026-02-08":{"added":139076,"removed":29},"2026-03-01":{"added":4960,"removed":39},"2026-03-06":{"added":36824,"removed":149},"2026-03-08":{"added":1616,"removed":1},"2026-03-16":{"added":1345,"removed":356},"2026-03-17":{"added":1150,"removed":86},"2026-03-18":{"added":46,"removed":32},"2026-03-20":{"added":6565,"removed":709},"2026-03-21":{"added":63301,"removed":1705},"2026-03-22":{"added":316337,"removed":4336},"2026-03-23":{"added":8488,"removed":1785},"2026-03-24":{"added":471,"removed":61},"2026-03-25":{"added":1670,"removed":210},"2026-03-27":{"added":31948,"removed":1963},"2026-03-28":{"added":6037,"removed":223},"2026-03-29":{"added":15537,"removed":2397},"2026-03-30":{"added":17062,"removed":4797},"2026-03-31":{"added":277,"removed":0},"2026-04-02":{"added":1902,"removed":2},"2026-04-11":{"added":0,"removed":1651},"2026-04-12":{"added":189,"removed":6},"2026-04-13":{"added":19,"removed":17},"2026-04-17":{"added":84,"removed":14},"2026-04-20":{"added":7912,"removed":309},"2026-04-22":{"added":455,"removed":433},"2026-04-24":{"added":2868,"removed":125},"2026-04-30":{"added":2708,"removed":259},"2026-05-02":{"added":31880,"removed":2176},"2026-05-04":{"added":5558,"removed":2070},"2026-05-05":{"added":648,"removed":22},"2026-05-06":{"added":2374,"removed":184},"2026-05-08":{"added":4835,"removed":204408},"2026-05-09":{"added":2274,"removed":65},"2026-05-10":{"added":3276,"removed":303},"2026-05-12":{"added":2467,"removed":436},"2026-05-13":{"added":1,"removed":1},"2026-05-15":{"added":3,"removed":0},"2026-05-21":{"added":420,"removed":1},"2026-05-22":{"added":2578,"removed":1193},"2026-05-26":{"added":459,"removed":77},"2026-05-28":{"added":952,"removed":10},"2026-05-29":{"added":573,"removed":61},"2026-05-30":{"added":2845,"removed":270},"2026-05-31":{"added":1377,"removed":56},"2026-06-01":{"added":2,"removed":0},"2026-06-02":{"added":96,"removed":47},"2026-06-03":{"added":85,"removed":21},"2026-06-04":{"added":1097,"removed":159},"2026-06-11":{"added":1473,"removed":2171},"2026-06-13":{"added":3620,"removed":308},"2026-06-14":{"added":2109,"removed":130},"2026-06-15":{"added":51,"removed":53},"2026-06-17":{"added":27068,"removed":4},"2026-06-18":{"added":102,"removed":100},"2026-06-19":{"added":1895,"removed":90},"2026-06-20":{"added":420,"removed":5},"2026-06-26":{"added":105,"removed":11},"2026-06-29":{"added":534,"removed":253},"2026-06-30":{"added":79,"removed":88},"2026-07-01":{"added":108,"removed":5},"2026-07-03":{"added":431,"removed":26},"2026-07-04":{"added":1950,"removed":1448},"2026-07-06":{"added":48,"removed":0},"2026-07-10":{"added":6283,"removed":2420},"2026-07-11":{"added":11966,"removed":3631},"2026-07-12":{"added":34047,"removed":945},"2026-07-13":{"added":15096,"removed":498},"2026-07-14":{"added":5748,"removed":204},"2026-07-15":{"added":3837,"removed":427},"2026-07-16":{"added":34780,"removed":851},"2026-07-17":{"added":20769,"removed":1499},"2026-07-18":{"added":45,"removed":45},"2026-07-19":{"added":16222,"removed":3963},"2026-07-21":{"added":5843,"removed":267},"2026-07-22":{"added":93,"removed":5},"2026-07-23":{"added":2140,"removed":186},"2026-07-24":{"added":2606,"removed":297},"2026-07-25":{"added":2627,"removed":212},"2026-07-26":{"added":8851,"removed":1251},"2026-07-28":{"added":3054,"removed":488},"2026-07-29":{"added":10199,"removed":188},"2026-07-31":{"added":11,"removed":5},"2026-08-01":{"added":133,"removed":25},"2026-08-02":{"added":36,"removed":4},"2026-08-08":{"added":4363,"removed":241},"2026-08-09":{"added":11987,"removed":812},"2026-08-13":{"added":4008,"removed":35},"2026-08-15":{"added":1318,"removed":767},"2026-08-17":{"added":76,"removed":23},"2026-08-28":{"added":8319,"removed":317},"2026-08-29":{"added":4628,"removed":290},"2026-08-30":{"added":84289,"removed":465},"2026-09-07":{"added":8253,"removed":44},"2026-09-08":{"added":4141,"removed":2516},"2026-09-12":{"added":8815,"removed":558},"2026-09-13":{"added":18458,"removed":1438},"2026-09-19":{"added":7250,"removed":162},"2026-09-20":{"added":1374,"removed":31}},"omitted":{"binary":68,"symlinks":0,"submodules":0,"excluded":3}}} From 3aecc4e2fd8bcb98cd62571e9b7151de1986acb7 Mon Sep 17 00:00:00 2001 From: Thorsten Hindermann Date: Sun, 20 Sep 2026 14:10:31 +0200 Subject: [PATCH 3/3] docs: refresh maintenance delivery statistics --- docs/project-statistics.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/project-statistics.md b/docs/project-statistics.md index 69b038ac..4311ab8b 100644 --- a/docs/project-statistics.md +++ b/docs/project-statistics.md @@ -1153,26 +1153,26 @@ Profil 2 verwendet Git-getrackte Textdateien und sichtbare Git-Aktivitaet. Die W | Kennzahl / Metric | Wert / Value | |---|---:| -| Textbasis / Text base | 751471 lines | +| Textbasis / Text base | 751599 lines | | Textdateien / Text files | 3487 | | Beobachtbarer Zeitraum / Observable period | 2025-09-28..2026-09-20 | | Aktivtage / Active days | 99 | -| Relevante Commits / Relevant commits | 620 | -| Zeilen je Aktivtag / Lines per active day | 7590.6 | +| Relevante Commits / Relevant commits | 621 | +| Zeilen je Aktivtag / Lines per active day | 7591.9 | | 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 | 94.9x | | Speedup vs. 125 lines/day | 60.7x | -| Methodik / Methodology | v2; source `b918a1463b6e` | +| Methodik / Methodology | v2; source `05eeb6b688dd` | ### Artefaktmix / Artifact Mix ```text Produktiv / Production [####................] 18.6% | 140068 -Tests [#...................] 7.3% | 55081 -Dokumentation / Documentation [##########..........] 51.2% | 384417 -Skripte / Scripts [#...................] 3.8% | 28874 +Tests [#...................] 7.3% | 55139 +Dokumentation / Documentation [##########..........] 51.1% | 384438 +Skripte / Scripts [#...................] 3.8% | 28923 Konfiguration / Configuration [#...................] 5.7% | 43165 Daten und Medien / Data and media [#...................] 0.0% | 1 Sonstiger Text / Other text [###.................] 13.3% | 99865 @@ -1390,7 +1390,7 @@ Die Faktoren vergleichen sichtbare Lieferdichte mit den dokumentierten manuellen Scale: 0..10000 lines/day Experienced manual [#...................] 80 Thorsten solo [#...................] 125 -Visible repository [###############.....] 7590.6 +Visible repository [###############.....] 7591.9 ``` Die gemeinsame Skala vergleicht Referenzen und sichtbare Lieferdichte. Sie schreibt die Git-Aktivitaet keiner Person oder KI pauschal zu. @@ -1416,6 +1416,6 @@ DE: Das Fenster beginnt am 2025-09-28 und endet am 2026-09-20. Es enthaelt 99 ak | 2026-06 | 42176 | | 2026-07 | 205615 | | 2026-08 | 122136 | -| 2026-09 | 52908 | +| 2026-09 | 53040 |