fix(core): reject non-root module targets instead of dropping them silently - #15
Merged
Merged
Conversation
…lently modulesBody() is only ever called with "root". Scoped outputs are composed entirely from a scope's includes, so a module declaring targets: [mobile] passed loadModules (mobile is a real manifest target key) and was then discarded at render time — its body reached no generated file and the build still exited 0. loadModules now throws AICTX_CONFIG_INVALID naming the offending target and the fix (move the content to a rule file, add it to that scope's includes). The unknown-target error is kept ahead of it so a typo still reads as a typo. The meta-skill docs promised the behavior that was never implemented — the authoring-modules target table listed `api` → apps/api/AGENTS.md. Corrected there and in manifest-schema.md and docs/configuration.md, plus a section on routing package-scoped content through .ai/rules/ + scopes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ApprovabilityVerdict: Approved This PR adds explicit validation to reject non-root module targets with a helpful error, instead of silently dropping their content. The change is small, well-tested, and improves fail-fast behavior. The author owns this codebase. You can customize Macroscope's approvability policy. Learn more. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
modulesBody(target, modules)is called with the literal"root"at all three call sites inrender.ts. Scoped outputs —apps/<pkg>/AGENTS.md,apps/<pkg>/CLAUDE.md,.claude/rules/*.md— are produced bybuildScopedAgents/buildScopedClaude, which read only a scope'scodexIncludes/claudeIncludes. Modules never feed them.But
loadModulesvalidates a module'stargetsagainstmanifest.targets, sotargets: [mobile]passes as a "known target" and is then discarded at render time.ai-context buildprints success and exits 0. The module's body reaches no generated file.Found in a consuming repo: a mobile-interaction-guidance module had sat with
targets: [mobile]since it was written. Its entire body — a surface-type decision matrix, sheet/tray rules, loading-state escalation thresholds — had never reached a single agent, and every build and verify run in that repo was green the whole time.The fix
loadModulesnow throwsAICTX_CONFIG_INVALIDon a non-root target, naming the offending target and the route to take instead. The unknown-target error is kept ahead of it so a typo still reads as a typo rather than as a scoping mistake.I chose to collapse the redundancy rather than implement composition into scoped outputs. Scope includes already fully own those files, and the target-to-scope mapping composition would need does not cleanly exist:
buildScopedClaudeis keyed by scope rather than target, a scope may have nocodexTargetat all and write to paths absent frommanifest.targets, and one scope can emit the same body to severalcodexAgentspaths. Package-scoped content has a working route already —.ai/rules/plus scope includes.Docs
The meta-skill documented the behavior that was never implemented.
authoring-modules.mdlistedapi→apps/api/AGENTS.mdin its targets table, which is what led the consuming repo into this. Corrected there, inmanifest-schema.md, and indocs/configuration.md, plus a new section on routing package-scoped content through.ai/rules/+ scopes.Verification
pnpm -r typecheckclean.Changeset included: core minor, templates patch.
🤖 Generated with Claude Code
Note
Reject modules with non-root targets instead of silently dropping them
Previously, modules whose frontmatter named a non-root target were silently ignored at render time. Now
loadModulesin config.ts throws aAICTX_CONFIG_INVALIDerror immediately, instructing authors to move package-scoped content to a rule file included by a scope instead.rootis the only legal module target.Macroscope summarized e6a9103.