Skip to content

Stage 0 Wave C: continuous integration - #4

Merged
June74 merged 15 commits into
mainfrom
codex/stage0-wave-c
Aug 17, 2026
Merged

Stage 0 Wave C: continuous integration#4
June74 merged 15 commits into
mainfrom
codex/stage0-wave-c

Conversation

@June74

@June74 June74 commented Aug 17, 2026

Copy link
Copy Markdown
Owner

What changed and why

Stage 0 Wave C — continuous integration. Wave A established governance and records; Wave B created the toolchain pins and the solution's dependency graph; this wave makes both enforceable.

Adds .github/workflows/ci.yml with six jobs, and records their names as the required checks branch protection will gate on.

Job Runner Verifies
build-and-test windows C# formatting, restore, build, tests, and a minimum discovered-test count
plugin ubuntu The Obsidian plugin installs from the committed lockfile and type-checks
docs ubuntu Every repository-internal Markdown link resolves
secret-scan ubuntu gitleaks finds no secret across full history
dependency-review ubuntu No vulnerable or incompatible-licence dependency is introduced
artifact windows Publish, checksum, and SBOM generation succeed

.NET jobs run on Windows because D-003 fixes Windows 11 x64 as the only supported platform and the Stage 0 gate requires a clean Windows build. Jobs that do not touch the build run on Ubuntu.

Requirements and decisions satisfied

  • Stage 0's CI bullet: formatting, build, unit tests, dependency review, secret scanning, SBOM generation, and checksummed development artifacts.
  • D-091 — the branch-protection ruleset was documented in Wave A and its "Required checks" section left explicitly to be filled in by Wave C. It now names six real, verified-green job names.
  • D-018 — the artifact carries a SHA-256 file. This is integrity evidence only. A checksum published on the same channel as the file it describes proves the bytes were not corrupted; it proves nothing about who produced them. Automatic installation requires a signature or signed attestation anchored to a pinned project identity, which arrives at Stage 8. The artifact here is explicitly a development artifact: unsigned, manual-install only.
  • D-003 — Windows runners for everything that compiles or tests.

Security and privacy effects

This changes the trust surface, in the direction of more enforcement.

  • Secret scanning gains a required check. GitHub's native secret scanning and push protection were already enabled on this repository, but neither is a status check — branch protection cannot require them. The secret-scan job closes that gap. It runs with fetch-depth: 0 so a secret introduced and later removed is still caught, --exit-code 1 so a finding fails the job rather than being reported and ignored, and --redact so detected values never reach the world-readable log.
  • gitleaks is version-pinned and downloaded directly rather than through a marketplace action, and every third-party action is pinned to an explicit tag. Workflow permissions are contents: read throughout.
  • Two repository settings are proposed but not applied in this PR: enabling non-provider secret-scanning patterns and validity checks. Both require action outside the repository tree.

No secret value appears anywhere in this branch. The gitleaks scan over full history is clean.

Verification

The workflow is green on this branch's head commit, with all five push-applicable jobs passing and dependency-review correctly skipped on a push event (the action only operates on a pull-request diff).

Runtime execution found three defects that two careful static reviews could not. Both reviews read the YAML closely and one caught a real bug, but these three were only findable by running it:

  1. The workflow never fired. Triggers were push: [main] plus pull_request: [main], so pushing a feature branch triggered nothing at all — it was unverifiable by construction. The push trigger now also matches codex/**.
  2. .gitattributes and .editorconfig disagreed about line endings. The former declared eol=lf for all text; the latter declared crlf for .cs, .csproj, .props, and .targets. Invisible until a formatter ran, then roughly a hundred ENDOFLINE errors. .gitattributes was incomplete and now matches the convention .editorconfig already established and that .sln already followed. The only content change was stripping a stray UTF-8 BOM from three Program.cs files — no logic changed.
  3. The checksum file did not verify. PowerShell's Out-File wrote CRLF, so the filename field became openmemory-dev.zip\r and sha256sum -c reported FAILED open or read. The step now writes explicit LF. This is why the plan required downloading the artifact and checking the hash rather than asserting the file exists — a checksum nobody has verified is decoration.

A fourth defect was caught by review before it ran: the artifact job's dotnet publish loop had no exit-code check, and PowerShell does not treat a native command's non-zero exit as terminating. A failure in one project would have let the loop continue, Compress-Archive would have succeeded on the partial set, and the job would have gone green with a valid checksum over an incomplete package.

Checksum round-trip: the artifact was downloaded from the completed run and sha256sum -c returned openmemory-dev.zip: OK.

Independent review: every task received a spec-and-quality review, and the completed branch was reviewed by codex exec.

Remaining limitations or deferred work

  • Branch protection is NOT enabled by this PR. The ruleset and its required checks are documented; enabling it requires the repository owner's explicit approval, and .github/branch-protection.md says so plainly rather than claiming a capability that does not exist.
  • Duplicate runs on pull requests. With push matching codex/** and pull_request targeting main, a PR from a codex/* branch fires both events. The concurrency group keys on github.ref, which differs between them, so it does not deduplicate. Two runs post identically-named checks against the same commit. This costs Actions minutes and could make required-check resolution ambiguous, which is why check behaviour on a real pull request should be observed before protection is enabled.
  • dependency-review is skipped on push events and GitHub treats a skipped required check as passing. That is correct here — the action is meaningless without a pull-request diff — but it is a deliberate choice, not an accident.
  • No TypeScript formatter. dotnet format covers C# without a new dependency; adding Prettier for one seven-line stub file belongs to Stage 6, when the plugin is real.
  • Code signing and attestation remain at Stage 8 per D-018 and F-007.
  • The heading "Ruleset to enable in Wave C" in .github/branch-protection.md still reads as a forward reference to a milestone now effectively passed. Left for whichever change next touches that document.

  • Every commit is signed off (git commit -s), per the Developer Certificate of Origin.
  • This change contains no secret value, credential, private transcript, or personal memory content.
  • Planned behavior is not described as though it were implemented.

June74 and others added 15 commits August 16, 2026 22:00
Records seven brainstorming decisions: checksummed development
artifact, a CI secret-scanning job alongside the already-enabled
native scanning, validity checks, Windows runners for .NET jobs, a
discovered-test-count assertion, C#-only formatting, and one SBOM
generator covering both ecosystems.

Also records the three repository settings changes this wave needs,
so the footprint outside the repository tree is visible up front.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Injun Lee <2006ijlee@gmail.com>
Six tasks: the correctness jobs, the security and release jobs,
pushing and driving every job green, recording the required checks,
the approval-gated repository settings and branch protection, and
integration verification with independent Codex review.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Injun Lee <2006ijlee@gmail.com>
The test step asserts a minimum discovered-test count because dotnet
test exits 0 both when no tests exist and when discovery is broken.

Signed-off-by: Injun Lee <2006ijlee@gmail.com>
gitleaks is version-pinned and downloaded directly rather than through
a marketplace action with licensing conditions. It runs with full
history, redacted output, and a failing exit code on any finding.
Pinned to v8.30.1; uses the `git` subcommand rather than the
deprecated `detect` subcommand per that release's README.

The artifact job publishes the three executables, checksums the
archive, and uploads it with a CycloneDX SBOM. Per D-018 the checksum
is integrity evidence only; it is not publisher authentication.

Signed-off-by: Injun Lee <2006ijlee@gmail.com>
pwsh does not treat a non-zero exit from a native command as a
terminating error, and the runner only fails a pwsh step on an
uncaught PowerShell exception or explicit exit. Without a check, a
failing dotnet publish (e.g. McpBridge fails to compile) would let
the loop continue, leaving a partial artifacts/publish/ directory
that Compress-Archive and Get-FileHash would happily process into a
consistent-looking but incomplete, misleading package.

Add an explicit $LASTEXITCODE check after each publish call, so a
failure in the first iteration aborts before the second one runs.

Signed-off-by: Injun Lee <2006ijlee@gmail.com>
Push/pull_request triggers only fire for main, so pushing a feature
branch produces no run and this task is forbidden from opening a PR.
workflow_dispatch lets the workflow be run manually against this
branch to get real execution evidence, without widening the
push/pull_request trigger scope.

Signed-off-by: Injun Lee <2006ijlee@gmail.com>
workflow_dispatch requires the workflow to already exist on the
default branch, so it cannot dispatch a run for this branch before
it merges to main. All work in this repo happens on codex/* branches
before merging (see codex/stage0-wave-a, -b, -c), so extending the
push trigger to that pattern lets CI actually execute and be verified
pre-merge, which is the only way to get runtime proof without opening
a pull request.

Signed-off-by: Injun Lee <2006ijlee@gmail.com>
Wave B's files were never format-checked by CI. dotnet format
--verify-no-changes failed on windows-latest with ENDOFLINE errors
across every .cs file, because .gitattributes forced eol=lf for all
text files while .editorconfig requires eol=crlf for .cs/.csproj/
.props/.targets. Running dotnet format locally fixed the working
tree, but committing it as-is would have git silently renormalize the
line endings back to LF on the next checkout (per .gitattributes),
reproducing the same CI failure in a loop.

Fixed by adding eol=crlf entries to .gitattributes for
.cs/.csproj/.props/.targets, matching the extensions .editorconfig
already treats as crlf, then renormalizing the tree. The only content
diff (not just whitespace) is a stray UTF-8 BOM that dotnet format
stripped from three Program.cs files; no logic or member reordering.

Signed-off-by: Injun Lee <2006ijlee@gmail.com>
Step 4's end-to-end check (download the artifact, run sha256sum -c on
it) failed: Out-File on windows-latest wrote
openmemory-dev.zip.sha256 with a trailing CRLF, so sha256sum read the
filename as 'openmemory-dev.zip\r', which does not match the actual
extracted file, and reported FAILED open or read instead of OK. Write
the file directly via File.WriteAllText with an explicit LF so the
checksum file is consumable by the standard sha256sum -c tool
regardless of which OS produced it.

Signed-off-by: Injun Lee <2006ijlee@gmail.com>
Names the six job names branch protection will require, and states
that renaming a job silently un-enforces it unless both files change.

Signed-off-by: Injun Lee <2006ijlee@gmail.com>
…ral reason

Finding 1: Replace internal task number reference with durable condition.
Status now says enablement is pending repository owner approval, not Task 5.

Finding 2: Rewrite section to resolve contradiction with status.
Clarify that the original reason for deferral (no checks) is now resolved.
State that the remaining gate is owner approval, not missing prerequisites.
Confirm that main is protected by process until enablement, and codex exec
review remains the operative control.

Rename section from "Why it is deferred" to "Enablement is pending
repository owner approval" to reflect current state.

Signed-off-by: Injun Lee <2006ijlee@gmail.com>
Fix A: drop the codex/** push trigger. A codex/* branch with an open PR
fired both push and pull_request events; the concurrency group keys on
github.ref, which differs between the two, so it never deduplicated.
gh pr checks 4 showed twelve entries for six jobs, with dependency-review
reporting fail and skipping simultaneously. pull_request now covers CI on
every push to this branch since PR #4 is open.

Fix B: bump actions/checkout v4->v7, actions/setup-node v4->v7,
actions/setup-dotnet v4->v6, actions/upload-artifact v4->v7, and
actions/dependency-review-action v4->v5 (anchore/sbom-action stays at v0,
already current). All were several majors behind and the old majors
declare Node 20, which GitHub has deprecated. Verified each bumped
action's action.yml at the new tag still accepts every input this
workflow passes, unchanged in meaning: setup-dotnet's global-json-file,
setup-node's node-version, upload-artifact's name/path/if-no-files-found,
checkout's fetch-depth, and dependency-review-action's zero inputs.

Fix C: add .github/dependabot.yml for the github-actions ecosystem only,
so pinned actions rotting silently (as just happened) gets caught going
forward instead of discovered incidentally in an unrelated log. Created
the missing "dependencies" label (gh label create) rather than leave the
file referencing one that doesn't exist.

Fix D: amend the wave design spec's out-of-scope list, which forbade
Dependabot version-update config, to mark that exclusion superseded
rather than silently contradicting it by adding Fix C. Original
rationale kept visible per AGENTS.md's record-superseded-don't-rewrite
rule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Injun Lee <2006ijlee@gmail.com>
Non-provider patterns and validity checks are GitHub Secret Protection
features. The advanced_security field is absent from this repository's
security_and_analysis response, so the tier is not provisioned: the API
accepts a PATCH with HTTP 200 and changes nothing, and the toggles do
not render in settings.

The detection gap is covered by C-2's gitleaks job rather than by a
native setting, which makes that decision load-bearing rather than
redundant. Also records the dependency graph as a fourth settings
change the original list missed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Injun Lee <2006ijlee@gmail.com>
…act assertion, docs

Applies the controller's adjudicated corrections from the independent
Codex review (task-6-report.md):

1. Plan now agrees with the spec: non-provider patterns and validity
   checks are marked superseded/unavailable in Task 5 and the
   completion criteria, instead of instructing executors to enable
   them. Original text preserved as superseded per AGENTS.md.
2. Pin anchore/sbom-action's Syft version explicitly (syft-version:
   v1.51.0) so the SBOM generator can't drift silently under a pinned
   action wrapper.
3. The artifact job's "Package and checksum" step now asserts all
   three publish directories exist and are non-empty before
   Compress-Archive, throwing (non-zero exit) if not — closing the gap
   where a partial publish could still be zipped, checksummed, and
   uploaded green.
4. branch-protection.md's dependency-review description no longer
   claims licence checking; the action has no allow-licenses/
   deny-licenses inputs configured, and choosing a licence policy is
   a product decision that hasn't been made.
5. Corrects the backwards "silently un-enforces" claim in
   branch-protection.md, the Wave C spec, and the Wave C plan: GitHub
   fails closed on a required check that never reports (the PR blocks
   indefinitely), unlike a job that reports skipped (treated as
   passing). The job-rename-must-update-both-files rule is unchanged.
6. Moves the spec's misplaced "Availability note" paragraph to after
   the complete decisions table in §3, instead of splitting the table
   in two.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Injun Lee <2006ijlee@gmail.com>
A fourth occurrence of the fail-open claim survived at line 37, phrased
as "silently stops being enforced" rather than "un-enforces", which is
why the verification grep passed. GitHub fails closed: an unreported
required context blocks every merge rather than silently passing.

Also corrects two licence-enforcement overstatements: the plan's
required-check template, which would have reintroduced the very
wording an earlier fix removed from branch-protection.md, and the
troubleshooting table.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Injun Lee <2006ijlee@gmail.com>
@June74
June74 merged commit aa67d61 into main Aug 17, 2026
6 checks passed
@June74
June74 deleted the codex/stage0-wave-c branch August 17, 2026 23:11
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.

1 participant