{"record":{"id":"8ae5a7ce1ee1a887","repo":"ruvnet/RuView","slug":"manifest-must-be-an-object-with-a-markers-array","errorCode":null,"errorMessage":"manifest must be an object with a 'markers' array","messagePattern":"manifest must be an object with a 'markers' array","errorType":"validation","errorClass":"ManifestError","httpStatus":null,"severity":"error","filePath":"scripts/check_fix_markers.py","lineNumber":75,"sourceCode":"\nOK_MARK = \"PASS\"\nBAD_MARK = \"FAIL\"\nARROW = \"->\"\n\n\nclass ManifestError(Exception):\n    pass\n\n\ndef load_manifest() -> dict:\n    if not MANIFEST_PATH.exists():\n        raise ManifestError(f\"manifest not found: {MANIFEST_PATH}\")\n    try:\n        data = json.loads(MANIFEST_PATH.read_text(encoding=\"utf-8\"))\n    except json.JSONDecodeError as e:\n        raise ManifestError(f\"manifest is not valid JSON: {e}\") from e\n    if not isinstance(data, dict) or not isinstance(data.get(\"markers\"), list):\n        raise ManifestError(\"manifest must be an object with a 'markers' array\")\n    ids = [m.get(\"id\") for m in data[\"markers\"]]\n    dupes = {i for i in ids if ids.count(i) > 1}\n    if dupes:\n        raise ManifestError(f\"duplicate marker ids: {sorted(dupes)}\")\n    return data\n\n\ndef _pattern_found(text: str, pattern: str) -> bool:\n    if len(pattern) >= 2 and pattern.startswith(\"/\") and pattern.endswith(\"/\"):\n        return re.search(pattern[1:-1], text, re.MULTILINE) is not None\n    return pattern in text\n\n\ndef check_marker(marker: dict) -> tuple[bool, list[str]]:\n    \"\"\"Return (ok, problems) for a single marker.\"\"\"\n    problems: list[str] = []\n    files = marker.get(\"files\", [])\n    require = marker.get(\"require\", [])","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/scripts/check_fix_markers.py#L57-L93","documentation":"After parsing, load_manifest() requires the top level of scripts/fix-markers.json to be a JSON object containing a 'markers' key that is a list. It raises ManifestError('manifest must be an object with a \\'markers\\' array') when the parsed value is an array, a string, or an object whose 'markers' is missing or not a list. This is a shape check, not a syntax check — the JSON itself is valid.","triggerScenarios":"Writing the manifest as a bare top-level array of markers; nesting the markers list under a different key such as 'fixes' or 'entries'; having 'markers' hold an object (e.g. keyed by id) instead of a list.","commonSituations":"Contributors familiar with a different manifest format restructure the file; automated tools rewrite the JSON with a different top-level shape; copy-paste from another project's marker file.","solutions":["Wrap the data as {\"markers\": [...]} with the list directly under the 'markers' key","Ensure 'markers' is a JSON array of objects — not a dict keyed by marker id","Check each element is an object with the expected 'id' field, since the duplicate check reads m.get('id')","Re-run `python scripts/check_fix_markers.py --list` to confirm the manifest loads"],"exampleFix":"# before\n[\n  { \"id\": \"RuView#396\", \"files\": [\"...\"] }\n]\n\n# after\n{\n  \"markers\": [\n    { \"id\": \"RuView#396\", \"files\": [\"...\"] }\n  ]\n}","handlingStrategy":"validation","validationCode":"import json\nfrom pathlib import Path\n\ndef manifest_shape_ok(path: str) -> bool:\n    data = json.loads(Path(path).read_text(encoding=\"utf-8\"))\n    return isinstance(data, dict) and isinstance(data.get(\"markers\"), list)","typeGuard":"def is_fix_marker_manifest(data: object) -> bool:\n    return isinstance(data, dict) and isinstance(data.get(\"markers\"), list)","tryCatchPattern":null,"preventionTips":["Model new manifests on an existing valid entry rather than writing the shape from memory","Add a schema test that load_manifest() succeeds on the committed manifest","Keep 'markers' as the single top-level list; do not nest it under another key"],"tags":["json","manifest","schema","validation","python","scripts"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}