Skip to content

fix: add directory support to consumer sync drift check#1445

Merged
stranske merged 2 commits intomainfrom
fix/drift-check-directory-support
Feb 10, 2026
Merged

fix: add directory support to consumer sync drift check#1445
stranske merged 2 commits intomainfrom
fix/drift-check-directory-support

Conversation

@stranske
Copy link
Copy Markdown
Owner

@stranske stranske commented Feb 10, 2026

Source: Issue #1435

Automated Status Summary

Scope

PR #1409 (issue #1407) merged with CONCERNS from both verify:compare providers (openai 78%, anthropic 85%). The core unresolved concern is that custom regex-based glob matching was supposed to be replaced with a standard minimatch library, but:

  1. bot-comment-dismiss.js still defines ~140 lines of custom glob functions instead of importing minimatch.
  2. The vendored minimatch at .github/scripts/node_modules/minimatch/ is a 0.0.0-local reimplementation, not the real npm package.
  3. Consumer repos will break on sync — template files pr-context-graphql.js and merge_manager.js both require('minimatch'), but the template directory has no node_modules/minimatch/ and the sync manifest does not include it.

Context for Agent

Related Issues/PRs

Context for Agent

Related Issues/PRs

Context for Agent

Related Issues/PRs

Tasks

Replace custom glob in bot-comment-dismiss.js

  • Remove the inline escapeRegExp, expandBraces, globToRegExp, and minimatch functions (lines 21–163) from .github/scripts/bot-comment-dismiss.js
  • Add const { minimatch } = require('minimatch'); import at the top of bot-comment-dismiss.js
  • Update shouldIgnorePath in bot-comment-dismiss.js to call minimatch(normalized, pattern, { dot: true, nocomment: true, nonegate: true }) — matching the options used in merge_manager.js
  • Apply the identical changes to templates/consumer-repo/.github/scripts/bot-comment-dismiss.js

Standardize the vendored minimatch module

  • Replace .github/scripts/node_modules/minimatch/package.json version from 0.0.0-local to 10.0.1-vendored (or current real minimatch major) and add a comment in the file explaining it is a vendored subset
  • Verify existing tests pass with the vendored module: node --test .github/scripts/__tests__/bot-comment-dismiss.test.js && node --test tests/test/dismiss/dismiss-bot-comment.test.js

Fix consumer repo sync for minimatch

  • Copy .github/scripts/node_modules/minimatch/ to templates/consumer-repo/.github/scripts/node_modules/minimatch/
  • Copy .github/scripts/package.json to templates/consumer-repo/.github/scripts/package.json
  • Add the node_modules/minimatch/ directory and package.json to .github/sync-manifest.yml under the scripts section
  • Verify the sync manifest health check passes

Standardize minimatch options

  • Ensure bot-comment-dismiss.js, pr-context-graphql.js, and merge_manager.js all use consistent minimatch options ({ dot: true, nocomment: true, nonegate: true })
  • Verify all JS tests still pass: node --test .github/scripts/__tests__/*.test.js && node --test tests/test/dismiss/*.test.js

Acceptance criteria

  • bot-comment-dismiss.js does NOT contain any of: function escapeRegExp, function expandBraces, function globToRegExp, or a local function minimatch.
    Verification: grep -c "function escapeRegExp\|function expandBraces\|function globToRegExp" .github/scripts/bot-comment-dismiss.js returns 0.

  • bot-comment-dismiss.js imports minimatch via require('minimatch').
    Verification: grep -c "require.*minimatch" .github/scripts/bot-comment-dismiss.js returns 1.

  • Template copy matches: diff .github/scripts/bot-comment-dismiss.js templates/consumer-repo/.github/scripts/bot-comment-dismiss.js exits 0.

  • Consumer template has node_modules/minimatch/: test -f templates/consumer-repo/.github/scripts/node_modules/minimatch/index.js exits 0.

  • All existing JS tests pass: node --test .github/scripts/__tests__/*.test.js && node --test tests/test/dismiss/*.test.js exits 0.

  • Glob matching behavior is unchanged: brace expansion (src/*.{ts,tsx}), character classes (src/[ab].ts), and escaped metacharacters (docs/\\[draft\\].md) continue to pass.
    Verification: tests in bot-comment-dismiss.test.js and dismiss-bot-comment.test.js cover these patterns and pass.

  • Head SHA: bea55c4

  • Latest Runs: ✅ success — Gate

  • Required: gate: ✅ success

  • | Workflow / Job | Result | Logs |

  • |----------------|--------|------|

  • | Agents PR meta manager | ❔ in progress | View run |

  • | CI Autofix Loop | ✅ success | View run |

  • | Gate | ✅ success | View run |

  • | Health 40 Sweep | ✅ success | View run |

  • | Health 44 Gate Branch Protection | ❔ in progress | View run |

  • | Health 45 Agents Guard | ✅ success | View run |

  • | Health 50 Security Scan | ✅ success | View run |

  • | Health 72 Template Sync | ✅ success | View run |

  • | Health 73 Template Completeness | ✅ success | View run |

  • | Maint 52 Validate Workflows | ✅ success | View run |

  • | PR 11 - Minimal invariant CI | ✅ success | View run |

  • | Selftest CI | ✅ success | View run |

  • | Validate Sync Manifest | ✅ success | View run |

  • Head SHA: d50c21d

  • Latest Runs: ❔ in progress — Gate

  • Required: gate: ❔ in progress

  • | Workflow / Job | Result | Logs |

  • |----------------|--------|------|

  • | Agents Auto-Pilot | ⏭️ skipped | View run |

  • | Agents Bot Comment Handler | ✅ success | View run |

  • | Agents Keepalive Loop | ✅ success | View run |

  • | Agents PR meta manager | ❔ in progress | View run |

  • | Agents Verifier | ✅ success | View run |

  • | Auto-label Dependabot PRs | ⏭️ skipped | View run |

  • | CI Autofix Loop | ✅ success | View run |

  • | Copilot code review | ❔ in progress | View run |

  • | Create Issue from Verification (DEPRECATED) | ⏭️ skipped | View run |

  • | Create Issue from Verification (Enhanced) | ⏭️ skipped | View run |

  • | Create New PR from Verification | ⏭️ skipped | View run |

  • | Gate | ❔ in progress | View run |

  • | Health 40 Sweep | ✅ success | View run |

  • | Health 44 Gate Branch Protection | ✅ success | View run |

  • | Health 45 Agents Guard | ✅ success | View run |

  • | Health 50 Security Scan | ✅ success | View run |

  • | Health 73 Template Completeness | ✅ success | View run |

  • | Maint 52 Validate Workflows | ✅ success | View run |

  • | PR 11 - Minimal invariant CI | ✅ success | View run |

  • | Selftest CI | ✅ success | View run |

  • | Validate Sync Manifest | ✅ success | View run |

Head SHA: 56a8768
Latest Runs: ✅ success — Gate
Required: gate: ✅ success

Workflow / Job Result Logs
Agents PR meta manager ❔ in progress View run
CI Autofix Loop ✅ success View run
Gate ✅ success View run
Health 40 Sweep ✅ success View run
Health 44 Gate Branch Protection ✅ success View run
Health 45 Agents Guard ✅ success View run
Health 50 Security Scan ✅ success View run
Maint 52 Validate Workflows ✅ success View run
PR 11 - Minimal invariant CI ✅ success View run
Selftest CI ✅ success View run

The check_consumer_sync_drift.py script crashed with IsADirectoryError
when encountering directory entries (minimatch/, brace-expansion/,
balanced-match/) in the sync manifest.

Extract file comparison into _check_file() helper and add directory
handling that recursively compares all files within directory entries,
matching the fix applied to the sync workflow in PR #1435.
Copilot AI review requested due to automatic review settings February 10, 2026 08:54
@github-actions github-actions bot added the autofix Opt-in automated formatting & lint remediation label Feb 10, 2026
The drift check script uses requests.Session() to directly query the
GitHub API for file content comparison across consumer repos. This is
appropriate for a Python utility script that runs in CI with its own
token management, not through the JS-based retry wrapper.
@stranske-keepalive
Copy link
Copy Markdown
Contributor

stranske-keepalive bot commented Feb 10, 2026

Automated Status Summary

Head SHA: 439a9bd
Latest Runs: ⏳ pending — Gate
Required contexts: Gate / gate, Health 45 Agents Guard / guard
Required: core tests (3.11): ⏳ pending, core tests (3.12): ⏳ pending, docker smoke: ⏳ pending, gate: ⏳ pending

Workflow / Job Result Logs
(no jobs reported) ⏳ pending

Coverage Overview

  • Coverage history entries: 1

Coverage Trend

Metric Value
Current 93.12%
Baseline 85.00%
Delta +8.12%
Minimum 70.00%
Status ✅ Pass

Top Coverage Hotspots (lowest coverage)

File Coverage Missing
src/cli_parser.py 81.8% 4
src/percentile_calculator.py 95.0% 1
src/aggregator.py 95.0% 2
src/__init__.py 100.0% 0
src/ndjson_parser.py 100.0% 0

Updated automatically; will refresh on subsequent CI/Docker completions.


Keepalive checklist

Scope

PR #1409 (issue #1407) merged with CONCERNS from both verify:compare providers (openai 78%, anthropic 85%). The core unresolved concern is that custom regex-based glob matching was supposed to be replaced with a standard minimatch library, but:

  1. bot-comment-dismiss.js still defines ~140 lines of custom glob functions instead of importing minimatch.
  2. The vendored minimatch at .github/scripts/node_modules/minimatch/ is a 0.0.0-local reimplementation, not the real npm package.
  3. Consumer repos will break on sync — template files pr-context-graphql.js and merge_manager.js both require('minimatch'), but the template directory has no node_modules/minimatch/ and the sync manifest does not include it.

Context for Agent

Related Issues/PRs

Context for Agent

Related Issues/PRs

Context for Agent

Related Issues/PRs

Tasks

Replace custom glob in bot-comment-dismiss.js

  • Remove the inline escapeRegExp, expandBraces, globToRegExp, and minimatch functions (lines 21–163) from .github/scripts/bot-comment-dismiss.js
  • Add const { minimatch } = require('minimatch'); import at the top of bot-comment-dismiss.js
  • Update shouldIgnorePath in bot-comment-dismiss.js to call minimatch(normalized, pattern, { dot: true, nocomment: true, nonegate: true }) — matching the options used in merge_manager.js
  • Apply the identical changes to templates/consumer-repo/.github/scripts/bot-comment-dismiss.js

Standardize the vendored minimatch module

  • Replace .github/scripts/node_modules/minimatch/package.json version from 0.0.0-local to 10.0.1-vendored (or current real minimatch major) and add a comment in the file explaining it is a vendored subset
  • Verify existing tests pass with the vendored module: node --test .github/scripts/__tests__/bot-comment-dismiss.test.js && node --test tests/test/dismiss/dismiss-bot-comment.test.js

Fix consumer repo sync for minimatch

  • Copy .github/scripts/node_modules/minimatch/ to templates/consumer-repo/.github/scripts/node_modules/minimatch/
  • Copy .github/scripts/package.json to templates/consumer-repo/.github/scripts/package.json
  • Add the node_modules/minimatch/ directory and package.json to .github/sync-manifest.yml under the scripts section
  • Verify the sync manifest health check passes

Standardize minimatch options

  • Ensure bot-comment-dismiss.js, pr-context-graphql.js, and merge_manager.js all use consistent minimatch options ({ dot: true, nocomment: true, nonegate: true })
  • Verify all JS tests still pass: node --test .github/scripts/__tests__/*.test.js && node --test tests/test/dismiss/*.test.js

Acceptance criteria

  • bot-comment-dismiss.js does NOT contain any of: function escapeRegExp, function expandBraces, function globToRegExp, or a local function minimatch.
    Verification: grep -c "function escapeRegExp\|function expandBraces\|function globToRegExp" .github/scripts/bot-comment-dismiss.js returns 0.

  • bot-comment-dismiss.js imports minimatch via require('minimatch').
    Verification: grep -c "require.*minimatch" .github/scripts/bot-comment-dismiss.js returns 1.

  • Template copy matches: diff .github/scripts/bot-comment-dismiss.js templates/consumer-repo/.github/scripts/bot-comment-dismiss.js exits 0.

  • Consumer template has node_modules/minimatch/: test -f templates/consumer-repo/.github/scripts/node_modules/minimatch/index.js exits 0.

  • All existing JS tests pass: node --test .github/scripts/__tests__/*.test.js && node --test tests/test/dismiss/*.test.js exits 0.

  • Glob matching behavior is unchanged: brace expansion (src/*.{ts,tsx}), character classes (src/[ab].ts), and escaped metacharacters (docs/\\[draft\\].md) continue to pass.
    Verification: tests in bot-comment-dismiss.test.js and dismiss-bot-comment.test.js cover these patterns and pass.

  • [ ]

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 15eb835fc4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

for child in sorted(local_path.rglob("*")):
if child.is_file():
rel = child.relative_to(local_path)
remote_target = f"{target}/{rel}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize directory target before building child API path

When is_directory entries rely on target = entry.get("target", source), the manifest’s directory sources include a trailing slash (for example .../minimatch/), so remote_target = f"{target}/{rel}" builds paths with // in the middle. Those doubled separators are sent directly to the GitHub contents endpoint and can be interpreted as a different path, which leads to false missing reports for every file under that directory. This only appears for directory entries with trailing-slash targets, so trimming the trailing slash (or using path-join semantics) before appending rel avoids the drift false positives.

Useful? React with 👍 / 👎.

@stranske-keepalive
Copy link
Copy Markdown
Contributor

stranske-keepalive bot commented Feb 10, 2026

🤖 Keepalive Loop Status

PR #1445 | Agent: Codex | Iteration 0/5

Current State

Metric Value
Iteration progress [----------] 0/5
Action wait (missing-agent-label)
Disposition skipped (transient)
Gate success
Tasks 0/62 complete
Timeout 45 min (default)
Timeout usage 3m elapsed (7%, 42m remaining)
Keepalive ❌ disabled
Autofix ❌ disabled

🔍 Failure Classification

| Error type | infrastructure |
| Error category | resource |
| Suggested recovery | Confirm the referenced resource exists (repo, PR, branch, workflow, or file). |

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes the consumer sync drift checker to handle is_directory entries in .github/sync-manifest.yml, avoiding crashes when the manifest includes directory-based dependencies (e.g., vendored minimatch and transitive deps).

Changes:

  • Adds a _check_file() helper to compare a single local file against its GitHub Contents API counterpart.
  • Introduces directory handling by walking local directories and comparing each contained file.
  • Fixes an errors.append(...) call to use errors.add(...) (since errors is a set).

Comment on lines +126 to +128
if is_directory or local_path.is_dir():
# Recursively compare all files within the directory
for child in sorted(local_path.rglob("*")):
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

is_directory or local_path.is_dir() will treat a directory as a directory even when the manifest entry is not marked is_directory, and it can also try to rglob() a path marked is_directory that isn’t actually a directory (leading to NotADirectoryError). This should be driven by the manifest flag, and mismatches between is_directory and local_path.is_dir() should be reported as an error instead of silently choosing one behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +130 to +132
rel = child.relative_to(local_path)
remote_target = f"{target}/{rel}"
_check_file(child, remote_target, repo)
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

When constructing remote_target for directory entries, target from the manifest is typically a directory path that already ends with / (e.g. .github/scripts/node_modules/minimatch/). Using f"{target}/{rel}" can produce a double-slash path and also relies on OS-specific path separators via rel (problematic if run on Windows). Normalize target (strip trailing /) and build the remote path with POSIX separators (and ideally URL-encode the path segment) before calling the GitHub Contents API.

Copilot uses AI. Check for mistakes.
@stranske stranske merged commit 910f0ab into main Feb 10, 2026
119 checks passed
@stranske stranske deleted the fix/drift-check-directory-support branch February 10, 2026 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autofix Opt-in automated formatting & lint remediation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants