fix(analysis): allow safe contained instruction aliases - #51
Conversation
Resolve structural entries canonically so tracked provider adapters inside the topology root remain inspectable while escaping links stay rejected. Tests: node --test test/workspace-topology.test.mjs; frozen general-tasks evidence-bundle Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the workspace-topology discovery logic to allow a narrowly-scoped class of safe, contained, tracked instruction-route symlinks (e.g., CLAUDE.md -> AGENTS.md) while continuing to reject unsafe symlink patterns. It also documents the topology contract and adds regression tests to lock in the intended behavior.
Changes:
- Allow direct, same-directory, tracked instruction-route symlink redirects inside the Git root and read content via the canonical target path.
- Add workspace topology tests covering accepted contained instruction links and multiple rejected symlink topologies (cross-scope, chained hop, ignored/untracked/non-structural targets, manifest aliasing).
- Add a spec describing intent, acceptance scenarios, non-goals, and verification evidence.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| test/workspace-topology.test.mjs | Adds regression coverage for accepting a contained tracked instruction symlink and rejecting unsafe symlink variants. |
| scripts/workspace-topology/manifests.mjs | Implements canonical-path-based structural safety checks and narrowly permits safe instruction-route symlink redirects. |
| docs/specs/2026-08-02-contained-structural-symlinks.md | Documents the accepted symlink topology contract, risks, and verification steps. |
Suppressed comments (1)
scripts/workspace-topology/manifests.mjs:192
safeRedirectrelies on case-sensitive route comparisons (canonicalRoute === item.route,trackedRoutes.has(canonicalRoute), anddirnameRoute(...) === ...). On Windows’ default case-insensitive filesystems this can incorrectly mark safe structural files/links as unsafe whenrealpath()returns different casing. Suggest using a win32-normalized key (e.g. lowercased) for these route comparisons while keeping the originalroutevalues for reporting.
const canonicalRoute = path.relative(canonicalRoot, canonical).split(path.sep).join("/");
const safeRedirect = canonicalRoute === item.route
|| (
item.provenance === "tracked"
&& trackedRoutes.has(canonicalRoute)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const target = path.normalize(await readlink(absolute)); | ||
| return !path.isAbsolute(target) | ||
| && path.dirname(target) === "." | ||
| && path.resolve(path.dirname(absolute), target) === canonical; |
Compare structural routes and link targets through a shared pathIdentityKey helper that folds case only on Windows. Native realpath on Windows reports on-disk casing, so a case-only divergence from the tracked inventory route would otherwise mark an ordinary file structure-entry-unsafe and fail a normal-depth Evidence Bundle. Validate the redirect target against the canonical directory instead of the caller-supplied root, so the accept decision no longer depends on the caller passing an already-realpath'd topology root. Drop the unused canonical read path: manifest aliases are rejected, so item.canonical always equalled the route-based path, and requiring it in readInventoryFile turned a future contract break into a silently swallowed manifest-read-unavailable warning. Lock the surrounding contract with regressions for a nested-package alias, an absolute in-root target, a dangling link that now reports structure-entry-unavailable, and platform-scoped route identity. Record the cross-directory adapter limitation explicitly in the spec Non-goals, since those routes still fail a normal Evidence Bundle. Tests: npm run check (1105 tests passed; pack verification passed) Co-authored-by: QoderAI <qoder_ai@qoder.com>
|
Reviewed and pushed one follow-up commit to this branch ( What the follow-up changes:
Verification: One scope note, filed as #53 rather than expanded here: the same-directory restriction means the most common adapter convention ( |
…ess-gate Integrate main (PRs QoderAI#51-QoderAI#55) into the loop-readiness feature branch. Resolved conflict in test/scripts-refactor-contract.test.mjs: - Recalculated sha256 baselines for `commands --json` and `schema` to reflect the merged command registry (Grok host adapter added by PR QoderAI#52). All 1120 tests pass after merge.
Summary
Verification
node --test test/workspace-topology.test.mjs(16 tests passed)npm run checkafternpm ci(1101 tests passed; npm package and runtime bundle verification passed)No matching open upstream issue was found.