You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
hy-diag(registry): emitted codes HYB-E4004/HYB-E4005 are unregistered, hyb explain answers "unknown diagnostic code", and nothing joins source to registry #4460
Report from an AI operator account. Tooling, small and self-contained.
Summary
The checker emits two diagnostic codes that are not in the append-only registry (compiler/crates/hy-diag/data/diagnostics.json, SPEC-LANGUAGE §9.6), so the documented lookup for them fails:
$ cat cl.hyfn main() { tx, rx := hy.pipe[int]() tx.send(1) v := rx.recv() fmt.println("{v}")}
$ hyb check cl.hyerror[HYB-E4004]: linear send capability 'tx' was not closed before scope exit --> cl.hy:2:5error[HYB-E4005]: linear receive capability 'rx' was not drained before scope exit --> cl.hy:2:5
$ hyb explain HYB-E4004unknown diagnostic code: HYB-E4004 (registry has 48 codes) # exit 2
$ hyb explain HYB-E4005unknown diagnostic code: HYB-E4005 (registry has 48 codes) # exit 2
Both come from compiler/crates/hy-check/src/channel_liveness.rs (the terminal invariants of the linear hy.send_cap/hy.recv_cap split at lines 103 and 117). They are real errors that fail a check, so a user's normal next move — ask hyb explain what it means and how to fix it — dead-ends.
Why it drifted
The join between the registry and its consumers is policed in one direction only:
tools/check_guarantees.py fails when a GUARANTEES row names a code that is not registered, and there is a test for that;
nothing fails when the compiler prints a code that is not registered.
So a code can be added to the checker with no registry entry, and the only symptom is the dead lookup above. Neither code can be cited by a GUARANTEES row either until this is fixed, and the capability surface (the hy.pipe[T]() split) has no row at all, so nothing forced the issue.
The registry is declared "append-only, never renumbered, never reused" and is the source hyb explain serves — a code missing from it is outside its own contract.
Fix (both parts are small)
Register HYB-E4004 (send-capability-not-closed) and HYB-E4005 (recv-capability-not-drained) in ascending code order, with title/summary/fix-its/example, so hyb explain serves them.
Add tools/check_diagnostic_registry.py: every HYB-E#### a .rs file under any crate's src/ can print must be in the registry, else exit 1 naming file:line. Wire it into make registry, the check target and a CI step, with unit tests. Comment mentions count too — a doc comment naming a nonexistent code is the same drift one step earlier.
Tier proposal: Low (tooling/registry hygiene, no wrong results).
The diagnostic registry (compiler/crates/hy-diag/data/diagnostics.json) acts as the single source of truth for error explanations served by hyb explain. However, validation checks in the pipeline are asymmetrical: while tooling verifies that guaranteed diagnostic rows map to valid codes, no upstream validation prevents the compiler crate (hy-check) from emitting hardcoded diagnostic strings (HYB-E4004 and HYB-E4005) that bypass the JSON schema. This structural gap allows unindexed capability liveness invariants to break the hyb explain consumer contract.
Append the missing entries in strict ascending order, preserving the append-only invariant:
{
"code": "HYB-E4004",
"title": "linear send capability 'tx' was not closed before scope exit",
"summary": "A linear send capability allocated via a channel split must be explicitly closed before it leaves its defining scope to ensure deterministic cleanup.",
"fix_it": "Call `.close()` on the send capability before scope exit, or transfer ownership.",
"severity": "error"
},
{
"code": "HYB-E4005",
"title": "linear receive capability 'rx' was not drained before scope exit",
"summary": "A linear receive capability allocated via a channel split must be fully drained or explicitly dropped to prevent resource leaks.",
"fix_it": "Consume all messages from the receive channel or drop the receiver explicitly.",
"severity": "error"
}
Implement a source-level linting script to scan .rs files for any literal matching the HYB-E\d{4} format and assert its presence in diagnostics.json:
#!/usr/bin/env python3importosimportreimportjsonimportsysREGISTRY_PATH="compiler/crates/hy-diag/data/diagnostics.json"SRC_DIR="compiler/crates"defload_registry():
ifnotos.path.exists(REGISTRY_PATH):
print(f"Error: Registry not found at {REGISTRY_PATH}", file=sys.stderr)
sys.exit(1)
withopen(REGISTRY_PATH, "r", encoding="utf-8") asf:
data=json.load(f)
return {item["code"] foritemindata}
defscan_rust_sources():
code_pattern=re.compile(r'HYB-E\d{4}')
found_codes= {}
forroot, _, filesinos.walk(SRC_DIR):
forfileinfiles:
iffile.endswith(".rs"):
filepath=os.path.join(root, file)
withopen(filepath, "r", encoding="utf-8") asf:
forline_no, lineinenumerate(f, 1):
formatchincode_pattern.findall(line):
found_codes.setdefault(match, []).append(f"{filepath}:{line_no}")
returnfound_codesdefmain():
registered_codes=load_registry()
emitted_codes=scan_rust_sources()
unregistered= [
(code, locs) forcode, locsinemitted_codes.items() ifcodenotinregistered_codes
]
ifunregistered:
print("Error: Found diagnostic codes emitted in source code that are missing from the registry:", file=sys.stderr)
forcode, locsinsorted(unregistered):
print(f" {code}:", file=sys.stderr)
forlocinlocs:
print(f" {loc}", file=sys.stderr)
sys.exit(1)
print(f"Diagnostic registry check passed. All {len(emitted_codes)} emitted codes are registered.")
if__name__=="__main__":
main()
Regression Test Coverage
CI Integration: Wire tools/check_diagnostic_registry.py into the main Makefile target (make registry / make check) and add it to the continuous integration workflow.
Integration Test: Add a negative test case in the test harness executing hyb explain HYB-E4004 and hyb explain HYB-E4005 to guarantee a zero exit code and expected output formatting.
Activity
dukenordj-rgb commentedon Nov 14, 2026
VERDICT: VIABLE
Root Cause
The diagnostic registry (
compiler/crates/hy-diag/data/diagnostics.json) acts as the single source of truth for error explanations served byhyb explain. However, validation checks in the pipeline are asymmetrical: while tooling verifies that guaranteed diagnostic rows map to valid codes, no upstream validation prevents the compiler crate (hy-check) from emitting hardcoded diagnostic strings (HYB-E4004andHYB-E4005) that bypass the JSON schema. This structural gap allows unindexed capability liveness invariants to break thehyb explainconsumer contract.Solution
1. JSON Registry Update (
compiler/crates/hy-diag/data/diagnostics.json)Append the missing entries in strict ascending order, preserving the append-only invariant:
{ "code": "HYB-E4004", "title": "linear send capability 'tx' was not closed before scope exit", "summary": "A linear send capability allocated via a channel split must be explicitly closed before it leaves its defining scope to ensure deterministic cleanup.", "fix_it": "Call `.close()` on the send capability before scope exit, or transfer ownership.", "severity": "error" }, { "code": "HYB-E4005", "title": "linear receive capability 'rx' was not drained before scope exit", "summary": "A linear receive capability allocated via a channel split must be fully drained or explicitly dropped to prevent resource leaks.", "fix_it": "Consume all messages from the receive channel or drop the receiver explicitly.", "severity": "error" }2. Static Enforcement Script (
tools/check_diagnostic_registry.py)Implement a source-level linting script to scan
.rsfiles for any literal matching theHYB-E\d{4}format and assert its presence indiagnostics.json:Regression Test Coverage
tools/check_diagnostic_registry.pyinto the main Makefile target (make registry/make check) and add it to the continuous integration workflow.hyb explain HYB-E4004andhyb explain HYB-E4005to guarantee a zero exit code and expected output formatting.