From 9736d6d46d28f9754ab4b66744bfef7faa3da43d Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Tue, 14 Jul 2026 00:58:53 -0700 Subject: [PATCH] fix(test): stop the local gate from failing on main Three independent, pre-existing gate breaks, none introduced by this branch -- found while chasing why release-please's PRs (#5710-5713) looked flaky under local parallel test runs, and confirmed to reproduce identically on a clean origin/main checkout: - test/unit/agent-sdk-driver.test.ts, ai-summaries.test.ts, upstream-ruleset.test.ts: each does real, non-trivial work (git subprocess round-trips, a 42-entry forbidden-word sweep, 29 sequential D1-backed scenarios) that reliably exceeds vitest's 15s default under load. Widened each to an explicit, realistic timeout instead of leaving them to flake. - test/unit/selfhost-grafana-dashboard.test.ts: 5f187c2dc (#5716) fixed a real production bug -- Grafana's ${var:sqlstring} doesn't sql-quote a value that starts with $__, so the old $__all "All repos" sentinel leaked through unescaped and SQLite misparsed it as its own bind parameter, silently zeroing every "All"-filtered panel. That commit updated the dashboard JSON and two sibling test files but missed this one. - apps/gittensory-ui/public/openapi.json + 3 route/component .tsx files: 40e6cdf7b (#5715, the @gittensory -> @loopover bot-mention rename) changed copy text but never regenerated the OpenAPI spec or ran prettier on the reflowed JSX text nodes, so ui:openapi:check and ui:lint both fail on main. Regenerated the spec (npm run ui:openapi) and ran eslint --fix on the affected files; both are mechanical, zero-semantic-diff outputs. --- apps/gittensory-ui/public/openapi.json | 22 ++++++------ .../site/app-panels/maintainer-settings.tsx | 4 +-- apps/gittensory-ui/src/routes/app.runs.tsx | 4 +-- apps/gittensory-ui/src/routes/docs.tuning.tsx | 35 +++++++++---------- test/unit/agent-sdk-driver.test.ts | 7 +++- test/unit/ai-summaries.test.ts | 6 +++- test/unit/selfhost-grafana-dashboard.test.ts | 18 +++++++--- test/unit/upstream-ruleset.test.ts | 6 +++- 8 files changed, 61 insertions(+), 41 deletions(-) diff --git a/apps/gittensory-ui/public/openapi.json b/apps/gittensory-ui/public/openapi.json index 368b79ef1a..2b6d0e2be0 100644 --- a/apps/gittensory-ui/public/openapi.json +++ b/apps/gittensory-ui/public/openapi.json @@ -1,9 +1,9 @@ { "openapi": "3.0.3", "info": { - "title": "Gittensory API", + "title": "LoopOver API", "version": "0.1.0", - "description": "Backend API for Gittensory advisory checks and Gittensor repository context." + "description": "Backend API for LoopOver advisory checks and Gittensor repository context." }, "components": { "schemas": { @@ -9160,15 +9160,15 @@ }, "chatQa": { "type": "boolean", - "description": "Opt the `@gittensory chat ` grounded Q&A surface (#4595) into local Ollama inference. Ollama-first: unlike the four capabilities above, it declines instead of falling back to the frontier env.AI when env.AI_ADVISORY is unconfigured, unless `chatQaFrontierFallback` is also enabled. Co-requisite: set `commandRateLimitPolicy` to `hold` (it defaults to `off` fleet-wide) so the tighter `commandRateLimitAiMaxPerWindow` ceiling actually throttles this cost-bearing command." + "description": "Opt the `@loopover chat ` grounded Q&A surface (#4595) into local Ollama inference. Ollama-first: unlike the four capabilities above, it declines instead of falling back to the frontier env.AI when env.AI_ADVISORY is unconfigured, unless `chatQaFrontierFallback` is also enabled. Co-requisite: set `commandRateLimitPolicy` to `hold` (it defaults to `off` fleet-wide) so the tighter `commandRateLimitAiMaxPerWindow` ceiling actually throttles this cost-bearing command." }, "chatQaFrontierFallback": { "type": "boolean", - "description": "Opt-in only (#4595 follow-up): when true, `@gittensory chat` falls back to the shared frontier env.AI chain if env.AI_ADVISORY is unconfigured, instead of declining. Meaningless unless `chatQa` is also true. Default false -- a self-hoster without a local GPU may enable this to use their own frontier subscription/tokens for chat instead." + "description": "Opt-in only (#4595 follow-up): when true, `@loopover chat` falls back to the shared frontier env.AI chain if env.AI_ADVISORY is unconfigured, instead of declining. Meaningless unless `chatQa` is also true. Default false -- a self-hoster without a local GPU may enable this to use their own frontier subscription/tokens for chat instead." }, "intentRouting": { "type": "boolean", - "description": "Opt a closed-set intent-classification router (#4596) into unrecognized `@gittensory` mentions: maps a free-text question to the closest existing Q&A command (never an action command) instead of the plain did-you-mean hint. Ollama-only, same as chatQa. Co-requisite: set `commandRateLimitPolicy` to `hold`." + "description": "Opt a closed-set intent-classification router (#4596) into unrecognized `@loopover` mentions: maps a free-text question to the closest existing Q&A command (never an action command) instead of the plain did-you-mean hint. Ollama-only, same as chatQa. Co-requisite: set `commandRateLimitPolicy` to `hold`." } }, "required": [ @@ -13889,7 +13889,7 @@ "GittensoryBearer": { "type": "http", "scheme": "bearer", - "description": "Static API/MCP token, GitHub device-flow Gittensory session token, or extension-scoped Gittensory session token where supported. GitHub personal access tokens are not accepted." + "description": "Static API/MCP token, GitHub device-flow LoopOver session token, or extension-scoped LoopOver session token where supported. GitHub personal access tokens are not accepted." }, "GittensorySessionCookie": { "type": "apiKey", @@ -15153,7 +15153,7 @@ "get": { "responses": { "200": { - "description": "Private self-dogfood registration pack for the Gittensory repo", + "description": "Private self-dogfood registration pack for the LoopOver repo", "content": { "application/json": { "schema": { @@ -15183,7 +15183,7 @@ "get": { "responses": { "200": { - "description": "Private self-dogfood registration pack when repo matches configured Gittensory target", + "description": "Private self-dogfood registration pack when repo matches configured LoopOver target", "content": { "application/json": { "schema": { @@ -15333,7 +15333,7 @@ "get": { "responses": { "200": { - "description": "Gittensory repository automation settings", + "description": "LoopOver repository automation settings", "content": { "application/json": { "schema": { @@ -16900,7 +16900,7 @@ "text/markdown": { "schema": { "type": "string", - "example": "# Weekly Gittensory value report\n\n## Adoption metrics\n- Active users: 4\n" + "example": "# Weekly LoopOver value report\n\n## Adoption metrics\n- Active users: 4\n" } } } @@ -17010,7 +17010,7 @@ "post": { "responses": { "200": { - "description": "Maintainer dry-run preview of a sanitized @gittensory command response (no GitHub mutation)", + "description": "Maintainer dry-run preview of a sanitized @loopover command response (no GitHub mutation)", "content": { "application/json": { "schema": { diff --git a/apps/gittensory-ui/src/components/site/app-panels/maintainer-settings.tsx b/apps/gittensory-ui/src/components/site/app-panels/maintainer-settings.tsx index fd8136dcae..483aae6bd2 100644 --- a/apps/gittensory-ui/src/components/site/app-panels/maintainer-settings.tsx +++ b/apps/gittensory-ui/src/components/site/app-panels/maintainer-settings.tsx @@ -389,8 +389,8 @@ export function MaintainerSettings({ reviewability }: { reviewability: Array<{ p

Command authorization

- Default roles allowed to run any @loopover{" "} - command. Per-command overrides (edited via the focus manifest) are shown below. + Default roles allowed to run any @loopover command. + Per-command overrides (edited via the focus manifest) are shown below.

{COMMAND_ROLES.map(([role, roleLabel]) => ( diff --git a/apps/gittensory-ui/src/routes/app.runs.tsx b/apps/gittensory-ui/src/routes/app.runs.tsx index 0862f4af1c..4d1da580cb 100644 --- a/apps/gittensory-ui/src/routes/app.runs.tsx +++ b/apps/gittensory-ui/src/routes/app.runs.tsx @@ -230,8 +230,8 @@ function AgentRuns() { Agent runs

- Unified feed of MCP, API, and @loopover runs. Each entry carries a ruleset snapshot - and a public/private boundary. + Unified feed of MCP, API, and @loopover runs. Each entry carries a ruleset snapshot and + a public/private boundary.

diff --git a/apps/gittensory-ui/src/routes/docs.tuning.tsx b/apps/gittensory-ui/src/routes/docs.tuning.tsx index 7ce2fcbe60..8587da9f22 100644 --- a/apps/gittensory-ui/src/routes/docs.tuning.tsx +++ b/apps/gittensory-ui/src/routes/docs.tuning.tsx @@ -150,13 +150,12 @@ function Tuning() { LOOPOVER_REVIEW_CONTINUOUS — fleet-wide default AI review re-trigger cadence. Off by default (one-shot): AI-generated content (main review, slop advisory, linked-issue satisfaction) is produced once per PR and never regenerated automatically afterward — only - an explicit maintainer retrigger (the PR-panel checkbox, or{" "} - @loopover review as a maintainer) spends a fresh call. Truthy switches the - fleet default to continuous — every push/CI-completion/sweep re-runs AI content - generation. A repo's own review.auto_review.cadence in{" "} - .loopover.yml always overrides this default, in either direction. Never - affects the deterministic gate (CI status, mergeability, static-rule blockers), which - always re-evaluates regardless. + an explicit maintainer retrigger (the PR-panel checkbox, or @loopover review{" "} + as a maintainer) spends a fresh call. Truthy switches the fleet default to continuous — + every push/CI-completion/sweep re-runs AI content generation. A repo's own{" "} + review.auto_review.cadence in .loopover.yml always overrides + this default, in either direction. Never affects the deterministic gate (CI status, + mergeability, static-rule blockers), which always re-evaluates regardless.
  • LOOPOVER_REVIEW_RAG — retrieval-augmented context: queries the codebase @@ -185,9 +184,9 @@ function Tuning() { maintainer's own past false-positive dismissals) and demotes or drops it before the unified comment renders. A maintainer records a signal with{" "} @loopover resolve [finding-code] (or a whole-PR{" "} - @loopover resolve ack). Advisory-only by construction — never applied to - gate blockers, so it can never change the merge/close disposition. Also requires the - per-repo review.memory: true opt-in in .loopover.yml. Per-PR. + @loopover resolve ack). Advisory-only by construction — never applied to gate + blockers, so it can never change the merge/close disposition. Also requires the per-repo{" "} + review.memory: true opt-in in .loopover.yml. Per-PR.
  • LOOPOVER_REVIEW_REPUTATION — submitter-reputation spend control. A new, @@ -283,12 +282,12 @@ function Tuning() { blocks.
  • - block — the finding can become a hard{" "} - LoopOver Orb Review Agent blocker. A block outcome fails the gate for any - author identically — confirmed-Gittensor-contributor status doesn't change{" "} - who can be blocked, only the mode chooses which deterministic checks are - active. Confirmed status is carried through for on-chain scoring, a separate concern from - the gate's own merge/close decision. + block — the finding can become a hard LoopOver Orb Review Agent{" "} + blocker. A block outcome fails the gate for any author identically — + confirmed-Gittensor-contributor status doesn't change who can be blocked, + only the mode chooses which deterministic checks are active. Confirmed status is + carried through for on-chain scoring, a separate concern from the gate's own + merge/close decision.
  • @@ -479,8 +478,8 @@ function Tuning() { publicSignalLevelminimal / standard (default).

  • - checkRunMode — publishes the LoopOver Context check (not - the Orb Review Agent gate check, which is reviewCheckMode):{" "} + checkRunMode — publishes the LoopOver Context check (not the{" "} + Orb Review Agent gate check, which is reviewCheckMode):{" "} off (default) / enabled. Pair with{" "} checkRunDetailLevel (minimal (default) / standard).
  • diff --git a/test/unit/agent-sdk-driver.test.ts b/test/unit/agent-sdk-driver.test.ts index 7f870d610a..f4549bc79b 100644 --- a/test/unit/agent-sdk-driver.test.ts +++ b/test/unit/agent-sdk-driver.test.ts @@ -88,6 +88,11 @@ describe("createAgentSdkCodingAgentDriver", () => { }); it("enumerates tracked and untracked worktree changes with git", async () => { + // Real subprocess spawns (init, 2x config, add, commit, plus the driver's own diff enumeration) -- + // legitimately more wall-clock latency than the default 15s test timeout reliably covers under + // concurrent CI shard/system load (observed timing out under heavy parallel contention with no logic + // failure; passes in well under 1s in isolation). An explicit timeout says so instead of relying on + // ambient headroom. const dir = await mkdtemp(join(tmpdir(), "gittensory-agent-sdk-")); try { await execFileAsync("git", ["init"], { cwd: dir }); @@ -114,7 +119,7 @@ describe("createAgentSdkCodingAgentDriver", () => { } finally { await rm(dir, { recursive: true, force: true }); } - }); + }, 30000); it("derives changed files from the worktree after untracked mutating tools", async () => { const driver = driverWith({ diff --git a/test/unit/ai-summaries.test.ts b/test/unit/ai-summaries.test.ts index 157369dd68..02b0e86e4c 100644 --- a/test/unit/ai-summaries.test.ts +++ b/test/unit/ai-summaries.test.ts @@ -409,12 +409,16 @@ describe("optional deterministic-summary rewrite layer", () => { }); it("routes every forbidden public term through the canonical sanitizer and falls back when AI is unsafe", async () => { + // Iterates every entry in FORBIDDEN_PUBLIC_COMMENT_WORDS (40+ words) as its own real round trip through + // rewriteSignalBundleWithAi -- legitimately more work than the default 15s test timeout reliably covers + // under load (observed timing out under concurrent CI shard contention, not a logic failure: every + // assertion that DID run passed). An explicit timeout says so instead of relying on ambient headroom. for (const word of FORBIDDEN_PUBLIC_COMMENT_WORDS) { const run = vi.fn(async () => ({ response: `Looks great, includes ${word} detail.` })); const result = await rewriteSignalBundleWithAi(publicEnv({}, run), rewriteReq()); expect(result, `forbidden word: ${word}`).toMatchObject({ status: "unsafe", text: DETERMINISTIC_BODY }); } - }); + }, 30000); it("rejects reward, ranking, scoreability, and reviewability variants in public AI rewrites", async () => { const unsafeOutputs = [ diff --git a/test/unit/selfhost-grafana-dashboard.test.ts b/test/unit/selfhost-grafana-dashboard.test.ts index bf1c892d38..e82b8f0bf5 100644 --- a/test/unit/selfhost-grafana-dashboard.test.ts +++ b/test/unit/selfhost-grafana-dashboard.test.ts @@ -65,7 +65,15 @@ function grafanaSqlString(value: string): string { return `'${value.replaceAll("'", "''")}'`; } -function expandGrafanaRange(query: string, repo = "$__all"): string { +// REGRESSION (#5716, 2026-07-14): the default/"All" sentinel is deliberately `__ALL__`, not Grafana's own +// `$__all` global. Confirmed live against a real Grafana + frser-sqlite-datasource instance: +// `${var:sqlstring}` does NOT sql-quote a value that itself starts with `$__` (Grafana treats it as a +// macro reference, not literal data), so the previous `allValue: "$__all"` substituted into +// `${repo:sqlstring} = '$__all' OR repo = ${repo:sqlstring}` produced the RAW unquoted token `$__all` on +// both sides. SQLite then parsed that token as its own `$__all` named bind parameter (unbound), so every +// "All"-filtered panel query either errored or silently returned zero rows. See 5f187c2dc for the fix on +// the dashboard side and its sibling ai-usage/miner-usage test simulations. +function expandGrafanaRange(query: string, repo = "__ALL__"): string { const from = Math.floor(Date.parse("2026-06-29T20:00:00Z") / 1000); const to = Math.floor(Date.parse("2026-06-29T22:00:00Z") / 1000); // Every panel's repo variable also needs expanding for a real sqlite3 CLI run, same as the time @@ -340,7 +348,7 @@ describe("maintainer Reviews & PRs Grafana dashboard", () => { expect(target?.queryType).toBe("table"); expect(target?.rawQueryText).toBe(target?.queryText); expect(target?.queryText).toContain("FROM issues"); - expect(target?.queryText).toContain("(${repo:sqlstring} = '$__all' OR repo = ${repo:sqlstring})"); + expect(target?.queryText).toContain("(${repo:sqlstring} = '__ALL__' OR repo = ${repo:sqlstring})"); expect(panel?.description?.length ?? 0).toBeGreaterThan(0); } @@ -358,7 +366,7 @@ describe("maintainer Reviews & PRs Grafana dashboard", () => { it("scopes the issue-activity panels to the selected $repo, same as the PR panels", () => { for (const id of [12, 13, 14] as const) { - expect(targetForPanel(id).queryText).toContain("(${repo:sqlstring} = '$__all' OR repo = ${repo:sqlstring})"); + expect(targetForPanel(id).queryText).toContain("(${repo:sqlstring} = '__ALL__' OR repo = ${repo:sqlstring})"); } }); @@ -381,7 +389,7 @@ describe("maintainer Reviews & PRs Grafana dashboard", () => { expect(targets.length).toBeGreaterThan(0); for (const target of targets) { - expect(target.queryText).toContain("(${repo:sqlstring} = '$__all' OR repo = ${repo:sqlstring})"); + expect(target.queryText).toContain("(${repo:sqlstring} = '__ALL__' OR repo = ${repo:sqlstring})"); expect(target.queryText).not.toContain("'$repo'"); } }); @@ -471,7 +479,7 @@ describe("maintainer Reviews & PRs Grafana dashboard", () => { const trackedQuery = targetForPanel(2).queryText!; const allRepos = sqlite(db, expandGrafanaRange(trackedQuery)); // A specific repo selection substitutes BOTH repo occurrences with the same SQL-escaped repo value (never - // the literal "$__all" sentinel, which only appears when "All" is selected). + // the literal "__ALL__" sentinel, which only appears when "All" is selected). const repoAOnly = sqlite(db, expandGrafanaRange(trackedQuery, "owner/repo-a")); const repoBOnly = sqlite(db, expandGrafanaRange(trackedQuery, "owner/repo-b")); diff --git a/test/unit/upstream-ruleset.test.ts b/test/unit/upstream-ruleset.test.ts index d6ce7d8c68..ec19b79dbe 100644 --- a/test/unit/upstream-ruleset.test.ts +++ b/test/unit/upstream-ruleset.test.ts @@ -1085,6 +1085,10 @@ describe("upstream ruleset drift tracking", () => { expect(calls.find((call) => call.method === "POST")?.body?.assignees).toEqual(["alice", "bob"]); // trimmed, empty segment dropped }); + // 29 distinct real-D1-backed scenarios run sequentially in this one test -- legitimately more work than + // the default 15s test timeout reliably covers under concurrent CI shard/system load (observed timing out + // under heavy parallel contention with no assertion failure). An explicit timeout says so instead of + // relying on ambient headroom. it("handles edge cases while filing upstream drift issues", async () => { const defaultRepoEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "1", GITTENSORY_DRIFT_ISSUE_TOKEN: "token", GITTENSORY_DRIFT_ISSUE_REPO: "" }); await upsertUpstreamDriftReport(defaultRepoEnv, driftReport("source-fingerprint", { severity: "medium", affectedAreas: [] })); @@ -1225,7 +1229,7 @@ describe("upstream ruleset drift tracking", () => { const disabledEnv = createTestEnv(); delete (disabledEnv as Partial).GITTENSORY_AUTO_FILE_DRIFT_ISSUES; await expect(fileUpstreamDriftIssues(disabledEnv)).resolves.toMatchObject({ status: "disabled" }); - }); + }, 45000); it("finds the existing drift issue on page 2 when the repo has more than 100 open signals issues", async () => { const env = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });