Skip to content

A spec.md symlink to a file elsewhere in the same project is refused since 1.8.0 #2100

Closed

Description

@Didstriker

What happened

Since 1.8.0, every command that reads main specs refuses an openspec/specs/<capability>/spec.md that is a symlink to a file elsewhere in the same repository:

✖ Error: Path is outside the allowed directory: <repo>/openspec/specs/login/spec.md

list --specs reports "No specs found", show and validate fail, and archive refuses to merge deltas into the spec. 1.7.0 handles the same layout correctly. Still present in 1.14.2.

We keep our main specs next to the product documentation they belong to (docs/<area>/specs/<capability>.md), not under openspec/specs/. Each openspec/specs/<capability>/spec.md is a relative symlink to that file, so OpenSpec and the docs read and write the same bytes. 1.7.0 supported this explicitly: #1437 made "symlinked specs/<capability>/spec.md files … discovered instead of silently dropped".

What you expected instead

A spec.md link whose target stays inside the same project to be accepted, as in 1.7.0.

The containment check from #1499 uses the specs root as the boundary for a spec.md link. But since #2052 (1.14.2), openspec/specs itself may be a project-owned symlink to a directory in another repository, and a symlinked capability directory may resolve outside the project (with an OpenSpecExternalSpecWrite warning). A spec.md link that stays inside the project is the more contained case. For example, the project root could be the boundary for a capability's spec.md link, while targets outside the project are still refused.

Moving to directory symlinks doesn't work for this layout: a symlinked capability directory is accepted by show and archive but invisible to list --specs and validate --specs, and a symlinked openspec/specs only works if the docs adopt the <capability>/spec.md naming.

Minimal steps to reproduce

mkdir -p repo/docs/auth/specs repo/openspec/specs/login repo/openspec/changes && cd repo && git init -q
printf '# login\n\n## Purpose\nUsers sign in with their credentials to reach their account.\n\n## Requirements\n\n### Requirement: Sign in\nThe system SHALL let a user sign in with valid credentials.\n\n#### Scenario: Valid credentials\n- **WHEN** the user submits valid credentials\n- **THEN** the user is signed in\n' > docs/auth/specs/login.md
ln -s ../../../docs/auth/specs/login.md openspec/specs/login/spec.md

npx @fission-ai/openspec@1.7.0 list --specs                 # login     requirements 1
npx @fission-ai/openspec@1.7.0 validate login --type spec   # Specification 'login' is valid

npx @fission-ai/openspec@1.14.2 list --specs                # Error: Path is outside the allowed directory
npx @fission-ai/openspec@1.14.2 validate login --type spec  # Error: Path is outside the allowed directory

Related: #2050 / #2052, the same regression since 1.8.0 for symlinked openspec/changes and openspec/specs directories.

OpenSpec version

1.14.2 (works in 1.7.0, broken since 1.8.0)

Coding agent and model

Claude Code (not agent-specific: plain CLI commands reproduce it)

OS and Node version

Ubuntu 24.04, Node 26.5.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    approvedChange management gate before proposal/implementation PRs

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions