Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 6 additions & 4 deletions .github/workflows/publish-gallery-proposal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,17 @@ jobs:
cat > "$issue_body" <<EOF
## Validated gallery proposal

The automated gallery audit produced the following validated catalog proposal.
The automated gallery audit produced the following validated catalog proposal. This workflow does not create a branch or pull request.

- [Inspect the source workflow run]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$SOURCE_RUN_ID)

$(cat "$SUMMARY_PATH")

## Copilot task
## Operator handoff

Treat the edited issue body as the complete source of truth. Implement exactly the additions, URL updates, retirements, and notes that remain in this issue; ignore deleted recommendations. Modify the catalog files, run \`npm run test:gallery-audit\` and \`npm run build\`, and open a draft pull request to \`main\`. Do not add unrelated changes, approve, merge, or enable automerge.
Edit the proposals above before assigning this issue to an implementation agent. Delete unwanted proposals, correct retained card fields and destinations, and add any implementation notes.

Treat the edited issue body as the complete source of truth. The assigned agent must implement exactly the additions, URL updates, retirements, and notes that remain; ignore deleted recommendations. Modify \`static/templates.json\` and \`static/retired-templates.json\` as directed, run \`npm run test:gallery-audit\` and \`npm run build\`, and open a draft pull request to \`main\`. Do not add unrelated changes, approve, merge, or enable automerge.
EOF
issue_url=$(gh issue create --title "Gallery content proposal $SOURCE_RUN_ID" --body-file "$issue_body")
issue_url=$(gh issue create --repo "$GITHUB_REPOSITORY" --title "Gallery content proposal $SOURCE_RUN_ID" --body-file "$issue_body")
printf 'Proposal issue created: %s\n' "$issue_url" >> "$GITHUB_STEP_SUMMARY"
2 changes: 1 addition & 1 deletion Contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Catalog changes may be submitted manually or proposed by the automated maintenan
- avoid duplicate resources after URL normalization; and
- include strong evidence for removals or retirements.

Generated maintenance pull requests are always drafts. Their bodies label additions as `A<n>`, URL updates as `U<n>`, retirements as `R<n>`, and skipped items as `S<n>`. An authorized maintainer can comment `Reject: A1, U1, R1` to remove those changes from the current proposal. A maintainer must review the complete catalog diff and merge it through the normal protected-branch process before publication.
Automated maintenance creates an unassigned proposal issue, not a pull request. The issue labels additions as `A<n>`, URL updates as `U<n>`, retirements as `R<n>`, and skipped items as `S<n>`. Each addition includes the complete proposed card fields and destination, and each retirement identifies the card and gives the removal reason and evidence. An authorized maintainer edits or deletes proposals in the issue, then assigns the issue to an implementation agent. The agent creates a draft pull request, which a maintainer must review and merge through the normal protected-branch process before publication.

See [Automated gallery maintenance](./docs/automated-gallery-maintenance.md) and [Content source policy](./docs/content-source-policy.md) for the operating and source-approval requirements.

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ The repository deploys through `.github/workflows/deploy.yml` after reviewed cha

## Automated Maintenance

The weekly maintenance workflow audits existing entries, discovers catalog-aligned content from approved sources, and opens one draft pull request for human review. Its Copilot curator uses the Azure Cosmos DB Agent Kit and a repository humanizer skill. A new proposal requests review from `jagord_microsoft`; GitHub notification routing sends that request to `jagord@microsoft.com`. The workflow never merges or publishes catalog changes automatically.
The weekly maintenance workflow audits existing entries, discovers catalog-aligned content from approved sources, and opens an unassigned proposal issue. An operator edits the proposed additions, URL updates, and retirements, then assigns the issue to an implementation agent. Only that assigned agent creates a draft pull request for human review. The curator uses the Azure Cosmos DB Agent Kit and a repository humanizer skill. The workflow never creates a pull request, merges changes, or publishes catalog changes automatically.

See [Automated gallery maintenance](./docs/automated-gallery-maintenance.md) for setup and operations, and [Content source policy](./docs/content-source-policy.md) for source eligibility.
9 changes: 6 additions & 3 deletions scripts/gallery-audit/promotion.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,21 @@ export function proposalItem(id, action, entry, previousUrl = null, suffix = '')

export function proposalCardDetails(entry) {
return [
' - Catalog change: Add this card to `static/templates.json`.',
` - Description: ${markdownText(entry.description, '**MISSING**')}`,
` - Preview: ${markdownText(entry.preview, '**MISSING**')}`,
` - Website: ${markdownText(entry.website, '**MISSING**')}`,
` - Author: ${markdownText(Array.isArray(entry.author) ? entry.author.join(', ') : entry.author, '**MISSING**')}`,
` - Source: ${markdownText(entry.source, '**MISSING**')}`,
` - Date: ${markdownText(entry.date, '**MISSING**')}`,
` - Tags: ${markdownText(entry.tags?.length ? entry.tags.join(', ') : null, '**MISSING**')}`,
` - Website: ${markdownText(entry.website, '**MISSING**')}`,
` - Preview: ${markdownText(entry.preview, '**MISSING**')}`,
` - Source: ${markdownText(entry.source, '**MISSING**')}`,
];
}

export function retirementProof(entry) {
const evidence = entry.retirementEvidence ?? {};
return [
' - Catalog change: Remove this card from `static/templates.json` and append its retirement record to `static/retired-templates.json`.',
` - Reason: ${markdownText(entry.retirementReason)}`,
` - Audit outcome: ${markdownText(evidence.auditOutcome)}`,
` - HTTP status: ${markdownText(evidence.httpStatus)}`,
Expand Down Expand Up @@ -191,6 +193,7 @@ export function promotionMarkdown(result, generatedAt) {
'', `URL updates: ${updates.length}`, '',
...updates.flatMap((entry, index) => [
...proposalItem(`U${index + 1}`, 'Update', { title: entry.title, source: entry.url }, entry.previousUrl),
' - Catalog change: Replace this card\'s source URL in `static/templates.json`.',
` - Reason: ${markdownText(entry.recommendationReason)}`,
` - Criteria: ${markdownText(entry.recommendationCriteria?.join(', '))}`,
]),
Expand Down
3 changes: 3 additions & 0 deletions scripts/gallery-audit/test/audit.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -812,11 +812,14 @@ test('numbers every proposal issue item for unambiguous issue editing', () => {
skippedAdditions: [{ url: 'https://example.com/skip', reason: 'already-cataloged' }],
}, '2026-09-18T00:00:00.000Z');
assert.match(markdown, /\*\*A1\*\* Add \[First addition\]/);
assert.match(markdown, /Catalog change: Add this card to `static\/templates\.json`/);
assert.match(markdown, /Reason: Relevant example\./);
assert.match(markdown, /Criteria: specific/);
assert.match(markdown, /\*\*U1\*\* Update \[Moved\]/);
assert.match(markdown, /Catalog change: Replace this card's source URL in `static\/templates\.json`/);
assert.match(markdown, /Reason: Canonical redirect\./);
assert.match(markdown, /\*\*R1\*\* Retire \[First retirement\]/);
assert.match(markdown, /Catalog change: Remove this card from `static\/templates\.json` and append its retirement record to `static\/retired-templates\.json`/);
assert.match(markdown, /Reason: Superseded\./);
assert.match(markdown, /\*\*S1\*\* https:\/\/example\.com\/skip/);
assert.match(markdown, /Edit this issue before assigning it to Copilot/);
Expand Down
4 changes: 3 additions & 1 deletion scripts/gallery-audit/test/configuration.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ test('keeps audit read-only and publishes only an issue through trusted workflow
assert.match(publisher, /workflow_run\.event != 'pull_request'/);
assert.match(publisher, /workflow_run\.head_branch == github\.event\.repository\.default_branch/);
assert.match(publisher, /permissions:\s*\n\s*actions: read\s*\n\s*issues: write/);
assert.match(publisher, /gh issue create --title "Gallery content proposal \$SOURCE_RUN_ID"/);
assert.match(publisher, /gh issue create --repo "\$GITHUB_REPOSITORY" --title "Gallery content proposal \$SOURCE_RUN_ID"/);
assert.match(publisher, /This workflow does not create a branch or pull request/);
assert.match(publisher, /Edit the proposals above before assigning this issue to an implementation agent/);
assert.match(publisher, /Treat the edited issue body as the complete source of truth/);
assert.doesNotMatch(publisher, /contents: write|actions\/checkout|git push|gh pr create|gh pr edit|automation\/gallery-content-updates|GALLERY_UPDATE_TOKEN/);
});
Expand Down
Loading