Commit c171c3a
fix(ids): skip unfilled placeholders in extractPurpose priority chain
Follow-up to PR #748. With body-level matching gone, the priority
chain still picked up garbage purposes from files that have literal
unfilled template placeholders or JS interpolation in the SOURCES
the chain reads: YAML frontmatter, `## Purpose` sections, `# Title`
headings. The extractor was technically working — the source data
was just full of `{Brief description...}`, `{{TASK_TITLE}}`, and
`${context.componentName}` strings that nobody ever filled in.
## What this PR does
Adds `looksLikePlaceholder(candidate)` that detects:
1. Whole-string single placeholder: `{Brief...}`, `{{var}}`, `${ctx.x}`
2. Leading-token placeholder: `*${name}foo`, `${ctx.x} bar`,
`{Title} extra`
3. Dominant interpolation (>30% of string): catches
`${icon} @${id} — ${name}${archetype !== 'Specialist' ? ` (${archetype})` : ''} | ${title}`
where most of the string is `${}` blocks
The detector is conservative on real prose:
- `Manage feature flags with { enabled: true } syntax` → keeps
- `Use this skill when... ${variable not present}` (<30% interpolation) → keeps
- Empty / whitespace / nullish → keeps (caller handles)
Wired into all four `extractPurpose` strategies (frontmatter,
`## Purpose`, `## Overview`, `# Title`). When a strategy produces a
placeholder, it falls through to the next strategy. When all four
produce placeholders, the function returns the generic
`Entity at <path>` fallback rather than a nonsense purpose.
## Validation
Counted placeholders in `.aiox-core/data/entity-registry.yaml` before
and after this PR:
```
$ grep -E "purpose: ['\"]?\{[A-Z]|purpose:.*\{\{|purpose:.*\\\$\\{" \
.aiox-core/data/entity-registry.yaml | wc -l
# Before: 16
# After: 0
```
Examples of entries that now have sensible purposes (or sensible
generic fallbacks) instead of unfilled templates:
- `*${taskName.replace(/-/g, '-')}` → now resolves via header or
falls to `Entity at <path>`
- `{Brief description of what this task does and when to use it}` → now
falls through to the next strategy
- `Generated: ${new Date().toISOString()}` → captured by dominant-
interpolation heuristic and skipped
- `${icon} @${id} — ${name}${archetype !== ...}` → captured by
dominant-interpolation heuristic and skipped
## Tests
12 new tests added to `tests/core/ids/populate-entity-registry.test.js`:
- 4 cover `extractPurpose` falling through placeholders at each
strategy boundary
- 8 cover `looksLikePlaceholder` directly (positive cases for each
pattern + negative cases for normal prose that should NOT trip it)
Suite: 91/91 pass (was 79/79 on PR #748).
## Why not just fix the source files?
The placeholders live in templates and generators — they're SUPPOSED
to be unfilled when stored in the framework. They get filled at use-
time when the template instantiates an output. The registry's
inference is reading the template body as if it were a finished doc,
which is the actual bug. Fixing the inference (this PR) is correct;
trying to fill the templates would break their template-ness.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent d3caa47 commit c171c3a
4 files changed
Lines changed: 985 additions & 843 deletions
File tree
- .aiox-core
- data
- development/scripts
- tests/core/ids
0 commit comments