{"record":{"id":"fba53a777b819709","repo":"github/spec-kit","slug":"manifest-project-root-manifest-project-root-do-fba53a","errorCode":null,"errorMessage":"manifest.project_root ({manifest.project_root}) does not match project_root ({project_root_resolved})","messagePattern":"manifest\\.project_root \\((.+?)\\) does not match project_root \\((.+?)\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/integrations/generic/__init__.py","lineNumber":115,"sourceCode":"        )\n\n    def setup(\n        self,\n        project_root: Path,\n        manifest: IntegrationManifest,\n        parsed_options: dict[str, Any] | None = None,\n        **opts: Any,\n    ) -> list[Path]:\n        \"\"\"Install commands to the user-provided commands directory.\"\"\"\n        commands_dir = self._resolve_commands_dir(parsed_options, opts)\n\n        templates = self.list_command_templates()\n        if not templates:\n            return []\n\n        project_root_resolved = project_root.resolve()\n        if manifest.project_root != project_root_resolved:\n            raise ValueError(\n                f\"manifest.project_root ({manifest.project_root}) does not match \"\n                f\"project_root ({project_root_resolved})\"\n            )\n\n        dest = (project_root / commands_dir).resolve()\n        try:\n            dest.relative_to(project_root_resolved)\n        except ValueError as exc:\n            raise ValueError(\n                f\"Integration destination {dest} escapes \"\n                f\"project root {project_root_resolved}\"\n            ) from exc\n        dest.mkdir(parents=True, exist_ok=True)\n\n        script_type = opts.get(\"script_type\", \"sh\")\n        arg_placeholder = \"$ARGUMENTS\"\n        created: list[Path] = []\n","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/integrations/generic/__init__.py#L97-L133","documentation":"Raised by GenericIntegration.setup() (src/specify_cli/integrations/generic/__init__.py:115) when the IntegrationManifest was created against a different root than the resolved project_root argument. Like the copilot/forge/hermes setups, generic setup verifies manifest.project_root == project_root.resolve() before any writes, binding each manifest to exactly one project location.","triggerScenarios":"GenericIntegration().setup(root, manifest) with manifest built from a different directory or an unresolved path variant (symlink differences) — the strict equality on resolved paths fails.","commonSituations":"Manual/programmatic use of the generic integration where the manifest comes from a fixture or another project; macOS /tmp vs /private/tmp; passing a relative project_root while the manifest recorded an absolute one.","solutions":["Build the manifest with the same resolved root passed to setup: IntegrationManifest(project_root=project_root.resolve()).","Resolve project_root once and reuse that Path for both the manifest and the setup call.","Use the CLI path (specify init --integration generic --integration-options=\"--commands-dir ...\") which handles manifest creation."],"exampleFix":"# before\nmanifest = IntegrationManifest(project_root=Path(\"proj\"))\nGenericIntegration().setup(Path(\"/abs/proj\"), manifest, parsed_options={\"commands_dir\": \"x\"})\n\n# after\nroot = Path(\"/abs/proj\").resolve()\nmanifest = IntegrationManifest(project_root=root)\nGenericIntegration().setup(root, manifest, parsed_options={\"commands_dir\": \"x\"})","handlingStrategy":"validation","validationCode":"root = project_root.resolve()\nif manifest.project_root != root:\n    manifest = IntegrationManifest(project_root=root)","typeGuard":null,"tryCatchPattern":"try:\n    integration.setup(root, manifest, parsed_options=opts)\nexcept ValueError as e:\n    if \"manifest.project_root\" in str(e):\n        manifest = IntegrationManifest(project_root=root.resolve())\n        integration.setup(root, manifest, parsed_options=opts)\n    else:\n        raise","preventionTips":["One resolved root, one manifest — create both from the same Path.resolve().","Don't reuse manifests from fixtures or other projects.","Prefer the specify CLI, which constructs manifests correctly."],"tags":["filesystem","paths","integration-setup","spec-kit","generic"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}