{"record":{"id":"922cff3cd0b8dc41","repo":"bmad-code-org/BMAD-METHOD","slug":"duplicate-review-layer-id-identifier","errorCode":null,"errorMessage":"duplicate review layer id `{identifier}`","messagePattern":"duplicate review layer id `(.+?)`","errorType":"validation","errorClass":"RenderError","httpStatus":null,"severity":"error","filePath":"src/scripts/render_skill.py","lineNumber":83,"sourceCode":"        raise RenderError(f\"{label} must be a list, got {type(value).__name__}\")\n    result = []\n    for index, item in enumerate(value):\n        result.append(_require_string(item, f\"{label}[{index}]\"))\n    return result\n\n\ndef _require_review_layers(value: Any, label: str) -> list[dict[str, str]]:\n    if not isinstance(value, list):\n        raise RenderError(f\"{label} must be a list of tables\")\n    result: list[dict[str, str]] = []\n    seen: set[str] = set()\n    for index, item in enumerate(value):\n        item_label = f\"{label}[{index}]\"\n        if not isinstance(item, dict):\n            raise RenderError(f\"{item_label} must be a table\")\n        identifier = _require_string(item.get(\"id\"), f\"{item_label}.id\")\n        if identifier in seen:\n            raise RenderError(f\"duplicate review layer id `{identifier}`\")\n        seen.add(identifier)\n        layer = {\n            \"id\": identifier,\n            \"name\": _require_string(item.get(\"name\", identifier), f\"{item_label}.name\"),\n            \"instruction\": _require_string(\n                item.get(\"instruction\"), f\"{item_label}.instruction\", allow_empty=True\n            ),\n        }\n        if \"when\" in item:\n            layer[\"when\"] = _require_string(item[\"when\"], f\"{item_label}.when\")\n        result.append(layer)\n    return result\n\n\ndef _load_sources(skill_dir: Path) -> dict[str, str]:\n    sources: dict[str, str] = {}\n    for candidate in sorted(skill_dir.rglob(\"*.md\")):\n        if candidate.name == \"SKILL.md\":","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/bmad-code-org/BMAD-METHOD/blob/b70486b9bdcb0a404d329e2a763b57964e7f1360/src/scripts/render_skill.py#L65-L101","documentation":"Review-layer id values must be unique within one array; the renderer uses id as the key for layer selection/merge, so duplicates are ambiguous. The seen set detects the second occurrence of an id and aborts with the duplicated identifier.","triggerScenarios":"Two [[workflow.review_layers]] blocks share the same id = \"architect\" in the final merged customization. Because _resolve_customization_value receives the already-merged list, any repeated id across customize.toml and the custom/<skill>*.toml overrides that survived merging triggers it.","commonSituations":"Copy-pasting a layer block without changing the id; appending a new layer in a user override using an id already present in the base; non-keyed merge path producing repeated ids.","solutions":["Find the duplicated id named in the message and rename one occurrence.","To override an existing layer, rely on the keyed merge (same id replaces) instead of adding a second entry.","Validate that ids are unique across all customization layers before rendering."],"exampleFix":"# before\n[[workflow.review_layers]]\nid = \"architect\"\n[[workflow.review_layers]]\nid = \"architect\"   # duplicate\n\n# after\n[[workflow.review_layers]]\nid = \"architect\"\n[[workflow.review_layers]]\nid = \"pm\"","handlingStrategy":"validation","validationCode":"import tomllib\nfrom pathlib import Path\n\ndef validate_unique_layer_ids(toml_path: Path, dotted: str) -> None:\n    data = tomllib.loads(Path(toml_path).read_text(encoding=\"utf-8\"))\n    cur: object = data\n    for part in dotted.split(\".\"):\n        cur = cur[part]\n    ids = [item.get(\"id\") for item in cur if isinstance(item, dict)]\n    if len(ids) != len(set(ids)):\n        dupes = [i for i in ids if ids.count(i) > 1]\n        raise SystemExit(f\"duplicate review layer ids: {sorted(set(dupes))}\")","typeGuard":"def layer_ids_unique(layers: list[dict]) -> bool:\n    ids = [l.get(\"id\") for l in layers]\n    return len(ids) == len(set(ids))","tryCatchPattern":null,"preventionTips":["Use distinct id values for each review layer.","To override a base layer, reuse its id (keyed merge replaces) rather than adding a duplicate.","Run an id-uniqueness check over merged customization before rendering."],"tags":["python","toml","customization","validation","review-layers","uniqueness"],"backgroundTag":null,"analyzedSha":"b70486b9bdcb0a404d329e2a763b57964e7f1360","analyzedAt":"2026-08-13T01:21:12.247Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}