-
Notifications
You must be signed in to change notification settings - Fork 32
fix: drift check now detects new releases and auto-updates skill #763
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -127,8 +127,8 @@ function Get-GitHubLatestRelease { | |
| } | ||
| } | ||
| $release = $json | ConvertFrom-Json | ||
| # Truncate release notes to first 2000 chars to keep report manageable | ||
| $body = if ($release.body.Length -gt 2000) { $release.body.Substring(0, 2000) + '...' } else { $release.body } | ||
| # Truncate release notes to first 5000 chars | ||
| $body = if ($release.body.Length -gt 5000) { $release.body.Substring(0, 5000) + '...' } else { $release.body } | ||
| return @{ | ||
| status = 'ok' | ||
| repo = $Repo | ||
|
|
@@ -149,6 +149,50 @@ function Get-GitHubLatestRelease { | |
| } | ||
| } | ||
|
|
||
| function Get-GitHubReleasesSince { | ||
| param([string]$Repo, [string]$SinceTag) | ||
|
|
||
| try { | ||
| # Fetch recent releases (up to 10) | ||
| $json = gh api "repos/$Repo/releases?per_page=10" --jq '[.[] | {tag_name: .tag_name, name: .name, published_at: .published_at, body: .body}]' 2>&1 | ||
| if ($LASTEXITCODE -ne 0) { | ||
| return @{ | ||
| status = 'error' | ||
| repo = $Repo | ||
| error = "Failed to fetch releases: $json" | ||
| } | ||
| } | ||
| if (-not $json -or $json -eq 'null' -or $json -eq '[]') { | ||
| return @{ status = 'ok'; repo = $Repo; releases = @() } | ||
| } | ||
| $allReleases = $json | ConvertFrom-Json | ||
| # Collect releases newer than $SinceTag | ||
| $newReleases = @() | ||
| foreach ($r in $allReleases) { | ||
|
Comment on lines
+157
to
+171
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 MODERATE — Silent history gap when >10 releases exist since watermark (Flagged by: 3/3 reviewers)
Failing scenario: Watermark at Suggested fix: Return a $foundWatermark = $false
foreach ($r in $allReleases) {
if ($r.tag_name -eq $SinceTag) { $foundWatermark = $true; break }
# ... existing logic
}
# Include in return:
# watermark_found = $foundWatermark |
||
| if ($r.tag_name -eq $SinceTag) { break } | ||
|
Comment on lines
+156
to
+172
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 MODERATE — Silent data loss when watermark tag is not in the top 10 releases
There's no signal to the caller that the watermark wasn't found, so this is a silent failure. Fix suggestion: Track whether the watermark was hit and surface it: $watermarkFound = $false
foreach ($r in $allReleases) {
if ($r.tag_name -eq $SinceTag) { $watermarkFound = $true; break }
# ... collect release ...
}
return @{
status = 'ok'
repo = $Repo
releases = $newReleases
watermark_found = $watermarkFound
}Then the caller (or the agent) can warn/paginate when |
||
| $body = if ($r.body.Length -gt 5000) { $r.body.Substring(0, 5000) + '...' } else { $r.body } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 MODERATE — Null GitHub releases with no body have The same pattern exists in Suggested fix: $body = if ($r.body -and $r.body.Length -gt 5000) { $r.body.Substring(0, 5000) + '...' } else { if ($r.body) { $r.body } else { '' } } |
||
| $newReleases += @{ | ||
| tag = $r.tag_name | ||
| name = $r.name | ||
| published_at = $r.published_at | ||
| release_notes = $body | ||
| } | ||
| } | ||
| return @{ | ||
| status = 'ok' | ||
| repo = $Repo | ||
| releases = $newReleases | ||
| } | ||
| } | ||
| catch { | ||
| return @{ | ||
| status = 'error' | ||
| repo = $Repo | ||
| error = $_.Exception.Message | ||
| } | ||
| } | ||
| } | ||
|
|
||
| function Get-IndexPageLinks { | ||
| <# | ||
| .SYNOPSIS | ||
|
|
@@ -372,6 +416,11 @@ function ConvertFrom-SyncManifest { | |
| $currentItem.resolution_expected = $Matches[1].Trim() -eq 'true' | ||
| } | ||
| } | ||
| elseif ($trimmed -match '^last_reviewed_release:\s*(.+)$') { | ||
| if ($currentItem) { | ||
| $currentItem.last_reviewed_release = $Matches[1].Trim() | ||
| } | ||
| } | ||
| elseif ($trimmed -match '^coverage_gaps:') { | ||
| if ($currentItem) { | ||
| $currentItem.coverage_gaps = @() | ||
|
|
@@ -533,21 +582,45 @@ foreach ($manifestPath in $manifests) { | |
| } | ||
| 'releases' { | ||
| Write-Host " 📦 Checking releases for $($source.repo)..." -NoNewline | ||
| $result = Get-GitHubLatestRelease -Repo $source.repo | ||
| if ($result.status -eq 'ok' -and $result.latest) { | ||
| Write-Host " ✅ latest=$($result.latest.tag)" -ForegroundColor Green | ||
| } | ||
| elseif ($result.status -eq 'ok') { | ||
| Write-Host " ℹ️ No releases found" -ForegroundColor Yellow | ||
| $sinceTag = if ($source.ContainsKey('last_reviewed_release')) { $source.last_reviewed_release } else { $null } | ||
| if ($sinceTag) { | ||
| $result = Get-GitHubReleasesSince -Repo $source.repo -SinceTag $sinceTag | ||
| if ($result.status -eq 'ok' -and $result.releases.Count -gt 0) { | ||
| Write-Host " 🆕 $($result.releases.Count) new release(s) since $sinceTag" -ForegroundColor Yellow | ||
| } | ||
| elseif ($result.status -eq 'ok') { | ||
| Write-Host " ✅ up to date (last reviewed: $sinceTag)" -ForegroundColor Green | ||
| } | ||
| else { | ||
| Write-Host " ❌ Error: $($result.error)" -ForegroundColor Red | ||
| } | ||
| } | ||
| else { | ||
| Write-Host " ❌ Error: $($result.error)" -ForegroundColor Red | ||
| # No last_reviewed_release — fall back to latest-only | ||
| $singleResult = Get-GitHubLatestRelease -Repo $source.repo | ||
| $result = @{ | ||
| status = $singleResult.status | ||
| repo = $source.repo | ||
| releases = if ($singleResult.status -eq 'ok' -and $singleResult.latest) { @($singleResult.latest) } else { @() } | ||
| error = $singleResult.error | ||
| } | ||
| if ($result.status -eq 'ok' -and $result.releases.Count -gt 0) { | ||
| Write-Host " ✅ latest=$($result.releases[0].tag) (no last_reviewed_release set — add to manifest)" -ForegroundColor Yellow | ||
| } | ||
| elseif ($result.status -eq 'ok') { | ||
| Write-Host " ℹ️ No releases found" -ForegroundColor Yellow | ||
| } | ||
| else { | ||
| Write-Host " ❌ Error: $($result.error)" -ForegroundColor Red | ||
| } | ||
| } | ||
| $sourceResults += @{ | ||
| type = 'releases' | ||
| repo = $source.repo | ||
| result = $result | ||
| $entry = @{ | ||
| type = 'releases' | ||
| repo = $source.repo | ||
| last_reviewed_release = $sinceTag | ||
| result = $result | ||
| } | ||
| $sourceResults += $entry | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -632,16 +705,20 @@ foreach ($manifestPath in $manifests) { | |
| # - closed issues where resolution_expected is true (instruction may reference outdated workarounds) | ||
| # - untracked pages discovered via index crawling | ||
| # - untracked recently closed issues | ||
| # - new releases since last_reviewed_release | ||
| $actionableChanges = $results | ForEach-Object { $_.sources } | Where-Object { | ||
| $_.result.status -eq 'error' -or | ||
| ($_.type -eq 'issue' -and $_.resolution_expected -and $_.result.status -eq 'ok' -and $_.result.state -eq 'closed') | ||
| } | ||
| $hasNewReleases = ($results | ForEach-Object { $_.sources } | Where-Object { | ||
| $_.type -eq 'releases' -and $_.result.status -eq 'ok' -and $_.result.releases.Count -gt 0 -and $_.last_reviewed_release | ||
|
Comment on lines
+713
to
+714
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟢 MINOR — Fallback releases (no watermark) never trigger The Failing scenario: A new Suggested fix: Include fallback sources in the check, or treat "missing watermark" as actionable: $hasMissingWatermark = ($results | ForEach-Object { $_.sources } | Where-Object {
$_.type -eq 'releases' -and -not $_.last_reviewed_release -and $_.result.releases.Count -gt 0
} | Measure-Object).Count -gt 0 |
||
| } | Measure-Object).Count -gt 0 | ||
| $hasUntrackedPages = ($results | Where-Object { $_.untracked_pages.Count -gt 0 } | Measure-Object).Count -gt 0 | ||
| $hasUntrackedIssues = ($results | Where-Object { $_.untracked_closed_issues.Count -gt 0 } | Measure-Object).Count -gt 0 | ||
| $report = @{ | ||
| checked_at = (Get-Date -Format 'o') | ||
| manifests = $results | ||
| changes_detected = (($actionableChanges | Measure-Object).Count -gt 0) -or $hasUntrackedPages -or $hasUntrackedIssues | ||
| changes_detected = (($actionableChanges | Measure-Object).Count -gt 0) -or $hasUntrackedPages -or $hasUntrackedIssues -or $hasNewReleases | ||
| } | ||
|
|
||
| Write-Host "`n📊 Report:" -ForegroundColor Cyan | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -121,8 +121,9 @@ Read the staleness report above. For each signal, read the affected skill files | |
| ### Rules | ||
| 1. **Respect `divergence` sections** — NEVER remove: "Security Boundaries", "Safe Pattern: Checkout + Restore", "Common Patterns" | ||
| 2. **Classify P0-P3:** P0=factually wrong, P1=security, P2=new features, P3=nice-to-have | ||
| 3. **Only auto-fix P0 and P1.** Note P2 in PR description. Skip P3. | ||
| 4. **Update sync manifest** — `resolution_expected`, `last_reviewed`, new issues | ||
| 3. **Auto-fix P0, P1, AND P2.** Only skip P3 (cosmetic/nice-to-have). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 MODERATE — P2 auto-fix now includes security-sensitive content without explicit human gate (Flagged by: 2/3 reviewers) Changing Rule 3 from "Note P2 in PR description" to "Auto-fix P0, P1, AND P2" means the agent can autonomously modify the security patterns table, anti-patterns table, and trigger guide based solely on its interpretation of release notes. Rule 4 explicitly instructs it to add to "security patterns as appropriate." The draft PR does provide a human review gate before merge, which mitigates the risk. However, the prior P2-as-note-only policy was a deliberate safeguard — if the agent misinterprets a release note, incorrect security guidance ends up in draft content that reviewers may rubber-stamp. Suggestion: Consider keeping P2-as-note for changes that touch |
||
| 4. **For new releases:** Read ALL release notes in the report. For each new feature, check if our skill covers it. Add new features to the anti-patterns table, trigger guide, or security patterns as appropriate. Update the `last_reviewed_release` field in `gh-aw-workflows.sync.yaml` to the latest tag after processing. | ||
| 5. **Update sync manifest** — `resolution_expected`, `last_reviewed_release`, new issues | ||
|
|
||
| Commit each change: | ||
| ```bash | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟢 MINOR — Pre-existing null-safety note (not a regression)
Both
Get-GitHubLatestRelease(here) and the newGet-GitHubReleasesSince(line 173) access$release.body.Length/$r.body.Lengthwithout a null guard. This works in PowerShell 7 ($null.Length→ 0, so the else branch returns$null— no crash), but would fail in PowerShell 5.Since the workflow invokes
pwsh(PowerShell 7), this is safe today. Just noting for awareness — if portability matters, a guard likeif ($release.body -and $release.body.Length -gt 5000)would be more robust.