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
4 changes: 2 additions & 2 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile

# verify:json runs the exact 11-gate suite developers run locally
# `bun run gates` runs the exact 11-gate suite developers run locally
# (typecheck · check · lint · format · coverage · dup · deadcode · cycles
# · boundaries · build · audit), collects every failure (not fail-fast),
# and exits non-zero if any gate is red.
- name: Run gate suite
run: bun run verify:json
run: bun run gates
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ circular-import, build, and audit gates. CI runs the same suite. See
[docs/development.md](docs/development.md#the-gate-suite-verify) for what each
gate enforces.

`bun run verify:json` gives the same result as a compact JSON summary, handy for
`bun run gates` gives the same result as a compact JSON summary, handy for
scripting or quickly seeing which gate failed.

## What not to do
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: dev build preview clean docker docker-up docker-down install check typecheck \
test coverage lint lint-fix format format-check audit dup deadcode cycles mutation \
verify verify-json seed
verify gates seed

install:
bun install
Expand Down Expand Up @@ -60,8 +60,8 @@ verify:

# Same gates as `verify`, but emits a compact JSON summary (per-gate ok + metrics,
# excerpts only on failure). Built for AI/automated consumption; far fewer tokens.
verify-json:
bun run verify:json
gates:
bun run gates

clean:
rm -rf build/ node_modules/ .svelte-kit/ data/
Expand Down
2 changes: 1 addition & 1 deletion bunfig.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ preload = ["./src/test-setup.ts"]

# Collect coverage on every `bun test` for visibility. NOTE: Bun 1.3.x does not
# enforce coverageThreshold (it reports but does not fail), so the real gate is
# `scripts/check-coverage.ts` (run via `bun run coverage:check`, in `verify` and
# `scripts/coverage-check.ts` (run via `bun run coverage:check`, in `verify` and
# CI). The values below are kept in sync with that script as documentation.
# Octokit/network I/O wrappers (github.ts) and deep pipeline orchestration
# (migration.ts) are intentionally excluded as integration-tested concerns.
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ src/
profile/ the Profile workspace pages
login/ auth pages
api/ REST + SSE endpoints (see api.md)
scripts/ seed, coverage + boundary gates, postbuild, verify:json
scripts/ seed, coverage + boundary checks, postbuild, gates (full verify)
```

The `server/` layering is enforced by the `boundaries` gate: `core/` may not
Expand Down
18 changes: 9 additions & 9 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ keep the codebase honest.
- [Stack](#stack)
- [Dev loop](#dev-loop)
- [The gate suite (`verify`)](#the-gate-suite-verify)
- [Machine-readable verify (`verify:json`)](#machine-readable-verify-verifyjson)
- [Machine-readable verify (`gates`)](#machine-readable-verify-gates)
- [Testing](#testing)
- [Discovery tooling](#discovery-tooling)
- [Seeding the database](#seeding-the-database)
Expand Down Expand Up @@ -63,18 +63,18 @@ bun run verify
| `check` | `svelte-check --fail-on-warnings` | Svelte/TS diagnostics incl. a11y; must be **0/0** |
| `lint` | `biome check --error-on-warnings` | Lint + formatting + import-organization across `.ts`/`.js`/`.svelte` |
| `format:check` | `biome format` | Formatting drift (explicit) |
| `coverage:check` | `scripts/check-coverage.ts` | Test coverage below the floor (85% func / 82% line) |
| `coverage:check` | `scripts/coverage-check.ts` | Test coverage below the floor (85% func / 82% line) |
| `dup` | `jscpd` | Code duplication above 3% (lines) |
| `deadcode` | `knip` | Unused files / exports / dependencies |
| `cycles` | `madge --circular` | Circular imports |
| `boundaries` | `scripts/check-boundaries.ts` | Server layering: `core/` stays primitive, domains stay decoupled |
| `boundaries` | `scripts/boundaries.ts` | Server layering: `core/` stays primitive, domains stay decoupled |
| `build` | `vite build` + postbuild | The production build actually compiles |
| `audit` | `bun audit` | Dependency advisories |

A few notes:

- **Coverage** isn't enforced by Bun directly (it reports but doesn't fail), so
`scripts/check-coverage.ts` parses the summary and enforces the floor itself.
`scripts/coverage-check.ts` parses the summary and enforces the floor itself.
Floors sit a notch below actuals; ratchet them up as coverage climbs.
- **Duplication** is measured on production code only (tests are excluded). The
3% threshold is evaluated against the *lines* percentage. Some duplication is
Expand All @@ -85,9 +85,9 @@ A few notes:

---

## Machine-readable verify (`verify:json`)
## Machine-readable verify (`gates`)

`bun run verify:json` runs the same gates but emits a single compact JSON object
`bun run gates` runs the same gates but emits a single compact JSON object
instead of ~150 lines of human output — built for CI and AI/automation:

```jsonc
Expand All @@ -101,7 +101,7 @@ instead of ~150 lines of human output — built for CI and AI/automation:
and a red run still has enough to act on.
- It runs **all** gates (collecting every failure) rather than stopping at the
first, and exits non-zero iff any gate failed.
- Pass gate names to run a subset: `bun run verify:json typecheck lint`.
- Pass gate names to run a subset: `bun run gates typecheck lint`.

---

Expand Down Expand Up @@ -139,7 +139,7 @@ Beyond the hard gates, these surface issues the gates can't:
|---|---|---|
| `bun run deadcode` | knip | Unused files/exports/deps (a hard gate) |
| `bun run cycles` | madge | Circular imports (a hard gate) |
| `bun run boundaries` | `check-boundaries.ts` | Server layering: `core/` imports no domain; domains stay decoupled (a hard gate) |
| `bun run boundaries` | `boundaries.ts` | Server layering: `core/` imports no domain; domains stay decoupled (a hard gate) |
| `bun run dup` | jscpd | Duplication (a hard gate) |
| `bun run mutation` | Stryker | **Opt-in.** Mutates code to prove tests actually assert, not just execute |

Expand Down Expand Up @@ -213,7 +213,7 @@ for review before publishing.
- **Every gate must stay green.** A change that fixes one thing but reddens
another is a regression — fix it forward before moving on.
- **Small, atomic commits** with a clear message describing the root cause.
- **Verify before committing:** `bun run verify` (or `verify:json`) must pass.
- **Verify before committing:** `bun run verify` (or `gates`) must pass.

See [CONTRIBUTING](../CONTRIBUTING.md) for the contribution workflow and
[Architecture](architecture.md) for how the pieces fit together.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@
"format": "bunx --bun biome format --write .",
"format:check": "bunx --bun biome format .",
"test": "bun test",
"coverage:check": "bun scripts/check-coverage.ts",
"coverage:check": "bun scripts/coverage-check.ts",
"audit": "bun audit",
"dup": "jscpd",
"deadcode": "knip",
"cycles": "madge --circular --extensions ts,svelte --ts-config tsconfig.json src",
"boundaries": "bun scripts/check-boundaries.ts",
"boundaries": "bun scripts/boundaries.ts",
"mutation": "stryker run",
"ci": "bun run check && bun run build",
"seed": "bun scripts/seed.ts",
"release:prep": "bun scripts/release-prep.ts",
"verify": "bun run typecheck && bun run check && bun run lint && bun run format:check && bun run coverage:check && bun run dup && bun run deadcode && bun run cycles && bun run boundaries && bun run build && bun run audit",
"verify:json": "bun scripts/verify-json.ts"
"gates": "bun scripts/gates.ts"
},
"dependencies": {
"@octokit/auth-app": "^8.2.0",
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion scripts/check-coverage.ts → scripts/coverage-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const output = raw.replace(ansi, "");
if (proc.exitCode !== 0) {
process.stdout.write(output);
// Re-print just the failing tests LAST so they survive log truncation (e.g.
// verify:json's tail()) — the coverage table otherwise buries the one detail
// gates' tail()) — the coverage table otherwise buries the one detail
// that matters: which tests failed and why. `(fail)` lines name the test;
// the indented `error:`/assertion lines that follow give the reason.
const lines = output.split("\n");
Expand Down
6 changes: 3 additions & 3 deletions scripts/verify-json.ts → scripts/gates.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bun
/**
* verify:json — run the full gate suite and emit a compact JSON summary instead
* gates — run the full gate suite and emit a compact JSON summary instead
* of `verify`'s ~150 lines of human output (the vite build chunk listing alone
* is a large, low-signal block). Built for AI / automated consumption:
*
Expand All @@ -13,7 +13,7 @@
*
* Runs the exact commands behind the `verify` script, so a pass here means the
* same as a `verify` pass. Pass gate names as args to run a subset, e.g.
* bun run verify:json typecheck lint
* bun run gates typecheck lint
*/
export {};

Expand Down Expand Up @@ -143,7 +143,7 @@ const failures: string[] = [];
// Prepare: generate SvelteKit's `.svelte-kit/` (the $types + $lib path aliases)
// once, up front. On a fresh checkout it doesn't exist yet, and without it both
// `tsc` (typecheck) and `bun test` ($lib import resolution) fail. Doing it here
// — not relying on gate order — keeps subset runs (e.g. `verify:json coverage`)
// — not relying on gate order — keeps subset runs (e.g. `gates coverage`)
// correct too.
const sync = run(["bunx", "--bun", "svelte-kit", "sync"]);
if (sync.code !== 0) {
Expand Down
5 changes: 5 additions & 0 deletions src/lib/server/core/sse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ const SSE_HEADERS = {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
// Disable proxy response buffering (nginx and similar). Without this, a proxy
// buffers the event stream and can close it as "idle" or time the upstream out
// — surfacing as a 502 on the SSE endpoint. nginx honors this header the same
// way as `proxy_buffering off;`.
"X-Accel-Buffering": "no",
} as const;

/**
Expand Down
4 changes: 4 additions & 0 deletions src/lib/server/profile/analyze.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ function cleanSignals(over: Partial<RepoSignals> = {}): RepoSignals {
releasesCount: 0,
stargazerCount: 0,
watcherCount: 0,
forkCount: 0,
rulesetCount: 0,
branchProtectionRuleCount: 0,
branchProtectionRulesUsingUnmigratedFeatures: 0,
packagesCount: 0,
Expand Down Expand Up @@ -92,6 +94,7 @@ describe("analyzeRepo", () => {
packagesCount: 2,
usesLfs: true,
workflowFileCount: 4,
rulesetCount: 3,
}),
);

Expand All @@ -114,6 +117,7 @@ describe("analyzeRepo", () => {
expect(finding(profile, "actions-run-history")?.evidence).toBe(
"4 workflows (run history & artifacts not migrated)",
);
expect(finding(profile, "rulesets")?.evidence).toBe("3 rulesets");
});

test("rolls up applying considerations by severity", () => {
Expand Down
1 change: 1 addition & 0 deletions src/lib/server/profile/analyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ const DETECTORS: Record<string, Detector> = {
s.branchProtectionRulesUsingUnmigratedFeatures > 0
? `${count(s.branchProtectionRulesUsingUnmigratedFeatures, "rule")} using unmigrated features`
: null,
rulesets: (s) => (s.rulesetCount > 0 ? count(s.rulesetCount, "ruleset") : null),
"stars-watchers": (s) =>
s.stargazerCount > 0 || s.watcherCount > 0
? `${count(s.stargazerCount, "star")}, ${count(s.watcherCount, "watcher")}`
Expand Down
Loading