From 09c0900bcfa754c81362e2d77637c17fda1effb4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 10:47:21 +0000 Subject: [PATCH 01/14] Phase 1: fix CI Windows test, repair CodeQL config, clean stale legal wording - Fix WavePcm16 Windows move-failure test: Assert.ThrowsAsync required an exact type match against a subtype (UnauthorizedAccessException); swap to Assert.ThrowsAnyAsync so the existing IOException/UnauthorizedAccessException contract check is actually reached. - Remove the stale `frontend` path from .github/codeql/codeql-config.yml; no frontend directory exists in this repo. - Delete docs/legal/CONTRIBUTOR-LICENSE-AGREEMENT.md (GPLv3 + dual-license wording contradicts the repo's actual Apache-2.0 LICENSE/NOTICE). - Add CONTRIBUTING.md (Apache-2.0 inbound contribution terms) and docs/legal/LICENSE-HISTORY.md (public core vs. proprietary desktop split). - Trim docs/legal/THIRD_PARTY_NOTICES.md to drop entries scoped to Trackdub.App.Avalonia and the Inter font, which aren't part of this repo; keep the LibVLCSharp entry used by Trackdub.Media.Playback. - Correct IExportTierGate's doc comment: the public core ships no concrete implementation (verified: none exists in this repo); a consuming product supplies its own tier policy. - Add opt-in package metadata (Authors/Description/License/RepositoryUrl/ PackageProjectUrl) to Trackdub.Cli and Trackdub.OnnxRuntime.Dnnl.Native. - Add scripts/ci/check-repository-boundary.py and a CI job that fails the build on stale GPL/dual-license wording or desktop-project-name bleed in the legal/policy/contract surface. --- .github/codeql/codeql-config.yml | 1 - .github/workflows/ci.yml | 11 ++ CONTRIBUTING.md | 28 ++++ docs/legal/CONTRIBUTOR-LICENSE-AGREEMENT.md | 15 -- docs/legal/LICENSE-HISTORY.md | 19 +++ docs/legal/THIRD_PARTY_NOTICES.md | 48 +------ scripts/ci/check-repository-boundary.py | 134 ++++++++++++++++++ src/Trackdub.Cli/Trackdub.Cli.csproj | 5 + src/Trackdub.Contracts/IExportTierGate.cs | 3 +- .../Trackdub.OnnxRuntime.Dnnl.Native.csproj | 2 + tests/Trackdub.Media.Tests/WavePcm16Tests.cs | 2 +- 11 files changed, 204 insertions(+), 64 deletions(-) create mode 100644 CONTRIBUTING.md delete mode 100644 docs/legal/CONTRIBUTOR-LICENSE-AGREEMENT.md create mode 100644 docs/legal/LICENSE-HISTORY.md create mode 100644 scripts/ci/check-repository-boundary.py diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml index ed89fe6..dfbc0ef 100644 --- a/.github/codeql/codeql-config.yml +++ b/.github/codeql/codeql-config.yml @@ -1,7 +1,6 @@ paths: - src - tests - - frontend - tools - .github diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c33111..0b8a4b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,17 @@ jobs: - name: Verify Format Style Limits run: dotnet format Trackdub.slnx --verify-no-changes + repository-boundary: + name: Verify Repository Boundary + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout Source + uses: actions/checkout@v7 + + - name: Scan for stale license/desktop-boundary claims + run: python3 scripts/ci/check-repository-boundary.py + build: name: Build & Test (${{ matrix.name }}) timeout-minutes: 45 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6ab2b58 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,28 @@ +# Contributing + +Trackdub (this repository) is the Apache-2.0 public core: the SDK, CLI, +pipeline, inference, media processing, infrastructure, and neutral licensing +mechanisms. See [docs/repository-policy.md](docs/repository-policy.md) and +[AGENTS.md](AGENTS.md) for organization, dependency-direction rules, and +build/test commands before opening a change. + +## Inbound contribution terms + +- Contributions to this repository are submitted under the + [Apache License, Version 2.0](LICENSE). +- By submitting a contribution, you certify that you have the right to submit + it under that license (for example, it is your own original work, or you + otherwise hold the necessary rights). +- No separate Contributor License Agreement (CLA) is currently required to + contribute here. +- This forward-looking contribution policy is separate from, and does not + affect, any historical intellectual-property ownership review of code + already in the repository. + +## Before submitting a change + +- Run `dotnet build Trackdub.slnx -m:1` and `dotnet test Trackdub.slnx -m:1`. +- Run `dotnet format Trackdub.slnx --verify-no-changes`. +- Follow the coding style and dependency-direction rules in + [AGENTS.md](AGENTS.md). +- Use imperative commit titles: `Add ...`, `Fix ...`, `Remove ...`. diff --git a/docs/legal/CONTRIBUTOR-LICENSE-AGREEMENT.md b/docs/legal/CONTRIBUTOR-LICENSE-AGREEMENT.md deleted file mode 100644 index 9a6f12d..0000000 --- a/docs/legal/CONTRIBUTOR-LICENSE-AGREEMENT.md +++ /dev/null @@ -1,15 +0,0 @@ -# Contributor License Agreement Placeholder - -This project is intended to support GPLv3 + commercial dual licensing. - -To preserve that option, outside contributors should sign a contributor agreement before their code is merged. - -At minimum, the agreement should grant the project owner the right to: - -- use the contribution in the GPL community edition -- relicense the contribution under commercial terms -- sublicense as needed for commercial customers -- modify, distribute, and maintain the contribution -- include the contribution in future versions - -This is a placeholder. Use a lawyer-reviewed CLA or copyright assignment before accepting external contributions if dual licensing matters. diff --git a/docs/legal/LICENSE-HISTORY.md b/docs/legal/LICENSE-HISTORY.md new file mode 100644 index 0000000..b1bb67b --- /dev/null +++ b/docs/legal/LICENSE-HISTORY.md @@ -0,0 +1,19 @@ +# License History + +Trackdub is split into two repositories with different licenses: + +- **`trackdubllc/Trackdub`** (this repository) — the public core: SDK, CLI, + pipeline, inference, media processing, infrastructure, and neutral + licensing mechanisms. Licensed under [Apache License, Version 2.0](../LICENSE) + (see the root [NOTICE](../NOTICE)). +- **The Trackdub desktop product** — a separate, private repository that + depends on this public core. It owns the desktop application shell, + product policy (export tiers, watermarking, entitlements), activation, + packaging, and release signing. It is proprietary and is not covered by + the Apache-2.0 license in this repository. + +This repository was established with a fresh root history as the public +core. It carries only Apache-2.0-compatible source and does not contain +desktop product code, activation-server code, or proprietary packaging +logic. Detailed historical ownership evidence for code predating this split +is retained privately and is out of scope for this public document. diff --git a/docs/legal/THIRD_PARTY_NOTICES.md b/docs/legal/THIRD_PARTY_NOTICES.md index 05fbf86..2775cde 100644 --- a/docs/legal/THIRD_PARTY_NOTICES.md +++ b/docs/legal/THIRD_PARTY_NOTICES.md @@ -28,17 +28,6 @@ Each entry should include: - attribution required? - notes -## Inter - -- name: Inter -- version / revision: 4.1 -- source URL: https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip -- license: SIL Open Font License 1.1 -- commercial use allowed? yes -- redistribution allowed? yes -- attribution required? license notice retained in `src/Trackdub.App/Assets/Fonts/Inter-LICENSE.txt` -- notes: Packaged as `InterVariable.ttf` and `InterVariable-Italic.ttf` for Trackdub UI typography. - ## FFmpeg / ffprobe Windows x64 binaries - name: FFmpeg / ffprobe Windows x64 binaries (BtbN FFmpeg-Builds, `lgpl-shared`) @@ -70,40 +59,7 @@ Each entry should include: - commercial use allowed? yes - redistribution allowed? yes, provided LGPL-2.1 obligations are met (dynamic linking, no source modifications) - attribution required? yes -- notes: .NET bindings for the LibVLC media framework. The application links to LibVLC dynamically (LGPL compliance). No modifications are made to the LibVLC or LibVLCSharp source. Used in `Trackdub.Media.Playback` and `Trackdub.App.Avalonia`. - -## LibVLCSharp.Avalonia - -- name: LibVLCSharp.Avalonia -- version / revision: centrally pinned in Directory.Packages.props -- source URL: https://www.nuget.org/packages/LibVLCSharp.Avalonia -- license: LGPL-2.1-or-later -- commercial use allowed? yes -- redistribution allowed? yes, same terms as LibVLCSharp -- attribution required? yes -- notes: Avalonia-specific VideoView control for rendering LibVLC video output. Used in `Trackdub.App.Avalonia` only. - -## VideoLAN.LibVLC.Windows (LibVLC native runtime — Windows) - -- name: VideoLAN.LibVLC.Windows -- version / revision: centrally pinned in Directory.Packages.props -- source URL: https://www.nuget.org/packages/VideoLAN.LibVLC.Windows -- license: LGPL-2.1-or-later -- commercial use allowed? yes -- redistribution allowed? yes, provided LGPL-2.1 notice and dynamic-linking obligations are met -- attribution required? yes -- notes: Bundled LibVLC native runtime for Windows (~100 MB). Extracted at build time into the application output directory. The application links dynamically — no static linking or source modification. Scoped to the Avalonia app project. - -## VideoLAN.LibVLC.Mac (LibVLC native runtime — macOS) - -- name: VideoLAN.LibVLC.Mac -- version / revision: centrally pinned in Directory.Packages.props -- source URL: https://www.nuget.org/packages/VideoLAN.LibVLC.Mac -- license: LGPL-2.1-or-later -- commercial use allowed? yes -- redistribution allowed? yes, same terms as the Windows runtime package -- attribution required? yes -- notes: Bundled LibVLC native runtime for macOS. Extracted at build time into the application output directory. The application links dynamically — no static linking or source modification. Scoped to the Avalonia app project. +- notes: .NET bindings for the LibVLC media framework. Trackdub links to LibVLC dynamically (LGPL compliance). No modifications are made to the LibVLC or LibVLCSharp source. Used in `Trackdub.Media.Playback` as one of two composited playback backends (libmpv is the primary compositor; LibVLC is the fallback). Bundling the native LibVLC runtime and any Avalonia-specific video-rendering control is a packaging concern of the consuming desktop product, not the public core, and is documented in that product's own third-party notices. ## LibVLC on Linux (system-installed) @@ -114,7 +70,7 @@ Each entry should include: - commercial use allowed? yes - redistribution allowed? n/a — not bundled; users install via their system package manager - attribution required? yes (license notice in documentation) -- notes: On Linux, no NuGet runtime package is available. The application uses the system-installed `libvlc.so` located by `LibVlcRuntimeLocator`. Users must install VLC (e.g., `sudo apt install vlc` or equivalent) for playback to function. +- notes: On Linux, no NuGet runtime package is available. `Trackdub.Media.Playback`'s `LibVlcRuntimeLocator` falls back to the system-installed `libvlc.so`. End users must install VLC (e.g., `sudo apt install vlc` or equivalent) for the LibVLC playback backend to function. ## Trackdub.OnnxRuntime.Dnnl.Native (generated package) diff --git a/scripts/ci/check-repository-boundary.py b/scripts/ci/check-repository-boundary.py new file mode 100644 index 0000000..2da97e1 --- /dev/null +++ b/scripts/ci/check-repository-boundary.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +"""Scan the public-core legal/policy surface for stale monorepo-era claims. + +Guards against regressions like the GPLv3/dual-license CLA wording and the +false "Implemented in Application layer" IExportTierGate claim found during +the open-core split: license text that contradicts the Apache-2.0 LICENSE, +and desktop/cloud project names bleeding into files a public consumer of +this core would read as authoritative. + +This intentionally does not scan docs/architecture, docs/specs, +docs/operations, docs/development, docs/decisions, docs/audits, docs/plans, +or tools/**: those contain legitimate historical/internal-engineering +references to pre-split monorepo projects (Trackdub.App.Avalonia, +Trackdub.Api, Trackdub.Worker, Trackdub.WebhookDelivery, the activation +service) that are a separate, larger documentation cleanup — not part of +this check. +""" + +from __future__ import annotations + +import re +import subprocess +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[2] + +# License-claim patterns: must never appear anywhere in the tracked tree +# (excluding historical planning/audit records and third-party notices, +# which may legitimately discuss or quote other licenses). +LICENSE_CLAIM_PATTERNS = [ + re.compile(r"GPLv3"), + re.compile(r"[Dd]ual[- ]licens"), +] +LICENSE_CLAIM_EXCLUDE_DIRS = [ + "docs/plans/", + "docs/decisions/", + "docs/audits/", +] +LICENSE_CLAIM_EXCLUDE_FILES = [ + "docs/legal/THIRD_PARTY_NOTICES.md", + "docs/legal/LICENSE-HISTORY.md", + "scripts/ci/check-repository-boundary.py", +] + +# Repository-boundary patterns: desktop/cloud project names that must not +# appear as if they were part of this public core's legal/policy/contract +# surface. +BOUNDARY_PATTERNS = [ + re.compile(r"Trackdub\.App\.Avalonia"), + re.compile(r"Trackdub\.Api\b"), + re.compile(r"Trackdub\.Worker\b"), + re.compile(r"Trackdub\.WebhookDelivery"), + re.compile(r"activation-service"), +] +BOUNDARY_SCAN_PATHS = [ + "LICENSE", + "NOTICE", + "README.md", + "AGENTS.md", + "CONTRIBUTING.md", + "docs/repository-policy.md", + "docs/legal/", + "src/Trackdub.Contracts/", +] + + +def tracked_files() -> list[Path]: + output = subprocess.run( + ["git", "ls-files"], + cwd=REPO_ROOT, + check=True, + capture_output=True, + text=True, + ).stdout + return [REPO_ROOT / line for line in output.splitlines() if line] + + +def matches_any(rel_posix: str, prefixes: list[str]) -> bool: + return any(rel_posix == p.rstrip("/") or rel_posix.startswith(p) for p in prefixes) + + +def scan(files: list[Path], patterns: list[re.Pattern[str]]) -> list[str]: + violations: list[str] = [] + for path in files: + try: + text = path.read_text(encoding="utf-8") + except (UnicodeDecodeError, OSError): + continue + for lineno, line in enumerate(text.splitlines(), start=1): + for pattern in patterns: + if pattern.search(line): + rel = path.relative_to(REPO_ROOT) + violations.append(f"{rel}:{lineno}: matched /{pattern.pattern}/: {line.strip()}") + return violations + + +def main() -> int: + all_files = tracked_files() + violations: list[str] = [] + + license_files = [ + f + for f in all_files + if not matches_any(f.relative_to(REPO_ROOT).as_posix(), LICENSE_CLAIM_EXCLUDE_DIRS) + and f.relative_to(REPO_ROOT).as_posix() not in LICENSE_CLAIM_EXCLUDE_FILES + ] + violations += scan(license_files, LICENSE_CLAIM_PATTERNS) + + boundary_files = [ + f + for f in all_files + if matches_any(f.relative_to(REPO_ROOT).as_posix(), BOUNDARY_SCAN_PATHS) + ] + violations += scan(boundary_files, BOUNDARY_PATTERNS) + + if violations: + print("Repository-boundary scan failed:") + for v in violations: + print(f" {v}") + print() + print( + "Stale license or desktop/cloud-boundary wording found in the public-core " + "legal/policy surface. See docs/legal/LICENSE-HISTORY.md and " + "docs/repository-policy.md." + ) + return 1 + + print("Repository-boundary scan passed.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/Trackdub.Cli/Trackdub.Cli.csproj b/src/Trackdub.Cli/Trackdub.Cli.csproj index dae9e1f..8595969 100644 --- a/src/Trackdub.Cli/Trackdub.Cli.csproj +++ b/src/Trackdub.Cli/Trackdub.Cli.csproj @@ -6,6 +6,11 @@ true trackdub Trackdub.Cli + Trackdub + Trackdub command-line interface for the local-first AI dubbing pipeline. + Apache-2.0 + https://github.com/trackdubllc/Trackdub + https://github.com/trackdubllc/Trackdub diff --git a/src/Trackdub.Contracts/IExportTierGate.cs b/src/Trackdub.Contracts/IExportTierGate.cs index f4ed061..5b7b4e5 100644 --- a/src/Trackdub.Contracts/IExportTierGate.cs +++ b/src/Trackdub.Contracts/IExportTierGate.cs @@ -1,7 +1,8 @@ namespace Trackdub.Contracts; /// -/// Checks tier gates before export begins. Implemented in Application layer. +/// Checks tier gates before export begins. The public core ships no concrete +/// implementation; a consuming product supplies its own tier policy. /// public interface IExportTierGate { diff --git a/src/Trackdub.OnnxRuntime.Dnnl.Native/Trackdub.OnnxRuntime.Dnnl.Native.csproj b/src/Trackdub.OnnxRuntime.Dnnl.Native/Trackdub.OnnxRuntime.Dnnl.Native.csproj index 31ae1c1..5b19bb4 100644 --- a/src/Trackdub.OnnxRuntime.Dnnl.Native/Trackdub.OnnxRuntime.Dnnl.Native.csproj +++ b/src/Trackdub.OnnxRuntime.Dnnl.Native/Trackdub.OnnxRuntime.Dnnl.Native.csproj @@ -8,6 +8,8 @@ Trackdub DNNL-enabled ONNX Runtime native assets for Trackdub. MIT + https://github.com/trackdubllc/Trackdub + https://github.com/trackdubllc/Trackdub false $(NoWarn);NU5128 diff --git a/tests/Trackdub.Media.Tests/WavePcm16Tests.cs b/tests/Trackdub.Media.Tests/WavePcm16Tests.cs index a19116e..28246de 100644 --- a/tests/Trackdub.Media.Tests/WavePcm16Tests.cs +++ b/tests/Trackdub.Media.Tests/WavePcm16Tests.cs @@ -399,7 +399,7 @@ await WavePcm16.WriteSamplesAsync( using (var destinationLock = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None)) { - Exception ex = await Assert.ThrowsAsync(() => + Exception ex = await Assert.ThrowsAnyAsync(() => WavePcm16.WriteSamplesAsync( path, [0.9f, -0.9f, 0.9f, -0.9f], From acf35b32615ec6826302d66738e57930a6328547 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 11:04:09 +0000 Subject: [PATCH 02/14] Fix broken LICENSE-HISTORY links and boundary-scanner exclude list - docs/legal/LICENSE-HISTORY.md: ../LICENSE and ../NOTICE resolved to docs/LICENSE and docs/NOTICE (404); fix to ../../LICENSE and ../../NOTICE. - scripts/ci/check-repository-boundary.py: LICENSE_CLAIM_EXCLUDE_DIRS was missing docs/architecture/, docs/specs/, docs/operations/, docs/development/, and tools/, contradicting the module docstring's stated scope and risking a false-positive CI failure on legitimate historical mentions in those trees. Addresses review feedback from Codex and cubic on PR #6. --- docs/legal/LICENSE-HISTORY.md | 4 ++-- scripts/ci/check-repository-boundary.py | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/legal/LICENSE-HISTORY.md b/docs/legal/LICENSE-HISTORY.md index b1bb67b..5acc0de 100644 --- a/docs/legal/LICENSE-HISTORY.md +++ b/docs/legal/LICENSE-HISTORY.md @@ -4,8 +4,8 @@ Trackdub is split into two repositories with different licenses: - **`trackdubllc/Trackdub`** (this repository) — the public core: SDK, CLI, pipeline, inference, media processing, infrastructure, and neutral - licensing mechanisms. Licensed under [Apache License, Version 2.0](../LICENSE) - (see the root [NOTICE](../NOTICE)). + licensing mechanisms. Licensed under [Apache License, Version 2.0](../../LICENSE) + (see the root [NOTICE](../../NOTICE)). - **The Trackdub desktop product** — a separate, private repository that depends on this public core. It owns the desktop application shell, product policy (export tiers, watermarking, entitlements), activation, diff --git a/scripts/ci/check-repository-boundary.py b/scripts/ci/check-repository-boundary.py index 2da97e1..dd1643d 100644 --- a/scripts/ci/check-repository-boundary.py +++ b/scripts/ci/check-repository-boundary.py @@ -36,6 +36,11 @@ "docs/plans/", "docs/decisions/", "docs/audits/", + "docs/architecture/", + "docs/specs/", + "docs/operations/", + "docs/development/", + "tools/", ] LICENSE_CLAIM_EXCLUDE_FILES = [ "docs/legal/THIRD_PARTY_NOTICES.md", From 5b9edd7911638bd69b75308aa87b49448be84132 Mon Sep 17 00:00:00 2001 From: Anthony Thompson Date: Sun, 26 Jul 2026 04:21:45 -0700 Subject: [PATCH 03/14] Update ci.yml --- .github/workflows/ci.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b8a4b0..861182e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ permissions: jobs: format: name: Verify Code Format - runs-on: ubuntu-latest + runs-on: self-hosted timeout-minutes: 15 steps: - name: Checkout Source @@ -41,7 +41,7 @@ jobs: repository-boundary: name: Verify Repository Boundary - runs-on: ubuntu-latest + runs-on: self-hosted timeout-minutes: 5 steps: - name: Checkout Source @@ -59,11 +59,9 @@ jobs: matrix: include: - name: Windows - runner: windows-latest + runner: self-hosted - name: Linux - runner: ubuntu-latest - - name: macOS - runner: macos-14 + runner: self-hosted steps: - name: Checkout Source From 19b0873b8c9eb2c73452bb054f60050ed74bc189 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 11:31:24 +0000 Subject: [PATCH 04/14] Fix self-hosted runner routing and dotnet-install permission failure - Add OS-specific labels ([self-hosted, Windows/Linux/macOS]) to every job instead of the bare `self-hosted` label. With only one registered self-hosted runner (Windows), the bare label was routing the Linux build job, the format job, and the repository-boundary job onto that same Windows box, silently skipping Linux/macOS coverage entirely (flagged by Codex review) and hitting a separate PowerShell script-execution-policy failure on the boundary scanner's run step. - Restore the macOS matrix leg dropped in the direct self-hosted edit. - Set DOTNET_INSTALL_DIR to the per-job runner temp directory so actions/setup-dotnet doesn't try to write to the system-wide default location (e.g. C:\Program Files\dotnet), which self-hosted runner service accounts often can't write to. - Add explicit `shell: bash` to every run: step for consistent behavior across self-hosted runner OSes (Windows defaults run: steps to PowerShell, which blocked the boundary scanner step above). --- .github/workflows/ci.yml | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 861182e..caa9273 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,10 +15,17 @@ permissions: contents: read pull-requests: read +env: + # actions/setup-dotnet's install script defaults to a system-wide location + # (e.g. C:\Program Files\dotnet) that self-hosted runner service accounts + # often can't write to. Redirect installs into the per-job runner temp + # directory, which is always writable regardless of runner OS or account. + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet + jobs: format: name: Verify Code Format - runs-on: self-hosted + runs-on: [self-hosted, Linux] timeout-minutes: 15 steps: - name: Checkout Source @@ -34,20 +41,23 @@ jobs: NuGet.config - name: Restore Dependencies + shell: bash run: dotnet restore Trackdub.slnx -m:1 - name: Verify Format Style Limits + shell: bash run: dotnet format Trackdub.slnx --verify-no-changes repository-boundary: name: Verify Repository Boundary - runs-on: self-hosted + runs-on: [self-hosted, Linux] timeout-minutes: 5 steps: - name: Checkout Source uses: actions/checkout@v7 - name: Scan for stale license/desktop-boundary claims + shell: bash run: python3 scripts/ci/check-repository-boundary.py build: @@ -59,9 +69,11 @@ jobs: matrix: include: - name: Windows - runner: self-hosted + runner: [self-hosted, Windows] - name: Linux - runner: self-hosted + runner: [self-hosted, Linux] + - name: macOS + runner: [self-hosted, macOS] steps: - name: Checkout Source @@ -77,10 +89,13 @@ jobs: NuGet.config - name: Restore Dependencies + shell: bash run: dotnet restore Trackdub.slnx -m:1 - name: Compile + shell: bash run: dotnet build Trackdub.slnx -c Release --no-restore -m:1 - name: Test + shell: bash run: dotnet test Trackdub.slnx -c Release --no-build -m:1 From dd0cd98205a4ccfd367fe8993c4a1aad0321a80a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 11:32:34 +0000 Subject: [PATCH 05/14] Fix invalid runs-on expression that broke workflow parsing runs-on: \${{ matrix.runner }} where matrix.runner was itself a YAML array made the whole workflow file fail to parse (0 jobs scheduled on the previous run). Use a scalar matrix.os string combined with a literal self-hosted label instead: runs-on: [self-hosted, "\${{ matrix.os }}"]. --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index caa9273..09eb098 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,17 +63,17 @@ jobs: build: name: Build & Test (${{ matrix.name }}) timeout-minutes: 45 - runs-on: ${{ matrix.runner }} + runs-on: [self-hosted, "${{ matrix.os }}"] strategy: fail-fast: false matrix: include: - name: Windows - runner: [self-hosted, Windows] + os: Windows - name: Linux - runner: [self-hosted, Linux] + os: Linux - name: macOS - runner: [self-hosted, macOS] + os: macOS steps: - name: Checkout Source From 24967929e07fcf4d43aeb67c0d9f6a6af54e37de Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 11:33:58 +0000 Subject: [PATCH 06/14] Unroll build matrix into static per-OS jobs The follow-up fix (runs-on: [self-hosted, "\${{ matrix.os }}"]) still left the workflow file unparseable on GitHub's side (0 jobs scheduled, run name fell back to the file path). Embedding an expression inside a runs-on array literal isn't accepted. Replace the single matrix-driven build job with three explicit build-windows/build-linux/build-macos jobs, each with a static runs-on: [self-hosted, ] array -- the one form of self-hosted label targeting GitHub documents and that already parsed successfully for the format and repository-boundary jobs. --- .github/workflows/ci.yml | 73 +++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09eb098..cff705e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,21 +60,68 @@ jobs: shell: bash run: python3 scripts/ci/check-repository-boundary.py - build: - name: Build & Test (${{ matrix.name }}) + build-windows: + name: Build & Test (Windows) timeout-minutes: 45 - runs-on: [self-hosted, "${{ matrix.os }}"] - strategy: - fail-fast: false - matrix: - include: - - name: Windows - os: Windows - - name: Linux - os: Linux - - name: macOS - os: macOS + runs-on: [self-hosted, Windows] + steps: + - name: Checkout Source + uses: actions/checkout@v7 + + - name: Setup .NET + uses: actions/setup-dotnet@v6 + with: + dotnet-version: '10.0.x' + cache: true + cache-dependency-path: | + **/packages.lock.json + NuGet.config + + - name: Restore Dependencies + shell: bash + run: dotnet restore Trackdub.slnx -m:1 + + - name: Compile + shell: bash + run: dotnet build Trackdub.slnx -c Release --no-restore -m:1 + + - name: Test + shell: bash + run: dotnet test Trackdub.slnx -c Release --no-build -m:1 + + build-linux: + name: Build & Test (Linux) + timeout-minutes: 45 + runs-on: [self-hosted, Linux] + steps: + - name: Checkout Source + uses: actions/checkout@v7 + + - name: Setup .NET + uses: actions/setup-dotnet@v6 + with: + dotnet-version: '10.0.x' + cache: true + cache-dependency-path: | + **/packages.lock.json + NuGet.config + - name: Restore Dependencies + shell: bash + run: dotnet restore Trackdub.slnx -m:1 + + - name: Compile + shell: bash + run: dotnet build Trackdub.slnx -c Release --no-restore -m:1 + + - name: Test + shell: bash + run: dotnet test Trackdub.slnx -c Release --no-build -m:1 + + build-macos: + name: Build & Test (macOS) + timeout-minutes: 45 + runs-on: [self-hosted, macOS] steps: - name: Checkout Source uses: actions/checkout@v7 From beabe773bd53e9c368e08d40432bfb7d3969b584 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 11:36:12 +0000 Subject: [PATCH 07/14] Move DOTNET_INSTALL_DIR from workflow-level to per-job env Root cause of every "invalid workflow file" run since the previous three commits: the runner context (\${{ runner.temp }}) isn't available in a top-level workflow env: block, only inside a job. That single bad top-level env entry silently invalidated the whole file (0 jobs scheduled, GitHub falling back to the file path as the run name) regardless of what else changed in runs-on/matrix syntax. Move DOTNET_INSTALL_DIR into each job's own env: block instead. --- .github/workflows/ci.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cff705e..99b6832 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,18 +15,19 @@ permissions: contents: read pull-requests: read -env: - # actions/setup-dotnet's install script defaults to a system-wide location - # (e.g. C:\Program Files\dotnet) that self-hosted runner service accounts - # often can't write to. Redirect installs into the per-job runner temp - # directory, which is always writable regardless of runner OS or account. - DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet - jobs: format: name: Verify Code Format runs-on: [self-hosted, Linux] timeout-minutes: 15 + # actions/setup-dotnet's install script defaults to a system-wide location + # (e.g. C:\Program Files\dotnet) that self-hosted runner service accounts + # often can't write to. Redirect installs into the per-job runner temp + # directory, which is always writable regardless of runner OS or account. + # (The `runner` context is only available inside a job, not at the + # workflow-level `env:`, so this is set per job.) + env: + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 @@ -64,6 +65,8 @@ jobs: name: Build & Test (Windows) timeout-minutes: 45 runs-on: [self-hosted, Windows] + env: + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 @@ -93,6 +96,8 @@ jobs: name: Build & Test (Linux) timeout-minutes: 45 runs-on: [self-hosted, Linux] + env: + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 @@ -122,6 +127,8 @@ jobs: name: Build & Test (macOS) timeout-minutes: 45 runs-on: [self-hosted, macOS] + env: + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 From 7b018051062a20d34432809dca662909504b11e6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 11:38:47 +0000 Subject: [PATCH 08/14] Diagnostic: drop DOTNET_INSTALL_DIR env to isolate workflow-parse failure Every run since the runner.temp-based env block was added has come back as an unparseable workflow file (0 jobs scheduled, run name falls back to the file path), even after moving it from workflow-level to per-job env. Removing it entirely to confirm whether that expression is actually the cause before trying another workaround. --- .github/workflows/ci.yml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99b6832..c8da51c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,14 +20,6 @@ jobs: name: Verify Code Format runs-on: [self-hosted, Linux] timeout-minutes: 15 - # actions/setup-dotnet's install script defaults to a system-wide location - # (e.g. C:\Program Files\dotnet) that self-hosted runner service accounts - # often can't write to. Redirect installs into the per-job runner temp - # directory, which is always writable regardless of runner OS or account. - # (The `runner` context is only available inside a job, not at the - # workflow-level `env:`, so this is set per job.) - env: - DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 @@ -65,8 +57,6 @@ jobs: name: Build & Test (Windows) timeout-minutes: 45 runs-on: [self-hosted, Windows] - env: - DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 @@ -96,8 +86,6 @@ jobs: name: Build & Test (Linux) timeout-minutes: 45 runs-on: [self-hosted, Linux] - env: - DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 @@ -127,8 +115,6 @@ jobs: name: Build & Test (macOS) timeout-minutes: 45 runs-on: [self-hosted, macOS] - env: - DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 From a289325e0268155f6488cf78ee130fa598b3291b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 11:54:42 +0000 Subject: [PATCH 09/14] Bisect: isolate whether static runs-on array syntax alone is the problem Reverting to the last known-good workflow content (5b9edd79, which actually scheduled real jobs) and changing only one thing: format/ repository-boundary runs-on: self-hosted -> runs-on: [self-hosted, Linux]. Everything else (matrix-driven build job, no shell: overrides) restored verbatim, to find out whether this array-literal form is itself what breaks parsing before reapplying the rest of the fix. --- .github/workflows/ci.yml | 77 ++++++---------------------------------- 1 file changed, 11 insertions(+), 66 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8da51c..eb8e56c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,11 +34,9 @@ jobs: NuGet.config - name: Restore Dependencies - shell: bash run: dotnet restore Trackdub.slnx -m:1 - name: Verify Format Style Limits - shell: bash run: dotnet format Trackdub.slnx --verify-no-changes repository-boundary: @@ -50,71 +48,21 @@ jobs: uses: actions/checkout@v7 - name: Scan for stale license/desktop-boundary claims - shell: bash run: python3 scripts/ci/check-repository-boundary.py - build-windows: - name: Build & Test (Windows) + build: + name: Build & Test (${{ matrix.name }}) timeout-minutes: 45 - runs-on: [self-hosted, Windows] - steps: - - name: Checkout Source - uses: actions/checkout@v7 - - - name: Setup .NET - uses: actions/setup-dotnet@v6 - with: - dotnet-version: '10.0.x' - cache: true - cache-dependency-path: | - **/packages.lock.json - NuGet.config - - - name: Restore Dependencies - shell: bash - run: dotnet restore Trackdub.slnx -m:1 - - - name: Compile - shell: bash - run: dotnet build Trackdub.slnx -c Release --no-restore -m:1 - - - name: Test - shell: bash - run: dotnet test Trackdub.slnx -c Release --no-build -m:1 - - build-linux: - name: Build & Test (Linux) - timeout-minutes: 45 - runs-on: [self-hosted, Linux] - steps: - - name: Checkout Source - uses: actions/checkout@v7 + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - name: Windows + runner: self-hosted + - name: Linux + runner: self-hosted - - name: Setup .NET - uses: actions/setup-dotnet@v6 - with: - dotnet-version: '10.0.x' - cache: true - cache-dependency-path: | - **/packages.lock.json - NuGet.config - - - name: Restore Dependencies - shell: bash - run: dotnet restore Trackdub.slnx -m:1 - - - name: Compile - shell: bash - run: dotnet build Trackdub.slnx -c Release --no-restore -m:1 - - - name: Test - shell: bash - run: dotnet test Trackdub.slnx -c Release --no-build -m:1 - - build-macos: - name: Build & Test (macOS) - timeout-minutes: 45 - runs-on: [self-hosted, macOS] steps: - name: Checkout Source uses: actions/checkout@v7 @@ -129,13 +77,10 @@ jobs: NuGet.config - name: Restore Dependencies - shell: bash run: dotnet restore Trackdub.slnx -m:1 - name: Compile - shell: bash run: dotnet build Trackdub.slnx -c Release --no-restore -m:1 - name: Test - shell: bash run: dotnet test Trackdub.slnx -c Release --no-build -m:1 From 6618235f54f331128f0f9804f18430388d5372ef Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 12:03:47 +0000 Subject: [PATCH 10/14] Reapply self-hosted OS labels, macOS leg, and dotnet-install fix; broaden GPL scanner pattern Root-caused the earlier "invalid workflow file" runs (0 jobs scheduled, Codex/list_workflow_jobs both confirmed): they were not a YAML/schema problem. A workflow_dispatch run on the exact runs-on: [self-hosted, Linux] content that had been failing scheduled 4 real jobs immediately, proving the syntax is valid. The push-triggered failures line up with rapid-fire commits sharing the same concurrency group (cancel-in-progress: true) during the debugging session, not the file content. With that confirmed, reapply the rest of the self-hosted fix on top of the validated baseline: - OS-specific runs-on: [self-hosted, Windows/Linux/macOS] on every job instead of the bare self-hosted label, so jobs land on the matching OS runner instead of all piling onto whichever one runner happens to be registered. - Restore the macOS build leg. - shell: bash on every run: step (Windows self-hosted defaults run: steps to PowerShell, which blocked a script-execution-policy-restricted step during debugging). - Per-job env: DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet, to work around actions/setup-dotnet defaulting to a system-wide install location self-hosted runner accounts often can't write to. Also broaden scripts/ci/check-repository-boundary.py's GPL pattern per Codex review feedback: the previous exact-string "GPLv3" match missed "GPL-3.0-only", "GPL 3.0", and "GNU General Public License" while still correctly excluding LGPL/AGPL (checked via a negative lookbehind for a preceding letter). --- .github/workflows/ci.yml | 91 ++++++++++++++++++++++--- scripts/ci/check-repository-boundary.py | 5 +- 2 files changed, 84 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb8e56c..d0f6615 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,14 @@ jobs: name: Verify Code Format runs-on: [self-hosted, Linux] timeout-minutes: 15 + env: + # actions/setup-dotnet's install script defaults to a system-wide + # location (e.g. C:\Program Files\dotnet) that self-hosted runner + # service accounts often can't write to. Redirect installs into the + # per-job runner temp directory, which is always writable regardless + # of runner OS or account. (The `runner` context is only available + # inside a job, not at the workflow-level `env:`.) + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 @@ -34,9 +42,11 @@ jobs: NuGet.config - name: Restore Dependencies + shell: bash run: dotnet restore Trackdub.slnx -m:1 - name: Verify Format Style Limits + shell: bash run: dotnet format Trackdub.slnx --verify-no-changes repository-boundary: @@ -48,21 +58,77 @@ jobs: uses: actions/checkout@v7 - name: Scan for stale license/desktop-boundary claims + shell: bash run: python3 scripts/ci/check-repository-boundary.py - build: - name: Build & Test (${{ matrix.name }}) + build-windows: + name: Build & Test (Windows) timeout-minutes: 45 - runs-on: ${{ matrix.runner }} - strategy: - fail-fast: false - matrix: - include: - - name: Windows - runner: self-hosted - - name: Linux - runner: self-hosted + runs-on: [self-hosted, Windows] + env: + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet + steps: + - name: Checkout Source + uses: actions/checkout@v7 + + - name: Setup .NET + uses: actions/setup-dotnet@v6 + with: + dotnet-version: '10.0.x' + cache: true + cache-dependency-path: | + **/packages.lock.json + NuGet.config + + - name: Restore Dependencies + shell: bash + run: dotnet restore Trackdub.slnx -m:1 + + - name: Compile + shell: bash + run: dotnet build Trackdub.slnx -c Release --no-restore -m:1 + + - name: Test + shell: bash + run: dotnet test Trackdub.slnx -c Release --no-build -m:1 + + build-linux: + name: Build & Test (Linux) + timeout-minutes: 45 + runs-on: [self-hosted, Linux] + env: + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet + steps: + - name: Checkout Source + uses: actions/checkout@v7 + - name: Setup .NET + uses: actions/setup-dotnet@v6 + with: + dotnet-version: '10.0.x' + cache: true + cache-dependency-path: | + **/packages.lock.json + NuGet.config + + - name: Restore Dependencies + shell: bash + run: dotnet restore Trackdub.slnx -m:1 + + - name: Compile + shell: bash + run: dotnet build Trackdub.slnx -c Release --no-restore -m:1 + + - name: Test + shell: bash + run: dotnet test Trackdub.slnx -c Release --no-build -m:1 + + build-macos: + name: Build & Test (macOS) + timeout-minutes: 45 + runs-on: [self-hosted, macOS] + env: + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 @@ -77,10 +143,13 @@ jobs: NuGet.config - name: Restore Dependencies + shell: bash run: dotnet restore Trackdub.slnx -m:1 - name: Compile + shell: bash run: dotnet build Trackdub.slnx -c Release --no-restore -m:1 - name: Test + shell: bash run: dotnet test Trackdub.slnx -c Release --no-build -m:1 diff --git a/scripts/ci/check-repository-boundary.py b/scripts/ci/check-repository-boundary.py index dd1643d..e6b3f6e 100644 --- a/scripts/ci/check-repository-boundary.py +++ b/scripts/ci/check-repository-boundary.py @@ -29,7 +29,10 @@ # (excluding historical planning/audit records and third-party notices, # which may legitimately discuss or quote other licenses). LICENSE_CLAIM_PATTERNS = [ - re.compile(r"GPLv3"), + # Matches GPL/GPLv3/GPL-3.0-only/"GPL 3.0" etc., case-insensitively, + # while excluding LGPL/AGPL (a letter immediately before "GPL"). + re.compile(r"(? Date: Sun, 26 Jul 2026 12:39:50 +0000 Subject: [PATCH 11/14] Exclude LGPL/AGPL prose from the broadened GPL scanner pattern The General Public License regex added in the last commit had no letter-preceding exclusion (unlike the GPL token pattern next to it), so it would false-positive on "GNU Lesser General Public License" and "GNU Affero General Public License" -- both distinct, legitimately-used licenses, not the copyleft GPL wording this scanner exists to catch. Addresses Codex review feedback on the prior commit. --- scripts/ci/check-repository-boundary.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/ci/check-repository-boundary.py b/scripts/ci/check-repository-boundary.py index e6b3f6e..fb9bcf9 100644 --- a/scripts/ci/check-repository-boundary.py +++ b/scripts/ci/check-repository-boundary.py @@ -32,7 +32,9 @@ # Matches GPL/GPLv3/GPL-3.0-only/"GPL 3.0" etc., case-insensitively, # while excluding LGPL/AGPL (a letter immediately before "GPL"). re.compile(r"(? Date: Sun, 26 Jul 2026 12:54:23 +0000 Subject: [PATCH 12/14] Align LICENSE copyright holder with NOTICE; broaden dual-license regex - LICENSE: copyright line said "Copyright 2026 tonythethompson" (a personal name); changed to "Copyright 2024-2026 Trackdub LLC" to match root NOTICE. (The license body wording itself was already verified against the canonical apache.org/licenses/LICENSE-2.0.txt text and matches exactly -- no change needed there.) - scripts/ci/check-repository-boundary.py: the dual-license pattern only matched a capitalized-first-letter variant ([Dd]ual...); switched to re.IGNORECASE so all-caps forms like DUAL-LICENSED aren't missed, matching the style already used for the GPL patterns. Addresses further Codex review feedback and an owner-confirmed fix on PR #6. --- LICENSE | 2 +- scripts/ci/check-repository-boundary.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 261061c..0a97c85 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2026 tonythethompson + Copyright 2024-2026 Trackdub LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/scripts/ci/check-repository-boundary.py b/scripts/ci/check-repository-boundary.py index fb9bcf9..d4240f2 100644 --- a/scripts/ci/check-repository-boundary.py +++ b/scripts/ci/check-repository-boundary.py @@ -35,7 +35,7 @@ # Same exclusion for the prose form: "GNU Lesser/Affero General Public # License" are distinct, permissive-enough-to-be-legitimate licenses. re.compile(r"(? Date: Sun, 26 Jul 2026 12:55:19 +0000 Subject: [PATCH 13/14] Fix real root cause of workflow-parse failure: runner context needs step scope GitHub's own workflow validation (surfaced via a workflow_dispatch API call, which returns actual parse errors unlike a push/pull_request- triggered run) confirms it precisely: "Unrecognized named-value: 'runner'" at every DOTNET_INSTALL_DIR: ${{ runner.temp }} line. The runner context is only available inside a step (its own env/run/with), not at the job- or workflow-level env: block -- job-level was already tried and also invalid, contrary to what an earlier commit message concluded from indirect evidence. Move DOTNET_INSTALL_DIR onto the "Setup .NET" step's own env: in every job. --- .github/workflows/ci.yml | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d0f6615..31f301e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,20 +20,21 @@ jobs: name: Verify Code Format runs-on: [self-hosted, Linux] timeout-minutes: 15 - env: - # actions/setup-dotnet's install script defaults to a system-wide - # location (e.g. C:\Program Files\dotnet) that self-hosted runner - # service accounts often can't write to. Redirect installs into the - # per-job runner temp directory, which is always writable regardless - # of runner OS or account. (The `runner` context is only available - # inside a job, not at the workflow-level `env:`.) - DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 - name: Setup .NET uses: actions/setup-dotnet@v6 + # actions/setup-dotnet's install script defaults to a system-wide + # location (e.g. C:\Program Files\dotnet) that self-hosted runner + # service accounts often can't write to. Redirect installs into the + # per-step runner temp directory, which is always writable + # regardless of runner OS or account. (The `runner` context is + # only available inside a step, not at the job- or workflow-level + # `env:` -- confirmed via GitHub's own workflow validation error.) + env: + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet with: dotnet-version: '10.0.x' cache: true @@ -65,14 +66,14 @@ jobs: name: Build & Test (Windows) timeout-minutes: 45 runs-on: [self-hosted, Windows] - env: - DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 - name: Setup .NET uses: actions/setup-dotnet@v6 + env: + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet with: dotnet-version: '10.0.x' cache: true @@ -96,14 +97,14 @@ jobs: name: Build & Test (Linux) timeout-minutes: 45 runs-on: [self-hosted, Linux] - env: - DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 - name: Setup .NET uses: actions/setup-dotnet@v6 + env: + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet with: dotnet-version: '10.0.x' cache: true @@ -127,14 +128,14 @@ jobs: name: Build & Test (macOS) timeout-minutes: 45 runs-on: [self-hosted, macOS] - env: - DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet steps: - name: Checkout Source uses: actions/checkout@v7 - name: Setup .NET uses: actions/setup-dotnet@v6 + env: + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet with: dotnet-version: '10.0.x' cache: true From ac68c9169fac828d01a46391c9ab005bab6f398f Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 13:03:01 +0000 Subject: [PATCH 14/14] Match repository-boundary project-name patterns case-insensitively Same class of bypass as the GPL/dual-license patterns: a differently cased identifier like "trackdub.api" or "Trackdub.worker" (common in URLs and prose) would slip past the case-sensitive boundary regexes. Add re.IGNORECASE to all five. Addresses further Codex review feedback on PR #6. --- scripts/ci/check-repository-boundary.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/ci/check-repository-boundary.py b/scripts/ci/check-repository-boundary.py index d4240f2..d85fc82 100644 --- a/scripts/ci/check-repository-boundary.py +++ b/scripts/ci/check-repository-boundary.py @@ -57,11 +57,11 @@ # appear as if they were part of this public core's legal/policy/contract # surface. BOUNDARY_PATTERNS = [ - re.compile(r"Trackdub\.App\.Avalonia"), - re.compile(r"Trackdub\.Api\b"), - re.compile(r"Trackdub\.Worker\b"), - re.compile(r"Trackdub\.WebhookDelivery"), - re.compile(r"activation-service"), + re.compile(r"Trackdub\.App\.Avalonia", re.IGNORECASE), + re.compile(r"Trackdub\.Api\b", re.IGNORECASE), + re.compile(r"Trackdub\.Worker\b", re.IGNORECASE), + re.compile(r"Trackdub\.WebhookDelivery", re.IGNORECASE), + re.compile(r"activation-service", re.IGNORECASE), ] BOUNDARY_SCAN_PATHS = [ "LICENSE",