Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -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.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 6 additions & 2 deletions quality/schemas/GateAuthorization.schema.json
Original file line number Diff line number Diff line change
@@ -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<N>/<subjectCommitSha>/<runId>-attempt<runAttempt>/GateAuthorization.json after a small follow-up PR.",
"type": "object",
Expand Down Expand Up @@ -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",
Expand Down
14 changes: 11 additions & 3 deletions quality/schemas/GateEvidence.schema.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
146 changes: 131 additions & 15 deletions quality/scripts/validate_gate_evidence.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
},
Expand Down Expand Up @@ -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))
Expand All @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
Loading