Skip to content

T-T0O9-package-root-path-assumptions

Status: closed/done · Impact: high · Complexity: medium

Installed as a package, the code root (inside node_modules or a dist checkout) and the project root (the consumer repo holding docs/planning/ and sdlc.yaml) diverge for the first time. The audit found the core already clean — project root is flag-or-cwd, never derived from the code tree — but a handful of fallbacks and the shipped site scaffold still assume code root == repo root. This task removes those assumptions so D-0014’s consumer install cannot silently read or write the wrong tree.

LocationRole today
plugin/cli/registry_adapter.tsproject-root resolution: --project-root <path> else process.cwd(); ops with projectRoot in their input schema get it threaded — correct by construction
plugin/lib/config/load.ts#loadConfigreads <projectRoot>/sdlc.yaml; lexical path, no ancestor walk-up — running from a consumer subdirectory silently degrades to all-defaults
plugin/lib/services/docs/generate.ts#repoRootno-arg main() fallback: join(import.meta.dir, "..","..","..","..") — under node_modules that is the package, not the consumer project
plugin/lib/services/plugin/info-report.tsplugin root: CLAUDE_PLUGIN_ROOT first, else ../×4 from module — hop count is a layout assumption
plugin/lib/services/project/ops/setup.tspluginRoot = ../×4 from services/project/ops/ to reach its own lib/model/entities
plugin/cli/backlog_cli/create.tspluginRoot() = ../×2 from cli/backlog_cli/; reaches cli/sdlc.ts and lib/model/entities
plugin/site-template/package.jsongenerate script: bun run "$(git rev-parse --show-toplevel)/plugin/cli/sdlc.ts" docs generate site --project-root "$(git rev-parse --show-toplevel)" — assumes the CLI lives at <git-toplevel>/plugin/ and project root == git toplevel; false in every consumer
plugin/lib/util/paths.tsdocuments the “plain lexical absolute path (no walk-up)” intent

Correct today and untouched: all docs/planning reads are join(projectRoot, …); git rev-parse uses in cleanup/, scan-todos, and the pr/task skills resolve the consumer repo (their own project).

No shipped code derives a project location from the code tree. The ../×N self-locating hops are annotated as layout-invariant-dependent (the T-XTGT staged tree preserves the plugin/ subtree, so they survive) and covered by a test that pins the hop count to the tree shape. loadConfig finds sdlc.yaml by ancestor walk-up when invoked from a consumer subdirectory. The site scaffold’s scripts invoke the installed sdlc bin, not a git-toplevel-relative dev path.

  1. plugin/lib/services/docs/generate.ts: delete the repoRoot() code-tree fallback; the no-arg main() falls back to process.cwd() (matching the CLI adapter’s rule) and the doc-comment says why.
  2. plugin/lib/config/load.ts: when the caller did not pass an explicit --project-root and <cwd>/sdlc.yaml is absent, walk ancestors to the first directory containing sdlc.yaml (stop at the filesystem root); fall back to the current lexical behavior when none is found. Update plugin/lib/util/paths.ts’s no-walk-up doc note. Test: fixture tree with sdlc.yaml at root, invocation from a nested dir resolves the root config; absent sdlc.yaml keeps today’s defaults.
  3. Pin the self-location hops: a unit test asserting info-report.ts, setup.ts, and backlog_cli/create.ts resolve the real plugin/ root from their on-disk module locations (the test breaks if a future refactor moves a module without updating its hop count); add a one-line comment at each hop naming the T-XTGT layout invariant it relies on.
  4. plugin/site-template/package.json: replace the generate script with sdlc docs generate site --project-root .. (the site dir is a child of the project root; npm run/bun run put the installed sdlc bin on PATH in consumers). Keep the dev repo working: its generation is driven by the root build:doc-site script, not the scaffold script.
  5. Sweep for stragglers: re-run the audit greps (git rev-parse --show-toplevel, import.meta + .. hops, join(.*projectRoot misuse) over plugin/lib + plugin/cli and confirm every remaining hit is either project-scoped (correct) or annotated by step 3.
  6. Full quality suite; regenerate docs if any entity prose changed.
LocationKindChange
plugin/lib/services/docs/generate.tsmodifydrop repoRoot() code-tree fallback; cwd fallback + rationale comment
plugin/lib/config/load.tsmodifyancestor walk-up for sdlc.yaml when no explicit root
plugin/lib/config/tests/modifywalk-up tests (nested-dir resolution; no-sdlc.yaml default path)
plugin/lib/util/paths.tsmodifyupdate the no-walk-up doc note to describe the new rule
plugin/lib/services/plugin/info-report.tsmodifylayout-invariant comment on the ../×4 fallback
plugin/lib/services/project/ops/setup.tsmodifylayout-invariant comment on the ../×4 hop
plugin/cli/backlog_cli/create.tsmodifylayout-invariant comment on the ../×2 hop
plugin/lib/tests/modifyhop-count pinning test for the three self-locating modules
plugin/site-template/package.jsonmodifygenerate script → installed sdlc bin, no git rev-parse/plugin/ path
  • AC-1: grep -rn 'git rev-parse --show-toplevel' plugin/site-template plugin/lib plugin/cli returns only project-scoped uses (cleanup/, scan-todos, plugin self-diagnostics) — zero in site-template/ and zero deriving a consumer path from the code tree.
  • AC-2: sdlc docs generate invoked with no --project-root from a fixture project’s nested subdirectory writes into that project’s tree, not the package’s (walk-up test).
  • AC-3: the hop-count pinning test fails when any of the three self-locating modules is moved without updating its resolution (demonstrated in the test by a simulated wrong hop).
  • AC-4: existing suites green (bun test, bunx tsc --noEmit); sdlc docs generate --check reports no drift.
  • The staged-tree layout itself (T-XTGT owns the invariant; this task annotates and pins against it).
  • update-skill-doc and other maintainer-only skills that assume the dev repo layout — they never run in consumers.
  • Replacing the ../×N hops with a manifest/anchor-file mechanism — worth considering only if the layout invariant breaks later.
  • none hard; runs parallel to T-WGV4. End-to-end confirmation in an installed package comes with T-521G (pilot).
  • D-0014 Migration step 4; path audit 2026-07-02. Highest-severity findings addressed here: site-template generate script (breaks in every consumer), generate.ts code-tree fallback, missing sdlc.yaml walk-up. The registry .ts-filter finding from the same audit is T-XTGT step 1, not this task.

← Back to Tasks