Skip to content

C1 drift: re-sync unprotected template-tracked files#100

Merged
Chris-Wolfgang merged 25 commits into
vNextfrom
canonical-unprotected
Jun 1, 2026
Merged

C1 drift: re-sync unprotected template-tracked files#100
Chris-Wolfgang merged 25 commits into
vNextfrom
canonical-unprotected

Conversation

@Chris-Wolfgang

Copy link
Copy Markdown
Owner

Template-drift resolution for ETL-Test-Kit (initiative C1) — the unprotected half. Re-syncs 15 template-tracked infrastructure files (scripts, docs process files, .github templates, .gitattributes, etc.) to the canonical repo-template.

Excludes docfx_project/* (repo-specific docs — docs initiatives), protected config/workflow files (separate PR), and .gitkeep / expected-noise files.

Part of #72.

🤖 Generated with Claude Code

Template-drift resolution for ETL-Test-Kit — the non-protected half. Re-syncs
15 template-tracked infrastructure files to the canonical
repo-template. Excludes docfx_project/* (repo-specific docs), protected
config/workflow files (separate PR), and .gitkeep / expected-noise files.

Part of #72.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 22, 2026 15:30

Copilot AI left a comment

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.

Pull request overview

Resyncs a set of template-tracked infrastructure files to reduce drift from the canonical repo-template, focusing on the “unprotected” half (scripts, docs, templates, and git attributes). This introduces new maintenance-framework tooling and hardening/validation scripts while updating existing setup/docs guidance.

Changes:

  • Added GitHub repo bootstrap utilities (labels provisioning, maintenance issue creation, branch ruleset setup/fix).
  • Added DocFX gh-pages deployment validation script and maintenance framework templates.
  • Updated docs and repository templates (CONTRIBUTING.md, PR/issue templates, formatting/security docs, .gitattributes) to match the template baseline.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
scripts/Validate-DocsDeploy.sh Adds post-deploy validation for DocFX gh-pages contents.
scripts/templates/maintenance-parent-body.md Adds canonical body template for the parent Maintenance issue.
scripts/setup.ps1 Updates setup guidance to include labels + maintenance issue provisioning.
scripts/Setup-Maintenance.ps1 Adds idempotent script to create the parent Maintenance issue via gh.
scripts/Setup-Labels.ps1 Updates label provisioning to include Maintenance framework labels.
scripts/Setup-BranchRuleset.ps1 Adds interactive ruleset creation for main with required checks and security gates.
scripts/format.ps1 Improves messaging around required SDK version for dotnet format.
scripts/Fix-BranchRuleset.ps1 Adds script to disable/rename existing rulesets and re-run setup.
docs/WORKFLOW_SECURITY.md Updates protected-file patterns to include workflow files.
docs/RELEASE-WORKFLOW-SETUP.md Tweaks troubleshooting guidance wording.
docs/README-FORMATTING.md Updates formatting prerequisites and line-ending guidance.
CONTRIBUTING.md Resynced contributing guide content to template baseline.
.github/pull_request_template.md Adds guidance for linking Maintenance sub-issues.
.github/ISSUE_TEMPLATE/maintenance-task.yaml Adds Maintenance-task issue form.
.gitattributes Clarifies/enforces LF strategy for PowerShell scripts and shebang compatibility.

Comment thread scripts/Setup-Labels.ps1
Comment thread scripts/Setup-BranchRuleset.ps1 Outdated
Comment on lines +104 to +128
$rulesetOutput = gh api `
-H "Accept: application/vnd.github+json" `
-H "X-GitHub-Api-Version: 2022-11-28" `
"/repos/$Repository/rulesets" `
--paginate `
--jq '.[] | select(.name == "Protect main branch")' 2>&1

if ($LASTEXITCODE -ne 0) {
Write-Warning "⚠️ Could not check for existing rulesets (API returned exit code $LASTEXITCODE). Continuing..."
} elseif ($rulesetOutput) {
$matchingRulesets = $rulesetOutput | ConvertFrom-Json
$existingRuleset = $matchingRulesets | Select-Object -First 1

if ($existingRuleset) {
Write-Host "✅ Ruleset 'Protect main branch' already exists!" -ForegroundColor Green
Write-Host " View it at: https://github.com/$Repository/settings/rules" -ForegroundColor Cyan
$response = Read-Host "`nDo you want to continue anyway? This may fail. (y/N)"
if ($response -ne 'y' -and $response -ne 'Y') {
Write-Host "Exiting." -ForegroundColor Yellow
exit 0
}
}
} else {
Write-Host "ℹ️ Ruleset 'Protect main branch' does not exist yet." -ForegroundColor Gray
}
Comment thread scripts/Fix-BranchRuleset.ps1
Comment thread CONTRIBUTING.md Outdated
Comment on lines +1 to +3
# Contributing to {{PROJECT_NAME}}

Thank you for your interest in contributing to **Wolfgang.Etl.TestKit**! We welcome contributions to help improve this project.
Thank you for your interest in contributing to **{{PROJECT_NAME}}**! We welcome contributions to help improve this project.
Comment thread .github/ISSUE_TEMPLATE/maintenance-task.yaml
Chris-Wolfgang and others added 2 commits May 22, 2026 12:27
The C1 drift re-sync brought the canonical CONTRIBUTING.md but left the
template's {{PROJECT_NAME}} placeholder literal. Replace it with this repo's
project name so the synced CONTRIBUTING.md is correct, not a raw template.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Normalize to the package name for ETL-family consistency (the prior
commit fell back to the repo name because the local clone lacked a
single src/ project dir to derive from).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 22, 2026 16:31

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 7 comments.

Comment thread scripts/Setup-Labels.ps1
Comment thread scripts/Setup-Labels.ps1
Comment thread scripts/Setup-BranchRuleset.ps1 Outdated
Comment on lines +105 to +109
-H "Accept: application/vnd.github+json" `
-H "X-GitHub-Api-Version: 2022-11-28" `
"/repos/$Repository/rulesets" `
--paginate `
--jq '.[] | select(.name == "Protect main branch")' 2>&1
Comment thread scripts/Setup-BranchRuleset.ps1 Outdated
Comment on lines +113 to +128
} elseif ($rulesetOutput) {
$matchingRulesets = $rulesetOutput | ConvertFrom-Json
$existingRuleset = $matchingRulesets | Select-Object -First 1

if ($existingRuleset) {
Write-Host "✅ Ruleset 'Protect main branch' already exists!" -ForegroundColor Green
Write-Host " View it at: https://github.com/$Repository/settings/rules" -ForegroundColor Cyan
$response = Read-Host "`nDo you want to continue anyway? This may fail. (y/N)"
if ($response -ne 'y' -and $response -ne 'Y') {
Write-Host "Exiting." -ForegroundColor Yellow
exit 0
}
}
} else {
Write-Host "ℹ️ Ruleset 'Protect main branch' does not exist yet." -ForegroundColor Gray
}
Comment thread scripts/Setup-BranchRuleset.ps1 Outdated
Comment on lines +193 to +196
@{ context = "Stage 3: macOS Tests (.NET 6.0-10.0)" },
@{ context = "Security Scan (DevSkim)" },
@{ context = "Security Scan (CodeQL) (csharp)" },
@{ context = "Secrets Scan (gitleaks)" }
Comment thread scripts/Fix-BranchRuleset.ps1
Comment thread scripts/Setup-BranchRuleset.ps1 Outdated
Comment on lines +277 to +280
Write-Host " - Stage 3: macOS Tests (.NET 6.0-10.0)" -ForegroundColor DarkGray
Write-Host " - Security Scan (DevSkim)" -ForegroundColor DarkGray
Write-Host " - Security Scan (CodeQL) (csharp)" -ForegroundColor DarkGray
Write-Host " - Secrets Scan (gitleaks)" -ForegroundColor DarkGray
Chris-Wolfgang and others added 2 commits May 22, 2026 12:52
Fold-down of the repo-template fix (PR #389): Replace-Placeholders now
writes with -Encoding utf8NoBOM -NoNewline. Rolled to every repo so the
campaign stays consistent with the updated canonical.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Propagates the three files folded into repo-template during drift Phase 0:
tests/Directory.Build.props + benchmarks/Directory.Build.props
(TreatWarningsAsErrors=false) and tests/.editorconfig (test-project analyzer
relaxations). Excluded from the initial C1 sync; rolled out now so every repo
matches the canonical template.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 22, 2026 17:27

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.

Comment thread tests/.editorconfig
Comment thread scripts/Setup-Labels.ps1
Chris-Wolfgang and others added 2 commits May 22, 2026 13:50
Removes the TreatWarningsAsErrors=false override on test projects. The
genuinely test-inappropriate analyzer rules are already silenced per-rule in
tests/.editorconfig; test code now inherits the root Directory.Build.props
(TreatWarningsAsErrors in Release, like src/examples). benchmarks remain
exempt for now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The explicit interface member declared 'object' but the non-generic
System.Collections.IEnumerator.Current is 'object?' in a nullable
context, so returning it tripped CS8603. Declare the member 'object?'
to match the interface.

Surfaced by holding test projects to TreatWarningsAsErrors (the
tests/Directory.Build.props drop) — a genuine latent bug, now fixed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 22, 2026 20:19

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.

Comment thread scripts/Setup-Labels.ps1
Comment thread scripts/Setup-Labels.ps1
Comment thread scripts/Setup-BranchRuleset.ps1 Outdated
Comment on lines +107 to +110
"/repos/$Repository/rulesets" `
--paginate `
--jq '.[] | select(.name == "Protect main branch")' 2>&1

Append a github-actions package-ecosystem to .github/dependabot.yml so
Dependabot opens weekly PRs to bump pinned action versions in the repo's
GitHub workflows. Grouped under a single PR per week. Existing nuget
ecosystems are left unchanged.

Initiative CI2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Scope addition to this PR — CI2 stacked.

This canonical-unprotected branch now also adds a github-actions package-ecosystem to .github/dependabot.yml so Dependabot opens weekly grouped PRs to bump pinned action versions in .github/workflows/*. Existing nuget ecosystems are unchanged.

Initiative CI2 rides this existing branch per your guidance ("add to existing branch").

Keep-a-Changelog skeleton with an [Unreleased] section so release
notes can accumulate here between releases instead of being lost.
Follows the canonical format used across the fleet.

Initiative D3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 23, 2026 17:23
@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Scope addition to this PR — D3 stacked.

Adds CHANGELOG.md (Keep-a-Changelog skeleton with an [Unreleased] section) so release notes can accumulate here between releases instead of being lost in commit history. Canonical content — identical across the fleet.

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.

Comment thread scripts/Setup-Labels.ps1
Comment thread scripts/Setup-Labels.ps1
- scripts/Setup-BranchRuleset.ps1: jq filter wrapped in '[ .[] | select() ]'
  so the output is always valid JSON even with multiple matches; gh stderr
  redirected to a temp file so it can't poison stdout.
- scripts/Fix-BranchRuleset.ps1: same stderr-isolation fix on the rulesets
  fetch (no more '2>&1' merge).
- scripts/Validate-DocsDeploy.sh: distinguish 'versions.json never created'
  from 'versions.json failed validation' in step 4 so the skip message
  reflects reality.
- .github/dependabot.yml: drop the stale 'dotnet' label (no longer in the
  Setup-Labels.ps1 taxonomy).
- REPO-INSTRUCTIONS.md: replace the stale label list (dependabot-*, dotnet)
  with the current Maintenance-framework labels Setup-Labels.ps1 actually
  creates.

IAsyncEnumerable-Extensions also gets README.md / CONTRIBUTING.md updated
to point at docs/README-FORMATTING.md since the root copy was removed by
the earlier D7 cleanup.

Fan-out of the round-2 Copilot fixes verified against DateTime-Extensions
(#178 pilot).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Chris-Wolfgang Chris-Wolfgang changed the base branch from main to vNext May 23, 2026 23:58
@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Retargeted: base changed from `main` to `vNext`.

Per the per-repo release plan, this PR now merges into the repo's `vNext` integration branch instead of directly into `main`. vNext currently equals `canonical-protected`; merging this PR brings the unprotected canonical work onto vNext, where the Release build + automated tests run as the release gate.

After both halves are on vNext and the gate is green, the per-repo release flow is:

  1. Merge `canonical-protected` → `main` (admin bypass — protected files).
  2. Merge `vNext` → `main` (its diff vs. main is then purely unprotected → normal review).
  3. Tag a release; `release.yaml` publishes the new version.

These scripts were run once when this repo was bootstrapped from
repo-template and are not needed afterward:

- scripts/setup.ps1                              (orchestrator for the others)
- scripts/Setup-Maintenance.ps1                  (created the Maintenance issues)
- scripts/Setup-BranchRuleset.ps1                (created the main branch ruleset)
- scripts/Setup-GitHubPages.ps1                  (bootstrapped gh-pages branch)
- scripts/templates/maintenance-parent-body.md   (template used by Setup-Maintenance)

repo-template keeps these files — they remain available there so any new
repo created from the template can still run them at bootstrap.

Recurring utilities are intentionally kept: Setup-Labels.ps1 (idempotent),
Fix-BranchRuleset.ps1, format.ps1, Validate-DocsDeploy.sh, build-pr.ps1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Chris-Wolfgang and others added 14 commits May 25, 2026 10:56
The formatting section referenced `pwsh ./format.ps1` and linked to
`README-FORMATTING.md` at the repo root, but the actual paths are
`scripts/format.ps1` and `docs/README-FORMATTING.md`. Both are
broken as written — contributors who follow the instructions get a
"file not found" on the script and a 404 on the link.

Correcting both references to match the canonical repo layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two related problems:
  1. EXAMPLE comments showed `.\format.ps1` (current directory), but
     the script lives at `scripts/format.ps1`. Contributors who copy-
     pasted the examples got "file not found".
  2. The script used `Get-ChildItem -Path .` to find the solution,
     which only worked when invoked from the repo root. Invoking from
     anywhere else (including the scripts/ folder where the script
     lives) failed with "No solution file found".

Now the script pins cwd to the repo root via Resolve-Path
$PSScriptRoot/..  inside a Push-Location / try / finally Pop-Location
block, so it works from any working directory inside the repo. The
EXAMPLE comments and the Check-mode error message both reference
the correct `.\scripts\format.ps1` path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…0004)

The AsyncFixer bullet listed "Ensures proper ConfigureAwait() usage" as
one of its capabilities. That's inaccurate — ConfigureAwait enforcement
in this fleet is handled by Meziantou's MA0004 (plus SonarAnalyzer's
S3216 and Microsoft's CA2007), not by AsyncFixer. AsyncFixer covers
other async anti-patterns: AsyncFixer01–05, cancellation-token
propagation, fire-and-forget detection.

Updated the bullet to describe what AsyncFixer actually does and added a
note pointing readers at the right analyzer for ConfigureAwait
enforcement.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When setup.ps1 reads the repo from an SSH-style git remote
(`git@github.com:owner/repo.git`), the placeholder replacement can
leave a leading "@" and/or trailing ".git" in the Repository parameter
of this script. Both break `gh api /repos/<repo>/rulesets` calls — the
"@" turns into a 404 and the ".git" suffix is rejected as an unknown
repo.

Added a normalization step right after the parameter declaration that
strips both. Auto-detection via `gh repo view` already produces a
clean nameWithOwner value, so this only affects the user-supplied or
placeholder-substituted path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three related fixes so the local script behaves like the per-PR
workflow instead of silently passing where CI would fail:

  1. Linux/macOS gitleaks install used `curl ... | tar -C /usr/local/bin`,
     which requires sudo for normal users and fails out of the box on
     most local dev shells. Now installs to $HOME/.local/bin and adds
     that to PATH (only if not already there). Matches the user-writable
     pattern the CI workflows use.

  2. When CoverageReport/Summary.txt was missing after a successful test
     run, the script printed "skipping threshold check" and reported
     success. pr.yaml fails the job in that situation. Align: emit a
     hard failure with a clear message about ReportGenerator and mark
     Coverage as failed.

  3. "No test projects found in ./tests — skipping" let the script
     finish "All checks passed" even when the repo had src/ projects.
     Same rule as the pr.yaml tests-gate strict mode now: if ./src has
     projects, refuse the silent skip and fail. If neither has projects
     (template-pack / in-dev repos), graceful skip is preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The prerequisites listed ".NET 8.0 SDK or later", but the repo targets
net10.0 (and multi-targets older TFMs). SDK 8.0 cannot load a net10.0
csproj — contributors who follow the instructions would hit NETSDK1045
"does not recognize TargetFramework net10.0".

Bumped to .NET 10.0 SDK with a short note about why older SDKs
won't work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`git worktree add` requires the target path NOT to exist, but the
script created the path first via `mktemp -d` and then immediately
called `git worktree add "$WORK_DIR" origin/gh-pages` — failing with
"fatal: '<path>' already exists" on every invocation.

Fix: `rmdir` the directory right after mktemp reserves the unique
name. mktemp still guarantees uniqueness, the trap still cleans up
afterward, and worktree-add now succeeds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The [ValidateScript] guard for -Repository was dropped in an earlier
refactor, letting callers pass a full URL
(https://github.com/owner/repo), a value with a leading "@" from an
SSH remote, or other malformed inputs. gh api would later fail with a
confusing 404 and the user wouldn't know the parameter format was the
problem.

Restored an early format check via [ValidatePattern]:
  - empty       (auto-detect via `gh repo view` kicks in downstream)
  - the template {{GITHUB_USERNAME}}/{{REPO_NAME}} placeholder
    (also replaced downstream by setup.ps1)
  - strict `owner/repo` (no /, no @, no whitespace)

Anything else fails at parameter binding with a clear error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Windows gitleaks install path uses Invoke-WebRequest with
-UseBasicParsing, which is a Windows-PowerShell-5.1-only flag. The
script's shebang is pwsh, so PowerShell 7+ errors on the unsupported
flag and gitleaks never installs locally.

Same fix we applied to docfx.yaml's Invoke-WebRequest earlier — drop
the flag (it's unnecessary in pwsh).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Policy: TreatWarningsAsErrors=true applies to ALL projects in Release
(src + tests + examples + benchmarks). The subtree Directory.Build.props
files in benchmarks/ were overriding the root
property back to false for their respective subtrees, contradicting the
stated convention.

Per-csproj <NoWarn> is the right tool for stylistic / test-pattern /
benchmark-specific suppressions — broad TWEA flips at the subtree level
are too coarse and let real regressions slip through CI. (The
.editorconfig [tests/**/*.cs] relaxations don't help either: the Windows
runner doesn't honor them, so test-project warnings need per-csproj
<NoWarn> regardless.)

Each removed file contained only the MSBuild Import + the TWEA override,
nothing else worth preserving. After this commit, benchmarks/ subtree(s)
inherit root TWEA=true in Release.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`REPO_NAME=${REPO_URL##*/}` worked for HTTPS remotes
(`https://github.com/owner/repo[.git]` → "repo") but is brittle for
SSH-style remotes (`git@github.com:owner/repo.git`). For example
if someone has an SSH remote without the `.git` suffix, the parsing
falls back to using parts of the host instead of the repo name.

Changing the parameter expansion to strip after either `/` or `:`
(`${REPO_URL##*[/:]}`) so both URL forms produce just "repo".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After installing dotnet-reportgenerator-globaltool, the script invokes
`reportgenerator` immediately. On a shell where the .NET installer
hasn't yet appended $HOME/.dotnet/tools (Linux/macOS) or
$env:USERPROFILE\.dotnet\tools (Windows) to PATH — common for
fresh terminals or pwsh sessions launched from a script — the call
fails with "reportgenerator: command not found".

Switched to update-or-install (idempotent for already-installed
tools) and added an explicit PATH-prepend after the install so the
subsequent reportgenerator call always finds the binary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…esets

Fixes two bugs in the canonical-unprotected scripts that were fanned out
from repo-template:

- Setup-Labels.ps1: the ValidatePattern attribute string was left
  unterminated and the [string]$Repository declaration + closing paren
  were dropped, leaving a duplicated script body that ran with
  $Repository unbound. Param block restored to a single well-formed
  declaration; duplicated body removed.

- Fix-BranchRuleset.ps1: gh api --paginate concatenates multiple JSON
  array payloads when results span pages, which breaks ConvertFrom-Json.
  Switched to ?per_page=100 in a single call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every shippable src csproj across the fleet had `<AssemblyVersion>1.0.0</AssemblyVersion>`
hardcoded — last touched at v1.0.0 and never bumped. Released NuGet
packages from v1.1.0 onward have shipped with AssemblyVersion=1.0.0
even though the package <Version> bumped normally.

Drop the explicit AssemblyVersion line. .NET SDK derives AssemblyVersion,
FileVersion, and InformationalVersion from <Version> when they are not
specified, so all four track every <Version> bump automatically going
forward. They are not byte-identical — AssemblyVersion is normalized to
a 4-part System.Version, InformationalVersion can carry a +<git-sha>
under SourceLink — but they all reflect the current Version, which is
the point.

Mirrors DateTime-Extensions PR #184 (already merged to main).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

2 participants