Skip to content

feat(collections): auto-generate collection.md with maturity filtering#1316

Open
WilliamBerryiii wants to merge 3 commits intomainfrom
feat/collection-md-autogen-maturity-1256
Open

feat(collections): auto-generate collection.md with maturity filtering#1316
WilliamBerryiii wants to merge 3 commits intomainfrom
feat/collection-md-autogen-maturity-1256

Conversation

@WilliamBerryiii
Copy link
Copy Markdown
Member

Description

Added auto-generation of collection.md artifact tables from YAML manifests with channel-based maturity filtering and sentinel marker support. The generation engine in Prepare-Extension.ps1 gained a -Channel parameter that controls which maturity levels appear in output, and New-CollectionReadme was rewritten to parse marker-delimited sections, filter items by maturity, and write back updated content preserving hand-written introductions and footers. Collection validation now detects mismatched or reversed markers as warnings for backward compatibility. The encoding for Set-ContentIfChanged was corrected to UTF-8 without BOM. Nineteen new Pester tests cover maturity filtering, template marker handling, and Split-CollectionMdByMarkers edge cases.

Related Issue(s)

Closes #1256

Type of Change

Select all that apply:

Code & Documentation:

  • Bug fix (non-breaking change fixing an issue)
  • New feature (non-breaking change adding functionality)
  • Breaking change (fix or feature causing existing functionality to change)
  • Documentation update

Infrastructure & Configuration:

  • GitHub Actions workflow
  • Linting configuration (markdown, PowerShell, etc.)
  • Security configuration
  • DevContainer configuration
  • Dependency update

AI Artifacts:

  • Reviewed contribution with prompt-builder agent and addressed all feedback
  • Copilot instructions (.github/instructions/*.instructions.md)
  • Copilot prompt (.github/prompts/*.prompt.md)
  • Copilot agent (.github/agents/*.agent.md)
  • Copilot skill (.github/skills/*/SKILL.md)

Other:

  • Script/automation (.ps1, .sh, .py)
  • Other (please describe):

Sample Prompts (for AI Artifact Contributions)

Testing

Automated testing:

  • Pester test suite: 2002 tests executed — 1935 passed, 0 failed, 2 skipped (7m40s)
  • New test coverage: 19 new tests in Prepare-Extension.Tests.ps1 (+233 lines) and Validate-Collections.Tests.ps1 (+157 lines)
    • Maturity filtering: excludes experimental when stable-only, includes experimental when allowed, excludes deprecated regardless
    • Template markers: preserves intro and replaces marker section, writes back updated artifacts, handles files without markers, preserves footer content
    • Split-CollectionMdByMarkers: no markers, empty string throws, correct intro/footer parsing, partial markers, reversed markers, duplicate markers
  • Lint validation: 100% compliance across markdown, PowerShell, YAML, frontmatter, links, spelling, and skill structure checks
  • Plugin generation: All 14 plugins regenerated successfully — 116 agents, 143 commands, 245 instructions, 30 skills, 0 lint errors

Manual testing: Not performed.

Checklist

Required Checks

  • Documentation is updated (if applicable)
  • Files follow existing naming conventions
  • Changes are backwards compatible (if applicable)
  • Tests added for new functionality (if applicable)

AI Artifact Contributions

Required Automated Checks

The following validation commands must pass before merging:

  • Markdown linting: npm run lint:md
  • Spell checking: npm run spell-check
  • Frontmatter validation: npm run lint:frontmatter
  • Skill structure validation: npm run validate:skills
  • Link validation: npm run lint:md-links
  • PowerShell analysis: npm run lint:ps

Bill Berry added 3 commits April 6, 2026 21:12
…kers

- thread channel and maturity parameters through extension readme generation
- add template markers to collection.md files with backward-compatible processing
- add marker validation rules to collection validation pipeline
- add unit tests for maturity filtering, marker handling, and marker validation

🔧 - Generated by Copilot
- expand New-CollectionReadme help with in-place update and marker behavior (IV-001)
- add ValidateNotNullOrEmpty to AllowedMaturities parameter (IV-002)
- centralize marker strings as script-scope constants in both scripts (IV-003)
- remove unused Existing key from Split-CollectionMdByMarkers return (IV-004)
- add duplicate-BEGIN-marker edge case test (IV-005)
- add empty-string-throws test for Split-CollectionMdByMarkers (IV-006)
- standardize Set-ContentIfChanged encoding to utf8NoBOM (IV-007)

🔧 - Generated by Copilot
@WilliamBerryiii WilliamBerryiii requested a review from a team as a code owner April 7, 2026 23:23
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.69%. Comparing base (a1928f3) to head (be32616).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1316      +/-   ##
==========================================
+ Coverage   87.63%   87.69%   +0.05%     
==========================================
  Files          61       61              
  Lines        9328     9376      +48     
==========================================
+ Hits         8175     8222      +47     
- Misses       1153     1154       +1     
Flag Coverage Δ
pester 85.30% <100.00%> (+0.09%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
scripts/collections/Modules/CollectionHelpers.psm1 99.31% <100.00%> (ø)
scripts/collections/Validate-Collections.ps1 93.25% <100.00%> (+0.53%) ⬆️
scripts/extension/Prepare-Extension.ps1 94.74% <100.00%> (+0.29%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Advisory review — this PR is from a maintainer. Findings are informational only.

Review Summary

The implementation is well-structured and the PR meets quality standards. The description is clear, linked issue is present, type-of-change selections are accurate, and the 19 new Pester tests provide solid coverage of the new behavior. The utf8utf8NoBOM encoding fix in Set-ContentIfChanged is a correct bug fix. The maturity-filtering design is clean and consistent with existing channel logic.

Two advisory observations are raised below.


Issue Alignment ✅

Closes #1256. The PR delivers auto-generation of collection.md artifact tables with channel-based maturity filtering and sentinel-marker support, which matches the stated intent. The write-back behavior, backward-compatible marker validation, and plugin regeneration are all coherent.


PR Template Compliance ✅

Section Status
Description ✅ Complete and detailed
Related Issue Closes #1256
Type of Change ✅ New feature + Script/automation — both correct
Testing ✅ Comprehensive (2002 tests, 0 failures, 19 new tests, lint & plugin generation)
Checklist ✅ All required items checked

Coding Standards

💡 Split-CollectionMdByMarkers — incomplete function decoration (advisory)

Every other function in Prepare-Extension.ps1 uses [CmdletBinding()], [OutputType()], and a full comment block (.DESCRIPTION, .PARAMETER, .OUTPUTS, .EXAMPLE). The PSScriptAnalyzer configuration explicitly sets PSProvideCommentHelp.ExportedOnly = $false, so the convention applies to private functions too. The inline comment above includes a suggested complete signature.

💡 Duplicate marker constants — DRY concern (advisory)

$script:CollectionMdBeginMarker and $script:CollectionMdEndMarker are now defined identically in both Prepare-Extension.ps1 and Validate-Collections.ps1. Since CollectionHelpers.psm1 is imported by both, exporting the constants from there would eliminate the duplication and make any future string change a single-point edit.


Code Quality ✅

No bugs, security issues, or logic errors found. The Split-CollectionMdByMarkers parsing logic handles all the edge cases covered by tests (no markers, partial markers, reversed markers, footer content). The write-back side-effect in New-CollectionReadme is clearly documented in the function's comment block. Test coverage is thorough.

Note

🔒 Integrity filter blocked 1 item

The following item were blocked because they don't meet the GitHub integrity level.

  • #1256 issue_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

Generated by PR Review for issue #1316

Comment on lines +27 to +29
$script:CollectionMdBeginMarker = '<!-- BEGIN AUTO-GENERATED ARTIFACTS -->'
$script:CollectionMdEndMarker = '<!-- END AUTO-GENERATED ARTIFACTS -->'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maintainability: Marker constants are duplicated between Validate-Collections.ps1 and Prepare-Extension.ps1

The same two string literals are now defined independently in two scripts (see Prepare-Extension.ps1 lines 68–69). If either marker string ever changes, both files must be updated in sync — a silent drift risk.

CollectionHelpers.psm1 is already imported by both scripts and is the natural home for shared collection constants. Consider exporting them from there, for example as module-level variables:

# In CollectionHelpers.psm1
$script:CollectionMdBeginMarker = '<!-- BEGIN AUTO-GENERATED ARTIFACTS -->'
$script:CollectionMdEndMarker   = '<!-- END AUTO-GENERATED ARTIFACTS -->'

Export-ModuleMember -Variable CollectionMdBeginMarker, CollectionMdEndMarker

Then both calling scripts consume $CollectionMdBeginMarker directly after Import-Module, eliminating the duplication.

Comment on lines +382 to +390
function Split-CollectionMdByMarkers {
<#
.SYNOPSIS
Splits collection.md content at auto-generation markers.
#>
param(
[Parameter(Mandatory)]
[string]$Content
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Convention: Split-CollectionMdByMarkers is missing [CmdletBinding()], [OutputType()], and full comment-based help

Every other function in this file uses [CmdletBinding()] + [OutputType()] and provides a complete comment block (.SYNOPSIS, .DESCRIPTION, .PARAMETER, .OUTPUTS, .EXAMPLE). The PSScriptAnalyzer config sets PSProvideCommentHelp.ExportedOnly = $false, so the convention applies here too.

Suggested completion:

function Split-CollectionMdByMarkers {
    <#
    .SYNOPSIS
        Splits collection.md content at auto-generation markers.
    .DESCRIPTION
        Parses the raw file content and returns a hashtable with Intro, Footer,
        and HasMarkers fields. When the BEGIN/END markers are absent or appear in
        the wrong order, HasMarkers is $false and Intro contains the original content.
    .PARAMETER Content
        Raw string content of the collection.md file. Must not be empty.
    .OUTPUTS
        [hashtable] With keys HasMarkers (bool), Intro (string), Footer (string).
    .EXAMPLE
        $parts = Split-CollectionMdByMarkers -Content (Get-Content 'ado.collection.md' -Raw)
        if ($parts.HasMarkers) { Write-Host $parts.Intro }
    #>
    [CmdletBinding()]
    [OutputType([hashtable])]
    param(
        [Parameter(Mandatory)]
        [string]$Content
    )
    ...

Refs: PSScriptAnalyzer.psd1 lines 43–48, Set-ContentIfChanged as a comparable complete function.

Copy link
Copy Markdown
Contributor

@katriendg katriendg left a comment

Choose a reason for hiding this comment

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

Left a few comments, I believe ensuring the plugin:generate takes care of generating the <collectionid>.collection.md file is a good approach, and we could have the Extension prepare regenerate what is needed based on maturity filtering.

Plugins always take everything without maturity filtering, so that in itself so the more I reflect on this, the more I think we need a generation for the plugins (the default), and regenerate one for stable/pre-release when packaging the extension.

Also this will impact the following documentation addition:

🟡 Comment 2 — Documentation gap: contributor guide has no mention of the two-zone collection.md convention

  • File: docs/contributing/ai-artifacts-common.md and .github/copilot-instructions.md
  • Category: Documentation
  • Severity: Should fix before merge

docs/contributing/ai-artifacts-common.md (the primary contributor guide for AI artifacts) extensivelydocuments *.collection.yml structure, maturity values, and plugin generation — but has no mention of:

  1. The <!-- BEGIN AUTO-GENERATED ARTIFACTS --> / <!-- END AUTO-GENERATED ARTIFACTS --> marker format.
  2. That collection.md has two zones (hand-authored intro vs auto-generated table) and contributors must only edit the intro.
  3. What command to run to refresh the artifact table after editing a YAML.
  4. How to seed a new collection with the correct marker structure.

.github/copilot-instructions.md line 104 says "Only edit the intro zone; the artifact table is overwritten by plugin:generate" — this was added during the review session but the underlying plugin:generate wiring gap (Comment 2 above) means this statement is currently inaccurate.

A ### Collection Markdown Auto-Generation subsection should be added to docs/contributing/ai-artifacts-common.md explaining the marker format, the two-zone rule, and the correct command to sync the artifact table. This section should be updated once Comment 2 is resolved so the documented command is accurate.

$null = $artifactSections.AppendLine()
}

# Write back updated artifact section into collection.md when markers are present
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Comment 1 — Critical Gap: collection.md writeback is only wired to extension:prepare, not plugin:generate

The sentinel-marker writeback that updates the artifact table in collection.md runs inside New-CollectionReadme, which is called only from Invoke-ExtensionCollectionsGeneration — and that function is only invoked by npm run extension:prepare (and :prerelease).

npm run plugin:generate calls Generate-Plugins.ps1, which never calls Prepare-Extension.ps1 and never writes back to collection.md.

This means:

  • A contributor who adds an item to ado.collection.yml and runs npm run plugin:generate (as the docs instruct) will see the plugins/ado/README.md update correctly, but collections/ado.collection.md will remain stale — the artifact table between the markers will not reflect the new item.
  • The collection.md only refreshes when someone runs extension:prepare, which contributors won't do unless they're on the extension release path.
  • .github/copilot-instructions.md line 107 currently states: "run npm run plugin:generate to regenerate plugin outputs under plugins/ and update the artifact sections in collection.md files." This is incorrect — plugin:generate does not update collection.md.

Recommended fix: Move the collection.md writeback into Generate-Plugins.ps1 (or a shared helper), so that plugin:generate is the single command that keeps both plugins/*/README.md and collections/*.collection.md in sync. Alternatively, add a dedicated npm run collection:sync script and update all contributor-facing docs to reference it as a required step after editing a collection YAML.

Import-Module (Join-Path $PSScriptRoot "../collections/Modules/CollectionHelpers.psm1") -Force

# Auto-generation marker constants shared across Split-CollectionMdByMarkers and New-CollectionReadme
$script:CollectionMdBeginMarker = '<!-- BEGIN AUTO-GENERATED ARTIFACTS -->'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Comment 3 — Suggestion: marker string literals duplicated across two files

  • File: scripts/extension/Prepare-Extension.ps1 (lines 68-69) and scripts/plugins/Modules/PluginHelpers.psm1
  • Category: Maintainability
  • Severity: Suggestion

Once Comment 1 is fixed, the marker strings <!-- BEGIN AUTO-GENERATED ARTIFACTS --> and <!-- END AUTO-GENERATED ARTIFACTS --> will be defined in two separate files. If the strings ever change, both must be updated in sync — a silent breakage risk.

Both files already import CollectionHelpers.psm1. Exporting the constants from that module (or a new MarkerConstants.psm1) would give a single source of truth. At minimum, add a comment in each file cross-referencing the other.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

collection.md: auto-generation, maturity safety, and custom header support

3 participants