Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
064af8e
feat(docs): implement documentation build and database workflow with …
dkalinovInfra Aug 6, 2026
fcf562a
Potential fix for pull request finding
dkalinovInfra Aug 6, 2026
bd6461d
Potential fix for pull request finding
dkalinovInfra Aug 6, 2026
9052b2b
Applying some code review comments
dkalinovInfra Aug 6, 2026
ebf3310
Merge branch 'dkalinov/mcp-db-git-workflow' of https://github.com/Ign…
dkalinovInfra Aug 6, 2026
fd8ea33
feat(docs): enhance submodule checkout process for documentation build
dkalinovInfra Aug 6, 2026
ab8f617
chore: update Node.js version to 24.x in build workflows
dkalinovInfra Aug 6, 2026
9030d3c
fix: update publish job condition to check direct dependency result
dkalinovInfra Aug 6, 2026
771bb31
feat: add build summary reporting for documentation pipeline
dkalinovInfra Aug 6, 2026
4558787
fix: update action versions to v6 and v8 for improved functionality
dkalinovInfra Aug 6, 2026
c463dc5
fix: update job conditions to handle cancelled states in build workflow
dkalinovInfra Aug 7, 2026
934c241
feat: implement derive-components script to improve component frontma…
dkalinovInfra Aug 7, 2026
8187dc8
fix: enhance document compression checks and improve component handli…
dkalinovInfra Aug 7, 2026
4cd9783
fix: enhance document compression handling and improve retry logic in…
dkalinovInfra Aug 7, 2026
7db3bd8
Merge branch 'master' into dkalinov/mcp-db-git-workflow
dkalinovInfra Aug 10, 2026
53395e1
Potential fix for pull request finding
dkalinovInfra Aug 10, 2026
1e66bee
Fixing some tests failures
dkalinovInfra Aug 10, 2026
8132188
fix: remove unnecessary blank line in documentation database tests
dkalinovInfra Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 71 additions & 14 deletions .github/actions/build-framework-docs/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,44 @@ runs:
using: composite
steps:
- uses: actions/checkout@v6
with:
submodules: recursive

Comment thread
dkalinovInfra marked this conversation as resolved.
# Deliberately not `submodules: recursive` — that also clones blazor/api-docs,
# blazor/igniteui-blazor and react/igniteui-react. api-docs is a private repository
# the default token cannot read, and none of the three are used by the documentation
# pipeline. Only the sources this framework reads are checked out.
- name: Check out documentation submodules
shell: bash
env:
FW: ${{ inputs.framework }}
run: |
set -euo pipefail
BASE=packages/igniteui-mcp/igniteui-doc-mcp
case "$FW" in
angular)
SUBS="angular/igniteui-docfx angular/igniteui-angular-samples angular/igniteui-angular-examples" ;;
react)
SUBS="common/igniteui-xplat-docs react/igniteui-react-examples" ;;
blazor)
SUBS="common/igniteui-xplat-docs blazor/igniteui-blazor-examples" ;;
webcomponents)
SUBS="common/igniteui-xplat-docs webcomponents/igniteui-wc-examples" ;;
*)
echo "::error::Unknown framework '$FW'"; exit 1 ;;
esac
for sub in $SUBS; do
echo "--- $sub ---"
git submodule update --init "$BASE/$sub"
done

# Node 24, not 22: rewrite-api-links.ts uses URLPattern, which only became a
# global in Node 24. On 22 it fails with "URLPattern is not defined".
- uses: actions/setup-node@v6
with:
node-version: 22.x
node-version: 24.x
cache: yarn

# The cross-platform gulp build restores the docfx dotnet tool.
- uses: actions/setup-dotnet@v4
- uses: actions/setup-dotnet@v6
if: inputs.framework != 'angular'
with:
dotnet-version: 8.x
Expand Down Expand Up @@ -99,6 +127,7 @@ runs:
if [ "$MODE" = "full" ]; then
npm run "compress:$FW" -- --batch submit
npm run "compress:$FW" -- --batch poll
npm run "derive-components:$FW"
npx tsx scripts/update-baseline.ts --framework "$FW" --full
else
npm run "diff:$FW"
Expand All @@ -110,38 +139,66 @@ runs:
if [ "$CHANGED" -gt 0 ]; then
npm run "compress:$FW" -- --batch submit --manifest dist/diff-manifest.json
npm run "compress:$FW" -- --batch poll
# Only the freshly compressed docs need it; restored ones already carry
# derived values from the committed DB.
npm run "derive-components:$FW"
fi
npm run "update-baseline:$FW"
fi

- name: Report compression stats
# Compression must yield one document per input. A batch entry that fails is
# simply absent from docs_final — the pipeline continues, the DB is published
# short, and document-count floors are far too loose to notice one missing file.
# This is not hypothetical: a full run reported "375 succeeded, 1 failed" and
# silently dropped hierarchicalgrid-editing.md.
IN=$(find "dist/docs_prepeared/$FW" -name '*.md' -not -name '_*' | wc -l)
OUT=$(find "dist/docs_final/$FW" -name '*.md' -not -name '_*' | wc -l)
if [ "$OUT" -lt "$IN" ]; then
echo "::warning::$FW: $OUT of $IN documents present after compression — retrying failed batch entries"
# `--batch retry` only submits a new batch; polling downloads its results.
# batchPoll reads state.retry_batch_id, so it follows the retry batch.
if npm run "compress:$FW" -- --batch retry; then
npm run "compress:$FW" -- --batch poll || true
npm run "derive-components:$FW" || true
fi
OUT=$(find "dist/docs_final/$FW" -name '*.md' -not -name '_*' | wc -l)
fi
if [ "$OUT" -lt "$IN" ]; then
echo "::error::$FW: compression produced only $OUT of $IN documents. Refusing to publish an incomplete set."
comm -23 \
<(find "dist/docs_prepeared/$FW" -name '*.md' -not -name '_*' -printf '%f\n' | sort) \
<(find "dist/docs_final/$FW" -name '*.md' -not -name '_*' -printf '%f\n' | sort)
exit 1
fi
echo "$FW: $OUT of $IN documents compressed"

# Always runs, so a framework that skipped compression still reports why.
- name: Report build summary
if: always()
shell: bash
working-directory: packages/igniteui-mcp/igniteui-doc-mcp
run: |
STATS="dist/docs_final/${{ inputs.framework }}/_compression_stats.json"
COUNT=$(find "dist/docs_final/${{ inputs.framework }}" -name '*.md' -not -name '_*' | wc -l)
echo "### ${{ inputs.framework }}: $COUNT documents" >> "$GITHUB_STEP_SUMMARY"
if [ -f "$STATS" ]; then
node -e "const s=require('./$STATS');console.log('- model: '+s.model+'\n- tokens: '+(s.total_tokens||0))" >> "$GITHUB_STEP_SUMMARY"
fi
npx tsx scripts/report-build-summary.ts \
--framework "${{ inputs.framework }}" \
--mode "${{ inputs.mode }}" >> "$GITHUB_STEP_SUMMARY"

- name: Upload compressed docs
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: docs-final-${{ inputs.framework }}
path: packages/igniteui-mcp/igniteui-doc-mcp/dist/docs_final/${{ inputs.framework }}
retention-days: 5

# build-db reads _tocName from here. Without it every row's toc_name would be NULL.
- name: Upload prepared docs
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: docs-prepeared-${{ inputs.framework }}
path: packages/igniteui-mcp/igniteui-doc-mcp/dist/docs_prepeared/${{ inputs.framework }}
retention-days: 5

- name: Upload updated baseline
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: docs-baseline-${{ inputs.framework }}
path: packages/igniteui-mcp/igniteui-doc-mcp/docs_baseline/${{ inputs.framework }}
Expand Down
42 changes: 28 additions & 14 deletions .github/workflows/build-docs-db.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:

react:
needs: angular
if: always() && !cancelled() && !contains(needs.*.result, 'failure') && contains(inputs.frameworks, 'react')
if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && contains(inputs.frameworks, 'react')
runs-on: ubuntu-latest
timeout-minutes: 330
steps:
Expand All @@ -61,7 +61,7 @@ jobs:

blazor:
needs: react
if: always() && !cancelled() && !contains(needs.*.result, 'failure') && contains(inputs.frameworks, 'blazor')
if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && contains(inputs.frameworks, 'blazor')
runs-on: ubuntu-latest
timeout-minutes: 330
steps:
Expand All @@ -76,7 +76,7 @@ jobs:

webcomponents:
needs: blazor
if: always() && !cancelled() && !contains(needs.*.result, 'failure') && contains(inputs.frameworks, 'webcomponents')
if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && contains(inputs.frameworks, 'webcomponents')
runs-on: ubuntu-latest
timeout-minutes: 330
steps:
Expand All @@ -95,26 +95,26 @@ jobs:
# angular-only database.
assemble:
needs: [angular, react, blazor, webcomponents]
if: always() && !cancelled() && !contains(needs.*.result, 'failure')
if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 22.x
node-version: 24.x
cache: yarn
- name: Install packages
run: yarn --frozen-lockfile

- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
pattern: docs-final-*
path: packages/igniteui-mcp/igniteui-doc-mcp/dist/docs_final
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
pattern: docs-prepeared-*
path: packages/igniteui-mcp/igniteui-doc-mcp/dist/docs_prepeared
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
pattern: docs-baseline-*
path: packages/igniteui-mcp/igniteui-doc-mcp/docs_baseline
Expand All @@ -139,13 +139,24 @@ jobs:
# minimal docs_prepeared entries build-db needs to populate toc_name.
- name: Restore frameworks not rebuilt in this run
working-directory: packages/igniteui-mcp/igniteui-doc-mcp
env:
REQUESTED: ${{ inputs.frameworks }}
run: |
set -euo pipefail
for fw in angular react blazor webcomponents; do
if [ ! -d "dist/docs_final/$fw" ] || [ -z "$(ls -A dist/docs_final/$fw 2>/dev/null)" ]; then
echo "$fw was not rebuilt — restoring from the committed DB"
npx tsx scripts/restore-docs-final.ts --framework "$fw" --toc-stubs
if [ -d "dist/docs_final/$fw" ] && [ -n "$(ls -A "dist/docs_final/$fw" 2>/dev/null)" ]; then
continue
fi
# A framework that was rebuilt but has no docs here means its artifact did
# not arrive. Restoring from the DB would silently publish stale docs for it
# with counts that look perfectly healthy, so fail instead.
if echo "$REQUESTED" | grep -qw "$fw"; then
echo "::error::$fw was part of this run but its artifact is missing — refusing to build a database from stale $fw documents."
ls -R dist/docs_final || true
exit 1
fi
echo "$fw was not part of this run — restoring from the committed DB"
npx tsx scripts/restore-docs-final.ts --framework "$fw" --toc-stubs
done

- name: Build database
Expand All @@ -157,7 +168,7 @@ jobs:
npx tsc spec/unit/docs-db-counts-spec.ts --target es6 --module commonjs --esModuleInterop --skipLibCheck
npx jasmine spec/unit/docs-db-counts-spec.js

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v7
with:
name: igniteui-docs-db
path: |
Expand All @@ -169,14 +180,17 @@ jobs:
# pushed to a protected branch and nothing auto-merges.
publish:
needs: assemble
if: success()
# Not `success()`: at job level that evaluates the whole ancestor chain, so a run
# scoped to a subset of frameworks (leaving the others skipped) would make it false
# and silently skip publishing. Check the direct dependency's result instead.
if: always() && !cancelled() && needs.assemble.result == 'success'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v6
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
name: igniteui-docs-db
path: artifact
Expand Down
28 changes: 27 additions & 1 deletion packages/igniteui-mcp/igniteui-doc-mcp/docs/knowledgebase.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Documentation Processing Knowledgebase

Lessons learned and issues encountered while building the documentation processing pipelines. Entries 1-16 are from the Angular pipeline; entries 17-22 from React and MCP server; entries 23-28 from WebComponents and cross-platform improvements; entries 29-33 from Blazor, cross-platform architecture, and prompt improvements.
Lessons learned and issues encountered while building the documentation processing pipelines. Entries 1-16 are from the Angular pipeline; entries 17-22 from React and MCP server; entries 23-28 from WebComponents and cross-platform improvements; entries 29-34 from Blazor, cross-platform architecture, and prompt improvements.

## 1. LLM Compression: Wrong Component Prefix (Hallucination)

Expand Down Expand Up @@ -371,6 +371,32 @@ This is the opposite logic from what you might expect — `exclude` means "hide

**Rule:** This three-pronged approach is needed because the LLM's merge behavior is a chain: it first decides sections are "redundant" → merges headers → then drops examples from the merged section. Blocking any single step isn't enough — all three rules must reinforce each other.

## 34. LLM Compression: `component` Frontmatter Drifts and Names Sample-App Classes

**Problem:** The `component` field was decided entirely by the compression model, and it is not stable across runs. Two full rebuilds with the same model (`gpt-5.6-luna`) over effectively unchanged sources produced **1231 of 1232 documents with changed content and 374 with a changed `component` field** — 144 listing fewer components, 129 more, 64 genuinely different names, 37 merely reordered.

The worst case was `angular/angular-reactive-form-validation.md`:

```
before: IgxSelectComponent, IgxInputDirective, IgxComboComponent, IgxDatePickerComponent, …
after: DateValueValidatorDirective, DateValueAsyncValidatorDirective, ReactiveFormsSampleComponent, MyComponent
```

The model listed the **sample application's own classes** while the document body still documented `IgxSelectComponent`, `IgxInputDirective` and a dozen more. The prompt invited this by asking for "the exact class name(s) **as found in the document's source code**" — which those demo classes literally are.

**Impact:** `component` drives `list_components` and component-filtered `search_docs`. A document indexed under `MyComponent` is effectively unreachable. Dropped entries (`cli-component-templates.md` went 28 → 6) shrink discoverability, and pure reordering churns the committed DB binary for no benefit.

**Fix:** Two layers, because neither is sufficient alone.

1. **Prompt** (all four compress scripts): every name must carry the platform prefix; never list classes the sample application defines for itself; list every component the doc covers rather than a subset; order by first appearance with the primary subject first.
2. **Deterministic post-pass** — `scripts/derive-components.ts`, wired into every `pipeline:*` after compression. It keeps a supplied name when it carries an Ignite UI prefix, or the API index knows it, or a heading names it; otherwise it drops it. It then puts the filename-derived primary first and adds indexed components named in headings.

**Rule:** Prefer the **prefix** over API-index membership when deciding whether a name is real. The index built from `llms-full.txt` is incomplete — it lacks the data-visualisation components (`IgxCategoryChartComponent`), so filtering on index membership alone silently deletes valid entries. Equally, do not require the platform's own prefix exclusively: Angular docs legitimately reference `Igc*` Web Components wrappers (`IgcDockManagerComponent`, `IgcRatingComponent`, `IgcTileManagerComponent`), and the Excel library documents unprefixed classes (`Workbook`, `WorksheetChart`) that only the heading check preserves.

**Rule:** Never let the derivation *substitute* when it has no positive evidence — falling back to "every component mentioned in the body" buries the subject under components used incidentally by demo code (`badge.md` became `IgxAvatarComponent, IgxBadgeComponent, IgxIconService, IgxListComponent…`). The one exception is when *every* supplied name was rejected, which means the model returned nothing usable.

**Rule:** A `full` rebuild rewrites essentially the whole corpus even when nothing upstream changed. Prefer `incremental`, which only recompresses genuinely changed documents and therefore cannot churn metadata wholesale.

## Related Documentation

| Document | Description | Status |
Expand Down
Loading