{
  "suite": "ids",
  "description": "Identifier parsing and normalisation. `parse` is the structural verdict; `normalize` is what a typed or transcribed identifier resolves to. An implementation that gets normalisation wrong turns a correctly transcribed identifier into a 404.",
  "parseCases": [
    {
      "id": "parse-central",
      "input": "GAVR-A7K2QXR8",
      "expect": {
        "kind": "GAVR",
        "tail": "A7K2QXR8"
      }
    },
    {
      "id": "parse-local-depth-one",
      "input": "GAVLR-42-A7K2QX",
      "expect": {
        "kind": "GAVLR",
        "path": [42],
        "tail": "A7K2QX"
      }
    },
    {
      "id": "parse-local-depth-three",
      "input": "GAVLR-42-7-3-A7K2QX",
      "note": "The path IS the delegation chain, root first. Uniqueness falls out of it with no global coordination.",
      "expect": {
        "kind": "GAVLR",
        "path": [42, 7, 3],
        "tail": "A7K2QX"
      }
    },
    {
      "id": "refuse-depth-beyond-the-network-cap",
      "input": "GAVLR-1-2-3-4-A7K2QX",
      "note": "Every hop is infrastructure the resolver does not control, so unbounded depth is a denial of service rather than a feature.",
      "expect": {
        "kind": null
      }
    },
    {
      "id": "refuse-leading-zero-segment",
      "input": "GAVLR-042-A7K2QX",
      "note": "Two spellings of one identifier is exactly the ambiguity a permanent namespace cannot afford.",
      "expect": {
        "kind": null
      }
    },
    {
      "id": "refuse-tail-below-minimum",
      "input": "GAVR-A7K2Q",
      "expect": {
        "kind": null
      }
    },
    {
      "id": "accept-tail-at-maximum",
      "input": "GAVR-A7K2QXR8A7K2QXR8",
      "note": "Validators accept 6 to 16 even though the allocator mints 8, so the length can grow later without a v2 format and without breaking a regex a consumer has deployed.",
      "expect": {
        "kind": "GAVR",
        "tail": "A7K2QXR8A7K2QXR8"
      }
    },
    {
      "id": "refuse-letters-outside-the-alphabet",
      "input": "GAVR-A7K2QXRU",
      "note": "U is dropped by Crockford to keep accidental obscenities out of generated ids.",
      "expect": {
        "kind": null
      }
    }
  ],
  "normalizeCases": [
    {
      "id": "normalize-lowercase",
      "input": "gavr-a7k2qxr8",
      "expect": {
        "normalized": "GAVR-A7K2QXR8"
      }
    },
    {
      "id": "normalize-crockford-confusables",
      "input": "GAVR-AIK2QXRO",
      "note": "I and L fold to 1, O folds to 0, because they collide in most typefaces and in OCR.",
      "expect": {
        "normalized": "GAVR-A1K2QXR0"
      }
    },
    {
      "id": "normalize-must-not-fold-the-prefix",
      "input": "gavlr-42-a7k2qx",
      "note": "THE trap here. GAVLR contains an L: folding the whole string rewrites the namespace itself to GAV1R and turns every local identifier into a parse failure. The prefix is never folded.",
      "expect": {
        "normalized": "GAVLR-42-A7K2QX"
      }
    },
    {
      "id": "normalize-refuses-what-cannot-be-repaired",
      "input": "GAVR-!!!!!!!!",
      "expect": {
        "normalized": null
      }
    }
  ]
}
