diff --git a/.env.example b/.env.example index 8c472eb9d9..630573cb5b 100644 --- a/.env.example +++ b/.env.example @@ -157,6 +157,8 @@ GITTENSORY_REVIEW_DRAFT=false # # /setup returns 400; with it, enter the token in the browser form # # or send an x-setup-token / Bearer header. Never put this token in # # the URL; query strings leak to logs, proxies, and browser history. +# SETUP_OUTPUT_PATH=/data/gittensory-app.env # overrides where the first-run setup wizard writes the generated +# # GitHub App credentials file. Default: /data/gittensory-app.env. # ADMIN_GITHUB_LOGINS=your-github-login # REQUIRED for control-panel ("operator") access. A comma- or # # whitespace-separated allowlist of GitHub logins, case-insensitive. # # FAIL-CLOSED: unset/empty means NOBODY can sign into the dashboard, @@ -220,6 +222,9 @@ REDIS_URL=redis://redis:6379 # REQUIRED for the self-host review # BROWSER_WS_ENDPOINT= # ws:// URL of a browserless/chrome instance for visual-review # # screenshot capture. Unset = visual review is fully inert (no # # screenshots, no error) — this feature is entirely optional. +# REVIEW_AUDIT_DIR= # persist visual-review screenshot PNGs to this filesystem path +# # instead of re-rendering on demand. Unset = re-render each time. +# # Only relevant when BROWSER_WS_ENDPOINT above is set. # DISCORD_WEBHOOK_URL= # one Discord channel for per-action notifications (merged/closed/ # # manual) on ANY repo you review. Unset = no Discord notifications. # # Collection and schema are auto-created at startup. Off when unset. @@ -253,6 +258,9 @@ REDIS_URL=redis://redis:6379 # REQUIRED for the self-host review # LITESTREAM_SECRET_ACCESS_KEY= # LITESTREAM_ENDPOINT= # e.g. s3.us-west-002.backblazeb2.com (omit for AWS S3) # LITESTREAM_REGION=us-east-1 +# BACKUP_ACKNOWLEDGED=false # silences the boot-time warning about running SQLite with no + # acknowledged backup once you've wired Litestream (or an + # equivalent) above. Default false (warning shown). # --- Queue worker (#977/#1201) --- # QUEUE_CONCURRENCY=4 # max concurrent job-processing loops per instance. Default 4; set 1 @@ -399,6 +407,11 @@ REDIS_URL=redis://redis:6379 # REQUIRED for the self-host review # DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/... # global fallback for any repo without its own # DISCORD_REPO_WEBHOOKS={"owner/repoA":"https://discord.com/api/webhooks/...","owner/repoB":"https://..."} # per-repo # +# Slack notifications: the same per-action events (merged/closed/manual) as Discord above, posted as a Block +# Kit section to one Slack channel. Unset = no Slack notifications. Unlike Discord there is no per-repo map +# today — every repo shares this one webhook. +# SLACK_WEBHOOK_URL=https://hooks.slack.com/services/... +# # Sentry error tracking. OFF when SENTRY_DSN is unset. Official self-host release images bake # GITTENSORY_VERSION=gittensory-selfhost@; initSentry uses that as the release id unless # SENTRY_RELEASE is set explicitly (useful for custom/local images). @@ -480,6 +493,12 @@ REDIS_URL=redis://redis:6379 # REQUIRED for the self-host review # AI_EMBED_MODEL=nomic-embed-text:latest # embedding model for RAG (openai-compatible /embeddings). Its output # # dimension must match QDRANT_DIM. Used only when RAG is enabled # # (GITTENSORY_REVIEW_RAG + allowlist). +# AI_EMBED_BASE_URL= # route embeddings to a SEPARATE openai-compatible endpoint instead +# # of the review chain's own AI provider — e.g. a dedicated local +# # Ollama for embeddings while Claude/Codex handle review. Unset = +# # embeddings use the same provider as everything else; setting +# # AI_EMBED_MODEL above alone does nothing without this. +# AI_EMBED_API_KEY= # bearer credential for AI_EMBED_BASE_URL, if it requires one. # --- Gittensory Orb (#1255; ALWAYS-ON fleet-calibration telemetry) --- # TELEMETRY NOTICE: running this self-hosted image contributes anonymized gate-calibration data to @@ -498,8 +517,13 @@ REDIS_URL=redis://redis:6379 # REQUIRED for the self-host review # ORB_AIR_GAP=false # air-gapped/OFFLINE deployments only: compute locally, never send # ORB_ANONYMIZE=true # HMAC-hash repo/PR before export (default true; false = raw names) # ORB_COLLECTOR_URL=https://gittensory-api.aethereal.dev/v1/orb/ingest # gittensory's hosted collector (default; override for your own) +# ORB_COLLECTOR_TOKEN= # bearer credential for a private/self-hosted collector (ORB_COLLECTOR_URL +# # above). Unset when using gittensory's own hosted collector. # # Token broker (optional): get GitHub tokens from the central Orb (you installed the Orb App) instead of running # your own GitHub App. Set the enrollment secret the operator issued for your install; unset = use your own App key. # ORB_ENROLLMENT_SECRET= # one-time enrollment secret (a secret — keep it out of version control) # ORB_BROKER_URL=https://gittensory-api.aethereal.dev # the Orb broker base (default; override for a private deployment) +# ORB_RELAY_MODE=push # push | pull. push registers a public relay URL the broker calls; +# # pull is the right fit behind NAT/tailnet — your instance drains +# # events outbound instead of exposing an inbound endpoint. Default: push. diff --git a/.gittensory.yml.example b/.gittensory.yml.example index b9d4534e58..d625b13545 100644 --- a/.gittensory.yml.example +++ b/.gittensory.yml.example @@ -383,6 +383,13 @@ settings: # Bool. Default: false. includeMaintainerAuthors: false + # Standing rule: the repo OWNER's own PRs (and ADMIN_GITHUB_LOGINS fleet-operator PRs, #2133) are NEVER + # auto-closed by default -- they may still auto-merge when clean + passing, or fall to a manual hold. + # Set true to make owner/admin-authored PRs eligible for auto-close like a contributor's (still gated by + # the `close` autonomy class below + adverse-signal conditions). Automation-bot PRs stay exempt regardless + # of this setting. Bool. Default: false. + closeOwnerAuthors: false + # Require a linked issue on every PR (dashboard equivalent of the toggle that # can auto-promote gate.linkedIssue to block). Bool. Default: false. requireLinkedIssue: false @@ -512,3 +519,26 @@ settings: # commandRateLimitMaxPerWindow: 20 # Positive integer. Per-command invocation limit for a CHEAP command (cache-only, no AI call) within the window. Default: 20. # commandRateLimitAiMaxPerWindow: 5 # Positive integer. Tighter limit for an AI-cost-bearing command (ask/blockers/preflight/reviewability/packet/duplicate-check/next-action/repo-fit). Default: 5. # commandRateLimitWindowHours: 24 # Positive integer. Rolling window (hours) both limits above count against. Default: 24. + +# Per-repo activation overrides for the converged review features that ship behind a deployment-wide +# GITTENSORY_REVIEW_* env kill-switch (rag/reputation/unifiedComment/safety). Each key is `true` (force on +# for this repo, subject to the env flag still being enabled), `false` (force off), or omitted (falls back +# to the GITTENSORY_REVIEW_REPOS allowlist default -- an operator who sets nothing keeps today's behavior). +# features: +# rag: true +# reputation: false +# unifiedComment: true +# safety: true + +# Registry-review lane (#2435): lets a self-hosted maintainer point gittensory at their OWN structured +# registry (e.g. a subnet/plugin/package catalog) without a gittensory code change -- reviewing additions +# to a data file the same way it reviews code. Uncomment and set at least entryFileGlob + collectionField +# (both required; the block is ignored with a warning if either is missing). +# contentLane: +# entryFileGlob: registry/*.json # Glob for the structured entry files this lane reviews. Required. +# collectionField: entries # The JSON field holding the collection this lane diffs. Required. +# providerFileGlob: providers/*.ts # Optional glob for source files the entries are validated against. +# artifactGlob: dist/registry.json # Optional glob for a generated/build artifact to cross-check. +# maxAppendedEntries: 1 # Positive integer cap on new entries per PR. Default: unbounded. +# duplicateKeyFields: [slug] # Field name(s) used to detect a duplicate entry. Default: [] (no dedup check). +# validatorId: my-registry-validator # Optional identifier for a custom per-entry validator. Default: none. diff --git a/README.md b/README.md index 8e109c2814..e9c7bb6f43 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,12 @@ Gittensory CI and gittensory review score, gate, and comment on pull requests. T - **Unified review comment** — renders the public PR feedback as one in-place comment instead of multiple panels. - **Per-repo activation** — capabilities roll forward (and back) one flag and one repo at a time via the `GITTENSORY_REVIEW_REPOS` allowlist. +**Check-run and comment surfaces, disambiguated** (a common point of confusion — these are three independent, separately-configured things, not layers of the same feature): + +- **`Gittensory Orb Review Agent`** (`gate.*` / `settings.gateCheckMode` / `settings.reviewCheckMode`, off by default) — the authoritative GitHub Check Run carrying the gate's pass/fail verdict. This is the one worth making a required status check. +- **`Gittensory Context`** (`settings.checkRunMode` / `settings.checkRunDetailLevel`, off by default) — a separate, purely advisory Check Run. At its default `checkRunDetailLevel: minimal` it publishes no findings at all; even at `standard`/`deep` it only re-renders content already shown elsewhere. Never make this one required. +- **Inline review comments** (`GITTENSORY_REVIEW_INLINE_COMMENTS` + `.gittensory.yml`'s `review.inline_comments`, off by both by default) — real, reply-able line-anchored PR review comment threads (CodeRabbit-style). This is the ONLY one of the three that posts an interactive per-line thread; the two check runs above never do. + See [Tuning your reviews](https://gittensory.aethereal.dev/docs/tuning) for the full flag, setting, and `.gittensory.yml` reference. ## Start Here diff --git a/apps/gittensory-ui/src/lib/selfhost-env-reference.ts b/apps/gittensory-ui/src/lib/selfhost-env-reference.ts index 827be1ad72..373322c55a 100644 --- a/apps/gittensory-ui/src/lib/selfhost-env-reference.ts +++ b/apps/gittensory-ui/src/lib/selfhost-env-reference.ts @@ -11,11 +11,11 @@ export const SELFHOST_ENV_REFERENCE_ROWS: SelfHostEnvReferenceRow[] = [ }, { name: "AI_EMBED_API_KEY", - firstReference: "src/server.ts:428", + firstReference: "src/server.ts:445", }, { name: "AI_EMBED_BASE_URL", - firstReference: "src/server.ts:425", + firstReference: "src/server.ts:442", }, { name: "AI_EMBED_MODEL", @@ -43,7 +43,7 @@ export const SELFHOST_ENV_REFERENCE_ROWS: SelfHostEnvReferenceRow[] = [ }, { name: "BACKUP_ACKNOWLEDGED", - firstReference: "src/server.ts:367", + firstReference: "src/server.ts:384", }, { name: "BROWSER_WS_ENDPOINT", @@ -79,7 +79,7 @@ export const SELFHOST_ENV_REFERENCE_ROWS: SelfHostEnvReferenceRow[] = [ }, { name: "CRON_INTERVAL_MS", - firstReference: "src/server.ts:904", + firstReference: "src/server.ts:921", }, { name: "DATABASE_PATH", @@ -111,11 +111,11 @@ export const SELFHOST_ENV_REFERENCE_ROWS: SelfHostEnvReferenceRow[] = [ }, { name: "GITHUB_CACHE_TTL_SECONDS", - firstReference: "src/server.ts:496", + firstReference: "src/server.ts:513", }, { name: "GITTENSORY_REPO_CONFIG_DIR", - firstReference: "src/server.ts:284", + firstReference: "src/server.ts:289", }, { name: "GITTENSORY_VERSION", @@ -127,11 +127,11 @@ export const SELFHOST_ENV_REFERENCE_ROWS: SelfHostEnvReferenceRow[] = [ }, { name: "MAINTENANCE_ADMISSION_ENABLED", - firstReference: "src/selfhost/maintenance-admission.ts:129", + firstReference: "src/selfhost/maintenance-admission.ts:126", }, { name: "MIGRATIONS_DIR", - firstReference: "src/server.ts:380", + firstReference: "src/server.ts:397", }, { name: "OBSERVABILITY_SMOKE_POLL_MS", @@ -191,7 +191,7 @@ export const SELFHOST_ENV_REFERENCE_ROWS: SelfHostEnvReferenceRow[] = [ }, { name: "ORB_BROKER_URL", - firstReference: "src/server.ts:953", + firstReference: "src/server.ts:970", }, { name: "ORB_COLLECTOR_TOKEN", @@ -207,7 +207,7 @@ export const SELFHOST_ENV_REFERENCE_ROWS: SelfHostEnvReferenceRow[] = [ }, { name: "ORB_RELAY_MODE", - firstReference: "src/server.ts:955", + firstReference: "src/server.ts:972", }, { name: "OTEL_EXPORTER_OTLP_ENDPOINT", @@ -243,7 +243,7 @@ export const SELFHOST_ENV_REFERENCE_ROWS: SelfHostEnvReferenceRow[] = [ }, { name: "PORT", - firstReference: "src/server.ts:703", + firstReference: "src/server.ts:720", }, { name: "PUBLIC_API_ORIGIN", @@ -259,11 +259,11 @@ export const SELFHOST_ENV_REFERENCE_ROWS: SelfHostEnvReferenceRow[] = [ }, { name: "QDRANT_URL", - firstReference: "src/server.ts:515", + firstReference: "src/server.ts:532", }, { name: "QUEUE_BACKGROUND_CONCURRENCY", - firstReference: "src/selfhost/queue-common.ts:102", + firstReference: "src/selfhost/queue-common.ts:120", }, { name: "REDIS_URL", @@ -271,7 +271,7 @@ export const SELFHOST_ENV_REFERENCE_ROWS: SelfHostEnvReferenceRow[] = [ }, { name: "REVIEW_AUDIT_DIR", - firstReference: "src/server.ts:560", + firstReference: "src/server.ts:577", }, { name: "SELFHOST_BUNDLE_ALL", @@ -307,7 +307,11 @@ export const SELFHOST_ENV_REFERENCE_ROWS: SelfHostEnvReferenceRow[] = [ }, { name: "SETUP_OUTPUT_PATH", - firstReference: "src/server.ts:820", + firstReference: "src/server.ts:837", + }, + { + name: "SLACK_WEBHOOK_URL", + firstReference: "src/services/notify-discord.ts:173", }, ]; @@ -315,15 +319,15 @@ export const SELFHOST_ENV_REFERENCE_MARKDOWN = [ "| Name | First reference |", "| --- | --- |", "| `AI_COMBINE` | `src/selfhost/ai.ts:936` |", - "| `AI_EMBED_API_KEY` | `src/server.ts:428` |", - "| `AI_EMBED_BASE_URL` | `src/server.ts:425` |", + "| `AI_EMBED_API_KEY` | `src/server.ts:445` |", + "| `AI_EMBED_BASE_URL` | `src/server.ts:442` |", "| `AI_EMBED_MODEL` | `src/selfhost/ai.ts:832` |", "| `AI_ON_MERGE` | `src/selfhost/ai.ts:938` |", "| `AI_PROVIDER` | `src/selfhost/ai-config.ts:43` |", "| `ANTHROPIC_AI_BASE_URL` | `src/selfhost/ai.ts:836` |", "| `ANTHROPIC_AI_MODEL` | `src/selfhost/ai.ts:57` |", "| `ANTHROPIC_API_KEY` | `src/selfhost/ai.ts:835` |", - "| `BACKUP_ACKNOWLEDGED` | `src/server.ts:367` |", + "| `BACKUP_ACKNOWLEDGED` | `src/server.ts:384` |", "| `BROWSER_WS_ENDPOINT` | `src/selfhost/stubs/puppeteer.ts:11` |", "| `CLAUDE_AI_EFFORT` | `src/selfhost/ai.ts:108` |", "| `CLAUDE_AI_MODEL` | `src/selfhost/ai.ts:49` |", @@ -332,7 +336,7 @@ export const SELFHOST_ENV_REFERENCE_MARKDOWN = [ "| `CODEX_AI_MODEL` | `src/selfhost/ai.ts:53` |", "| `CODEX_AI_TIMEOUT_MS` | `src/selfhost/ai.ts:112` |", "| `CODEX_HOME` | `src/selfhost/ai.ts:274` |", - "| `CRON_INTERVAL_MS` | `src/server.ts:904` |", + "| `CRON_INTERVAL_MS` | `src/server.ts:921` |", "| `DATABASE_PATH` | `src/server.ts:250` |", "| `DATABASE_URL` | `src/selfhost/preflight.ts:201` |", "| `DISCORD_REPO_WEBHOOKS` | `src/services/notify-discord.ts:41` |", @@ -340,12 +344,12 @@ export const SELFHOST_ENV_REFERENCE_MARKDOWN = [ "| `FOREGROUND_LIVENESS_ENABLED` | `src/selfhost/foreground-liveness.ts:41` |", "| `GITHUB_APP_ID` | `src/selfhost/orb-collector.ts:59` |", "| `GITHUB_APP_PRIVATE_KEY` | `src/selfhost/orb-collector.ts:166` |", - "| `GITHUB_CACHE_TTL_SECONDS` | `src/server.ts:496` |", - "| `GITTENSORY_REPO_CONFIG_DIR` | `src/server.ts:284` |", + "| `GITHUB_CACHE_TTL_SECONDS` | `src/server.ts:513` |", + "| `GITTENSORY_REPO_CONFIG_DIR` | `src/server.ts:289` |", "| `GITTENSORY_VERSION` | `src/selfhost/health.ts:29` |", "| `HOME` | `src/selfhost/ai.ts:274` |", - "| `MAINTENANCE_ADMISSION_ENABLED` | `src/selfhost/maintenance-admission.ts:129` |", - "| `MIGRATIONS_DIR` | `src/server.ts:380` |", + "| `MAINTENANCE_ADMISSION_ENABLED` | `src/selfhost/maintenance-admission.ts:126` |", + "| `MIGRATIONS_DIR` | `src/server.ts:397` |", "| `OBSERVABILITY_SMOKE_POLL_MS` | `scripts/smoke-observability-traces.mjs:8` |", "| `OBSERVABILITY_SMOKE_TIMEOUT_MS` | `scripts/smoke-observability-traces.mjs:6` |", "| `OLLAMA_AI_API_KEY` | `src/selfhost/ai.ts:829` |", @@ -360,11 +364,11 @@ export const SELFHOST_ENV_REFERENCE_MARKDOWN = [ "| `ORB_AIR_GAP` | `src/selfhost/orb-collector.ts:161` |", "| `ORB_ANONYMIZE` | `src/selfhost/orb-collector.ts:174` |", "| `ORB_APP_ID` | `src/selfhost/orb-collector.ts:59` |", - "| `ORB_BROKER_URL` | `src/server.ts:953` |", + "| `ORB_BROKER_URL` | `src/server.ts:970` |", "| `ORB_COLLECTOR_TOKEN` | `src/selfhost/orb-collector.ts:205` |", "| `ORB_COLLECTOR_URL` | `src/selfhost/orb-collector.ts:172` |", "| `ORB_ENROLLMENT_SECRET` | `src/selfhost/orb-collector.ts:165` |", - "| `ORB_RELAY_MODE` | `src/server.ts:955` |", + "| `ORB_RELAY_MODE` | `src/server.ts:972` |", "| `OTEL_EXPORTER_OTLP_ENDPOINT` | `src/selfhost/otel.ts:47` |", "| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | `src/selfhost/otel.ts:45` |", "| `OTEL_SERVICE_ENVIRONMENT` | `src/selfhost/otel.ts:60` |", @@ -373,14 +377,14 @@ export const SELFHOST_ENV_REFERENCE_MARKDOWN = [ "| `OTEL_TRACES_SAMPLER` | `src/selfhost/otel.ts:74` |", "| `OTEL_TRACES_SAMPLER_ARG` | `src/selfhost/otel.ts:76` |", "| `PGVECTOR_ENABLED` | `src/server.ts:230` |", - "| `PORT` | `src/server.ts:703` |", + "| `PORT` | `src/server.ts:720` |", "| `PUBLIC_API_ORIGIN` | `src/selfhost/preflight.ts:192` |", "| `QDRANT_API_KEY` | `src/selfhost/qdrant-vectorize.ts:50` |", "| `QDRANT_DIM` | `src/selfhost/qdrant-vectorize.ts:71` |", - "| `QDRANT_URL` | `src/server.ts:515` |", - "| `QUEUE_BACKGROUND_CONCURRENCY` | `src/selfhost/queue-common.ts:102` |", + "| `QDRANT_URL` | `src/server.ts:532` |", + "| `QUEUE_BACKGROUND_CONCURRENCY` | `src/selfhost/queue-common.ts:120` |", "| `REDIS_URL` | `src/selfhost/preflight.ts:144` |", - "| `REVIEW_AUDIT_DIR` | `src/server.ts:560` |", + "| `REVIEW_AUDIT_DIR` | `src/server.ts:577` |", "| `SELFHOST_BUNDLE_ALL` | `scripts/build-selfhost.mjs:13` |", "| `SELFHOST_SERVICE` | `scripts/smoke-observability-traces.mjs:5` |", "| `SELFHOST_SETUP_TOKEN` | `src/selfhost/preflight.ts:186` |", @@ -389,5 +393,6 @@ export const SELFHOST_ENV_REFERENCE_MARKDOWN = [ "| `SENTRY_RELEASE` | `src/selfhost/otel.ts:62` |", "| `SENTRY_SERVER_NAME` | `src/selfhost/sentry.ts:383` |", "| `SENTRY_TRACES_SAMPLE_RATE` | `src/selfhost/sentry.ts:171` |", - "| `SETUP_OUTPUT_PATH` | `src/server.ts:820` |", + "| `SETUP_OUTPUT_PATH` | `src/server.ts:837` |", + "| `SLACK_WEBHOOK_URL` | `src/services/notify-discord.ts:173` |", ].join("\n"); diff --git a/scripts/gen-selfhost-env-reference.mjs b/scripts/gen-selfhost-env-reference.mjs index 32f6d1722e..20114e275b 100644 --- a/scripts/gen-selfhost-env-reference.mjs +++ b/scripts/gen-selfhost-env-reference.mjs @@ -95,7 +95,16 @@ function bindingElementName(element) { return null; } -function isEnvContainer(node) { +// Unwraps `(x)` and `x as T` (including chained casts like `env as unknown as Record`, the +// pattern src/services/notify-discord.ts uses to read an env key TypeScript's Env type doesn't declare) so +// isEnvContainer sees the underlying identifier/property-access instead of the cast wrapper (#2907). +function unwrapEnvExpression(node) { + if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node)) return unwrapEnvExpression(node.expression); + return node; +} + +function isEnvContainer(rawNode) { + const node = unwrapEnvExpression(rawNode); if (ts.isIdentifier(node)) return node.text === "env"; return ( ts.isPropertyAccessExpression(node) && diff --git a/src/api/routes.ts b/src/api/routes.ts index 2a4f6a07cc..0a035f9c0c 100644 --- a/src/api/routes.ts +++ b/src/api/routes.ts @@ -619,7 +619,10 @@ const repositorySettingsSchema = z.object({ publicAudienceMode: z.enum(["oss_maintainer", "gittensor_only"]).default("oss_maintainer"), publicSignalLevel: z.enum(["minimal", "standard"]).default("standard"), checkRunMode: z.enum(["off", "enabled"]).default("off"), - checkRunDetailLevel: z.enum(["minimal", "standard", "deep"]).default("standard"), + // Matches repository_settings.check_run_detail_level's own column default (#2907) -- the Context check's + // public output is intentionally minimal by design (see formatCheckRunOutput's doc comment), so a caller of + // this full-replace route that omits this field must land on the same safe default as a never-configured row. + checkRunDetailLevel: z.enum(["minimal", "standard", "deep"]).default("minimal"), gateCheckMode: z.enum(["off", "enabled"]).default("off"), // #2852: deliberately NO `.default()` here (unlike every sibling field above) -- this is a non-partial, // full-replace schema (see upsertRepositorySettings call below, which passes every parsed field straight diff --git a/test/integration/api.test.ts b/test/integration/api.test.ts index a5977448b0..24d0827782 100644 --- a/test/integration/api.test.ts +++ b/test/integration/api.test.ts @@ -269,6 +269,18 @@ describe("api routes", () => { await expect(response.json()).resolves.toMatchObject({ repoFullName: "acme/badged", badgeEnabled: true }); }); + it("REGRESSION (#2907): defaults checkRunDetailLevel to minimal, matching the DB column's own default, when omitted", async () => { + const app = createApp(); + const env = createTestEnv(); + const response = await app.request( + "/v1/internal/repos/acme/detail-level-default/settings", + { method: "POST", headers: { authorization: `Bearer ${env.INTERNAL_JOB_TOKEN}`, "content-type": "application/json" }, body: JSON.stringify({ checkRunMode: "enabled" }) }, + env, + ); + expect(response.status).toBe(200); + await expect(response.json()).resolves.toMatchObject({ checkRunMode: "enabled", checkRunDetailLevel: "minimal" }); + }); + it("downgrades qualityGateMode: block to advisory through the internal settings write endpoint too (#2267)", async () => { // Readiness/quality can never hard-block a PR — the internal full-settings write path (used by tooling, // not just the maintainer dashboard) gets the identical downgrade so it can't persist "block" either. diff --git a/test/unit/selfhost-env-reference-script.test.ts b/test/unit/selfhost-env-reference-script.test.ts index 725a906276..23f3957a49 100644 --- a/test/unit/selfhost-env-reference-script.test.ts +++ b/test/unit/selfhost-env-reference-script.test.ts @@ -51,6 +51,7 @@ function fixtureRoot(): string { [ "const serviceOnly = process.env.SERVICE_ONLY;", "const helperOnly = envString(env, 'SERVICE_HELPER_ONLY');", + "const casted = (env as unknown as Record).CASTED_ONLY;", "", ].join("\n"), ); @@ -62,6 +63,7 @@ describe("gen-selfhost-env-reference (#2081)", () => { expect(collectSelfHostEnvVars({ rootDir: fixtureRoot() })).toEqual([ { name: "ALIASED_ENV", firstReference: "src/selfhost/a.ts:5" }, { name: "BRACKET_ONLY", firstReference: "src/selfhost/a.ts:4" }, + { name: "CASTED_ONLY", firstReference: "src/services/notify-discord.ts:3" }, { name: "CTX_ONLY", firstReference: "src/selfhost/a.ts:10" }, { name: "DEFAULTED_ENV", firstReference: "src/selfhost/a.ts:5" }, { name: "DESTRUCTURED", firstReference: "src/selfhost/a.ts:5" },