T-YYZU-moon-yml-schema-depth-check
Status: open/ready · Impact: medium · Complexity: small
AUTO-DEFINED: this spec was best-effort machine-authored by /sdlc:task-auto-define on 2026-07-20 because the task is autonomy: autonomous/pr. Review the Goal, Approach, Today, Files-to-touch, and Acceptance-criteria carefully before trusting it.
A directory move left moon.yml files pointing at $schema paths whose
relative depth no longer resolved. Moon itself ignores $schema, so nothing
failed — editor validation just stopped working, silently. This task adds a
depth-aware check so a future move cannot re-break it without a loud failure.
After T-C9RD’s directory move, 12 moon.yml files carried $schema paths with stale relative depths (e.g. ../../.moon/cache/schemas/project.json pointing one level too shallow). Harmless to moon itself, which ignores $schema, but editor validation silently died with no error anywhere. Add a depth-aware $schema check that walks every moon.yml in the repo, resolves its $schema relative path against the file’s own location, and fails with file:line citations when the target does not exist. Wire it into the existing project-check pre-commit family in lefthook.yml alongside project-check-identifier-shape.
— from T-C9RD-consolidate-augmented-into-solutions
| Location | Role today |
|---|---|
.moon/workspace.yml | The one and only moon workspace root in this repo. find . -name .moon -type d returns exactly ./.moon, so every moon.yml in the tree belongs to this single workspace. |
.gitignore | Ignores .moon/cache/ — moon’s generated cache, where the fetched schemas/project.json lands. The directory is absent in a fresh checkout and in CI. |
packages/ts/obsidian-astro/moon.yml | Carries $schema: '../../.moon/cache/schemas/project.json' at a 3-deep location, resolving to packages/.moon/... — one level too shallow. |
solutions/determined/apps/vault-engine/moon.yml | One of the eight determined moon.yml files whose too-shallow 2-hop $schema was corrected to ../../../../ by PR #1175’s migration commit — the depth drift this task guards against has now happened (T-C9RD) and been hand-fixed (#1175) once each; nothing automated prevents a third round. |
solutions/augmented/apps/learn_web/moon.yml | A 4-deep project whose $schema depth is correct (../../../../), showing the intended shape at that depth. |
solutions/augmented/apps/augmented_web/moon.yml | Uses the remote https://moonrepo.dev/schemas/project.json form instead of a relative path — a shape the check must tolerate, not flag. |
solutions/augmented/apps/vimit_prototype/moon.yml | Has no $schema: key at all — the other shape the check must tolerate. |
.claude/skills/project-check/ | Home of the deterministic project-check scripts. No script inspects moon.yml today. |
.claude/skills/project-check/check_identifier_shape.ts | Adapter-shaped check (thin wrapper over a lib op) — the pattern used when the capability belongs in the SDLC substrate. |
.claude/skills/project-check/check_pipe_tail.ts | Self-contained check (all logic in-script, --skills-dir override for fixtures) — the pattern used for repo-local lints. |
.claude/skills/project-check/tests/check_pipe_tail.test.ts | bun:test suite that builds an ephemeral fixture tree under tmpdir() and shells the checker at it. The template for new check tests. |
.claude/skills/project-check/SKILL.md | Documents each check (a ### N. description block plus a ### Step N invocation block) and lists the commit-time scripts in its intro paragraph. |
lefthook.yml | Declares the project-check-* pre-commit family. Each entry is a comment block plus a run: line; lefthook aborts on the first non-zero exit. |
Proposed
Section titled “Proposed”A self-contained deterministic check, check_moon_schema_paths.ts, that walks
every moon.yml in the repo, resolves each relative $schema value against
the file’s own directory, and fails with file:line citations when the
normalized result is not the workspace-root .moon/cache/schemas/project.json.
Wired into the project-check-* pre-commit family in lefthook.yml, with the
one still-stale path (packages/ts/obsidian-astro/moon.yml) fixed in the same
change so the gate is green on landing. (The eight determined files this task
originally listed were fixed by PR #1175’s migration commit.)
The check compares normalized paths, not on-disk existence. .moon/cache/
is gitignored, so the schema target is absent in a fresh checkout and in CI —
an existence check would fail on all 29 currently-correct files. Normalizing
the relative path and comparing it to the known workspace-relative target
catches exactly the depth drift this task is about, and does so without
requiring moon sync to have run.
Approach
Section titled “Approach”- Add
.claude/skills/project-check/check_moon_schema_paths.ts, a self-containedbunscript following thecheck_pipe_tail.tsshape (all logic in-script, nolibop). This is a repo-infrastructure lint about the moon workspace, not an SDLC entity concern, so it stays a dev-repo self-lint likeapps/sdlc/scripts/check_command_seam.shrather than becoming ansdlcop — the op registry is the consumer API. - Give it a
--root <dir>flag defaulting to the repo root (resolved fromimport.meta.url, ascheck_identifier_shape.tsdoes) so tests can point it at an ephemeral fixture tree. - Walk the root for files named
moon.yml, pruningnode_modulesand.git. Read each and find the first line matching^\$schema:\s*(.+)$, recording the 1-based line number. Strip surrounding single or double quotes from the value. - Classify each value:
- No
$schema:line → skip, not a violation. - Value matching
^https?://→ skip, not a violation. - Otherwise → resolve: join the
moon.yml’s root-relative directory with the value, normalize away.and..segments, and compare to the literal.moon/cache/schemas/project.json.
- No
- On mismatch, record a violation carrying the root-relative file path, the
line number, the declared value, and the normalized result. Also flag a
value that normalizes above the root (leading
..survives normalization) with the same violation shape. - Emit the expected replacement value per violation —
../repeated once per path segment of the file’s directory, or./for the rootmoon.yml— so the failure message is directly actionable. - Print
Moon $schema paths: OKand exit0when clean; print oneFAIL <path>:<line>: ...line per violation followed by aN moon.yml $schema violation(s)summary and exit1otherwise. Exit2on an internal error (unreadable root), matching the family’s convention. - Fix the one remaining stale
$schemavalue:packages/ts/obsidian-astro/moon.yml(../../→../../../). The eight determined files this task originally listed were already corrected to../../../../by PR #1175’s migration commit. Without this fix the new gate blocks every subsequent commit. - Add
.claude/skills/project-check/tests/check_moon_schema_paths.test.tsmodeled oncheck_pipe_tail.test.ts: build an ephemeral tree undermkdtempSync(join(tmpdir(), ...)), writemoon.ymlfixtures, shell the checker with--root, assert exit codes and output substrings. Cover the correct-depth, too-shallow, too-deep, root-./, remote-URL, and no-$schemacases. - Register the check in
.claude/skills/project-check/SKILL.md: add a### 1h. moon.yml $schema depth (deterministic)description block after the### 1g.block, add a matching### Step 1hinvocation block afterStep 1g, and addcheck_moon_schema_paths.tsto the commit-time script list in the intro paragraph. - Add a
project-check-moon-schema-paths:entry to thepre-commitjob list inlefthook.yml, alongsideproject-check-identifier-shape, with a comment block explaining the normalized-path (not existence) semantics andrun: bun run .claude/skills/project-check/check_moon_schema_paths.ts.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
.claude/skills/project-check/check_moon_schema_paths.ts | new | The depth-aware $schema checker: walk, resolve, compare, cite, exit 0/1/2. |
.claude/skills/project-check/tests/check_moon_schema_paths.test.ts | new | bun:test suite over ephemeral moon.yml fixtures via --root. |
lefthook.yml | modify | Add project-check-moon-schema-paths: to the pre-commit project-check-* family. |
.claude/skills/project-check/SKILL.md | modify | Add the ### 1h. check description, the ### Step 1h invocation block, and the script name in the intro roster. |
packages/ts/obsidian-astro/moon.yml | modify | $schema ../../ → ../../../. (The eight determined moon.yml fixes originally listed here landed via PR #1175’s migration commit.) |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
bun run .claude/skills/project-check/check_moon_schema_paths.tsexits0and printsMoon $schema paths: OKagainst the repo after thepackages/ts/obsidian-astro/moon.ymlfix lands. - AC-2: Reverting
packages/ts/obsidian-astro/moon.ymlback to its../../value (or setting any correct-depthmoon.yml, e.g.solutions/determined/apps/vault-engine/moon.yml, too shallow) makes that same command exit1and print aFAIL <path>:<line>line naming that file, its line number, the declared value, and the expected replacement. - AC-3:
grep -rn --include=moon.yml -F '$schema' .(excludingnode_modules) shows every relative value normalizing to.moon/cache/schemas/project.jsonwhen resolved against its own file’s directory. - AC-4: The checker exits
0on a fixture tree whose onlymoon.ymlhas$schema: 'https://moonrepo.dev/schemas/project.json', and0on a fixture tree whose onlymoon.ymlhas no$schema:line — matching the livesolutions/augmented/apps/augmented_web/moon.ymlandsolutions/augmented/apps/vimit_prototype/moon.ymlshapes. - AC-5: The checker exits
0on the repo while.moon/cache/does not exist on disk, demonstrating the check does not depend onmoon synchaving run. - AC-6:
bun test ./.claude/skills/project-check/tests/check_moon_schema_paths.test.tsexits0, and the suite contains a case for each of: correct depth, too-shallow, too-deep, root./form, remote URL, and absent$schema. - AC-7:
lefthook.ymlcontains aproject-check-moon-schema-paths:entry underpre-commitwhoserun:invokesbun run .claude/skills/project-check/check_moon_schema_paths.ts, andlefthook run pre-commitexits0on the clean tree. - AC-8:
.claude/skills/project-check/SKILL.mdnamescheck_moon_schema_paths.tsin its commit-time script roster, in a### 1h.description block, and in a### Step 1hinvocation block.
Out of scope
Section titled “Out of scope”- Creating
.moon/cache/schemas/project.jsonor un-gitignoring.moon/cache/. - Adding a
$schema:key tomoon.ymlfiles that omit it (solutions/augmented/apps/vimit_prototype/moon.yml) — absence stays tolerated. - Rewriting
solutions/augmented/apps/augmented_web/moon.ymlfrom the remote URL form to the relative form. - Validating
moon.ymlcontent against the schema; this check verifies the pointer resolves, not that the document conforms. - Extending the check to
.moon/workspace.yml,.moon/toolchains.yml, or.moon/tasks/*.yml. - Any equivalent
$schemadepth check for non-moon YAML or JSON in the repo.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”Spawned by /sdlc:spawn-task-pr on 2026-07-20 UTC from T-C9RD-consolidate-augmented-into-solutions in https://github.com/sksizer/dev.