Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,18 @@ jobs:
fetch
- name: merge profiles + threshold gate
run: make cover-go-merge && make cover-go-check
- name: per-file coverage of secrets_managed.go (spec 013 G-19)
# The threshold gate checks package totals only. Print this one file's
# statement coverage from the merged profile so coverage-gaps.md can
# cite a measured figure.
if: matrix.module == 'api' && matrix.arch == 'amd64'
run: |
awk '$1 ~ /\/internal\/handlers\/secrets_managed\.go:/ { total += $2; if ($3 > 0) covered += $2; else { split($1, pos, ":"); missed = missed " " pos[2] } }
END { if (total == 0) { print "secrets_managed.go not found in profile"; exit 1 }
printf "G-19 coverage of secrets_managed.go = %d/%d statements (%.1f%%)\n", covered, total, 100 * covered / total
printf "G-19 uncovered blocks (startLine.col,endLine.col):%s\n", missed }' \
api/coverage/merged.out
(cd api && go tool cover -func=coverage/merged.out | grep -F 'internal/handlers/secrets_managed.go')

web:
name: web
Expand Down
25 changes: 25 additions & 0 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ Every job in every workflow must have:

Documented exceptions with longer timeouts are the five e2e jobs (60 minutes each) and the image build in `publish-edge.yaml` (35 minutes).

### Security and drift gates

These jobs block merging. Each gate with a fixture has a `hack/*_test.sh` self-test that proves it can fail; dependency review is validated by configuration review only. Fix the cause: never add an ignore file, a suppression comment or a looser threshold.

- **`vuln (go)`** runs `hack/govulncheck-all.sh`, which runs govulncheck over every module in `go.work`. It fails when a module reaches a symbol with a known vulnerability, and also when govulncheck itself hits a build or tool error. For a vulnerability, bump the affected dependency (or the Go toolchain) in that module's `go.mod`, run `make tidy`, and commit both. For any other error, fix the build or tool problem it reports.
- **`vuln (images)`** runs `hack/trivy-scan-images.sh` over the e2e images CI builds (`gameplane-test/<name>:e2e` and `:e2e-postgres`). It fails on any HIGH or CRITICAL vulnerability that has a fix, and on a scan error. For a vulnerability, bump the base-image digest, or bump the Go or npm dependency the finding names in that image's module. For a scan error, fix what Trivy reports and re-run.
- **`workflow lint`** also runs hadolint over every tracked Dockerfile, failing on any warning (`.hadolint.yaml`). Fix the Dockerfile: pin `apk`/`apt` package versions, and set a `SHELL` with `-o pipefail` before any piped `RUN` step.
- **`dependency review`** runs on pull requests only. It fails when the PR adds a dependency with a HIGH or CRITICAL advisory. Pick a version without the advisory, or drop the dependency.
- **`codegen drift`** runs `make generate manifests` and `make tidy`, then fails if `git diff` is not empty. Run the same commands locally and commit what they change (CLAUDE.md rule 7).
- **`submodule freshness`** runs `hack/check-submodule-freshness.sh`. It fails when the `modules` or `website` pointer names a commit that is not on that repo's `main`, when a gitlink is missing, or when the script cannot fetch that repo's `main`. For a pointer that is not on `main`, merge the submodule PR first, then bump the pointer to the merged commit. Otherwise restore the gitlink, or fix the fetch error, and re-run.

## Testing

Run the whole suite:
Expand Down Expand Up @@ -123,6 +134,20 @@ the dev ingress-nginx manifest pinned in `deploy/kind/up.sh`
to finish (its replica available).
It only runs when `deploy/kind/**` or the CI workflow file itself changes.

The `optional-components` bucket (`TestOptionalComponents_Lifecycle`) owns its
cluster because it runs `helm upgrade`. It checks that the MCP server, the
audit-syslog bridge and the tunnel are absent by default, then enables them
and checks each one. Its fixtures are `test/e2e/fixtures/frps.yaml` and
`test/e2e/fixtures/syslog-sink.yaml`.

The `e2e postgres / amd64 (kind)` job runs the API against PostgreSQL. It
boots the cluster with `./deploy/kind/e2e.sh up "$CLUSTER" e2e postgres`,
which applies `deploy/kind/postgres.yaml` and installs the chart with
`api.db.driver=postgres`. It then runs the `api-auth`, `api-roles` and
`api-rbac` buckets, restarting the API between them to reset the in-memory
login rate limiter. The frozen 001–012 upgrade path is covered separately by
`TestPostgres_MigrateFromFrozenBaseline` in the `api (postgres)` job.

## AI-assisted development

Much of this codebase is developed with AI coding assistants (Claude
Expand Down
6 changes: 3 additions & 3 deletions specs/013-expand-test-coverage/coverage-gaps.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Coverage Gap Record

**Summary**: 21/24 closed · 1 closed unproven · 1 in progress · 0 open · 1 unit-only by design
**Last updated**: 2026-10-15
**Summary**: 22/24 closed · 1 closed unproven · 0 in progress · 0 open · 1 unit-only by design
**Last updated**: 2026-10-16

| ID | Category | Gap | Story | FR | Priority | Status | Evidence | Notes |
|---|---|---|---|---|---|---|---|---|
Expand All @@ -23,7 +23,7 @@
| G-16 | e2e-optional | tunnel (tailscale, playit) | US3 | FR-003 | P2 | unit-only-by-design | | Tailscale and playit need external accounts and network reachability that CI cannot provide; covered by unit tests only (research R7). |
| G-17 | e2e-optional | telemetry receiver | US3 | FR-003 | P2 | closed | `TestTelemetryLifecycle` in bucket `telemetry` (job `e2e telemetry`), spec 022: https://github.com/GameplanePanel/Gameplane/blob/master/test/e2e/telemetry_e2e_test.go | |
| G-18 | e2e-optional | disabled state + no interference | US3 | FR-003 | P2 | closed | `e2e optional-components / amd64 (kind)` and `/ arm64 (kind)` https://github.com/GameplanePanel/Gameplane/actions/runs/38789759814 | `disabled` and `core-unaffected` subtests on #704: no MCP, bridge or tunnel Deployments or Services before enabling; afterward a GameServer reaches Running with a heartbeat and a viewer gets 403 on `/admin/audit`. |
| G-19 | unit | `secrets_managed.go` 100 % | US4 | FR-004 | P2 | in-progress | | `secrets_managed_test.go` added in https://github.com/GameplanePanel/Gameplane/pull/677; CI publishes only package totals, so the per-file figure is not yet measured. The `json.Marshal` error branch in `upsertLabelledSecret` is unreachable. |
| G-19 | unit | `secrets_managed.go` 100 % of reachable statements | US4 | FR-004 | P2 | closed | `go (api / amd64)` step "per-file coverage of secrets_managed.go" https://github.com/GameplanePanel/Gameplane/actions/runs/38951995010/job/117146106880 | Measured on #710: 27/28 statements (96.4 %); `deleteManagedSecret` 100 %, `upsertLabelledSecret` 95.5 %. The one uncovered block is lines 66–68, the `existing.Labels == nil` branch in `upsertLabelledSecret`. It cannot run: when `Labels` is nil, the feature-label check on line 62 has already returned. SC-004 was amended to 100 % of reachable statements (valgul, 2026-10-16). Tests from #677. |
| G-20 | unit | `ConsoleShell.test.tsx` ≥ 80 % lines | US4 | FR-004 | P2 | closed | `web` https://github.com/GameplanePanel/Gameplane/actions/runs/38189418314 | `ConsoleShell.tsx` 100 % lines, 95 % branches (lcov from https://github.com/GameplanePanel/Gameplane/pull/677). |
| G-21 | unit | `CloneServerDialog` ≥ 80 % lines + cancel/error | US4 | FR-004 | P2 | closed | `web` https://github.com/GameplanePanel/Gameplane/actions/runs/38189418314 | 89.29 % lines before T053; T053 adds API-error (409, 403, other status, non-API) and cancel-discards-changes cases in https://github.com/GameplanePanel/Gameplane/pull/677. |
| G-22 | unit | `ServerActionsMenu` ≥ 80 % lines + cancel/error | US4 | FR-004 | P2 | closed | `web` https://github.com/GameplanePanel/Gameplane/actions/runs/38189418314 | 100 % lines, branches and functions. The menu makes no API calls; cancel and API errors live in its dialogs, covered by their own tests (G-21 for clone). |
Expand Down
2 changes: 1 addition & 1 deletion specs/013-expand-test-coverage/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ specs/013-expand-test-coverage/
├── research.md # Phase 0 (R0 baseline + R1–R11 decisions)
├── data-model.md # Phase 1: gate, fixture, gap, record entities
├── quickstart.md # Phase 1: how to validate each story in CI
├── OPEN-DECISIONS.md # OD-1..OD-3, all ruled (rule 10)
├── OPEN-DECISIONS.md # OD-1..OD-5, all ruled (rule 10)
├── contracts/
│ ├── static-gates.md # per-gate trigger / failing condition / proof script contract
│ ├── coverage-gap-record.md # coverage-gaps.md format + validator rules
Expand Down
4 changes: 3 additions & 1 deletion specs/013-expand-test-coverage/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,9 @@ migrations and queries work identically.
telemetry-receiver) have at least one E2E test in a CI bucket, and the test verifies
the component's primary operation works correctly.
- **SC-004**: api/internal/handlers/secrets_managed.go (92 lines, upsertLabelledSecret,
deleteManagedSecret) has 100% test coverage; web components ConsoleShell,
deleteManagedSecret) has 100% test coverage of reachable statements (the
`existing.Labels == nil` branch in upsertLabelledSecret cannot run, because the
feature-label check above it has already returned); web components ConsoleShell,
CloneServerDialog, ServerActionsMenu each have a .test.tsx file with ≥ 80% line
coverage.
- **SC-005**: When the Postgres build tag is enabled, a named e2e bucket runs the same
Expand Down
14 changes: 8 additions & 6 deletions specs/013-expand-test-coverage/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,9 @@ description: "Task list for 013-expand-test-coverage"

## Phase 6: User Story 4 – Unit coverage gaps (Priority: P2)

**Goal**: 100 % of `secrets_managed.go`, and ≥ 80 % lines for ConsoleShell, CloneServerDialog and ServerActionsMenu (research R8, SC-004).
**Goal**: 100 % of the reachable statements in `secrets_managed.go`, and ≥ 80 % lines for ConsoleShell, CloneServerDialog and ServerActionsMenu (research R8, SC-004).

**Independent Test**: the `go (api / amd64)` coverage artifact shows `secrets_managed.go` at 100 %, and the `web` job's lcov shows each of the three components at ≥ 80 % lines.
**Independent Test**: the `go (api / amd64)` step "per-file coverage of secrets_managed.go" shows every statement covered except the uncovered blocks it lists, and each of those is unreachable (on #710: 27/28, the one miss being the `existing.Labels == nil` branch at lines 66–68), and the `web` job's lcov shows each of the three components at ≥ 80 % lines.

**PR**: `test: unit coverage for managed secrets and console shell`. Independent of every other phase.

Expand Down Expand Up @@ -351,7 +351,7 @@ description: "Task list for 013-expand-test-coverage"

*Implementation note (2026-10-12)*: lcov from run 38189418314: `CloneServerDialog.tsx` 89.29 % lines / 69.23 % branches, with a Cancel case but no API-error case, so T053 adds the API-error and cancel-discards-changes cases. `ServerActionsMenu.tsx` 100 % lines / branches / functions; it makes no API calls, so cancel and API errors are dialog-level (G-22 note). `ConsoleShell.tsx` 100 % lines. G-19 stays in progress: CI reports only package coverage for Go.

**Checkpoint**: SC-004 stays pending until G-19 has per-file proof for `secrets_managed.go` (G-20 to G-22 are proven). Module thresholds stay unchanged.
**Checkpoint**: G-19 to G-22 are proven. G-19 was measured in CI at 27/28 statements (96.4 %) for `secrets_managed.go`, which is 100 % of its reachable statements: the one miss is the unreachable `existing.Labels == nil` branch (lines 66–68). valgul chose "Measure in CI" and amended SC-004 to match, 2026-10-16. Module thresholds stay unchanged.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

---

Expand Down Expand Up @@ -394,9 +394,11 @@ description: "Task list for 013-expand-test-coverage"

## Phase 8: Polish & cross-cutting

- [ ] T059 [P] Update `docs/contributing.md`, under "CI and workflows", with one short paragraph per new gate: what fails it, and how to fix it without suppressing it. Update "E2E testing" for the `optional-components` bucket and the `e2e-postgres` job. CI's `make check-links` verifies the new anchors.
- [ ] T060 [P] Grep the full feature diff for forbidden directives: `git diff origin/master -- . ':!specs' | grep -nE "nolint|eslint-disable|@ts-ignore|hadolint ignore|trivyignore|allow-ghsas"` must print nothing (SC-006).
- [ ] T061 Confirm `coverage-gaps.md` has no `open` or `in-progress` rows and that `make check-coverage-gaps` passes in CI. Then run `/speckit-analyze` on this folder.
- [X] T059 [P] Update `docs/contributing.md`, under "CI and workflows", with one short paragraph per new gate: what fails it, and how to fix it without suppressing it. Update "E2E testing" for the `optional-components` bucket and the `e2e-postgres` job. CI's `make check-links` verifies the new anchors.
- [X] T060 [P] Grep the full feature diff for forbidden directives: `git diff origin/master -- . ':!specs' ':!*.md' | grep -nE "nolint|eslint-disable|@ts-ignore|hadolint ignore|trivyignore|allow-ghsas"` must print nothing (SC-006).
Result (2026-10-16): over the feature diff, from before #671 to master after #707, the command prints nothing. Markdown is excluded (valgul, 2026-10-16) because its only matches are prose in CLAUDE.md and the agent and skill docs telling contributors not to use these directives; SC-006 covers in-source directives.
- [X] T061 Confirm `coverage-gaps.md` has no `open` or `in-progress` rows and that `make check-coverage-gaps` passes in CI. Then run `/speckit-analyze` on this folder.
Result (2026-10-16): no open or in-progress rows; `make check-coverage-gaps` passed in "docs and repository checks" on #710 (https://github.com/GameplanePanel/Gameplane/actions/runs/38951995010/job/117146106043). `/speckit-analyze` found 0 critical issues; its two medium findings (the tasks.md checkpoint and the plan.md decision count) are fixed on #710, and SC-004 was amended.
- [ ] T062 Once every PR above is merged and every task is checked or withdrawn, `git mv specs/013-expand-test-coverage specs/done_013-expand-test-coverage` and update every in-repo reference (including `hack/check-coverage-gaps.sh`'s default path) in one `docs:` commit (Constitution IV).

---
Expand Down
Loading