{"record":{"id":"6798a4bd7feb110e","repo":"github/spec-kit","slug":"manifest-project-root-manifest-project-root-do-6798a4","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/forge/__init__.py","lineNumber":124,"sourceCode":"    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 Forge commands with custom processing.\n\n        Extends MarkdownIntegration.setup() to inject Forge-specific transformations\n        after standard template processing.\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 = self.commands_dest(project_root).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 = self.registrar_config.get(\"args\", \"{{parameters}}\")\n        created: list[Path] = []\n","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/integrations/forge/__init__.py#L106-L142","documentation":"Raised by ForgeIntegration.setup() (src/specify_cli/integrations/forge/__init__.py:124) when the IntegrationManifest's recorded project_root differs from the resolved project_root argument. All custom setup() implementations in spec-kit verify manifest-to-root binding before writing, so a manifest created for one location (or an unresolved/symlinked variant of it) is rejected here.","triggerScenarios":"Calling ForgeIntegration().setup(project_root, manifest) where manifest was instantiated with a different or non-equivalently-resolved root; e.g. manifest built from '.' while setup receives an absolute path, or the manifest object belongs to another project entirely.","commonSituations":"Test harnesses that create manifests in tmp_path but call setup with tmp_path_resolved variants; reusing one manifest across nested sub-projects; symlinked working directories (/tmp vs /private/tmp) making resolve() produce different strings.","solutions":["Create the manifest with the identical resolved root: manifest = IntegrationManifest(project_root=project_root.resolve()).","Pass the same Path object (or an identically resolved one) to setup().","Never share a manifest instance between different project roots.","Prefer the specify CLI (specify init --integration forge) which constructs the manifest correctly."],"exampleFix":"# before\nmanifest = IntegrationManifest(project_root=Path.cwd())\nForgeIntegration().setup(Path(\"/home/u/proj\"), manifest)\n\n# after\nroot = Path(\"/home/u/proj\").resolve()\nmanifest = IntegrationManifest(project_root=root)\nForgeIntegration().setup(root, manifest)","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)\nexcept ValueError as e:\n    if \"manifest.project_root\" in str(e):\n        manifest = IntegrationManifest(project_root=root.resolve())\n        integration.setup(root, manifest)\n    else:\n        raise","preventionTips":["Construct the manifest from the same resolved root passed to setup().","Beware symlinked roots (/tmp vs /private/tmp) — resolve() early.","Create a fresh manifest per project; never reuse across projects."],"tags":["filesystem","paths","integration-setup","spec-kit","forge"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}