Skip to content

hy-diag(registry): emitted codes HYB-E4004/HYB-E4005 are unregistered, hyb explain answers "unknown diagnostic code", and nothing joins source to registry #4460

Open

Description

@jorge123255

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.hy
fn main() {
    tx, rx := hy.pipe[int]()
    tx.send(1)
    v := rx.recv()
    fmt.println("{v}")
}

$ hyb check cl.hy
error[HYB-E4004]: linear send capability 'tx' was not closed before scope exit
  --> cl.hy:2:5
error[HYB-E4005]: linear receive capability 'rx' was not drained before scope exit
  --> cl.hy:2:5

$ hyb explain HYB-E4004
unknown diagnostic code: HYB-E4004 (registry has 48 codes)      # exit 2
$ hyb explain HYB-E4005
unknown 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)

  1. 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.
  2. 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).

Activity

  1. dukenordj-rgb commented on Nov 14, 2026

    @dukenordj-rgb
    Contributor

    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 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.

    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 .rs files for any literal matching the HYB-E\d{4} format and assert its presence in diagnostics.json:

    #!/usr/bin/env python3
    import os
    import re
    import json
    import sys
    
    REGISTRY_PATH = "compiler/crates/hy-diag/data/diagnostics.json"
    SRC_DIR = "compiler/crates"
    
    def load_registry():
        if not os.path.exists(REGISTRY_PATH):
            print(f"Error: Registry not found at {REGISTRY_PATH}", file=sys.stderr)
            sys.exit(1)
        with open(REGISTRY_PATH, "r", encoding="utf-8") as f:
            data = json.load(f)
        return {item["code"] for item in data}
    
    def scan_rust_sources():
        code_pattern = re.compile(r'HYB-E\d{4}')
        found_codes = {}
        
        for root, _, files in os.walk(SRC_DIR):
            for file in files:
                if file.endswith(".rs"):
                    filepath = os.path.join(root, file)
                    with open(filepath, "r", encoding="utf-8") as f:
                        for line_no, line in enumerate(f, 1):
                            for match in code_pattern.findall(line):
                                found_codes.setdefault(match, []).append(f"{filepath}:{line_no}")
                                
        return found_codes
    
    def main():
        registered_codes = load_registry()
        emitted_codes = scan_rust_sources()
        
        unregistered = [
            (code, locs) for code, locs in emitted_codes.items() if code not in registered_codes
        ]
        
        if unregistered:
            print("Error: Found diagnostic codes emitted in source code that are missing from the registry:", file=sys.stderr)
            for code, locs in sorted(unregistered):
                print(f"  {code}:", file=sys.stderr)
                for loc in locs:
                    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

    1. 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.
    2. 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions