Files
msd-core/docs
sim bbc3f131be refactor(#4653): make containment ONE decision, resolved two ways
Satisfies #4653 DW1 and DW9, which were the phase's outstanding acceptance
criteria: every other implementation must be deleted or route its containment
DECISION through the canonical predicate, and no surviving wrapper may decide
WHETHER a path is contained.

Three implementations were being retained with their own comparisons, on the
argument that each needs LEXICAL resolution — a realpath-based predicate is the
wrong tool wherever a symlink must be preserved rather than resolved. That
argument is correct about RESOLUTION and was being used to justify owning the
DECISION too. Those are separable, and separating them is what closes the
criteria honestly rather than by reinterpretation.

  isContainedIn(resolvedTarget, resolvedRoot, pathImpl?)   module-internal

is now the single place this repo decides containment. It is separator-aware, so
a sibling merely sharing a prefix (`<root>-evil` against `<root>`) is still
rejected. Two exported families sit on it and differ ONLY in how a candidate is
resolved before the decision:

  assertWithinRoot / tryWithinRoot                realpath-resolving
  assertWithinRootLexical / tryWithinRootLexical  path.resolve only, no I/O

The lexical pair carries `opts.pathImpl`, so win32 separator semantics stay
testable off Windows — that seam already existed in isPathConfined and would
have been lost by a naive collapse.

The three call sites now take their decision from the predicate and keep only
what is genuinely theirs:

  external-descriptor-trust isPathConfined   delegates outright; pathImpl forwarded
  installer-migrations ensureInsideConfig    delegates; keeps its own message and
                                             its LEXICAL fullPath, which callers
                                             consume for existsSync and journal rows
  gsd-tools.cjs isInsideDir                  delegates; keeps its own `target !==
                                             root` condition, and the separate
                                             symlink refusal above it stands

DW5 is not weakened by this. That criterion binds the symlink oracle and the
ancestor canonicalization; both are untouched. The only change inside
validatePath is three comparison lines becoming one call, and the rejection
string `Path escapes allowed directory: <resolved> is outside <base>` stays
byte-identical because it is an observable CLI contract.

What this does NOT do, stated plainly: the lexical family still cannot see a
symlink. That is a property of lexical resolution, not a gap in the seam, and
the three callers that need it are the three that must pair it with their own
symlink refusal — which is exactly what the fix earlier in this phase added at
the install sites. The doc comment says so at the definition, and CONTEXT.md and
docs/explanation/security-model.md are corrected: they previously described
these three as deliberately NOT routed through the predicate, which is no longer
true.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 18:33:01 -04:00
..

GSD Core documentation

Documentation is organised into four quadrants: tutorials help you learn by doing, how-to guides solve specific tasks, reference states authoritative facts, and explanation explores concepts and design decisions.

Language versions: English · Português (pt-BR) · 日本語 · 简体中文


Tutorials


How-to guides


Reference

  • Commands — every command with flags and examples
  • Configuration — full config schema, model profiles, git branching strategies
  • CLI tools — gsd-tools.cjs programmatic API for workflows and agents
  • JSON error mode — gsd-tools failure channels: faults (stderr, exit 1) vs degraded results (stdout, exit 0), and the reason-code taxonomy
  • Features — complete feature index
  • Inventory — installed skills and surface map
  • STATE.md schema — field-by-field reference for .planning/STATE.md
  • CONTEXT.md schema — field-by-field reference for .planning/phases/<N>/CONTEXT.md
  • PLAN.md schema — field-by-field reference for .planning/phases/<N>/PLAN.md
  • Planning artifacts — all .planning/ files and their roles
  • Review and verification capabilities — code review, security, and Nyquist capability ownership and hook contracts
  • Gate predicates — canonical specification of the phase-gate predicate vocabulary
  • Capability matrix — generated catalogue of every capability's role, tier, extension points, hook kinds, and engines.gsd
  • Exit code reference — generated catalogue of every registered process exit code, its name, meaning, and owning module, plus the reserved bands and the v1/v2 exit contract
  • Capability manifest — the full capability.json schema and validation rules
  • gsd capability command — install / update / remove / list reference for third-party capabilities
  • Workflow fragments — in-file <!-- gsd:section --> marker grammar for fragmentizing workflow markdown at emission time
  • Partition rules for compact-content splits — the protected-content list, sentinel syntax, and the five CI checks a workflow.compact_content spine/detail split must obey
  • Reviewer Lane Registry — generated catalogue of third-party reviewer lanes, with their flags, transport, and install commands

Explanation