Skip to content

fix(ids): scope purpose inference + sync self-entry on regen - #748

Merged
rafaelscosta merged 2 commits into
mainfrom
fix/ids-purpose-inference-and-self-entry
May 17, 2026
Merged

fix(ids): scope purpose inference + sync self-entry on regen#748
rafaelscosta merged 2 commits into
mainfrom
fix/ids-purpose-inference-and-self-entry

Conversation

@rafaelscosta

@rafaelscosta rafaelscosta commented May 17, 2026

Copy link
Copy Markdown
Collaborator

Closes the cosmetic follow-up noted after PR #747 — CodeRabbit's "Outside diff range" comments about entity-registry self-entry drift and the component-creation-guide purpose field describing "dataset analysis" for a component creation guide.

Bug 1: purpose inference matched body text it shouldn't have

extractPurpose() in .aiox-core/development/scripts/populate-entity-registry.js had a case-insensitive unanchored regex:

const descMatch = content.match(/(?:description|purpose|summary)[:]\s*(.+)/i);

It matched ANY occurrence in the file body — including example output inside installer transcripts. For component-creation-guide.md it picked up line 132 ? Task description: Analyzes provided dataset... (example of what a user might TYPE, not a description of the doc).

Fix: restructured extractPurpose() into a stricter priority chain:

  1. YAML frontmatter description: (anchored to the --- block)
  2. ## Purpose section first non-empty line
  3. ## Overview section first non-empty line (many guides use Overview)
  4. First # Title heading
  5. Generic Entity at <path> fallback

The previous body-level regex was removed deliberately — its false-positive rate (matching example output, code blocks, etc) outweighed the narrow legitimate case it covered.

Bug 2: self-entry drift in entities.data.entity-registry

The registry contains a record for itself because the data layer scan picks up every .yaml. That record's lastVerified was stuck at whatever value the previous scan happened to write, and checksum was likewise stale relative to the regenerated file.

Fix: new syncSelfRegistryEntry() function:

  • Sets lastVerified to the same lastUpdated written in metadata.lastUpdated (single source of truth for the regen event).
  • Sets checksum to sentinel sha256:<self-reference> because hashing the registry from inside the registry is circular — any computed hash invalidates itself the moment it's written. The sentinel signals "skip this comparison" to downstream validators.

Guarded: only fires when the self-entry's path is .aiox-core/data/entity-registry.yaml. The companion entity-registry.js module entry under entities.modules.* is NOT self-referential and is left alone.

Validation (after fix, on this branch)

$ grep -c "Analyzes provided dataset" .aiox-core/data/entity-registry.yaml
0

$ grep -c "Add MCP Server Task" .aiox-core/data/entity-registry.yaml
0

Self-entry now:

entity-registry:
  path: .aiox-core/data/entity-registry.yaml
  ...
  checksum: sha256:<self-reference>
  lastVerified: '2026-05-17T14:15:04.693Z'   # matches metadata.lastUpdated

Out of scope for this PR

Some purposes in the registry still look like leaked template placeholders ('{Brief description...}', '{{TASK_TITLE}}', '*${taskName.replace(...)}'). Those come from files whose own bodies contain unfilled handlebars or JS literals — the inference is working correctly, the source data is the problem. Separate cleanup.

The component-creation-guide entity is no longer in the registry at all on this branch — the file lives under .aiox-core/core/docs/ which the modules scan only covers for .js/.mjs, not .md. Its previous presence was an artifact of an older scan config; this regen cleans it up. If we want to track framework docs as entities going forward, that's a SCAN_CONFIG follow-up.

Test plan

  • node --check .aiox-core/development/scripts/populate-entity-registry.js → exit 0
  • node .aiox-core/development/scripts/populate-entity-registry.js → regen runs, no errors, 815 entities
  • grep confirms zero false purposes from the body-level regex
  • Self-entry has sentinel checksum + matching lastVerified
  • CI validation chain passes (validate:manifest, validate:registry-accuracy, etc)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • More reliable metadata extraction for the entity registry by prioritizing YAML frontmatter and avoiding incorrect body-level matches.
    • Registry synchronization improved to use a single consistent timestamp and avoid circular self-reference validation.
  • Tests

    • Added coverage for frontmatter parsing and regressions preventing body-level false positives.
  • Chores

    • Updated registry metadata and installation manifest entries.

Review Change Stack

Closes the cosmetic follow-up noted after PR #747 — CodeRabbit's
"Outside diff range" comments about entity-registry self-entry drift
and the `component-creation-guide` purpose field reading "Analyzes
provided dataset to identify patterns and insights" (clearly
unrelated to a component creation guide).

## Bug 1: purpose inference matched body text it shouldn't have

`extractPurpose()` in `populate-entity-registry.js` had a fallback
regex:

```js
const descMatch = content.match(/(?:description|purpose|summary)[:]\s*(.+)/i);
```

This is case-insensitive and unanchored — it matched ANY occurrence
in the file body, including example output inside installer
transcripts and fenced code blocks. For
`component-creation-guide.md`, it picked up line 132:

```
? Task description: Analyzes provided dataset to identify patterns and insights
```

…which is example output showing what a user might type, not a
description of the guide itself.

Fix: restructured `extractPurpose()` to a stricter priority chain:

1. **YAML frontmatter** — `description:` (or aliases) inside the
   `---` block at the top of the file. Anchored to the frontmatter
   region so body matches can't leak in.
2. **`## Purpose` section** — first non-empty line.
3. **`## Overview` section** — same shape as Purpose. Many guides
   use Overview instead (e.g. component-creation-guide.md itself).
4. **First `# Title` heading** — the document's name.
5. **Generic fallback** — `Entity at <path>`.

The previous body-level `(?:description|purpose|summary)[:]` regex
was REMOVED deliberately. Its false-positive rate (matching example
output, installer transcripts, code block content) outweighed the
narrow legitimate case it covered, which is now handled by the
frontmatter branch.

## Bug 2: self-entry drift in `entities.data.entity-registry`

The registry contains a record for itself because the data layer
scan picks up every `.yaml`. That record's `lastVerified` was stuck
at whatever value the previous scan happened to write, and the
`checksum` was likewise stale relative to the regenerated file.

Fix: added `syncSelfRegistryEntry()` which runs after the registry
object is assembled but before yaml.dump. It:

- Sets `lastVerified` to the same `lastUpdated` timestamp written
  into `metadata.lastUpdated` (single source of truth for this
  regen event).
- Sets `checksum` to a sentinel value `sha256:<self-reference>`
  because hashing the registry from inside the registry is circular —
  any computed hash invalidates itself the moment it's written.
  The sentinel signals "skip this comparison" to downstream
  validators; trying to compute a "real" hash here would just
  produce a number that doesn't match its own input.

The sync is guarded: it only fires when the self-entry's `path`
field is `.aiox-core/data/entity-registry.yaml`. The companion
`entity-registry.js` module under `entities.modules.*` is NOT
self-referential and is left alone.

## Validation

Regenerated the registry on this branch. After the fix:

- `grep -c "Analyzes provided dataset" .aiox-core/data/entity-registry.yaml` → `0`
- `grep -c "Add MCP Server Task" .aiox-core/data/entity-registry.yaml` → `0` (was the wrong purpose on the yaml self-entry)
- The yaml self-entry's `lastVerified` now matches `metadata.lastUpdated` exactly
- The yaml self-entry's `checksum` is the sentinel as expected

The `component-creation-guide` entity is no longer in the registry
at all — the file lives under `.aiox-core/core/docs/` which is not
in `SCAN_CONFIG` (the `modules` scan covers `.aiox-core/core` but
only for `*.js`/`*.mjs`, not `*.md`). Its previous presence was an
artifact of a prior scan run with broader config; this regen cleans
it up. If we want to start tracking framework docs as entities,
that's a separate concern handled in a follow-up to SCAN_CONFIG.

## Out of scope for this commit

A second-order cleanup that this PR does NOT do: there are still
some purposes in the registry that look like leaked template
placeholders (`'{Brief description of what this task does...}'`,
`'{{TASK_TITLE}}'`, `'*${taskName.replace(...)}'`). Those come from
files whose own bodies contain unfilled handlebars or JS literals —
the inference is technically working, the source is the problem.
Separate from this fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented May 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
aiox-core Ready Ready Preview, Comment May 17, 2026 2:28pm

Request Review

@github-actions github-actions Bot added the area: docs Documentation (docs/) label May 17, 2026
@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ba1898c6-a303-4eea-973a-4d6b4e72f073

📥 Commits

Reviewing files that changed from the base of the PR and between 88068b8 and d3caa47.

📒 Files selected for processing (1)
  • tests/core/ids/populate-entity-registry.test.js

Walkthrough

The registry script makes purpose extraction deterministic (frontmatter → sections → title), uses a single computed lastUpdated for registry metadata, and synchronizes the registry’s self-entry with a sentinel checksum. The install manifest and tests are updated to match these changes.

Changes

Entity Registry Generation and Self-Sync

Layer / File(s) Summary
Purpose Extraction Logic Refinement
.aiox-core/development/scripts/populate-entity-registry.js
extractPurpose now derives entity purpose by preferring YAML frontmatter fields (description, purpose, summary) from the top frontmatter block, then ## Purpose, then ## Overview, then # Title. Removed prior generic body-level fallback.
Registry Self-Synchronization
.aiox-core/development/scripts/populate-entity-registry.js
Registry generation defines a shared lastUpdated ISO timestamp, assigns it to registry.metadata.lastUpdated, introduces a sentinel checksum constant, and calls syncSelfRegistryEntry to set the registry YAML self-entry's lastVerified and checksum.
Generated Manifest Updates
.aiox-core/install-manifest.yaml
Updated generated_at timestamp and updated recorded SHA256 and size entries for data/entity-registry.yaml and development/scripts/populate-entity-registry.js.
Tests: extractPurpose regressions
tests/core/ids/populate-entity-registry.test.js
Added tests covering YAML frontmatter description: parsing (including quoted values), fallback to ## Overview, and regressions ensuring body-level description: outside frontmatter is ignored.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

area: core, type: test, area: installer

Suggested reviewers

  • oalanicolas
  • Pedrovaleriolopez
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: fixing purpose inference scoping and syncing the self-registry entry during regeneration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ids-purpose-inference-and-self-entry

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 17, 2026

Copy link
Copy Markdown
Contributor

📊 Coverage Report

Coverage report not available

📈 Full coverage report available in Codecov


Generated by PR Automation (Story 6.1)

coderabbitai[bot]
coderabbitai Bot previously requested changes May 17, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.aiox-core/development/scripts/populate-entity-registry.js:
- Around line 175-179: The current frontmatter extraction uses one regex that
matches the first of description|purpose|summary and returns even if that key's
value normalizes to empty, so update the logic to attempt keys in order
(description, then purpose, then summary) rather than a single combined match:
for the frontmatter string fm, test each key separately (e.g., try matching
/^description\s*:\s*(.+)$/im, then /^purpose\s*:\s*(.+)$/im, then
/^summary\s*:\s*(.+)$/im), trim and strip surrounding quotes from the captured
group, and only return the first non-empty cleaned value (still truncated to
substring(0,200)); replace the current fmDescMatch handling with this
ordered-per-key approach so an empty description won’t block valid
purpose/summary fallbacks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fee25240-d86f-4200-b10a-7db0254584b9

📥 Commits

Reviewing files that changed from the base of the PR and between e5cd355 and 88068b8.

📒 Files selected for processing (3)
  • .aiox-core/data/entity-registry.yaml
  • .aiox-core/development/scripts/populate-entity-registry.js
  • .aiox-core/install-manifest.yaml

Comment on lines +175 to +179
const fmDescMatch = fm.match(/^(?:description|purpose|summary)\s*:\s*(.+)$/im);
if (fmDescMatch) {
const cleaned = fmDescMatch[1].trim().replace(/^["']|["']$/g, '');
if (cleaned) return cleaned.substring(0, 200);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Frontmatter key fallback can skip valid purpose/summary values.

On Line 175, one regex handles all three keys at once. If description is present but normalizes to empty (e.g., ""), the function does not continue to purpose or summary, which can produce a weaker fallback purpose.

Suggested fix
-    const fmDescMatch = fm.match(/^(?:description|purpose|summary)\s*:\s*(.+)$/im);
-    if (fmDescMatch) {
-      const cleaned = fmDescMatch[1].trim().replace(/^["']|["']$/g, '');
-      if (cleaned) return cleaned.substring(0, 200);
-    }
+    for (const key of ['description', 'purpose', 'summary']) {
+      const fmDescMatch = fm.match(new RegExp(`^${key}\\s*:\\s*(.+)$`, 'im'));
+      if (!fmDescMatch) continue;
+      const cleaned = fmDescMatch[1].trim().replace(/^["']|["']$/g, '');
+      if (cleaned) return cleaned.substring(0, 200);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const fmDescMatch = fm.match(/^(?:description|purpose|summary)\s*:\s*(.+)$/im);
if (fmDescMatch) {
const cleaned = fmDescMatch[1].trim().replace(/^["']|["']$/g, '');
if (cleaned) return cleaned.substring(0, 200);
}
for (const key of ['description', 'purpose', 'summary']) {
const fmDescMatch = fm.match(new RegExp(`^${key}\\s*:\\s*(.+)$`, 'im'));
if (!fmDescMatch) continue;
const cleaned = fmDescMatch[1].trim().replace(/^["']|["']$/g, '');
if (cleaned) return cleaned.substring(0, 200);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.aiox-core/development/scripts/populate-entity-registry.js around lines 175
- 179, The current frontmatter extraction uses one regex that matches the first
of description|purpose|summary and returns even if that key's value normalizes
to empty, so update the logic to attempt keys in order (description, then
purpose, then summary) rather than a single combined match: for the frontmatter
string fm, test each key separately (e.g., try matching
/^description\s*:\s*(.+)$/im, then /^purpose\s*:\s*(.+)$/im, then
/^summary\s*:\s*(.+)$/im), trim and strip surrounding quotes from the captured
group, and only return the first non-empty cleaned value (still truncated to
substring(0,200)); replace the current fmDescMatch handling with this
ordered-per-key approach so an empty description won’t block valid
purpose/summary fallbacks.

The previous suite had a test "extracts from description field" that
fed a bare `description: ...` string and expected it to be matched.
That was exactly the body-level fallback regex that produced the
"Analyzes provided dataset" false purpose on component-creation-guide.md
(reported by CodeRabbit on PR #747). The refactor in this PR
intentionally removed that match, so the test must be updated to
reflect the new contract.

Changes to the test file:

* Replaced "extracts from description field" with two replacement tests:
  - "extracts from YAML frontmatter `description:` field" — feeds a
    `---\ndescription: X\n---` block and expects extraction from it.
  - "handles quoted YAML frontmatter description" — feeds
    `description: "X"` inside frontmatter and expects the quotes to
    be stripped.

* Added "falls back to ## Overview when no Purpose section" — covers
  the new strategy 3 in the priority chain (many guides like
  component-creation-guide.md use Overview instead of Purpose).

* Added TWO regression tests for the bug that motivated the refactor:
  - "does NOT match body-level `description:` outside frontmatter" —
    reproduces the exact component-creation-guide pattern (Overview
    section + a fenced code block with `Task description:` inside)
    and asserts the Overview wins.
  - "does NOT extract from `description:` lines without YAML
    frontmatter delimiters" — bare `description: X` without `---`
    delimiters falls through to the `# Title` header, NOT to the
    body line.

Test result: 79/79 pass. Existing 5 tests in the suite still pass.
Two new tests cover the YAML frontmatter branch (priority 1) and the
new `## Overview` branch (priority 3). Two regression tests lock in
the removal of the body-level matcher so it can't silently come back.
@github-actions github-actions Bot added the type: test Test coverage and quality label May 17, 2026
@rafaelscosta
rafaelscosta dismissed coderabbitai[bot]’s stale review May 17, 2026 14:28

Addressed in d3caa47 — test "extracts from description field" rewritten to match the refactored priority chain (YAML frontmatter required, not bare body line). Added 4 new tests covering YAML frontmatter, ## Overview fallback, and 2 regression tests for the body-level match removal. 79/79 jest pass.

@rafaelscosta
rafaelscosta merged commit 1f97f6f into main May 17, 2026
40 checks passed
@rafaelscosta
rafaelscosta deleted the fix/ids-purpose-inference-and-self-entry branch May 17, 2026 14:31
rafaelscosta added a commit that referenced this pull request May 17, 2026
Addresses CodeRabbit feedback on PR #749 — the self-entry sync logic
introduced in PR #748 was load-bearing (lastVerified + checksum drift
was the original CodeRabbit complaint) but lacked targeted tests.

Tests added cover the four invariants:

1. **Self-path match → mutates both fields**: when the entry's path
   equals `.aiox-core/data/entity-registry.yaml`, `lastVerified`
   becomes the new timestamp and `checksum` becomes the sentinel.

2. **Non-self path → no mutation**: the companion entry pointing at
   `.aiox-core/core/doctor/checks/entity-registry.js` (a real JS
   module, NOT self-referential) must NOT be touched. Catches the
   most likely regression: if the path guard is removed, the JS
   module entry would get clobbered with the yaml sentinel.

3. **Missing entities.data** is a no-op (defensive).

4. **Missing self-entry** is a no-op + leaves other data entries
   alone (defensive).

5. **Malformed registry** (`{}`, `null`) does not throw.

Required exporting `syncSelfRegistryEntry` from the script's
`module.exports` and consuming it in the test file. Suite: 96/96
pass (was 91/91).
rafaelscosta added a commit that referenced this pull request May 17, 2026
…#749)

* fix(ids): scope purpose inference + sync self-entry on regen

Closes the cosmetic follow-up noted after PR #747 — CodeRabbit's
"Outside diff range" comments about entity-registry self-entry drift
and the `component-creation-guide` purpose field reading "Analyzes
provided dataset to identify patterns and insights" (clearly
unrelated to a component creation guide).

## Bug 1: purpose inference matched body text it shouldn't have

`extractPurpose()` in `populate-entity-registry.js` had a fallback
regex:

```js
const descMatch = content.match(/(?:description|purpose|summary)[:]\s*(.+)/i);
```

This is case-insensitive and unanchored — it matched ANY occurrence
in the file body, including example output inside installer
transcripts and fenced code blocks. For
`component-creation-guide.md`, it picked up line 132:

```
? Task description: Analyzes provided dataset to identify patterns and insights
```

…which is example output showing what a user might type, not a
description of the guide itself.

Fix: restructured `extractPurpose()` to a stricter priority chain:

1. **YAML frontmatter** — `description:` (or aliases) inside the
   `---` block at the top of the file. Anchored to the frontmatter
   region so body matches can't leak in.
2. **`## Purpose` section** — first non-empty line.
3. **`## Overview` section** — same shape as Purpose. Many guides
   use Overview instead (e.g. component-creation-guide.md itself).
4. **First `# Title` heading** — the document's name.
5. **Generic fallback** — `Entity at <path>`.

The previous body-level `(?:description|purpose|summary)[:]` regex
was REMOVED deliberately. Its false-positive rate (matching example
output, installer transcripts, code block content) outweighed the
narrow legitimate case it covered, which is now handled by the
frontmatter branch.

## Bug 2: self-entry drift in `entities.data.entity-registry`

The registry contains a record for itself because the data layer
scan picks up every `.yaml`. That record's `lastVerified` was stuck
at whatever value the previous scan happened to write, and the
`checksum` was likewise stale relative to the regenerated file.

Fix: added `syncSelfRegistryEntry()` which runs after the registry
object is assembled but before yaml.dump. It:

- Sets `lastVerified` to the same `lastUpdated` timestamp written
  into `metadata.lastUpdated` (single source of truth for this
  regen event).
- Sets `checksum` to a sentinel value `sha256:<self-reference>`
  because hashing the registry from inside the registry is circular —
  any computed hash invalidates itself the moment it's written.
  The sentinel signals "skip this comparison" to downstream
  validators; trying to compute a "real" hash here would just
  produce a number that doesn't match its own input.

The sync is guarded: it only fires when the self-entry's `path`
field is `.aiox-core/data/entity-registry.yaml`. The companion
`entity-registry.js` module under `entities.modules.*` is NOT
self-referential and is left alone.

## Validation

Regenerated the registry on this branch. After the fix:

- `grep -c "Analyzes provided dataset" .aiox-core/data/entity-registry.yaml` → `0`
- `grep -c "Add MCP Server Task" .aiox-core/data/entity-registry.yaml` → `0` (was the wrong purpose on the yaml self-entry)
- The yaml self-entry's `lastVerified` now matches `metadata.lastUpdated` exactly
- The yaml self-entry's `checksum` is the sentinel as expected

The `component-creation-guide` entity is no longer in the registry
at all — the file lives under `.aiox-core/core/docs/` which is not
in `SCAN_CONFIG` (the `modules` scan covers `.aiox-core/core` but
only for `*.js`/`*.mjs`, not `*.md`). Its previous presence was an
artifact of a prior scan run with broader config; this regen cleans
it up. If we want to start tracking framework docs as entities,
that's a separate concern handled in a follow-up to SCAN_CONFIG.

## Out of scope for this commit

A second-order cleanup that this PR does NOT do: there are still
some purposes in the registry that look like leaked template
placeholders (`'{Brief description of what this task does...}'`,
`'{{TASK_TITLE}}'`, `'*${taskName.replace(...)}'`). Those come from
files whose own bodies contain unfilled handlebars or JS literals —
the inference is technically working, the source is the problem.
Separate from this fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(ids): align extractPurpose tests with refactored priority chain

The previous suite had a test "extracts from description field" that
fed a bare `description: ...` string and expected it to be matched.
That was exactly the body-level fallback regex that produced the
"Analyzes provided dataset" false purpose on component-creation-guide.md
(reported by CodeRabbit on PR #747). The refactor in this PR
intentionally removed that match, so the test must be updated to
reflect the new contract.

Changes to the test file:

* Replaced "extracts from description field" with two replacement tests:
  - "extracts from YAML frontmatter `description:` field" — feeds a
    `---\ndescription: X\n---` block and expects extraction from it.
  - "handles quoted YAML frontmatter description" — feeds
    `description: "X"` inside frontmatter and expects the quotes to
    be stripped.

* Added "falls back to ## Overview when no Purpose section" — covers
  the new strategy 3 in the priority chain (many guides like
  component-creation-guide.md use Overview instead of Purpose).

* Added TWO regression tests for the bug that motivated the refactor:
  - "does NOT match body-level `description:` outside frontmatter" —
    reproduces the exact component-creation-guide pattern (Overview
    section + a fenced code block with `Task description:` inside)
    and asserts the Overview wins.
  - "does NOT extract from `description:` lines without YAML
    frontmatter delimiters" — bare `description: X` without `---`
    delimiters falls through to the `# Title` header, NOT to the
    body line.

Test result: 79/79 pass. Existing 5 tests in the suite still pass.
Two new tests cover the YAML frontmatter branch (priority 1) and the
new `## Overview` branch (priority 3). Two regression tests lock in
the removal of the body-level matcher so it can't silently come back.

* 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>

* test(ids): add 5 regression tests for syncSelfRegistryEntry

Addresses CodeRabbit feedback on PR #749 — the self-entry sync logic
introduced in PR #748 was load-bearing (lastVerified + checksum drift
was the original CodeRabbit complaint) but lacked targeted tests.

Tests added cover the four invariants:

1. **Self-path match → mutates both fields**: when the entry's path
   equals `.aiox-core/data/entity-registry.yaml`, `lastVerified`
   becomes the new timestamp and `checksum` becomes the sentinel.

2. **Non-self path → no mutation**: the companion entry pointing at
   `.aiox-core/core/doctor/checks/entity-registry.js` (a real JS
   module, NOT self-referential) must NOT be touched. Catches the
   most likely regression: if the path guard is removed, the JS
   module entry would get clobbered with the yaml sentinel.

3. **Missing entities.data** is a no-op (defensive).

4. **Missing self-entry** is a no-op + leaves other data entries
   alone (defensive).

5. **Malformed registry** (`{}`, `null`) does not throw.

Required exporting `syncSelfRegistryEntry` from the script's
`module.exports` and consuming it in the test file. Suite: 96/96
pass (was 91/91).

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rafaelscosta added a commit that referenced this pull request May 18, 2026
* chore(release): bump to 5.2.7 — consolidate PRs #744-#750 from 5.2.6

Consolidates 7 PRs merged after the 5.2.6 release. None of them
required an immediate hotfix on their own, but together they unblock
cohort students from a real CI/CD failure mode (#739 Bug 1) and
remove three sources of registry drift.

## What ships in 5.2.7

- **#744** (66b302a) — Pipeline hardening: `publish_legacy_aiox_core` race fix,
  smoke timeout 90s→240s with dual visibility check, structured notify summary,
  Windows path escape fix via WORKSPACE_DIR env var. Plus the canonical
  `docs/guides/release-procedure.md` SOP.
- **#745** (342ef63) — Ecosystem alignment: `installation-troubleshooting.md`
  Issue 10 (install-side recovery for the npm-hijack symptom), aiox-pro-access.md
  link, slim wrappers for publish-npm.md/release-management.md tasks pointing
  at the SOP, devops.md Release Procedure section.
- **#746** (ef7a352) — Internal `.aiox-core/package.json` rebranded
  `@aiox-fullstack/core@4.31.1` → `@aiox-squads/core-internal@5.2.7` (private,
  no phantom peerDeps). New `scripts/validate-aiox-core-namespace.js` (5-rule
  gate) wired into `validate:publish`. Closes #739 Bug 2.
- **#747** (e5cd355) — 70 substitutions across 17 files realigning legacy
  `aiox-core/{templates,checklists,agents,tasks,workflows,agent-teams}/<X>`
  references to current `.aiox-core/...` layout. Unblocks
  `@po *validate-story-draft`. Closes #741.
- **#748** (1f97f6f) — IDS `extractPurpose()` rewritten as strict priority
  chain (frontmatter → ## Purpose → ## Overview → # Title → fallback).
  Removed body-level regex that picked up example transcripts as
  purposes. New `syncSelfRegistryEntry()` writes sentinel
  `sha256:<self-reference>` checksum for the registry's own record.
- **#749** (70fa5e2) — `looksLikePlaceholder()` filter excludes 16 garbage
  purposes (`{Brief...}`, `{{TASK_TITLE}}`, `${context.componentName}`).
- **#750** (21782cc) — Installer honors `--ci` / `--yes` / `--skipPrompts`
  flags at the CLAUDE.md merge prompt. Non-interactive runs no longer block
  waiting for keyboard input. Closes #739 Bug 1.

## Version sites bumped (5 — up from 4 in the previous SOP)

- `package.json` (root): 5.2.6 → 5.2.7
- `compat/aiox-core/package.json`: version + dep["@aiox-squads/core"] 5.2.6 → 5.2.7
- `packages/installer/package.json`: 3.3.5 → 3.3.6 (#750 touched installer src)
- `.aiox-core/package.json` (internal): 5.2.6 → 5.2.7 (validate-aiox-core-namespace
  enforces lockstep with root — new in 5.2.7, first release under the gate)
- `package-lock.json` refresh
- `CHANGELOG.md`: new [5.2.7] entry
- `docs/guides/release-procedure.md`: bump the "4 sites" rule to "5 sites"
  reflecting the validate-aiox-core-namespace gate added in PR #746

## Validation

- `npm run lint` → exit 0
- `npm run test:ci` → 8510 passed (151 skipped), 344/355 suites, 229s
- `npx jest tests/installer/ --no-coverage` → 276/276 pass
- `npm run validate:publish` → PASS (12911 files; namespace sync verified)

Pro install hijack fix (#742) shipped in 5.2.6 and remains in production —
this release does not re-iterate it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: address CodeRabbit nitpick on release-procedure.md lockstep wording

PR #751 CodeRabbit review flagged that the "Bump all five version sites in
lockstep" wording was ambiguous — the table below explicitly treats
`packages/installer/package.json` as conditional ("patch bump if installer
changed; otherwise leave"). Operators reading only line 36 could over-bump
the installer.

Reworded to distinguish:
- Root + internal manifest + compat: MUST stay in lockstep at same version
- packages/installer: bumped ONLY when installer source changed (not every release)

Also explicit on the two failure-mode detection layers:
- Smoke tests (catch silent publish mismatches)
- validate-aiox-core-namespace.js wired into validate:publish (rule 4 enforces lockstep)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rafaelscosta added a commit that referenced this pull request May 18, 2026
…#753)

* feat(ids): track .aiox-core/core/docs/ in entity registry SCAN_CONFIG

Follow-up to PR #748. The IDS registry's `modules` scan only catches
`.aiox-core/core/**/*.{js,mjs}`, so framework documentation under
`.aiox-core/core/docs/` was invisible to impact analysis. Operators
editing those guides got no signal in `aiox graph`, `*ids` queries,
or pre-push validation when references shifted.

## Files added to the registry

- `SHARD-TRANSLATION-GUIDE.md` — shard composition reference
- `component-creation-guide.md` — meta-agent enhanced template system
- `session-update-pattern.md` — session state regen pattern
- `template-syntax.md` — handlebars template syntax reference
- `troubleshooting-guide.md` — common framework recovery flows

Total entities: 816 → 820 (one was already auto-tracked under data; the
others are newly visible).

## Implementation

- Added `{ category: 'core-docs', basePath: '.aiox-core/core/docs',
  glob: '**/*.md', type: 'docs' }` to `SCAN_CONFIG`.
- Added explicit `docs: 0.5` to `ADAPTABILITY_DEFAULTS` (matches `data`
  type: reference material, mutable across major project evolution but
  rarely per-mission). Without this, the script would fall through to
  the generic 0.5 default — same numeric outcome but the explicit entry
  documents intent.

## Validation

- [x] `npm run lint` → exit 0
- [x] `node .aiox-core/development/scripts/populate-entity-registry.js`
  reports "Found 5 core-docs" + 100% resolution rate (1289/1289 deps)
- [x] All 5 markdown files appear under `entities.core-docs.*` with
  correct `type: docs` and parsed purposes (e.g.
  `component-creation-guide` purpose extracted from frontmatter).

Closes the 2026-05-17 handoff's Pacote E follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(ids): expand SCAN_CONFIG count to 15 for new core-docs category

The previous test pinned `SCAN_CONFIG.length` at 14 (10 original + 4
NOG-16A). Adding `core-docs` raises the count to 15. Test name and
assertions updated accordingly. Added a type/basePath assertion for
the new `core-docs` entry alongside the existing checks.

Suite: 96/96 pass (was 95/95 — net unchanged in pass count because the
pinning test simply changed its expectation).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tuanmedeiros pushed a commit to tuanmedeiros/aios-core-synkraay that referenced this pull request Jun 2, 2026
…I#748)

* fix(ids): scope purpose inference + sync self-entry on regen

Closes the cosmetic follow-up noted after PR SynkraAI#747 — CodeRabbit's
"Outside diff range" comments about entity-registry self-entry drift
and the `component-creation-guide` purpose field reading "Analyzes
provided dataset to identify patterns and insights" (clearly
unrelated to a component creation guide).

## Bug 1: purpose inference matched body text it shouldn't have

`extractPurpose()` in `populate-entity-registry.js` had a fallback
regex:

```js
const descMatch = content.match(/(?:description|purpose|summary)[:]\s*(.+)/i);
```

This is case-insensitive and unanchored — it matched ANY occurrence
in the file body, including example output inside installer
transcripts and fenced code blocks. For
`component-creation-guide.md`, it picked up line 132:

```
? Task description: Analyzes provided dataset to identify patterns and insights
```

…which is example output showing what a user might type, not a
description of the guide itself.

Fix: restructured `extractPurpose()` to a stricter priority chain:

1. **YAML frontmatter** — `description:` (or aliases) inside the
   `---` block at the top of the file. Anchored to the frontmatter
   region so body matches can't leak in.
2. **`## Purpose` section** — first non-empty line.
3. **`## Overview` section** — same shape as Purpose. Many guides
   use Overview instead (e.g. component-creation-guide.md itself).
4. **First `# Title` heading** — the document's name.
5. **Generic fallback** — `Entity at <path>`.

The previous body-level `(?:description|purpose|summary)[:]` regex
was REMOVED deliberately. Its false-positive rate (matching example
output, installer transcripts, code block content) outweighed the
narrow legitimate case it covered, which is now handled by the
frontmatter branch.

## Bug 2: self-entry drift in `entities.data.entity-registry`

The registry contains a record for itself because the data layer
scan picks up every `.yaml`. That record's `lastVerified` was stuck
at whatever value the previous scan happened to write, and the
`checksum` was likewise stale relative to the regenerated file.

Fix: added `syncSelfRegistryEntry()` which runs after the registry
object is assembled but before yaml.dump. It:

- Sets `lastVerified` to the same `lastUpdated` timestamp written
  into `metadata.lastUpdated` (single source of truth for this
  regen event).
- Sets `checksum` to a sentinel value `sha256:<self-reference>`
  because hashing the registry from inside the registry is circular —
  any computed hash invalidates itself the moment it's written.
  The sentinel signals "skip this comparison" to downstream
  validators; trying to compute a "real" hash here would just
  produce a number that doesn't match its own input.

The sync is guarded: it only fires when the self-entry's `path`
field is `.aiox-core/data/entity-registry.yaml`. The companion
`entity-registry.js` module under `entities.modules.*` is NOT
self-referential and is left alone.

## Validation

Regenerated the registry on this branch. After the fix:

- `grep -c "Analyzes provided dataset" .aiox-core/data/entity-registry.yaml` → `0`
- `grep -c "Add MCP Server Task" .aiox-core/data/entity-registry.yaml` → `0` (was the wrong purpose on the yaml self-entry)
- The yaml self-entry's `lastVerified` now matches `metadata.lastUpdated` exactly
- The yaml self-entry's `checksum` is the sentinel as expected

The `component-creation-guide` entity is no longer in the registry
at all — the file lives under `.aiox-core/core/docs/` which is not
in `SCAN_CONFIG` (the `modules` scan covers `.aiox-core/core` but
only for `*.js`/`*.mjs`, not `*.md`). Its previous presence was an
artifact of a prior scan run with broader config; this regen cleans
it up. If we want to start tracking framework docs as entities,
that's a separate concern handled in a follow-up to SCAN_CONFIG.

## Out of scope for this commit

A second-order cleanup that this PR does NOT do: there are still
some purposes in the registry that look like leaked template
placeholders (`'{Brief description of what this task does...}'`,
`'{{TASK_TITLE}}'`, `'*${taskName.replace(...)}'`). Those come from
files whose own bodies contain unfilled handlebars or JS literals —
the inference is technically working, the source is the problem.
Separate from this fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(ids): align extractPurpose tests with refactored priority chain

The previous suite had a test "extracts from description field" that
fed a bare `description: ...` string and expected it to be matched.
That was exactly the body-level fallback regex that produced the
"Analyzes provided dataset" false purpose on component-creation-guide.md
(reported by CodeRabbit on PR SynkraAI#747). The refactor in this PR
intentionally removed that match, so the test must be updated to
reflect the new contract.

Changes to the test file:

* Replaced "extracts from description field" with two replacement tests:
  - "extracts from YAML frontmatter `description:` field" — feeds a
    `---\ndescription: X\n---` block and expects extraction from it.
  - "handles quoted YAML frontmatter description" — feeds
    `description: "X"` inside frontmatter and expects the quotes to
    be stripped.

* Added "falls back to ## Overview when no Purpose section" — covers
  the new strategy 3 in the priority chain (many guides like
  component-creation-guide.md use Overview instead of Purpose).

* Added TWO regression tests for the bug that motivated the refactor:
  - "does NOT match body-level `description:` outside frontmatter" —
    reproduces the exact component-creation-guide pattern (Overview
    section + a fenced code block with `Task description:` inside)
    and asserts the Overview wins.
  - "does NOT extract from `description:` lines without YAML
    frontmatter delimiters" — bare `description: X` without `---`
    delimiters falls through to the `# Title` header, NOT to the
    body line.

Test result: 79/79 pass. Existing 5 tests in the suite still pass.
Two new tests cover the YAML frontmatter branch (priority 1) and the
new `## Overview` branch (priority 3). Two regression tests lock in
the removal of the body-level matcher so it can't silently come back.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tuanmedeiros pushed a commit to tuanmedeiros/aios-core-synkraay that referenced this pull request Jun 2, 2026
…SynkraAI#749)

* fix(ids): scope purpose inference + sync self-entry on regen

Closes the cosmetic follow-up noted after PR SynkraAI#747 — CodeRabbit's
"Outside diff range" comments about entity-registry self-entry drift
and the `component-creation-guide` purpose field reading "Analyzes
provided dataset to identify patterns and insights" (clearly
unrelated to a component creation guide).

## Bug 1: purpose inference matched body text it shouldn't have

`extractPurpose()` in `populate-entity-registry.js` had a fallback
regex:

```js
const descMatch = content.match(/(?:description|purpose|summary)[:]\s*(.+)/i);
```

This is case-insensitive and unanchored — it matched ANY occurrence
in the file body, including example output inside installer
transcripts and fenced code blocks. For
`component-creation-guide.md`, it picked up line 132:

```
? Task description: Analyzes provided dataset to identify patterns and insights
```

…which is example output showing what a user might type, not a
description of the guide itself.

Fix: restructured `extractPurpose()` to a stricter priority chain:

1. **YAML frontmatter** — `description:` (or aliases) inside the
   `---` block at the top of the file. Anchored to the frontmatter
   region so body matches can't leak in.
2. **`## Purpose` section** — first non-empty line.
3. **`## Overview` section** — same shape as Purpose. Many guides
   use Overview instead (e.g. component-creation-guide.md itself).
4. **First `# Title` heading** — the document's name.
5. **Generic fallback** — `Entity at <path>`.

The previous body-level `(?:description|purpose|summary)[:]` regex
was REMOVED deliberately. Its false-positive rate (matching example
output, installer transcripts, code block content) outweighed the
narrow legitimate case it covered, which is now handled by the
frontmatter branch.

## Bug 2: self-entry drift in `entities.data.entity-registry`

The registry contains a record for itself because the data layer
scan picks up every `.yaml`. That record's `lastVerified` was stuck
at whatever value the previous scan happened to write, and the
`checksum` was likewise stale relative to the regenerated file.

Fix: added `syncSelfRegistryEntry()` which runs after the registry
object is assembled but before yaml.dump. It:

- Sets `lastVerified` to the same `lastUpdated` timestamp written
  into `metadata.lastUpdated` (single source of truth for this
  regen event).
- Sets `checksum` to a sentinel value `sha256:<self-reference>`
  because hashing the registry from inside the registry is circular —
  any computed hash invalidates itself the moment it's written.
  The sentinel signals "skip this comparison" to downstream
  validators; trying to compute a "real" hash here would just
  produce a number that doesn't match its own input.

The sync is guarded: it only fires when the self-entry's `path`
field is `.aiox-core/data/entity-registry.yaml`. The companion
`entity-registry.js` module under `entities.modules.*` is NOT
self-referential and is left alone.

## Validation

Regenerated the registry on this branch. After the fix:

- `grep -c "Analyzes provided dataset" .aiox-core/data/entity-registry.yaml` → `0`
- `grep -c "Add MCP Server Task" .aiox-core/data/entity-registry.yaml` → `0` (was the wrong purpose on the yaml self-entry)
- The yaml self-entry's `lastVerified` now matches `metadata.lastUpdated` exactly
- The yaml self-entry's `checksum` is the sentinel as expected

The `component-creation-guide` entity is no longer in the registry
at all — the file lives under `.aiox-core/core/docs/` which is not
in `SCAN_CONFIG` (the `modules` scan covers `.aiox-core/core` but
only for `*.js`/`*.mjs`, not `*.md`). Its previous presence was an
artifact of a prior scan run with broader config; this regen cleans
it up. If we want to start tracking framework docs as entities,
that's a separate concern handled in a follow-up to SCAN_CONFIG.

## Out of scope for this commit

A second-order cleanup that this PR does NOT do: there are still
some purposes in the registry that look like leaked template
placeholders (`'{Brief description of what this task does...}'`,
`'{{TASK_TITLE}}'`, `'*${taskName.replace(...)}'`). Those come from
files whose own bodies contain unfilled handlebars or JS literals —
the inference is technically working, the source is the problem.
Separate from this fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(ids): align extractPurpose tests with refactored priority chain

The previous suite had a test "extracts from description field" that
fed a bare `description: ...` string and expected it to be matched.
That was exactly the body-level fallback regex that produced the
"Analyzes provided dataset" false purpose on component-creation-guide.md
(reported by CodeRabbit on PR SynkraAI#747). The refactor in this PR
intentionally removed that match, so the test must be updated to
reflect the new contract.

Changes to the test file:

* Replaced "extracts from description field" with two replacement tests:
  - "extracts from YAML frontmatter `description:` field" — feeds a
    `---\ndescription: X\n---` block and expects extraction from it.
  - "handles quoted YAML frontmatter description" — feeds
    `description: "X"` inside frontmatter and expects the quotes to
    be stripped.

* Added "falls back to ## Overview when no Purpose section" — covers
  the new strategy 3 in the priority chain (many guides like
  component-creation-guide.md use Overview instead of Purpose).

* Added TWO regression tests for the bug that motivated the refactor:
  - "does NOT match body-level `description:` outside frontmatter" —
    reproduces the exact component-creation-guide pattern (Overview
    section + a fenced code block with `Task description:` inside)
    and asserts the Overview wins.
  - "does NOT extract from `description:` lines without YAML
    frontmatter delimiters" — bare `description: X` without `---`
    delimiters falls through to the `# Title` header, NOT to the
    body line.

Test result: 79/79 pass. Existing 5 tests in the suite still pass.
Two new tests cover the YAML frontmatter branch (priority 1) and the
new `## Overview` branch (priority 3). Two regression tests lock in
the removal of the body-level matcher so it can't silently come back.

* fix(ids): skip unfilled placeholders in extractPurpose priority chain

Follow-up to PR SynkraAI#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 SynkraAI#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>

* test(ids): add 5 regression tests for syncSelfRegistryEntry

Addresses CodeRabbit feedback on PR SynkraAI#749 — the self-entry sync logic
introduced in PR SynkraAI#748 was load-bearing (lastVerified + checksum drift
was the original CodeRabbit complaint) but lacked targeted tests.

Tests added cover the four invariants:

1. **Self-path match → mutates both fields**: when the entry's path
   equals `.aiox-core/data/entity-registry.yaml`, `lastVerified`
   becomes the new timestamp and `checksum` becomes the sentinel.

2. **Non-self path → no mutation**: the companion entry pointing at
   `.aiox-core/core/doctor/checks/entity-registry.js` (a real JS
   module, NOT self-referential) must NOT be touched. Catches the
   most likely regression: if the path guard is removed, the JS
   module entry would get clobbered with the yaml sentinel.

3. **Missing entities.data** is a no-op (defensive).

4. **Missing self-entry** is a no-op + leaves other data entries
   alone (defensive).

5. **Malformed registry** (`{}`, `null`) does not throw.

Required exporting `syncSelfRegistryEntry` from the script's
`module.exports` and consuming it in the test file. Suite: 96/96
pass (was 91/91).

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tuanmedeiros pushed a commit to tuanmedeiros/aios-core-synkraay that referenced this pull request Jun 2, 2026
…m 5.2.6 (SynkraAI#751)

* chore(release): bump to 5.2.7 — consolidate PRs SynkraAI#744-SynkraAI#750 from 5.2.6

Consolidates 7 PRs merged after the 5.2.6 release. None of them
required an immediate hotfix on their own, but together they unblock
cohort students from a real CI/CD failure mode (SynkraAI#739 Bug 1) and
remove three sources of registry drift.

## What ships in 5.2.7

- **SynkraAI#744** (d949918) — Pipeline hardening: `publish_legacy_aiox_core` race fix,
  smoke timeout 90s→240s with dual visibility check, structured notify summary,
  Windows path escape fix via WORKSPACE_DIR env var. Plus the canonical
  `docs/guides/release-procedure.md` SOP.
- **SynkraAI#745** (b728a39) — Ecosystem alignment: `installation-troubleshooting.md`
  Issue 10 (install-side recovery for the npm-hijack symptom), aiox-pro-access.md
  link, slim wrappers for publish-npm.md/release-management.md tasks pointing
  at the SOP, devops.md Release Procedure section.
- **SynkraAI#746** (dea939e) — Internal `.aiox-core/package.json` rebranded
  `@aiox-fullstack/core@4.31.1` → `@aiox-squads/core-internal@5.2.7` (private,
  no phantom peerDeps). New `scripts/validate-aiox-core-namespace.js` (5-rule
  gate) wired into `validate:publish`. Closes SynkraAI#739 Bug 2.
- **SynkraAI#747** (e874103) — 70 substitutions across 17 files realigning legacy
  `aiox-core/{templates,checklists,agents,tasks,workflows,agent-teams}/<X>`
  references to current `.aiox-core/...` layout. Unblocks
  `@po *validate-story-draft`. Closes SynkraAI#741.
- **SynkraAI#748** (e8f8761) — IDS `extractPurpose()` rewritten as strict priority
  chain (frontmatter → ## Purpose → ## Overview → # Title → fallback).
  Removed body-level regex that picked up example transcripts as
  purposes. New `syncSelfRegistryEntry()` writes sentinel
  `sha256:<self-reference>` checksum for the registry's own record.
- **SynkraAI#749** (6113083) — `looksLikePlaceholder()` filter excludes 16 garbage
  purposes (`{Brief...}`, `{{TASK_TITLE}}`, `${context.componentName}`).
- **SynkraAI#750** (1d16b51) — Installer honors `--ci` / `--yes` / `--skipPrompts`
  flags at the CLAUDE.md merge prompt. Non-interactive runs no longer block
  waiting for keyboard input. Closes SynkraAI#739 Bug 1.

## Version sites bumped (5 — up from 4 in the previous SOP)

- `package.json` (root): 5.2.6 → 5.2.7
- `compat/aiox-core/package.json`: version + dep["@aiox-squads/core"] 5.2.6 → 5.2.7
- `packages/installer/package.json`: 3.3.5 → 3.3.6 (SynkraAI#750 touched installer src)
- `.aiox-core/package.json` (internal): 5.2.6 → 5.2.7 (validate-aiox-core-namespace
  enforces lockstep with root — new in 5.2.7, first release under the gate)
- `package-lock.json` refresh
- `CHANGELOG.md`: new [5.2.7] entry
- `docs/guides/release-procedure.md`: bump the "4 sites" rule to "5 sites"
  reflecting the validate-aiox-core-namespace gate added in PR SynkraAI#746

## Validation

- `npm run lint` → exit 0
- `npm run test:ci` → 8510 passed (151 skipped), 344/355 suites, 229s
- `npx jest tests/installer/ --no-coverage` → 276/276 pass
- `npm run validate:publish` → PASS (12911 files; namespace sync verified)

Pro install hijack fix (SynkraAI#742) shipped in 5.2.6 and remains in production —
this release does not re-iterate it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: address CodeRabbit nitpick on release-procedure.md lockstep wording

PR SynkraAI#751 CodeRabbit review flagged that the "Bump all five version sites in
lockstep" wording was ambiguous — the table below explicitly treats
`packages/installer/package.json` as conditional ("patch bump if installer
changed; otherwise leave"). Operators reading only line 36 could over-bump
the installer.

Reworded to distinguish:
- Root + internal manifest + compat: MUST stay in lockstep at same version
- packages/installer: bumped ONLY when installer source changed (not every release)

Also explicit on the two failure-mode detection layers:
- Smoke tests (catch silent publish mismatches)
- validate-aiox-core-namespace.js wired into validate:publish (rule 4 enforces lockstep)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tuanmedeiros pushed a commit to tuanmedeiros/aios-core-synkraay that referenced this pull request Jun 2, 2026
…SynkraAI#753)

* feat(ids): track .aiox-core/core/docs/ in entity registry SCAN_CONFIG

Follow-up to PR SynkraAI#748. The IDS registry's `modules` scan only catches
`.aiox-core/core/**/*.{js,mjs}`, so framework documentation under
`.aiox-core/core/docs/` was invisible to impact analysis. Operators
editing those guides got no signal in `aiox graph`, `*ids` queries,
or pre-push validation when references shifted.

## Files added to the registry

- `SHARD-TRANSLATION-GUIDE.md` — shard composition reference
- `component-creation-guide.md` — meta-agent enhanced template system
- `session-update-pattern.md` — session state regen pattern
- `template-syntax.md` — handlebars template syntax reference
- `troubleshooting-guide.md` — common framework recovery flows

Total entities: 816 → 820 (one was already auto-tracked under data; the
others are newly visible).

## Implementation

- Added `{ category: 'core-docs', basePath: '.aiox-core/core/docs',
  glob: '**/*.md', type: 'docs' }` to `SCAN_CONFIG`.
- Added explicit `docs: 0.5` to `ADAPTABILITY_DEFAULTS` (matches `data`
  type: reference material, mutable across major project evolution but
  rarely per-mission). Without this, the script would fall through to
  the generic 0.5 default — same numeric outcome but the explicit entry
  documents intent.

## Validation

- [x] `npm run lint` → exit 0
- [x] `node .aiox-core/development/scripts/populate-entity-registry.js`
  reports "Found 5 core-docs" + 100% resolution rate (1289/1289 deps)
- [x] All 5 markdown files appear under `entities.core-docs.*` with
  correct `type: docs` and parsed purposes (e.g.
  `component-creation-guide` purpose extracted from frontmatter).

Closes the 2026-05-17 handoff's Pacote E follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(ids): expand SCAN_CONFIG count to 15 for new core-docs category

The previous test pinned `SCAN_CONFIG.length` at 14 (10 original + 4
NOG-16A). Adding `core-docs` raises the count to 15. Test name and
assertions updated accordingly. Added a type/basePath assertion for
the new `core-docs` entry alongside the existing checks.

Suite: 96/96 pass (was 95/95 — net unchanged in pass count because the
pinning test simply changed its expectation).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: docs Documentation (docs/) type: test Test coverage and quality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant