diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 6393937..2e831bf 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,11 +1,11 @@ blank_issues_enabled: true contact_links: - name: Weltentwurf und Fraktionen - url: https://github.com/VibecodingGermany/Project_Nova/blob/main/docs/vision/Lore.md + url: https://github.com/VibecodingGermany/HashKrieg/blob/main/docs/vision/Lore.md about: Warum die beiden Fraktionen gegeneinander kaempfen. - name: Die offene Wirtschaftsentscheidung - url: https://github.com/VibecodingGermany/Project_Nova/blob/main/docs/vision/Konzept_Hashkrieg.md + url: https://github.com/VibecodingGermany/HashKrieg/blob/main/docs/vision/Konzept_Hashkrieg.md about: Soll die oekonomische Grundschleife umgedreht werden? Meinungen erwuenscht. - name: Sicherheitsluecke melden - url: https://github.com/VibecodingGermany/Project_Nova/security/advisories/new + url: https://github.com/VibecodingGermany/HashKrieg/security/advisories/new about: Bitte nicht als oeffentliches Issue. diff --git a/CHANGELOG.md b/CHANGELOG.md index 5291b5d..b6183aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -310,6 +310,19 @@ die Versionierung folgt (in der aktuellen Doku-Phase) dem Dokumentationsstand de bei 2 AE/Tick, bis eine gespielte Balance-Kalibrierung belastbare Werte gibt ### Behoben +- **Der Gate-Vertrag zeigte auf ein Repository, das es nicht mehr gibt (#14, Stufe 1).** + Das GitHub-Repo heißt seit dem 09.08.2026 `VibecodingGermany/HashKrieg`; + `GateEvidence.schema.json`, `GateAuthorization.schema.json` und + `validate_gate_evidence.py` pinnten weiter den alten Namen als harte + Konstante. Aufgefallen ist es niemandem, weil der Selbsttest des Validators + **dieselbe** alte Konstante in seinen Fixtures benutzte: er war mit sich + selbst konsistent und darum grün, während der echte Autorisierungspfad am + `const` durchgefallen wäre, sobald er läuft. Die Prüfung nimmt jetzt beide + Namen an (neuer zuerst), damit unter dem alten Namen archivierte Nachweise + gültig bleiben; **erzeugt** wird ausschließlich der neue. Jede der drei + Stellen trägt eine Abschaltklausel, damit die Übergangszeit ein Ende hat. Der + Selbsttest prüft die Übergangsregel jetzt wirklich: beide Namen laufen durch, + ein dritter scheitert an allen vier Grenzen - **Der Determinismus-Wächter sieht jetzt auch den KI-Strang (#74).** `NoFloatInSimulationTests` scannte `Scripts/Core` und `Scripts/Simulation`, nicht aber `Scripts/AI` und `Scripts/AI.Data` — dabei ist `SkirmishAiSystem` in der kanonischen diff --git a/quality/schemas/GateAuthorization.schema.json b/quality/schemas/GateAuthorization.schema.json index 4f21717..c3ec56b 100644 --- a/quality/schemas/GateAuthorization.schema.json +++ b/quality/schemas/GateAuthorization.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/VibecodingGermany/Project_Nova/quality/schemas/GateAuthorization.schema.json", + "$id": "https://github.com/VibecodingGermany/HashKrieg/quality/schemas/GateAuthorization.schema.json", "title": "Project Nova Gate Authorization Receipt", "description": "Two-phase authorization receipt per D-066 (G0-A2). A successful protected gate-evidence-authorize run emits this hash-bound candidate next to the unchanged GateEvidence.json. It binds gate, subject commit/tree, evidence carrier commit, evidence path/hash, trusted tool commit, repository, workflow and run/attempt/job id. The run never checks its own pending conclusion; later gates verify the exact workflow_dispatch run via the GitHub API. Versioned append-only under quality/authorizations/G//-attempt/GateAuthorization.json after a small follow-up PR.", "type": "object", @@ -53,7 +53,11 @@ "description": "Commit of the subject-independent trusted tools (validator, schema, workflow) that ran the protected authorize job." }, "repository": { - "const": "VibecodingGermany/Project_Nova" + "enum": [ + "VibecodingGermany/HashKrieg", + "VibecodingGermany/Project_Nova" + ], + "$comment": "Transition after the 2026-08-09 repository rename: the canonical name comes first; the pre-rename name stays accepted so receipts archived under it remain valid. The legacy allowance can be dropped in a dedicated PR once no one reads old evidence anymore." }, "workflow": { "const": "quality-gate.yml", diff --git a/quality/schemas/GateEvidence.schema.json b/quality/schemas/GateEvidence.schema.json index fc32ab6..81df302 100644 --- a/quality/schemas/GateEvidence.schema.json +++ b/quality/schemas/GateEvidence.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/VibecodingGermany/Project_Nova/quality/schemas/GateEvidence.schema.json", + "$id": "https://github.com/VibecodingGermany/HashKrieg/quality/schemas/GateEvidence.schema.json", "title": "Project Nova Gate Evidence", "description": "Schema 1.4 contract for one append-only gate attempt. Per D-066 a pass is authorized only by a two-phase GateAuthorization receipt that binds subject, evidence carrier, trusted tools and the completed protected run independently; the evidence itself never authorizes. G1 and later must declare the ordered priorGateReceipts chain G0..G(n-1) as the authorization proof, while priorGateEvidence remains the same-subject integrity chain. Commands and performance measurements remain bound to declared environments; plans and file presence are not evidence.", "type": "object", @@ -242,7 +242,11 @@ ], "properties": { "trustedRepository": { - "const": "VibecodingGermany/Project_Nova" + "enum": [ + "VibecodingGermany/HashKrieg", + "VibecodingGermany/Project_Nova" + ], + "$comment": "Transition after the 2026-08-09 repository rename: the canonical name comes first; the pre-rename name stays accepted so evidence archived under it remains valid. The legacy allowance can be dropped in a dedicated PR once no one reads old evidence anymore." }, "trustedCommitSha": { "$ref": "#/$defs/gitSha" @@ -621,7 +625,11 @@ "const": "github-actions" }, "repository": { - "const": "VibecodingGermany/Project_Nova" + "enum": [ + "VibecodingGermany/HashKrieg", + "VibecodingGermany/Project_Nova" + ], + "$comment": "Transition after the 2026-08-09 repository rename: the canonical name comes first; the pre-rename name stays accepted so evidence archived under it remains valid. The legacy allowance can be dropped in a dedicated PR once no one reads old evidence anymore." }, "workflowPath": { "const": ".github/workflows/quality-gate.yml" diff --git a/quality/scripts/validate_gate_evidence.py b/quality/scripts/validate_gate_evidence.py index 1db949e..9880c7c 100644 --- a/quality/scripts/validate_gate_evidence.py +++ b/quality/scripts/validate_gate_evidence.py @@ -55,7 +55,15 @@ SCHEMA_VERSION = "1.4.0" RECEIPT_SCHEMA_VERSION = "gate-authorization-v1" SCENARIO_AUTHORIZATION_STATUS = "two-phase-receipt-d066" -REPOSITORY = "VibecodingGermany/Project_Nova" +# Canonical repository identity after the 2026-08-09 rename. Every address +# the validator *emits* (GitHub API paths, the receipt "repository" field) +# uses this name only. +REPOSITORY = "VibecodingGermany/HashKrieg" +# Transition: checks accept the pre-rename name as well, so evidence and +# receipts archived under it stay valid. The legacy allowance can be dropped +# in a dedicated PR once no one reads old evidence anymore. +LEGACY_REPOSITORY = "VibecodingGermany/Project_Nova" +REPOSITORY_NAMES = (REPOSITORY, LEGACY_REPOSITORY) WORKFLOW_NAME = "quality-gate.yml" AUTHORIZING_JOB = "gate-evidence-authorize" # D-064: the trust bundle binds every tool/contract component per subject and @@ -1355,19 +1363,24 @@ def authorize_evidence( errors.append( ("E_TRUST_CONTEXT", "authorize requires the GitHub Actions runtime") ) - if env.get("GITHUB_REPOSITORY") != REPOSITORY: + if env.get("GITHUB_REPOSITORY") not in REPOSITORY_NAMES: errors.append( - ("E_TRUST_CONTEXT", f"GITHUB_REPOSITORY must be {REPOSITORY!r}") + ( + "E_TRUST_CONTEXT", + f"GITHUB_REPOSITORY must be one of {REPOSITORY_NAMES!r}", + ) ) workflow_ref = env.get("GITHUB_WORKFLOW_REF") or "" - expected_workflow_ref = ( - f"{REPOSITORY}/.github/workflows/{WORKFLOW_NAME}@refs/heads/main" + expected_workflow_refs = tuple( + f"{repository}/.github/workflows/{WORKFLOW_NAME}@refs/heads/main" + for repository in REPOSITORY_NAMES ) - if workflow_ref != expected_workflow_ref: + if workflow_ref not in expected_workflow_refs: errors.append( ( "E_TRUST_CONTEXT", - f"GITHUB_WORKFLOW_REF must equal {expected_workflow_ref!r}", + "GITHUB_WORKFLOW_REF must equal one of " + f"{expected_workflow_refs!r}", ) ) if not env.get("GITHUB_WORKFLOW"): @@ -2650,12 +2663,14 @@ def validate_document( if ci.get("headSha") != commit_sha: errors.append(("E_CI_SUBJECT", "CI headSha must equal subject commitSha")) - expected_ci_url = ( - "https://github.com/VibecodingGermany/Project_Nova/actions/runs/" - f"{ci.get('runId')}" + expected_ci_urls = tuple( + f"https://github.com/{repository}/actions/runs/{ci.get('runId')}" + for repository in REPOSITORY_NAMES ) - if ci.get("url") != expected_ci_url: - errors.append(("E_CI_URL", f"CI url must equal {expected_ci_url!r}")) + if ci.get("url") not in expected_ci_urls: + errors.append( + ("E_CI_URL", f"CI url must equal one of {expected_ci_urls!r}") + ) if verify_git and isinstance(commit_sha, str): workflow_path = ci.get("workflowPath") if isinstance(workflow_path, str): @@ -3081,7 +3096,7 @@ def command_result( "integrityWorkflowSha256": content_digests["integrityWorkflow"], }, "trustBundle": { - "trustedRepository": "VibecodingGermany/Project_Nova", + "trustedRepository": REPOSITORY, "trustedCommitSha": "9" * 40, "nodeVersion": "v24.4.1", "components": trust_bundle_components, @@ -3161,14 +3176,14 @@ def command_result( ], "ci": { "provider": "github-actions", - "repository": "VibecodingGermany/Project_Nova", + "repository": REPOSITORY, "workflowPath": ".github/workflows/quality-gate.yml", "runId": "123", "runAttempt": 1, "jobId": "456", "jobName": "integrity", "headSha": commit, - "url": "https://github.com/VibecodingGermany/Project_Nova/actions/runs/123", + "url": f"https://github.com/{REPOSITORY}/actions/runs/123", "conclusion": "success", "attestationArtifact": ci_attestation, }, @@ -3810,6 +3825,30 @@ def codes( "E_COMMAND_EXECUTOR", lambda value: value["commands"][0].update(id="worker-g0-self-test"), ), + ( + "repository-third-name", + "E_JSON_SCHEMA", + lambda value: value["ci"].update( + repository="VibecodingGermany/SomeOtherRepo" + ), + ), + ( + "trusted-repository-third-name", + "E_JSON_SCHEMA", + lambda value: value["trustBundle"].update( + trustedRepository="VibecodingGermany/SomeOtherRepo" + ), + ), + ( + "ci-url-third-name", + "E_CI_URL", + lambda value: value["ci"].update( + url=( + "https://github.com/VibecodingGermany/SomeOtherRepo" + "/actions/runs/123" + ) + ), + ), ] base_errors = codes(copy.deepcopy(fixture)) @@ -3818,6 +3857,26 @@ def codes( return 1 checks = 2 + + # Transition positive (2026-08-09 rename): evidence carrying the + # pre-rename repository identity must still validate, through the schema + # enums (trustBundle.trustedRepository, ci.repository) and the semantic + # ci.url check alike. + legacy_fixture = copy.deepcopy(fixture) + legacy_fixture["trustBundle"]["trustedRepository"] = LEGACY_REPOSITORY + legacy_fixture["ci"]["repository"] = LEGACY_REPOSITORY + legacy_fixture["ci"]["url"] = ( + f"https://github.com/{LEGACY_REPOSITORY}/actions/runs/123" + ) + legacy_errors = codes(legacy_fixture) + checks += 1 + if legacy_errors: + print( + "SELF-TEST FAIL: pre-rename repository identity was rejected: " + f"{sorted(legacy_errors)}" + ) + return 1 + for name, expected_code, mutate in cases: candidate = copy.deepcopy(fixture) mutate(candidate) @@ -4323,6 +4382,63 @@ def authorize( ) return 1 + # Transition positive (2026-08-09 rename): a protected runtime + # identifying itself under the pre-rename repository name still + # authorizes, while the emitted receipt carries the canonical + # name only. + legacy_environment = dict( + os.environ, + GITHUB_REPOSITORY=LEGACY_REPOSITORY, + GITHUB_WORKFLOW_REF=( + f"{LEGACY_REPOSITORY}/.github/workflows/" + f"{WORKFLOW_NAME}@refs/heads/main" + ), + ) + legacy_codes, legacy_receipt = authorize( + trusted_document, + trusted_evidence, + external / "receipt-legacy-repository.json", + legacy_environment, + ) + checks += 1 + if ( + legacy_codes + or legacy_receipt is None + or legacy_receipt.get("repository") != REPOSITORY + ): + print( + "SELF-TEST FAIL: pre-rename runtime identity was " + "rejected or the receipt lost the canonical repository " + f"name: {sorted(legacy_codes)}" + ) + return 1 + + # Negative: a third, unknown repository identity fails closed. + foreign_environment = dict( + os.environ, + GITHUB_REPOSITORY="VibecodingGermany/SomeOtherRepo", + GITHUB_WORKFLOW_REF=( + "VibecodingGermany/SomeOtherRepo/.github/workflows/" + f"{WORKFLOW_NAME}@refs/heads/main" + ), + ) + foreign_codes, foreign_receipt = authorize( + trusted_document, + trusted_evidence, + external / "receipt-foreign-repository.json", + foreign_environment, + ) + checks += 1 + if ( + foreign_receipt is not None + or "E_TRUST_CONTEXT" not in foreign_codes + ): + print( + "SELF-TEST FAIL: unknown repository identity escaped " + f"the trust-context lock: {sorted(foreign_codes)}" + ) + return 1 + # The emitted candidate satisfies the receipt contract. _, candidate_errors = validate_receipt( g0_receipt_out,