Skip to content

chore(ci): adopt gt repo governance - #26

Merged
pedromvgomes merged 5 commits into
mainfrom
chore/gt-repo-governance
Aug 22, 2026
Merged

chore(ci): adopt gt repo governance#26
pedromvgomes merged 5 commits into
mainfrom
chore/gt-repo-governance

Conversation

@pedromvgomes

@pedromvgomes pedromvgomes commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Onboards this repository onto gt's governance subsystem
(pedromvgomes/gt#31).

Important

Do not merge before gt v1 is cut. Every rendered caller pins
pedromvgomes/gt@v1, which does not exist yet.

Dependency updates, for the first time

There is no .github/dependabot.yml here today. This adds one covering
npm at the workspace root and github-actions, weekly with a 7-day
cooldown, plus the daily auto-merge batch for patch and minor bumps.

The npm entry is deliberately one entry at the root, and this repository
is why gt can get it right. It is a pnpm workspace — pnpm-workspace.yaml
lists worker and page — with a single pnpm-lock.yaml. Per-member
entries for /page and /worker would edit only those package.json
files, leave the root lockfile stale, and fail every frozen-lockfile
install.

gt got this wrong on first detection: it decided workspace-root-ness from
package.json's workspaces field, which pnpm does not use — members live
in the sibling YAML and the root manifest has no such field at all. So the
collapse never fired for the one package manager where the shared root
lockfile makes it matter most. Fixed in pedromvgomes/gt#31.

The reasoning is recorded as a note: in the spec, so a future sync cannot
quietly re-split it.

bulwark is on

No security scanning here today, so gt's stage is an addition rather than a
duplicate. Expect findings on the first run; nothing gates on them while
ci-gate is not the required check.

CD is off — a design question, not a migration

deploy.yml already ships from pushes to main, so delivery here is not
tag-shaped at all, while cd-orchestration is built around a tag trigger.
Turning CD on means deciding whether that changes — not just moving jobs.

Inert

The ci-* stages land as empty no-ops; end2end is omitted. ci.yml,
deploy.yml and validate-topology.yml keep running, and branch protection
is unchanged.

Onboards this repository onto gt's governance subsystem
(pedromvgomes/gt#31). A committed .gt-repo.yaml is the source of truth and
`gt repo sync` renders the files from it.

Must not merge before gt v1 is cut: every rendered caller pins
pedromvgomes/gt@v1, which does not exist yet.

There is no .github/dependabot.yml here today, so this repository gains
dependency updates for the first time — npm at the workspace root and
github-actions — plus the daily auto-merge batch for patch and minor bumps.

The npm entry is deliberately one entry at the ROOT. This is a pnpm
workspace with a single pnpm-lock.yaml, so per-member entries for /page and
/worker would edit only those manifests, leave the root lockfile stale, and
fail every frozen-lockfile install. gt got this wrong on first detection —
it decided workspace-root-ness from package.json's workspaces field, which
pnpm does not use — and pedromvgomes/gt#31 now recognises
pnpm-workspace.yaml. The reasoning is recorded as a note so a future sync
cannot quietly re-split it.

bulwark is on: no security scanning here today, so gt's stage is an addition
rather than a duplicate.

CD is off, and here it is a design question rather than a migration:
deploy.yml already ships from pushes to main, so delivery is not tag-shaped
at all. Turning CD on means deciding whether that changes.

The ci-* stages land as empty no-ops and end2end is omitted. ci.yml,
deploy.yml and validate-topology.yml keep running, and branch protection is
unchanged.

Claude-Session: https://claude.ai/code/session_01PvwKxJ5vnqa9h9XXyTEj43
Picks up pedromvgomes/gt#31's annotation. semgrep's secrets-inherit rule
flagged the rendered orchestrator; gt cannot enumerate a repository's secret
names, so the reasoning is recorded at each site rather than the rule being
silenced.

Claude-Session: https://claude.ai/code/session_01PvwKxJ5vnqa9h9XXyTEj43
The governance PR added the gt workflows but left ci.yml in place, so
ci-gate went green on a pipeline that ran nothing. This moves the work.

ci.yml's single `test` job becomes two stages: ci-build runs the
type-check (for TypeScript the build is the type-check — the worker's own
"build" script is `tsc --noEmit`) and ci-test runs `pnpm -r test`. Every
step is carried over unchanged; the install is repeated because each
stage is its own workflow on its own runner. ci.yml's concurrency group
is dropped rather than lost — ci-orchestration.yml already cancels
superseded PR runs.

Both stages declare packages: read. @wardnet/* resolve from GitHub
Packages, so a frozen-lockfile install fails without it, and a called
workflow can only narrow what the caller granted.

Action tags are pinned to the SHAs `@v4` resolved to at the time, so the
move changes no behaviour; Dependabot owns bumps from here.

.bulwark.yml goes back to `coverage.source: run`. It was scaffolded as
`report` by an earlier pass, but nothing produces a report — neither
workspace declares a test:coverage script and @vitest/coverage-v8 is not
a dependency. Under `run` bulwark skips packages with no such script and
reports no TypeScript coverage, which is at least true.

Re-synced with gt 1.3.0: ci-orchestration.yml and
dependabot-auto-merge.yml now name their secrets instead of inheriting
them, which is what actually works across owners — gt lives under
pedromvgomes, this repo under wardnet. `gt repo config` resolves
identically before and after apart from the version stamp.
@github-actions

github-actions Bot commented Aug 22, 2026

Copy link
Copy Markdown

bulwark

  • scan — no findings

  • coverage — see error output below

📦 Full bulwark output — complete scan and coverage logs.

Onboarding put bulwark's TypeScript check on this repo for the first time
and it went red on code the governance PR never touched: bulwark's eslint
is not diff-scoped (--diff-base is passed to semgrep only), so the whole
back-catalogue arrives at once.

Of eslint-plugin-security's 14 findings, 13 were detect-object-injection
on typed record lookups. That rule fires on any obj[key] and cannot be
disabled on its own — .bulwark.yml has no rule-level exclusion — so the
choice was to suppress it at fourteen sites or to change linter. Biome's
security + correctness sets report five things instead, and unlike the
object-injection noise they are worth acting on:

  - incidents.ts took a `from: Status` the body never read. Removed, with
    its 21 call sites. A transition function advertising a parameter that
    cannot affect the outcome misleads every caller.
  - topology-file.test.ts used __dirname and node:path. Now a URL
    relative to import.meta.url, which drops the node:path import and the
    CommonJS global. fileURLToPath takes .href rather than the URL object
    because @cloudflare/workers-types declares a global URL that is not
    node:url's, and passing the object does not type-check.
  - the same test's node:fs import is suppressed file-wide with a reason:
    it reads the real topology.yaml off disk under vitest and is never
    bundled into the Worker, which is the rule's actual concern.
  - mockServiceWorker.js trips noSecrets on msw's integrity checksum.
    bulwark's typescript.exclude only filters package discovery, and
    Biome ignores a nested config under --config-path, so the suppression
    has to be inline. msw regenerates that file and will drop it; CI says
    so again if that happens. The file already carries msw's own
    /* eslint-disable */, so it is in keeping.

Verified with bulwark v1.9.0, the release the action installs: biome
passes at the root and in both packages, and semgrep passes diff-scoped.
type-check and all 97 tests pass.
It ran one vitest file, worker/test/topology-file.test.ts, which
`pnpm -r test` in ci-test now runs anyway. Keeping it meant the worker
suite ran twice on any PR touching topology.yaml.

Coverage widens rather than narrows. validate-topology.yml was filtered
to `paths: [topology.yaml]`, so it only fired when that file changed; gt
CI has no path filter and runs on every pull request and every push to
main, and ci-preflight is still the scaffolded no-op that skips nothing.
A topology.yaml pushed straight to main carries no prior attestation, so
attest does not short-circuit the stages either.

deploy.yml is untouched: it still skips topology-only pushes, because
topology.yaml is fetched at runtime and needs no deploy. That is exactly
why the schema gate has to exist somewhere else, and it is now ci-test.
Both comments that pointed at the deleted file say so instead.
@pedromvgomes
pedromvgomes merged commit e9206f4 into main Aug 22, 2026
10 checks passed
@pedromvgomes
pedromvgomes deleted the chore/gt-repo-governance branch August 22, 2026 09:18
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