mirror of
https://github.com/hanzoai/hips.git
synced 2026-10-31 10:21:29 +00:00
The CI rule needed correcting twice in one pass, and the second correction came from measuring rather than from the brief. `hanzoai/hips` moved its own workflows to `.github/workflows` on 2026-09-08 with a sound reason: the forge reads that directory natively and github.com reads nothing else, so one directory serves both planes. Measured across the first-party checkouts, twelve keep their caller in `.hanzo/workflows/` and two forks carry an upstream's inherited `.github/workflows` — 24 files in datastore, six in kms, none of them ours. So the rule is not the directory's name. It is that a repository keeps exactly ONE, because the forge reads the first that exists rather than the union: a split silently retires half the pipeline, and the checks that would go red are the ones no longer running. The rest of this pass takes the per-app stores out of six more HIPs. The admin console had Postgres+Prisma, ClickHouse, Redis+BullMQ and MinIO of its own; serverless had a `hanzo_functions` database; commerce tracked idempotency in "Redis"; the zero-trust HIP named SPIFFE identities for `postgres` and `redis`, which are the upstreams we fork rather than the services we run. Payment processing also had Commerce POSTing balances into IAM through `add-balance` and `add-transaction`, which contradicts both HIP-0111's forbidden-verb list and the correction that Commerce owns the position and IAM mirrors it. Deployment carried the last `/api/` paths and the corpus's only `v2` (`api.hanzo.ai/v2`, offered as an example of path routing), plus a `resources:` block that provisioned a per-app Postgres and Redis. It now names which rank of the shared tier an app is a tenant of. HIP-0144 replaces the number this HIP was drafted at: 0138 was published as "Cloud — Unified Hanzo Binary" and retired, and the index generator refused the reuse — correctly, since every existing reference to HIP-0138 would have resolved to a different specification. Two self-test guards were reported as not holding. Both were the harness's own: the FM011 case appended a second `implementation-go:` key to a subject that already declared one, so the later key won and the contradiction was never built; and test-index restored HANZO_CAPABILITIES only when the caller had exported it, so every case after the capabilities case ran against a missing input. A check whose test is a no-op has never been shown to work. The corpus now passes lint, index --check and both self-tests, from 23 errors. Co-authored-by: Hanzo Dev <[email protected]>
232 lines
9.5 KiB
Python
232 lines
9.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Prove the lint fails. Run: python3 scripts/test-lint-hips.py
|
|
|
|
A check that has only ever been seen to pass has not been shown to work -- it is
|
|
indistinguishable from a check that returns success unconditionally, and this
|
|
repository has already shipped one of those (the GitHub workflow that printed
|
|
every missing field and then exited 0, so a HIP with no status went green).
|
|
|
|
So: copy the corpus, break it on purpose one way at a time, and require the lint
|
|
to report the specific code for that break. Every check the lint declares must
|
|
appear here, and the last test asserts exactly that -- if someone adds a check
|
|
and no test, this file fails.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
LINT = os.path.join(ROOT, "scripts", "lint-hips.py")
|
|
|
|
# A file that is structurally clean and small enough to mutate predictably.
|
|
SUBJECT = "hip-0517-branch-naming.md"
|
|
STANDARDS_SUBJECT = "hip-0119-hanzo-service-conventions.md"
|
|
|
|
|
|
def run(root: str) -> tuple[int, str]:
|
|
p = subprocess.run([sys.executable, LINT, "--root", root],
|
|
capture_output=True, text=True)
|
|
return p.returncode, p.stdout + p.stderr
|
|
|
|
|
|
def read(root: str, name: str) -> str:
|
|
return open(os.path.join(root, "HIPs", name), encoding="utf-8").read()
|
|
|
|
|
|
def write(root: str, name: str, text: str) -> None:
|
|
open(os.path.join(root, "HIPs", name), "w", encoding="utf-8").write(text)
|
|
|
|
|
|
# Each case: code, description, and a mutation taking the scratch root.
|
|
def m_fm001(root):
|
|
write(root, SUBJECT, read(root, SUBJECT).split("\n---\n", 1)[1])
|
|
|
|
def m_fm002(root):
|
|
write(root, SUBJECT, re.sub(r"^created:.*$", "", read(root, SUBJECT), count=1, flags=re.M))
|
|
|
|
def m_fm003(root):
|
|
write(root, SUBJECT, re.sub(r"^hip:.*$", "hip: 0999", read(root, SUBJECT), count=1, flags=re.M))
|
|
|
|
def m_fm004(root):
|
|
write(root, SUBJECT, re.sub(r"^status:.*$", "status: Baked", read(root, SUBJECT), count=1, flags=re.M))
|
|
|
|
def m_fm005(root):
|
|
write(root, SUBJECT, re.sub(r"^type:.*$", "type: Freeform", read(root, SUBJECT), count=1, flags=re.M))
|
|
|
|
def m_fm006(root):
|
|
write(root, STANDARDS_SUBJECT,
|
|
re.sub(r"^category:.*$", "category: Vibes", read(root, STANDARDS_SUBJECT), count=1, flags=re.M))
|
|
|
|
def m_fm007(root):
|
|
s = read(root, STANDARDS_SUBJECT)
|
|
write(root, STANDARDS_SUBJECT, re.sub(r"^(requires:.*)$", r"\1, HIP-8888", s, count=1, flags=re.M))
|
|
|
|
def m_fm008(root):
|
|
write(root, SUBJECT,
|
|
re.sub(r"^status:(.*)$", r"status:\1\nsuperseded-by: HIP-0106",
|
|
read(root, SUBJECT), count=1, flags=re.M))
|
|
|
|
def m_fm009(root):
|
|
os.rename(os.path.join(root, "HIPs", SUBJECT),
|
|
os.path.join(root, "HIPs", "hip-0517-Branch_Naming.md"))
|
|
|
|
def m_fm010(root):
|
|
write(root, SUBJECT,
|
|
re.sub(r"^status:(.*)$", r"status:\1\nimplementation-rust: mostly",
|
|
read(root, SUBJECT), count=1, flags=re.M))
|
|
|
|
def m_fm011(root):
|
|
# STANDARDS_SUBJECT is Final, which is half the contradiction already. Doing
|
|
# this to a Draft would prove nothing.
|
|
#
|
|
# REPLACE any implementation-go: it already declares. Appending a second one
|
|
# proved nothing: the front-matter readers here take one line at a time, so
|
|
# the later key won, the subject stayed `shipped`, and the contradiction this
|
|
# case exists to build was never built. The guard then correctly did not
|
|
# fire and the suite reported it as broken -- a check whose test is a no-op
|
|
# is a check that has never been shown to work.
|
|
text = re.sub(r"^implementation-go:.*\n", "", read(root, STANDARDS_SUBJECT), flags=re.M)
|
|
write(root, STANDARDS_SUBJECT,
|
|
re.sub(r"^status:(.*)$", r"status:\1\nimplementation-go: none",
|
|
text, count=1, flags=re.M))
|
|
|
|
def m_st001(root):
|
|
write(root, SUBJECT, re.sub(r"^# HIP-.*$", "", read(root, SUBJECT), count=1, flags=re.M))
|
|
|
|
def m_st002(root):
|
|
write(root, SUBJECT,
|
|
re.sub(r"^# HIP-.*$", "# HIP-0517: Something Else Entirely",
|
|
read(root, SUBJECT), count=1, flags=re.M))
|
|
|
|
def m_st003(root):
|
|
# Duplicate the heading every Standards Track HIP is REQUIRED to carry. This
|
|
# used to rename '## Motivation', which is advisory -- so when a corpus edit
|
|
# removed that heading from the subject, the replace matched nothing, the
|
|
# lint had no duplicate to report, and the check stopped proving anything
|
|
# while still being counted. Anchor a mutation on what the lint requires,
|
|
# never on what a document happens to have.
|
|
s = read(root, STANDARDS_SUBJECT)
|
|
write(root, STANDARDS_SUBJECT, s + "\n## Abstract\n\nA second one, on purpose.\n")
|
|
|
|
def m_st004(root):
|
|
s = read(root, STANDARDS_SUBJECT)
|
|
write(root, STANDARDS_SUBJECT, s.replace("### §2 ", "### §1 ", 1))
|
|
|
|
def m_st005(root):
|
|
s = read(root, STANDARDS_SUBJECT)
|
|
write(root, STANDARDS_SUBJECT, s.replace("\n## Abstract\n", "\n## Preface\n", 1))
|
|
|
|
def m_st006(root):
|
|
s = read(root, STANDARDS_SUBJECT)
|
|
write(root, STANDARDS_SUBJECT, s + "\n\n```go\nfunc dangling() {}\n")
|
|
|
|
def m_st007(root):
|
|
s = read(root, STANDARDS_SUBJECT)
|
|
write(root, STANDARDS_SUBJECT,
|
|
s.replace("### §2 The entry point", "### §2 The entry point\n\n#### §9.1 Misfiled", 1)
|
|
if "### §2 The entry point" in s
|
|
else s.replace("## Specification", "## Specification\n\n### 1 One\n\n#### 4.2 Misfiled", 1))
|
|
|
|
def m_lk001(root):
|
|
s = read(root, STANDARDS_SUBJECT)
|
|
write(root, STANDARDS_SUBJECT,
|
|
s.replace("## Abstract", "## Abstract\n\nSee [HIP-0106](./hip-0106-gone.md).", 1))
|
|
|
|
def m_ix003(root):
|
|
shutil.copy(os.path.join(root, "HIPs", SUBJECT),
|
|
os.path.join(root, "HIPs", "hip-0517-duplicate-claim.md"))
|
|
|
|
def m_pl001(root):
|
|
s = read(root, STANDARDS_SUBJECT)
|
|
write(root, STANDARDS_SUBJECT,
|
|
s.replace("## Abstract", "## Abstract\n\nThis is as good as Terraform.", 1))
|
|
|
|
def m_pl002(root):
|
|
s = read(root, STANDARDS_SUBJECT)
|
|
write(root, STANDARDS_SUBJECT,
|
|
s.replace("## Abstract", "## Abstract\n\nSee `hanzo-inc/payments` for the rest.", 1))
|
|
|
|
|
|
CASES = [
|
|
("FM001", "front matter removed", m_fm001),
|
|
("FM002", "created: removed", m_fm002),
|
|
("FM003", "hip: renumbered away from the filename", m_fm003),
|
|
("FM004", "status: set to a word not in the vocabulary", m_fm004),
|
|
("FM005", "type: set to a word not in the vocabulary", m_fm005),
|
|
("FM006", "category: set to a word not in the vocabulary", m_fm006),
|
|
("FM007", "requires: a HIP that does not exist", m_fm007),
|
|
("FM008", "a superseded-by tombstone field added", m_fm008),
|
|
("FM009", "filename given capitals and an underscore", m_fm009),
|
|
("FM010", "implementation-rust: set to a word not in the vocabulary", m_fm010),
|
|
("FM011", "a Final HIP declaring implementation-go: none", m_fm011),
|
|
("ST001", "H1 deleted", m_st001),
|
|
("ST002", "H1 title made to disagree with front matter", m_st002),
|
|
("ST003", "a second '## Abstract' introduced", m_st003),
|
|
("ST004", "two sections numbered §1", m_st004),
|
|
("ST005", "'## Abstract' renamed to '## Preface'", m_st005),
|
|
("ST006", "a code fence left open", m_st006),
|
|
("ST007", "a subsection numbered outside its parent", m_st007),
|
|
("LK001", "a body link pointed at a HIP file that does not exist", m_lk001),
|
|
("IX003", "a second file claiming HIP-0517", m_ix003),
|
|
("PL001", "'as good as Terraform' added", m_pl001),
|
|
("PL002", "a private-org repository cited", m_pl002),
|
|
]
|
|
|
|
|
|
def main() -> int:
|
|
base = tempfile.mkdtemp(prefix="hips-lint-test-")
|
|
try:
|
|
clean_root = os.path.join(base, "clean")
|
|
os.makedirs(clean_root)
|
|
shutil.copytree(os.path.join(ROOT, "HIPs"), os.path.join(clean_root, "HIPs"))
|
|
shutil.copy(os.path.join(ROOT, "README.md"), clean_root)
|
|
shutil.copy(os.path.join(ROOT, "vocabulary.json"), clean_root)
|
|
|
|
rc, out = run(clean_root)
|
|
if rc != 0:
|
|
print("CONTROL FAILED: the unmutated corpus does not lint clean.")
|
|
print(out)
|
|
return 1
|
|
print(f"control: clean corpus lints clean (exit 0)\n")
|
|
|
|
failures = []
|
|
for code, what, mutate in CASES:
|
|
root = os.path.join(base, code)
|
|
shutil.copytree(clean_root, root)
|
|
mutate(root)
|
|
rc, out = run(root)
|
|
caught = rc == 1 and re.search(rf"^ERROR {code}\b", out, re.M) is not None
|
|
print(f" {'PASS' if caught else 'FAIL'} {code} {what}")
|
|
if not caught:
|
|
failures.append((code, what, rc, out))
|
|
|
|
declared = set(re.findall(r'^\s*\("([A-Z]{2}\d{3})",',
|
|
open(LINT, encoding="utf-8").read(), re.M))
|
|
tested = {c for c, _, _ in CASES}
|
|
untested = declared - tested
|
|
print(f"\n {'PASS' if not untested else 'FAIL'} every declared check has a test"
|
|
+ (f" -- missing {sorted(untested)}" if untested else ""))
|
|
if untested:
|
|
failures.append(("COVERAGE", "declared but untested", 0, str(sorted(untested))))
|
|
|
|
if failures:
|
|
print(f"\n{len(failures)} of {len(CASES) + 1} checks did not fail as required:\n")
|
|
for code, what, rc, out in failures:
|
|
print(f"--- {code} ({what}) exit={rc} ---")
|
|
print("\n".join(out.splitlines()[-12:]) if isinstance(out, str) else out)
|
|
return 1
|
|
print(f"\nall {len(CASES)} checks fail on a deliberate break, and the clean "
|
|
f"corpus passes")
|
|
return 0
|
|
finally:
|
|
shutil.rmtree(base, ignore_errors=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|