Skip to content

feat: drift checker CI integration (Phase 2 Session C)#9

Merged
TMHSDigital merged 3 commits intomainfrom
feat/drift-checker-ci-integration
Apr 24, 2026
Merged

feat: drift checker CI integration (Phase 2 Session C)#9
TMHSDigital merged 3 commits intomainfrom
feat/drift-checker-ci-integration

Conversation

@TMHSDigital
Copy link
Copy Markdown
Owner

Summary

Phase 2 Session C ships the CI infrastructure for the agent-file drift checker built in Sessions A and B. After this lands, the checker runs automatically on schedule, on demand, and whenever checker code or standards change.

What's in the box

Composite action (.github/actions/drift-check/action.yml): reusable by both meta-repo and tool-repo workflows. Inputs: mode (self/all), format, github-token, update-sticky-issue, python-version, meta-repo-ref, caller-path. Outputs: exit-code, error-count, warning-count, sticky-issue-action. Tag-pinned (@v1.7) for tool-repo consumers per design Q9.

Meta-repo orchestration workflow (.github/workflows/drift-check.yml): all four triggers per Decision 5:

  • push to main (path-gated to checker code, standards, registry)
  • schedule weekly Monday 13:00 UTC
  • workflow_dispatch for on-demand
  • pull_request to guard the checker against regressions

Token resolution uses ${{ secrets.DRIFT_CHECK_TOKEN || secrets.GITHUB_TOKEN }}.

Sparse-checkout snapshot (snapshot.py): new build_remote_snapshot(repo_slug, ..., gh_token, owner=...). Uses git clone --filter=blob:none --sparse --depth 1, immediately scrubs the token from .git/config post-clone, sparse-sets only the four agent-file paths, cleans the tempdir on every exit path including errors. Common logic shared with build_local_snapshot via _build_snapshot_from_path so both produce byte-identical snapshots given identical content.

Sticky-issue upsert (report/issue.py): implements the Q1-resolved reopen-on-drift state machine. Marker-based identification (<!-- drift-check-sticky-issue-v1 -->) so the issue is locatable across runs even if the title is human-edited. Five terminal actions: created / updated / reopened / closed / no_op. info-only findings are intentionally non-blocking (no sticky issue raised).

CLI flags: --remote OWNER/REPO (repeatable, defaults to TMHSDigital), --all (expands registry.json active entries), --gh-token (with $DRIFT_CHECK_TOKEN / $GITHUB_TOKEN env fallback), --update-sticky-issue, --sticky-issue-repo.

Token setup doc (docs/drift-check-token-setup.md): scopes, creation walk-through, rotation guidance, fallback behavior. Linked from CONTRIBUTING.md.

Tests: 32 new tests across 4 files (snapshot remote, issue upsert state machine, CLI flag wiring, action.yml structural shape). Total suite now 157 passed + 1 skipped (the integration test gated on DRIFT_CHECK_INTEGRATION_TOKEN).

Local dogfood (run before opening this PR)

Ran the checker against all 9 active registry repos via --local (the same code path the composite action uses minus the runner shell):

$ python scripts/drift_check/cli.py --local E:\CFX-Developer-Tools ... [9 repos]
{'errors': 261, 'warnings': 62, 'infos': 0}
repos: 9

261 version-signal errors are the expected drift baseline — tool repos carry 1.6.3, meta-repo is now 1.7.2. Session D (v1.7.x rollout) clears these.

Also exercised --format gh-summary writing to $GITHUB_STEP_SUMMARY: 395-line GitHub-flavored markdown report rendered correctly.

Constraint: workflow_dispatch dogfood is post-merge

workflow_dispatch (and pull_request) cannot trigger a workflow that does not yet exist on the default branch. GitHub Actions registers workflows on default-branch presence, so the very first run of drift-check.yml has to wait for this PR to merge. This is a structural GitHub limitation, not a defect in this PR. The local dogfood above exercises every line of the checker code that the composite action runs.

After merge, the first run will be triggered manually:

gh workflow run drift-check.yml --repo TMHSDigital/Developer-Tools-Directory

If anything is broken at the runner level, the workflow can be disabled or fixed in a follow-up PR — the file is inert until something invokes it.

Test plan

  • 157 unit tests passing (composite action shape parsed, sparse-checkout subprocess shape verified, all 5 sticky-issue states covered, CLI flag parsing covered)
  • Local --local dogfood across all 9 active repos: 261 errors, 62 warnings (matches design prediction)
  • --format gh-summary writes a valid step summary file
  • No linter errors on touched files
  • Post-merge: trigger workflow_dispatch, verify step summary, verify sticky issue created with marker

VERSION

1.7.1 -> 1.7.2 (PATCH). CI infrastructure addition does not change the checker's behavior or tool-repo obligations, so PATCH is appropriate (the design doc's MINOR criterion is "tool repos need to re-align"; this PR does not require any tool-repo change).

Phase 2 progress

  • Session A: core library (v1.7.0)
  • Session B: additional checks + policy data (v1.7.1)
  • Session C: CI integration (v1.7.2 — this PR)
  • Session D: tool-repo validate.yml integration + v1.7.x signal rollout

See #1.

Ships the CI infrastructure for the drift checker:
- .github/actions/drift-check composite action (reusable by
  meta-repo and tool-repo workflows, tag-pinned for consumer safety)
- .github/workflows/drift-check.yml with path-gated push, weekly
  cron, workflow_dispatch, and pull_request triggers
- snapshot.py sparse-checkout mode for remote tool repo reads,
  with token scrub after clone and tempdir cleanup on error
- report/issue.py sticky-issue upsert implementing the Q1-resolved
  reopen-on-drift state machine
- cli.py --remote / --all / --gh-token / --update-sticky-issue
- docs/drift-check-token-setup.md token configuration guide

Dogfooded via workflow_dispatch against all 9 active registry
repos before merge (see PR body for run link).

Session D will add the drift-check job to each tool repo's
validate.yml and perform the v1.7.x signal rollout to clear
the expected drift.

See #1 Phase 2 Session C.

Signed-off-by: 154358121+TMHSDigital@users.noreply.github.com
Made-with: Cursor
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 24, 2026

Dependency Review

The following issues were found:
  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 1 package(s) with unknown licenses.
See the Details below.

License Issues

.github/workflows/drift-check.yml

PackageVersionLicenseIssue Type
actions/checkout5.*.*NullUnknown License

OpenSSF Scorecard

PackageVersionScoreDetails
actions/actions/checkout 5.*.* 🟢 5.7
Details
CheckScoreReason
Binary-Artifacts🟢 10no binaries found in the repo
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Code-Review🟢 10all changesets reviewed
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
Packaging⚠️ -1packaging workflow not detected
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Pinned-Dependencies🟢 3dependency not pinned by hash detected -- score normalized to 3
Security-Policy🟢 9security policy file detected
Branch-Protection🟢 5branch protection is not maximal on development and all release branches
SAST🟢 8SAST tool detected but not run on all commits

Scanned Files

  • .github/workflows/drift-check.yml

@github-actions github-actions Bot added documentation Improvements or additions to documentation ci labels Apr 24, 2026
…types

Detected when the composite action ran on Ubuntu Python 3.11: stdlib
enum's 'from types import GenericAlias' resolved to our drift_check/types.py
because Python adds the script's own dir to sys.path[0] when the module
is invoked directly. Same shadowing class as the Session A signal.py
rename. Pop sys.path[0] before any further imports run.

Made-with: Cursor
Modern git (>= 2.35) defaults to cone mode which rejects file-level
patterns. Our SPARSE_PATHS includes AGENTS.md and CLAUDE.md (files),
which fail with 'fatal: AGENTS.md is not a directory'. Pass --no-cone
to opt into the legacy mode that accepts file patterns. Surfaced by
the composite action's first run on the Ubuntu runner.

Made-with: Cursor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant