From e4e53aef7ebfd2162e4096fa57e888dc88b52e47 Mon Sep 17 00:00:00 2001
From: JSONbored <49853598+JSONbored@users.noreply.github.com>
Date: Tue, 14 Jul 2026 04:15:59 -0700
Subject: [PATCH 1/2] feat(config)!: Phase 6 - full-cutover rename internal
GITTENSORY_* constants to LOOPOVER_*
Phase 6 of the gittensory -> loopover rebrand epic (#5705): renames every
GITTENSORY_*-prefixed constant and env var identifier, plus the handful of
remaining camelCase identifiers Phase 4's broader sweep hadn't already
absorbed.
- ~58 GITTENSORY_* env var constants renamed to LOOPOVER_* across source,
wrangler.jsonc, .env.example/.env.selfhost.example, GitHub Actions
workflows, deployment shell scripts (a file extension the earlier sweeps
never covered), and migration-file comments. GITTENSORY_LEGACY_* (the
pre-rename check-run-name constants) and GITTENSORY_EXPERIMENTAL_GITTENSOR's
trailing "GITTENSOR" (the Bittensor subnet, not this rebrand's target)
deliberately preserved.
- Regenerated worker-configuration.d.ts (npm run cf-typegen) and
apps/gittensory-ui/src/lib/selfhost-env-reference.ts (npm run
selfhost:env-reference) rather than hand-editing the generated output.
- A handful of remaining camelCase identifiers Phase 4's blanket sweep didn't
reach: gittensoryFooter, the browser-extension global namespace keys
(__gittensoryMinerOpportunityBadge and friends), and DOM dataset property
names.
Reverted 15 lines in .env.example/.env.selfhost.example that my own sweep
initially over-renamed: "# GITTENSORY_X= # no longer read (removed by #4777)"
lines are deliberate operator-migration breadcrumbs documenting an EARLIER,
already-completed retirement (old gittensory_-prefixed vars replaced by
loopover_-prefixed ones under #4777) -- renaming the "old" side of that
breadcrumb to match the "new" side it's contrasted against would have made
both lines identical and the comment meaningless.
Advances #5705
BREAKING CHANGE: every GITTENSORY_* environment variable is now LOOPOVER_*.
No dual-read/alias, per the epic's full-cutover mandate. Operators must
rename these in their .env / secrets before deploying this change.
---
.../contributing-to-loopover/reference.md | 4 +-
.env.example | 12 +-
.github/workflows/ci.yml | 4 +-
.github/workflows/ui-deploy.yml | 2 +-
.github/workflows/ui-preview-deploy.yml | 2 +-
.github/workflows/ui-preview.yml | 2 +-
.loopover.yml.example | 40 ++---
CHANGELOG.md | 2 +-
CONVERGENCE_RUNBOOK.md | 24 +--
README.md | 6 +-
apps/loopover-extension/auth.js | 4 +-
apps/loopover-extension/content.js | 2 +-
apps/loopover-ui/public/openapi.json | 2 +-
.../components/site/proof-of-power-stats.tsx | 2 +-
apps/loopover-ui/src/lib/api/origin.ts | 2 +-
apps/loopover-ui/src/lib/openapi.ts | 2 +-
.../src/routes/docs.miner-quickstart.tsx | 2 +-
.../docs.self-hosting-backup-scaling.tsx | 4 +-
.../routes/docs.self-hosting-operations.tsx | 10 +-
.../routes/docs.self-hosting-quickstart.tsx | 2 +-
.../src/routes/docs.self-hosting-releases.tsx | 2 +-
apps/loopover-ui/wrangler.jsonc | 2 +-
config/examples/README.md | 22 +--
config/examples/TEMPLATES.md | 10 +-
config/examples/global.loopover.yml | 4 +-
config/examples/loopover.full.yml | 42 ++---
config/examples/loopover.minimal.yml | 6 +-
config/examples/repo-override.loopover.yml | 4 +-
config/examples/shared.loopover.yml | 2 +-
docker-compose.yml | 18 +-
migrations/0051_repo_chunks.sql | 4 +-
...repository_skip_automation_bot_authors.sql | 2 +-
packages/loopover-engine/CHANGELOG.md | 4 +-
.../src/advisory/gate-advisory.ts | 2 +-
.../loopover-engine/src/duplicate-winner.ts | 2 +-
.../loopover-engine/src/focus-manifest.ts | 2 +-
packages/loopover-mcp/CHANGELOG.md | 2 +-
packages/loopover-mcp/README.md | 18 +-
packages/loopover-mcp/bin/loopover-mcp.js | 92 +++++-----
packages/loopover-mcp/lib/local-branch.js | 4 +-
packages/loopover-miner/CHANGELOG.md | 4 +-
packages/loopover-miner/README.md | 2 +-
.../docs/discovery-plane-operator-guide.md | 2 +-
packages/loopover-miner/lib/update-check.js | 2 +-
scripts/backup.sh | 2 +-
scripts/check-engine-parity.ts | 6 +-
scripts/check-manifest-drift.mjs | 12 +-
scripts/check-ui-mcp-version-copy.mjs | 6 +-
scripts/deploy-selfhost-image.sh | 8 +-
scripts/export-ams-reporting-db.sh | 10 +-
scripts/export-grafana-reporting-db.sh | 4 +-
scripts/gen-command-reference.mjs | 2 +-
scripts/selfhost-docker-prune.sh | 2 +-
scripts/selfhost-post-update-check.sh | 2 +-
scripts/smoke-production.mjs | 4 +-
scripts/smoke-ui-browser.mjs | 2 +-
scripts/verify-backup.sh | 6 +-
src/api/routes.ts | 10 +-
src/config/gittensory-repo-focus-manifest.ts | 6 +-
src/db/schema.ts | 2 +-
src/env.d.ts | 42 ++---
src/github/commands.ts | 28 +--
src/github/footer.ts | 12 +-
src/github/repo-doc-pr.ts | 4 +-
src/index.ts | 10 +-
src/openapi/spec.ts | 2 +-
src/orb/oauth.ts | 4 +-
src/queue/duplicate-detection.ts | 2 +-
src/queue/job-dispatch.ts | 6 +-
src/queue/processors.ts | 16 +-
src/review/feature-activation.ts | 2 +-
src/review/gittensor-wire.ts | 8 +-
src/review/maintainer-recap-wire.ts | 24 +--
src/review/outcomes-wire.ts | 6 +-
src/review/parity-wire.ts | 6 +-
src/review/pr-reconciliation.ts | 6 +-
src/review/public-stats.ts | 12 +-
src/review/repo-doc-render.ts | 2 +-
src/review/sweep-watchdog.ts | 6 +-
src/rules/advisory.ts | 2 +-
src/scenarios/input-model.ts | 4 +-
src/selfhost/queue-common.ts | 2 +-
src/services/client-telemetry.ts | 4 +-
src/services/contributor-issue-draft.ts | 2 +-
src/services/maintainer-activation.ts | 2 +-
src/services/mcp-compatibility.ts | 12 +-
src/services/public-reuse-rate-trend.ts | 2 +-
src/services/subnet-interface.ts | 10 +-
src/settings/agent-actions.ts | 2 +-
src/settings/automation-bot-skip.ts | 4 +-
src/signals/engine.ts | 2 +-
src/signals/focus-manifest-loader.ts | 4 +-
src/types.ts | 8 +-
src/upstream/ruleset.ts | 10 +-
systemd/loopover-docker-prune.service.example | 2 +-
test/helpers/d1.ts | 8 +-
.../public-stats-route-error.test.ts | 2 +-
test/integration/public-stats-route.test.ts | 6 +-
test/unit/automation-bot-skip.test.ts | 14 +-
test/unit/backfill-2.test.ts | 2 +-
test/unit/backup-script.test.ts | 2 +-
test/unit/change-guardrail.test.ts | 6 +-
test/unit/check-manifest-drift-script.test.ts | 12 +-
test/unit/contributor-issue-draft.test.ts | 24 +--
test/unit/docs-miner-quickstart.test.ts | 2 +-
.../docs-selfhost-update-rollback.test.ts | 2 +-
test/unit/e2e-test-gen-render.test.ts | 10 +-
test/unit/extension-content.test.ts | 2 +-
test/unit/focus-manifest-loader.test.ts | 2 +-
test/unit/footer.test.ts | 18 +-
.../unit/gen-command-reference-script.test.ts | 2 +-
test/unit/generated-doc-refresh.test.ts | 4 +-
test/unit/github-commands.test.ts | 20 +--
test/unit/gittensor-wire.test.ts | 34 ++--
test/unit/gittensory-focus-manifest.test.ts | 28 +--
test/unit/index.test.ts | 32 ++--
test/unit/local-branch.test.ts | 4 +-
test/unit/local-scorer-adapter.test.ts | 4 +-
test/unit/maintainer-activation.test.ts | 4 +-
test/unit/maintainer-recap-wire.test.ts | 38 ++--
test/unit/mcp-cli-analyze-branch.test.ts | 2 +-
test/unit/mcp-cli-basics.test.ts | 34 ++--
test/unit/mcp-cli-burden-forecast.test.ts | 8 +-
test/unit/mcp-cli-doctor.test.ts | 170 +++++++++---------
test/unit/mcp-cli-find-opportunities.test.ts | 8 +-
test/unit/mcp-cli-issue-rag.test.ts | 8 +-
test/unit/mcp-cli-issue-slop.test.ts | 2 +-
test/unit/mcp-cli-label-audit.test.ts | 8 +-
test/unit/mcp-cli-lint-pr-text.test.ts | 2 +-
test/unit/mcp-cli-maintain.test.ts | 2 +-
test/unit/mcp-cli-maintainer-noise.test.ts | 8 +-
test/unit/mcp-cli-packets.test.ts | 98 +++++-----
test/unit/mcp-cli-profiles.test.ts | 42 ++---
test/unit/mcp-cli-review-pr.test.ts | 42 ++---
test/unit/mcp-cli-slop-risk.test.ts | 2 +-
test/unit/mcp-cli-tools.test.ts | 8 +-
test/unit/mcp-cli-upstream-drift.test.ts | 8 +-
test/unit/mcp-cli-validate-config.test.ts | 2 +-
test/unit/mcp-discovery.test.ts | 8 +-
test/unit/mcp-feasibility-gate.test.ts | 4 +-
test/unit/mcp-tool-rename-aliases.test.ts | 6 +-
test/unit/miner-cli.test.ts | 26 +--
test/unit/parity-wire.test.ts | 22 +--
test/unit/pr-reconciliation.test.ts | 4 +-
test/unit/public-accuracy-trend.test.ts | 6 +-
test/unit/public-reuse-rate-trend.test.ts | 6 +-
test/unit/public-review-volume-trend.test.ts | 8 +-
test/unit/public-stats.test.ts | 18 +-
test/unit/queue-2.test.ts | 6 +-
test/unit/queue-5.test.ts | 24 +--
test/unit/queue-lifecycle-guards.test.ts | 4 +-
test/unit/queue.test.ts | 6 +-
.../reconcile-live-duplicate-siblings.test.ts | 24 +--
test/unit/repo-doc-pr.test.ts | 22 +--
test/unit/repo-doc-render.test.ts | 40 ++---
.../routes-contributor-issue-draft.test.ts | 4 +-
test/unit/routes-focus-manifest.test.ts | 10 +-
test/unit/scenario-input-model.test.ts | 6 +-
.../self-dogfood-registration-pack.test.ts | 6 +-
test/unit/selfhost-ams-reporting.test.ts | 6 +-
test/unit/selfhost-backup-script.test.ts | 2 +-
.../unit/selfhost-docker-prune-script.test.ts | 10 +-
test/unit/selfhost-grafana-reporting.test.ts | 6 +-
test/unit/selfhost-image-deploy.test.ts | 20 +--
test/unit/selfhost-queue-common.test.ts | 2 +-
.../selfhost-verify-backup-script.test.ts | 50 +++---
test/unit/support/mcp-cli-harness.ts | 14 +-
test/unit/sweep-watchdog.test.ts | 4 +-
test/unit/unified-comment-parity.test.ts | 4 +-
test/unit/upstream-ruleset.test.ts | 68 +++----
worker-configuration.d.ts | 46 ++---
wrangler.jsonc | 24 +--
172 files changed, 995 insertions(+), 995 deletions(-)
diff --git a/.claude/skills/contributing-to-loopover/reference.md b/.claude/skills/contributing-to-loopover/reference.md
index 5a23dda2cf..a370438d1b 100644
--- a/.claude/skills/contributing-to-loopover/reference.md
+++ b/.claude/skills/contributing-to-loopover/reference.md
@@ -17,7 +17,7 @@ for maintainer approval (CI shows unverified → the engine **holds**, never clo
The single **required** status check is **`validate`** (it aggregates `changes, lint, test, workers,
mcp, ui, security`; a path-skipped job counts as success). **Codecov** posts `codecov/patch` (the real
coverage gate) and `codecov/project` (informational) independently. The review engine also posts its
-own check run named **`LoopOver Orb Review Agent`** (`src/github/app.ts` `GITTENSORY_GATE_CHECK_NAME`) — the gate
+own check run named **`LoopOver Orb Review Agent`** (`src/github/app.ts` `LOOPOVER_GATE_CHECK_NAME`) — the gate
verdict (§3), separate from CI. On a PR, jobs run only if their
path filter matched; on push to `main`, everything runs.
@@ -188,7 +188,7 @@ own PR.)
```ts
import { createTestEnv } from "../helpers/d1";
-const env = createTestEnv({ GITTENSORY_REVIEW_REPOS: "JSONbored/gittensory" });
+const env = createTestEnv({ LOOPOVER_REVIEW_REPOS: "JSONbored/gittensory" });
await env.DB.prepare(`INSERT INTO repositories (full_name, owner, name) VALUES (?,?,?)`)
.bind("JSONbored/gittensory", "JSONbored", "gittensory").run();
const row = await env.DB.prepare(`SELECT * FROM repositories WHERE full_name = ?`)
diff --git a/.env.example b/.env.example
index 2ddf48f055..5f914452bb 100644
--- a/.env.example
+++ b/.env.example
@@ -165,8 +165,8 @@ LOOPOVER_REVIEW_DRAFT=false
# TOKEN_ENCRYPTION_SECRET= # AES-256-GCM master secret for maintainer BYOK keys at rest
# DRAFT_TOKEN_ENCRYPTION_SECRET= # AES-256-GCM secret for the contributor OAuth token (draft flow)
# LOOPOVER_REVIEW_STATS_TOKEN= # bearer token guarding the stats data endpoint
-# GITTENSORY_DRIFT_ISSUE_TOKEN= # token for auto-filing drift issues
-# GITTENSORY_CONTRIBUTOR_ISSUE_TOKEN= # token for contributor-issue automation
+# LOOPOVER_DRIFT_ISSUE_TOKEN= # token for auto-filing drift issues
+# LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN= # token for contributor-issue automation
# PRODUCT_USAGE_HASH_SALT= # salt for hashing product-usage identifiers
# =============================================================================
@@ -258,7 +258,7 @@ LOOPOVER_REVIEW_DRAFT=false
# # request-latency metrics to tell those two cases apart).
REDIS_URL=redis://redis:6379 # REQUIRED for the self-host review runtime. The default compose stack
# # starts Redis automatically; override for an external Redis.
-# GITTENSORY_IMAGE=ghcr.io/jsonbored/loopover-selfhost:latest # image used by scripts/deploy-selfhost-image.sh;
+# LOOPOVER_IMAGE=ghcr.io/jsonbored/loopover-selfhost:latest # image used by scripts/deploy-selfhost-image.sh;
# # pin production rollouts to a release tag such as :orb-v0.1.0
# # or to an immutable @sha256 digest.
# GITHUB_CACHE_TTL_SECONDS=20 # Enables the Redis-backed GitHub GET-response cache when >0; set 0
@@ -335,8 +335,8 @@ REDIS_URL=redis://redis:6379 # REQUIRED for the self-host review
# # benchmarking on an RTX A5000 found 96 already near-optimal
# # (~34ms/chunk vs ~70ms/chunk at 32), so leave unset unless you
# # have hardware-specific data suggesting otherwise.
-# GITTENSORY_REPORTING_SOURCE_DATABASE_URL= # optional Postgres reporting reader URL. Defaults to DATABASE_URL.
-# GITTENSORY_BACKUP_SOURCE_DATABASE_URL= # optional Postgres backup reader URL. Defaults to DATABASE_URL.
+# LOOPOVER_REPORTING_SOURCE_DATABASE_URL= # optional Postgres reporting reader URL. Defaults to DATABASE_URL.
+# LOOPOVER_BACKUP_SOURCE_DATABASE_URL= # optional Postgres backup reader URL. Defaults to DATABASE_URL.
# MIGRATIONS_DIR=/app/migrations
# CRON_INTERVAL_MS=120000 # maintain/sweep + sync cadence (default ~2 min)
@@ -493,7 +493,7 @@ REDIS_URL=redis://redis:6379 # REQUIRED for the self-host review
# Install the systemd timer (systemd/loopover-docker-prune.{service,timer}.example) to reclaim stopped
# containers, unused images, and build cache on a schedule -- never volumes/application data. Run it
# manually at any time with `sh scripts/selfhost-docker-prune.sh`, or preview first with `--dry-run`.
-# GITTENSORY_DOCKER_PRUNE_RETAIN_HOURS=168 # age floor before something unused is eligible for pruning (7 days)
+# LOOPOVER_DOCKER_PRUNE_RETAIN_HOURS=168 # age floor before something unused is eligible for pruning (7 days)
# --- Observability: metrics + alerts + logs (#1206; requires --profile observability) ---
# The observability profile starts Prometheus (scrapes /metrics) + Alertmanager (alert rules in
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a881e4e6cb..aca2e23366 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -169,7 +169,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 45
env:
- VITE_GITTENSORY_API_ORIGIN: https://api.loopover.ai
+ VITE_LOOPOVER_API_ORIGIN: https://api.loopover.ai
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
@@ -422,7 +422,7 @@ jobs:
matrix:
shard: [1, 2, 3, 4, 5, 6]
env:
- VITE_GITTENSORY_API_ORIGIN: https://api.loopover.ai
+ VITE_LOOPOVER_API_ORIGIN: https://api.loopover.ai
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
diff --git a/.github/workflows/ui-deploy.yml b/.github/workflows/ui-deploy.yml
index 4f8727fa95..f627ef07b7 100644
--- a/.github/workflows/ui-deploy.yml
+++ b/.github/workflows/ui-deploy.yml
@@ -36,5 +36,5 @@ jobs:
# regen (mirrors the same fix in .github/workflows/ci.yml's "UI build" step).
- name: Validate frontend
env:
- VITE_GITTENSORY_API_ORIGIN: https://api.loopover.ai
+ VITE_LOOPOVER_API_ORIGIN: https://api.loopover.ai
run: npm run ui:openapi:check && npm run ui:lint && npm run ui:typecheck && npm run extension:lint && npm run miner-extension:lint && npm run extension:typecheck && npm run miner-extension:typecheck && npm run extension:build && npm run miner-extension:build && npm --workspace @loopover/ui run build
diff --git a/.github/workflows/ui-preview-deploy.yml b/.github/workflows/ui-preview-deploy.yml
index 05f0364d23..9d4e49508f 100644
--- a/.github/workflows/ui-preview-deploy.yml
+++ b/.github/workflows/ui-preview-deploy.yml
@@ -143,7 +143,7 @@ jobs:
}
},
"vars": {
- "VITE_GITTENSORY_API_ORIGIN": "https://api.loopover.ai"
+ "VITE_LOOPOVER_API_ORIGIN": "https://api.loopover.ai"
},
"main": "index.mjs",
"assets": {
diff --git a/.github/workflows/ui-preview.yml b/.github/workflows/ui-preview.yml
index e989a876dc..d755fb8916 100644
--- a/.github/workflows/ui-preview.yml
+++ b/.github/workflows/ui-preview.yml
@@ -47,7 +47,7 @@ jobs:
- name: Build UI
env:
- VITE_GITTENSORY_API_ORIGIN: https://api.loopover.ai
+ VITE_LOOPOVER_API_ORIGIN: https://api.loopover.ai
# Preview-only: enables the synthetic demo session (useSession().signInPreview) so reviewbot can
# screenshot the authenticated /app/* dashboard via ?preview=1 instead of the sign-in wall. The
# production build (ui-deploy.yml) does NOT set this, so the escape hatch is dead-code-eliminated
diff --git a/.loopover.yml.example b/.loopover.yml.example
index a517a0362d..9d7ae80587 100644
--- a/.loopover.yml.example
+++ b/.loopover.yml.example
@@ -39,7 +39,7 @@
#
# NOTE: some capabilities (safety scanning, CI/full-file grounding, RAG,
# reputation control, the unified comment) are switched on at the deployment
-# level by the operator's GITTENSORY_REVIEW_* feature flags AND a per-repo
+# level by the operator's LOOPOVER_REVIEW_* feature flags AND a per-repo
# cutover allowlist. This file tunes behavior; it does not enable those
# deployment-wide capabilities.
# ============================================================================
@@ -404,8 +404,8 @@ gate:
# deterministic gate policy above. Omit the block to keep the byte-identical
# defaults.
#
-# SELF-HOST ONLY (`review.shared_config`, #2046): when `GITTENSORY_REPO_CONFIG_DIR` is mounted,
-# place a shared review base at `${GITTENSORY_REPO_CONFIG_DIR}/_shared/.loopover.yml` (see
+# SELF-HOST ONLY (`review.shared_config`, #2046): when `LOOPOVER_REPO_CONFIG_DIR` is mounted,
+# place a shared review base at `${LOOPOVER_REPO_CONFIG_DIR}/_shared/.loopover.yml` (see
# `config/examples/shared.loopover.yml`). Per-repo `review:` keys overlay it field-by-field —
# repo value wins when set, shared fills gaps, defaults stay byte-identical. Absent shared base is
# the common case and changes nothing. A malformed shared base warns and is ignored (never blocks a
@@ -472,7 +472,7 @@ review:
# When true, the unified review comment gains a compact "review effort: N/5 (~M min)" chip.
# Bool | null. Default: null/false — byte-identical (#2184, part of #1971). Also requires the operator's
- # GITTENSORY_REVIEW_IMPACT_MAP env flag to be on — this manifest field alone cannot enable it. When both are
+ # LOOPOVER_REVIEW_IMPACT_MAP env flag to be on — this manifest field alone cannot enable it. When both are
# on, a deterministic impact map (which other repo files plausibly need re-checking, from the RAG index +
# changed symbols) is computed, rendered as a compact section in the unified review comment, and fed to the
# AI reviewer as additive reference context.
@@ -480,12 +480,12 @@ review:
# When true, the AI reviewer's prompt gains an additive "repo quality-culture profile" reference block --
# typical merged-PR size + common accepted labels, derived from this repo's own merge history. Reference-only
- # grounding; never a gate/scoring input. Requires operator flag GITTENSORY_REVIEW_CULTURE_PROFILE. Bool or
+ # grounding; never a gate/scoring input. Requires operator flag LOOPOVER_REVIEW_CULTURE_PROFILE. Bool or
# null. Default: null/false — byte-identical. (#2995)
# culture_profile: false
# Per-repo FORCE-OFF for the self-improvement/auto-tune cron pass (#4104) -- `false` excludes this repo from
- # tuning even though it's otherwise agent-configured and the operator's GITTENSORY_REVIEW_SELFTUNE flag is on.
+ # tuning even though it's otherwise agent-configured and the operator's LOOPOVER_REVIEW_SELFTUNE flag is on.
# Deliberately FORCE-OFF-ONLY (no `true` override): unlike most `review.*` toggles above, this does NOT live
# under a per-repo allowlist -- selftune's own scoping is whether the repo already opted into the
# acting-autonomy surface, a separate consent boundary this key must not bypass. Bool or null. Default:
@@ -493,7 +493,7 @@ review:
# selftune: false
# Repeat-false-positive suppression (#2179, part of #1964). Bool | null. Default: null/false — byte-identical
- # (no suppression-store read, no matching). Also requires the operator's GITTENSORY_REVIEW_MEMORY env flag to
+ # (no suppression-store read, no matching). Also requires the operator's LOOPOVER_REVIEW_MEMORY env flag to
# be on -- this manifest field alone cannot enable it. When both are on, an advisory (non-blocking) AI finding
# is matched against this repo's stored review_suppression signals (a maintainer's own past false-positive
# dismissals) before it is surfaced, and demoted/dropped on a match. ADVISORY-ONLY: never applied to gate
@@ -534,7 +534,7 @@ review:
# e2e_test_auto_trigger: false
# Inline-comment layer toggles (#1956 / #1958). Bool | null. Default: null/false — byte-identical.
- # Requires operator flag GITTENSORY_REVIEW_INLINE_COMMENTS + cutover allowlist + review.inline_comments: true.
+ # Requires operator flag LOOPOVER_REVIEW_INLINE_COMMENTS + cutover allowlist + review.inline_comments: true.
# inline_comments: false
# When true, the AI reviewer ALSO leaves quiet, non-blocking inline PR comments on specific changed lines.
# suggestions: false
@@ -543,7 +543,7 @@ review:
# inline_comments_per_category: 3
# Fix-handoff blocks (#2176). Bool | null. Default: null/false — byte-identical.
- # Requires operator flag GITTENSORY_REVIEW_FIX_HANDOFF + cutover allowlist AND this toggle.
+ # Requires operator flag LOOPOVER_REVIEW_FIX_HANDOFF + cutover allowlist AND this toggle.
# fixHandoff: false
# Read-only auto-merge readiness summary (#2051). Bool | null. Default: null/false — byte-identical.
@@ -967,7 +967,7 @@ settings:
# instructions: "Flag any migration missing a matching down-path note."
# # When true, the AI reviewer ALSO leaves quiet, non-blocking inline PR comments on specific changed
# # lines, in addition to the decision summary. Bool or null. Default: null/false (no inline comments).
-# # Operator-gated too (GITTENSORY_REVIEW_INLINE_COMMENTS + allowlist).
+# # Operator-gated too (LOOPOVER_REVIEW_INLINE_COMMENTS + allowlist).
# inline_comments: false
# # When true, an inline finding whose fix is precise enough to anchor to one line is ALSO rendered as
# # a one-click GitHub suggestion block. Only takes effect when inline_comments is already on. Bool or
@@ -981,7 +981,7 @@ settings:
# # compact "review effort: N/5 (~M min)" chip -- a deterministic, no-AI complexity/time estimate from the
# # changed files' added-line volume and file-type mix. Bool or null. Default: null/false.
# effort_score: false
-# # When true (AND the operator's GITTENSORY_REVIEW_IMPACT_MAP env flag is also on), a deterministic
+# # When true (AND the operator's LOOPOVER_REVIEW_IMPACT_MAP env flag is also on), a deterministic
# # impact map -- which other repo files plausibly need re-checking, from the RAG index + changed
# # symbols -- is computed, rendered as a compact unified-comment section, and fed to the AI reviewer
# # as additive reference context. Bool or null. Default: null/false (#2184, part of #1971).
@@ -989,14 +989,14 @@ settings:
# # When true, the AI reviewer's prompt gains an additive "repo quality-culture profile" reference block --
# # typical merged-PR size + common accepted labels, derived from this repo's OWN recent merge history
# # (recent_merged_pull_requests). Reference-only grounding, never a gate/scoring input; requires the operator
-# # flag GITTENSORY_REVIEW_CULTURE_PROFILE. Bool or null. Default: null/false. (#2995)
+# # flag LOOPOVER_REVIEW_CULTURE_PROFILE. Bool or null. Default: null/false. (#2995)
# culture_profile: false
# # Per-repo FORCE-OFF for the self-improvement/auto-tune cron pass (#4104) -- false excludes this repo from
# # tuning even though it's otherwise agent-configured and the operator flag is on. FORCE-OFF-ONLY, no true
# # override -- selftune's own scoping is the acting-autonomy consent boundary, not a per-repo allowlist. Bool
# # or null. Default: null/true -- no change to today's agent-configured-repos-only behavior.
# selftune: false
-# # When true (AND the operator's GITTENSORY_REVIEW_MEMORY env flag is also on), an advisory (non-blocking)
+# # When true (AND the operator's LOOPOVER_REVIEW_MEMORY env flag is also on), an advisory (non-blocking)
# # AI finding is matched against this repo's stored review_suppression signals (a maintainer's own past
# # false-positive dismissals) before it is surfaced, and demoted/dropped on a match. ADVISORY-ONLY: never
# # applied to gate blockers -- it can never change the merge/close disposition. Bool or null.
@@ -1037,7 +1037,7 @@ settings:
# openai_compatible_model: null # Overrides OPENAI_COMPATIBLE_AI_MODEL for this repo. String or null. (#3902)
# anthropic_model: null # Overrides ANTHROPIC_AI_MODEL for this repo's BYOK Messages API reviewer. String or null. (#3902)
# # Per-repo before/after screenshot-capture config (#3609 preview / #3610 routes). Only takes effect when
-# # the operator has ALSO enabled GITTENSORY_REVIEW_SCREENSHOTS + this repo's cutover allowlist -- this
+# # the operator has ALSO enabled LOOPOVER_REVIEW_SCREENSHOTS + this repo's cutover allowlist -- this
# # config narrows/redirects that feature, it never turns it on by itself. All-null/empty/default ⇒
# # byte-identical to today (GitHub-native preview discovery, automatic file-to-route inference).
# visual:
@@ -1084,7 +1084,7 @@ settings:
# # renders per side, ~4s wall-clock per side measured in practice. Bool. Default: false (no scroll capture).
# gif: false
# # Config-as-code enable/disable for this repo, layered ON TOP OF (never a replacement for) the
-# # GITTENSORY_REVIEW_SCREENSHOTS + per-repo cutover-allowlist env-var gate above (#4083). Bool or null.
+# # LOOPOVER_REVIEW_SCREENSHOTS + per-repo cutover-allowlist env-var gate above (#4083). Bool or null.
# # Default: null (unset) ⇒ defers entirely to that env-var gate's own decision -- byte-identical to today.
# # Explicit `false` (set once at the global-default `.loopover.yml`, or overridden per-repo here) forces
# # capture off for this repo even when the env-var gate would otherwise allow it. Explicit `true` opts this
@@ -1128,9 +1128,9 @@ settings:
# auto_pause_after_reviewed_commits: 3
# Per-repo activation overrides for the converged review features that ship behind a deployment-wide
-# GITTENSORY_REVIEW_* env kill-switch (rag/reputation/unifiedComment/safety/grounding/e2eTests/screenshots/
+# LOOPOVER_REVIEW_* env kill-switch (rag/reputation/unifiedComment/safety/grounding/e2eTests/screenshots/
# improvementSignal). 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 --
+# enabled), `false` (force off), or omitted (falls back to the LOOPOVER_REVIEW_REPOS allowlist default --
# an operator who sets nothing keeps today's behavior). Exception: `safety` is force-on-only -- an untrusted
# repo-controlled `false` is treated as "no opinion" rather than an active force-off (#2269), since a
# lower-trust actor must never be able to silently defeat the operator's own security-hardening enablement.
@@ -1158,8 +1158,8 @@ settings:
# Optional ecosystem/network PLUGINS -- distinct from `features:` above, which only toggles gittensory's own
# converged review capabilities. Each key here couples this instance to an external system and is OFF unless
-# BOTH a deployment-wide GITTENSORY_EXPERIMENTAL_* env kill-switch AND an explicit per-repo `true` are set (no
-# GITTENSORY_REVIEW_REPOS allowlist fallback -- unlike `features:`, there is no default-on path). `gittensor`
+# BOTH a deployment-wide LOOPOVER_EXPERIMENTAL_* env kill-switch AND an explicit per-repo `true` are set (no
+# LOOPOVER_REVIEW_REPOS allowlist fallback -- unlike `features:`, there is no default-on path). `gittensor`
# is the first plugin: gittensory's original subnet mining-registry/scoring integration (per-repo emission
# share, maintainer cut, label multipliers pulled from the gittensor subnet's registry), now opt-in rather
# than a core dependency -- a self-host instance that never sets this has zero footprint from it: no fetch
@@ -1206,7 +1206,7 @@ settings:
# that folds gate-precision + outcome-calibration across every scanned repo into one report (distinct from
# the single-repo reviewRecap above). Operator-level, not per-repo -- only meaningful on the gittensory
# self-repo's own manifest (the repo this instance identifies as); a present block there wins over the
-# GITTENSORY_MAINTAINER_RECAP / GITTENSORY_RECAP_CADENCE env vars, which stay the fallback when absent.
+# LOOPOVER_MAINTAINER_RECAP / LOOPOVER_RECAP_CADENCE env vars, which stay the fallback when absent.
# maintainerRecap:
# enabled: true # Bool. Default: false (env vars decide instead).
# cadence: weekly # daily | weekly. Default: weekly. Invalid values fall back to weekly.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 622d03adb1..f05711fb80 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -65,7 +65,7 @@
- Render the public PR feedback as one unified review comment
-- Gate per-PR review capabilities behind per-repo activation (GITTENSORY_REVIEW_* flags)
+- Gate per-PR review capabilities behind per-repo activation (LOOPOVER_REVIEW_* flags)
diff --git a/CONVERGENCE_RUNBOOK.md b/CONVERGENCE_RUNBOOK.md
index 9dba2999c9..65fbb62f54 100644
--- a/CONVERGENCE_RUNBOOK.md
+++ b/CONVERGENCE_RUNBOOK.md
@@ -15,15 +15,15 @@ This runbook records the **post-port** operating model for the reviewbot → git
- `#982` — dashboard / observability
- `#1030` — decommission legacy reviewbot identity + repo, keep gittensory as the single project
-The old vendor/embed plan is obsolete. The review system now lives in **gittensory-native codepaths** guarded by `GITTENSORY_REVIEW_*` flags. There is no `REVIEWBOT_ENGINE_ENABLED` path in this repository.
+The old vendor/embed plan is obsolete. The review system now lives in **gittensory-native codepaths** guarded by `LOOPOVER_REVIEW_*` flags. There is no `REVIEWBOT_ENGINE_ENABLED` path in this repository.
## Current architecture
- **Single project:** gittensory is the only source repo for the converged review system.
- **Native port:** review features live under `src/review/**`, `src/queue/processors.ts`, and related first-party modules.
-- **Public comment path:** the unified in-place PR comment is driven by the native bridge and the `GITTENSORY_REVIEW_UNIFIED_COMMENT` flag.
+- **Public comment path:** the unified in-place PR comment is driven by the native bridge and the `LOOPOVER_REVIEW_UNIFIED_COMMENT` flag.
- **Infra model:** D1 / Queue / AI / optional Vectorize / optional R2 / optional Browser bindings are declared directly in gittensory.
-- **Config model:** rollout is controlled by `GITTENSORY_REVIEW_*` flags plus the per-repo allowlist `GITTENSORY_REVIEW_REPOS`.
+- **Config model:** rollout is controlled by `LOOPOVER_REVIEW_*` flags plus the per-repo allowlist `LOOPOVER_REVIEW_REPOS`.
- **Parity model:** parity is measured as a shadow/deploy-time comparison against authoritative legacy audit rows; local checkout validation proves structure and safety, not historical decision identity.
## What issue `#1030` means in this repo
@@ -48,15 +48,15 @@ Those are operator actions. This repo should document them clearly and avoid imp
Primary native review flags and surfaces:
-- `GITTENSORY_REVIEW_UNIFIED_COMMENT` — single public PR comment
-- `GITTENSORY_REVIEW_SAFETY` — prompt-injection defang + secret scan
-- `GITTENSORY_REVIEW_GROUNDING` — CI + full-file grounding
-- `GITTENSORY_REVIEW_RAG` — retrieval-augmented context
-- `GITTENSORY_REVIEW_REPUTATION` — internal spend gate
-- `GITTENSORY_REVIEW_OPS` — operator stats / anomaly surfaces
-- `GITTENSORY_REVIEW_SELFTUNE` — tightening-only self-tuning loop
-- `GITTENSORY_REVIEW_PARITY_AUDIT` — shadow parity recording
-- `GITTENSORY_REVIEW_REPOS` — per-repo cutover allowlist
+- `LOOPOVER_REVIEW_UNIFIED_COMMENT` — single public PR comment
+- `LOOPOVER_REVIEW_SAFETY` — prompt-injection defang + secret scan
+- `LOOPOVER_REVIEW_GROUNDING` — CI + full-file grounding
+- `LOOPOVER_REVIEW_RAG` — retrieval-augmented context
+- `LOOPOVER_REVIEW_REPUTATION` — internal spend gate
+- `LOOPOVER_REVIEW_OPS` — operator stats / anomaly surfaces
+- `LOOPOVER_REVIEW_SELFTUNE` — tightening-only self-tuning loop
+- `LOOPOVER_REVIEW_PARITY_AUDIT` — shadow parity recording
+- `LOOPOVER_REVIEW_REPOS` — per-repo cutover allowlist
These replace the old notion of a separate reviewbot engine toggle.
diff --git a/README.md b/README.md
index 044a68072f..3140347abe 100644
--- a/README.md
+++ b/README.md
@@ -31,20 +31,20 @@ See [Privacy and security](https://gittensory.aethereal.dev/docs/privacy-securit
## Review Capabilities
-LoopOver CI and LoopOver review score, gate, and comment on pull requests. The review algorithm is open-source; operators tune behavior through per-repo settings and the `GITTENSORY_REVIEW_*` feature flags, every one of which ships **OFF** and is opt-in per repo.
+LoopOver CI and LoopOver review score, gate, and comment on pull requests. The review algorithm is open-source; operators tune behavior through per-repo settings and the `LOOPOVER_REVIEW_*` feature flags, every one of which ships **OFF** and is opt-in per repo.
- **Safety scan** — defangs untrusted PR title/body/diff (prompt-injection neutralization) before the AI reviewer reads them, and scans the diff for leaked secrets, surfacing a `secret_leak` blocker.
- **CI + full-file grounding** — grounds the AI reviewer with the PR's finished CI status and the full post-change content of the changed files, so claims are verified against reality instead of predicted.
- **Codebase RAG** — retrieval-augmented context that queries the codebase vector index for related callers, modules, and conventions and appends them to the reviewer prompt (additive only; inert until an index exists).
- **Submitter-reputation gating** — an internal-only spend control that downgrades new / burst / low-reputation submitters to a deterministic-only review, never surfaced on any public comment, label, or check.
- **Unified review comment** — renders the public PR feedback as one in-place comment instead of multiple panels. With `.loopover.yml`'s `review.changed_files_summary` also on (off by default), it gains a deterministic, no-AI "Changed files" collapsible: one row per file category (source/test/docs/config/generated), with file counts and +/- totals.
-- **Per-repo activation** — capabilities roll forward (and back) one flag and one repo at a time via the `GITTENSORY_REVIEW_REPOS` allowlist.
+- **Per-repo activation** — capabilities roll forward (and back) one flag and one repo at a time via the `LOOPOVER_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):
- **`LoopOver Orb Review Agent`** (`gate.*` / `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.
- **`LoopOver 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` + `.loopover.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. With `.loopover.yml`'s `review.suggestions` also on, a precise line-anchored fix is additionally rendered as a one-click, committable GitHub suggested-change block. With `review.finding_categories` also on (off by default), each finding is additionally tagged with a category — security/correctness/performance/maintainability/tests/style — in both the inline comment label and the unified comment's "Finding categories" collapsible; a deterministic path/keyword fallback covers whatever the model omits.
+- **Inline review comments** (`LOOPOVER_REVIEW_INLINE_COMMENTS` + `.loopover.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. With `.loopover.yml`'s `review.suggestions` also on, a precise line-anchored fix is additionally rendered as a one-click, committable GitHub suggested-change block. With `review.finding_categories` also on (off by default), each finding is additionally tagged with a category — security/correctness/performance/maintainability/tests/style — in both the inline comment label and the unified comment's "Finding categories" collapsible; a deterministic path/keyword fallback covers whatever the model omits.
See [Tuning your reviews](https://gittensory.aethereal.dev/docs/tuning) for the full flag, setting, and `.loopover.yml` reference.
diff --git a/apps/loopover-extension/auth.js b/apps/loopover-extension/auth.js
index b4913736ac..2e72b3dfa1 100644
--- a/apps/loopover-extension/auth.js
+++ b/apps/loopover-extension/auth.js
@@ -20,7 +20,7 @@ const LOCAL_SESSION_KEYS = [
];
const GITHUB_TOKEN_PREFIX_PATTERN = /^(ghp|gho|ghu|ghs|ghr|github_pat)_/i;
-const GITTENSORY_SESSION_PATTERN = /^gts_[a-f0-9]{64}$/i;
+const LOOPOVER_SESSION_PATTERN = /^gts_[a-f0-9]{64}$/i;
export function extensionStorage(chromeLike = globalThis.chrome) {
if (!chromeLike?.storage?.local || !chromeLike?.storage?.sync) {
@@ -52,7 +52,7 @@ export function validateExtensionSessionToken(value) {
if (looksLikeGitHubPersonalAccessToken(token)) {
throw new Error("GitHub personal access tokens are not accepted. Create a LoopOver extension token instead.");
}
- if (!GITTENSORY_SESSION_PATTERN.test(token)) {
+ if (!LOOPOVER_SESSION_PATTERN.test(token)) {
throw new Error("Extension tokens must be LoopOver session tokens that start with gts_.");
}
return token;
diff --git a/apps/loopover-extension/content.js b/apps/loopover-extension/content.js
index c0a35ff110..2bf81d96bb 100644
--- a/apps/loopover-extension/content.js
+++ b/apps/loopover-extension/content.js
@@ -181,7 +181,7 @@ function renderActions(body, actions) {
body.appendChild(container);
}
-if (globalThis.__GITTENSORY_EXTENSION_TEST__) {
+if (globalThis.__LOOPOVER_EXTENSION_TEST__) {
globalThis.__gittensoryContentInternals = {
matchGitHubPageTarget,
matchPullRequestTarget,
diff --git a/apps/loopover-ui/public/openapi.json b/apps/loopover-ui/public/openapi.json
index 6ca2defd8c..8d1ffc2e0a 100644
--- a/apps/loopover-ui/public/openapi.json
+++ b/apps/loopover-ui/public/openapi.json
@@ -13955,7 +13955,7 @@
}
},
"404": {
- "description": "Public stats are disabled (GITTENSORY_PUBLIC_STATS off)"
+ "description": "Public stats are disabled (LOOPOVER_PUBLIC_STATS off)"
},
"503": {
"description": "Public stats are temporarily unavailable"
diff --git a/apps/loopover-ui/src/components/site/proof-of-power-stats.tsx b/apps/loopover-ui/src/components/site/proof-of-power-stats.tsx
index 8d4be3684c..728c08c7c0 100644
--- a/apps/loopover-ui/src/components/site/proof-of-power-stats.tsx
+++ b/apps/loopover-ui/src/components/site/proof-of-power-stats.tsx
@@ -14,7 +14,7 @@ import {
} from "@/components/site/proof-of-power-stats-model";
// Proof of Power (#1059): the above-the-fold homepage stats band. Polls the public, unauthenticated
-// /v1/public/stats endpoint every 60s. The endpoint 404s until GITTENSORY_PUBLIC_STATS is enabled, so until then
+// /v1/public/stats endpoint every 60s. The endpoint 404s until LOOPOVER_PUBLIC_STATS is enabled, so until then
// (or on any failure) this renders NOTHING — the homepage is byte-identical to today. Counts only; no PR content.
const intFmt = new Intl.NumberFormat("en");
diff --git a/apps/loopover-ui/src/lib/api/origin.ts b/apps/loopover-ui/src/lib/api/origin.ts
index 876f1f8a15..127b0867a5 100644
--- a/apps/loopover-ui/src/lib/api/origin.ts
+++ b/apps/loopover-ui/src/lib/api/origin.ts
@@ -1,7 +1,7 @@
const DEFAULT_API_ORIGIN = "https://api.loopover.ai";
export function getApiOrigin(): string {
- const configured = import.meta.env.VITE_GITTENSORY_API_ORIGIN?.trim();
+ const configured = import.meta.env.VITE_LOOPOVER_API_ORIGIN?.trim();
const origin = configured || DEFAULT_API_ORIGIN;
return origin.replace(/\/+$/, "");
}
diff --git a/apps/loopover-ui/src/lib/openapi.ts b/apps/loopover-ui/src/lib/openapi.ts
index 7834dbf66b..50a781d533 100644
--- a/apps/loopover-ui/src/lib/openapi.ts
+++ b/apps/loopover-ui/src/lib/openapi.ts
@@ -185,7 +185,7 @@ export function generateCurl(op: OpenApiOperation, server: string, token?: strin
const url = server.replace(/\/$/, "") + op.path;
const lines = [`curl -X ${op.method.toUpperCase()} '${url}' \\`];
if (op.requiresAuth) {
- lines.push(` -H 'Authorization: Bearer ${token ? token : "$GITTENSORY_TOKEN"}' \\`);
+ lines.push(` -H 'Authorization: Bearer ${token ? token : "$LOOPOVER_TOKEN"}' \\`);
}
lines.push(` -H 'Accept: application/json'`);
if (op.method !== "get" && op.method !== "delete") {
diff --git a/apps/loopover-ui/src/routes/docs.miner-quickstart.tsx b/apps/loopover-ui/src/routes/docs.miner-quickstart.tsx
index 183f0ea76d..bbfbf63e97 100644
--- a/apps/loopover-ui/src/routes/docs.miner-quickstart.tsx
+++ b/apps/loopover-ui/src/routes/docs.miner-quickstart.tsx
@@ -69,7 +69,7 @@ gittensory-mcp doctor --json`}
/>
Session tokens are LoopOver tokens backed by GitHub identity, not your
- GitHub PATs. Source upload stays disabled (GITTENSORY_UPLOAD_SOURCE=false) and
+ GitHub PATs. Source upload stays disabled (LOOPOVER_UPLOAD_SOURCE=false) and
local absolute paths are redacted from anything that leaves your machine. Log out anytime
with gittensory-mcp logout.
diff --git a/apps/loopover-ui/src/routes/docs.self-hosting-backup-scaling.tsx b/apps/loopover-ui/src/routes/docs.self-hosting-backup-scaling.tsx
index 53ddeab002..c8061aad3e 100644
--- a/apps/loopover-ui/src/routes/docs.self-hosting-backup-scaling.tsx
+++ b/apps/loopover-ui/src/routes/docs.self-hosting-backup-scaling.tsx
@@ -172,12 +172,12 @@ docker compose --profile backup run --rm backup sh /scripts/verify-backup.sh /ba
lang="bash"
code={`docker compose --profile backup run --rm \\
-e VERIFY_RESTORE_SCRATCH=1 \\
- -e GITTENSORY_VERIFY_SCRATCH_DATABASE_URL=postgres://user:pass@host:5432/loopover_verify \\
+ -e LOOPOVER_VERIFY_SCRATCH_DATABASE_URL=postgres://user:pass@host:5432/loopover_verify \\
backup sh /scripts/verify-backup.sh`}
/>
The scratch restore runs pg_restore --clean against{" "}
- GITTENSORY_VERIFY_SCRATCH_DATABASE_URL, so point it at a dedicated database you
+ LOOPOVER_VERIFY_SCRATCH_DATABASE_URL, so point it at a dedicated database you
can afford to drop. The script refuses to run when that URL equals the live backup source.
diff --git a/apps/loopover-ui/src/routes/docs.self-hosting-operations.tsx b/apps/loopover-ui/src/routes/docs.self-hosting-operations.tsx
index 1e9e5aa89f..b5c9b9ac80 100644
--- a/apps/loopover-ui/src/routes/docs.self-hosting-operations.tsx
+++ b/apps/loopover-ui/src/routes/docs.self-hosting-operations.tsx
@@ -933,7 +933,7 @@ SENTRY_ORG_SLUG=
-
.env and any *_FILE secret mounts — deploy scripts only write
- back GITTENSORY_IMAGE (image path) or SENTRY_RELEASE /{" "}
+ back LOOPOVER_IMAGE (image path) or SENTRY_RELEASE /{" "}
LOOPOVER_VERSION (source path).
-
@@ -1004,18 +1004,18 @@ SENTRY_ORG_SLUG=
scripts/deploy-selfhost-image.sh pulls a tag or digest, restarts only the{" "}
loopover service, waits for it to report healthy via{" "}
docker inspect's health status (configurable timeout, default 180s), and
- then persists the resolved image reference back to GITTENSORY_IMAGE in{" "}
+ then persists the resolved image reference back to LOOPOVER_IMAGE in{" "}
.env so the next plain invocation reuses it.
ghcr.io/jsonbored/gittensory-selfhost (the pre-rename name) is no longer
@@ -1121,7 +1121,7 @@ git merge --ff-only origin/main
# equivalent manual checks:
curl -sf http://localhost:8787/ready
docker compose ps loopover
-grep -E '^(GITTENSORY_IMAGE|LOOPOVER_VERSION|SENTRY_RELEASE)=' .env
+grep -E '^(LOOPOVER_IMAGE|LOOPOVER_VERSION|SENTRY_RELEASE)=' .env
docker inspect --format '{{.Config.Image}}' "$(docker compose ps -q loopover)"`}
/>
diff --git a/apps/loopover-ui/src/routes/docs.self-hosting-quickstart.tsx b/apps/loopover-ui/src/routes/docs.self-hosting-quickstart.tsx
index b86d3a5b6b..af9337fcbc 100644
--- a/apps/loopover-ui/src/routes/docs.self-hosting-quickstart.tsx
+++ b/apps/loopover-ui/src/routes/docs.self-hosting-quickstart.tsx
@@ -117,7 +117,7 @@ curl http://localhost:8787/ready`}
ghcr.io/jsonbored/gittensory-selfhost (the pre-rename name) is no longer
diff --git a/apps/loopover-ui/src/routes/docs.self-hosting-releases.tsx b/apps/loopover-ui/src/routes/docs.self-hosting-releases.tsx
index a56dc3ecfd..b9c85dfcc8 100644
--- a/apps/loopover-ui/src/routes/docs.self-hosting-releases.tsx
+++ b/apps/loopover-ui/src/routes/docs.self-hosting-releases.tsx
@@ -110,7 +110,7 @@ curl http://localhost:8787/ready
/>
Both scripts pin a version: the image script accepts a tag/digest argument or{" "}
- GITTENSORY_IMAGE; the prebuilt script derives SENTRY_RELEASE/
+ LOOPOVER_IMAGE; the prebuilt script derives SENTRY_RELEASE/
LOOPOVER_VERSION from the checked-out commit (
git rev-parse --short=8 HEAD) unless you set SENTRY_RELEASE{" "}
yourself. A plain{" "}
diff --git a/apps/loopover-ui/wrangler.jsonc b/apps/loopover-ui/wrangler.jsonc
index f0d6589435..b36be59a3a 100644
--- a/apps/loopover-ui/wrangler.jsonc
+++ b/apps/loopover-ui/wrangler.jsonc
@@ -20,7 +20,7 @@
},
},
"vars": {
- "VITE_GITTENSORY_API_ORIGIN": "https://api.loopover.ai",
+ "VITE_LOOPOVER_API_ORIGIN": "https://api.loopover.ai",
},
"routes": [
{
diff --git a/config/examples/README.md b/config/examples/README.md
index 11e77ac772..c6faf96043 100644
--- a/config/examples/README.md
+++ b/config/examples/README.md
@@ -1,7 +1,7 @@
# Self-host private config — layout, precedence, and examples
This directory ships **generic, safe** examples for the self-host **private** config directory
-(`GITTENSORY_REPO_CONFIG_DIR`, default `/config` in the Docker image / `docker-compose.yml`). It
+(`LOOPOVER_REPO_CONFIG_DIR`, default `/config` in the Docker image / `docker-compose.yml`). It
contains no real policy, thresholds, logins, or repo names — copy what you need into your own
mounted config directory and edit it there (never in this repo).
@@ -21,11 +21,11 @@ The canonical manifest filename is **`.loopover.yml`**.
For a repo `owner/repo`, the reader tries, in priority order:
```
-${GITTENSORY_REPO_CONFIG_DIR}/owner__repo/.loopover.yml # 1. owner-qualified folder (recommended)
-${GITTENSORY_REPO_CONFIG_DIR}/repo/.loopover.yml # 2. bare repo-name folder
-${GITTENSORY_REPO_CONFIG_DIR}/owner__repo.yml # 3. flat file (back-compat, brand-agnostic)
-${GITTENSORY_REPO_CONFIG_DIR}/.loopover.yml # 4. global default, shared by every repo
-${GITTENSORY_REPO_CONFIG_DIR}/_shared/.loopover.yml # 5. shared base (#1959), lowest priority
+${LOOPOVER_REPO_CONFIG_DIR}/owner__repo/.loopover.yml # 1. owner-qualified folder (recommended)
+${LOOPOVER_REPO_CONFIG_DIR}/repo/.loopover.yml # 2. bare repo-name folder
+${LOOPOVER_REPO_CONFIG_DIR}/owner__repo.yml # 3. flat file (back-compat, brand-agnostic)
+${LOOPOVER_REPO_CONFIG_DIR}/.loopover.yml # 4. global default, shared by every repo
+${LOOPOVER_REPO_CONFIG_DIR}/_shared/.loopover.yml # 5. shared base (#1959), lowest priority
```
`.yaml` and `.json` are accepted everywhere `.yml` is. Every one of these files uses the **exact
@@ -41,10 +41,10 @@ From highest to lowest priority:
1. **Private per-repo file**, deep-merged over **2** and **3** when more than one exists (see
below) — or used alone when it is the only private layer present.
-2. **Private global default** (`${GITTENSORY_REPO_CONFIG_DIR}/.loopover.yml`) — deep-merged
+2. **Private global default** (`${LOOPOVER_REPO_CONFIG_DIR}/.loopover.yml`) — deep-merged
under **1** when both exist; used alone when a repo has no per-repo file of its own and no
shared base is mounted.
-3. **Private shared base** (`${GITTENSORY_REPO_CONFIG_DIR}/_shared/.loopover.yml`, #1959) — the
+3. **Private shared base** (`${LOOPOVER_REPO_CONFIG_DIR}/_shared/.loopover.yml`, #1959) — the
lowest-priority private layer, deep-merged under both **1** and **2**. An operator running many
repos writes a house review policy (e.g. a default `review.tone`, `path_filters`, or
`exclude_paths`) here **once** instead of copy-pasting it into every repo's per-repo file or
@@ -62,7 +62,7 @@ only the interaction *among* the three private layers is new (the per-repo/globa
shipped first; the shared base is the newest, lowest layer, #1959).
This chain governs *per-repo review policy* only. A separate, lower-level set of **deployment
-environment variables** (`GITTENSORY_REVIEW_*` flags, AI provider keys/models, self-host runtime
+environment variables** (`LOOPOVER_REVIEW_*` flags, AI provider keys/models, self-host runtime
knobs, etc.) configures the deployment itself and sits **underneath** all 5 layers above — a
`.loopover.yml`/private-config value never overrides an operator's env-level kill-switch, it only
narrows what's already permitted. See the generated, always-current
@@ -150,7 +150,7 @@ common `exclude_paths` — **once**, instead of copy-pasting it into every repo'
even the global default. That policy lives at:
```
-${GITTENSORY_REPO_CONFIG_DIR}/_shared/.loopover.yml
+${LOOPOVER_REPO_CONFIG_DIR}/_shared/.loopover.yml
```
(`.yaml`/`.json` also accepted, same lookup order as every other candidate — see
@@ -296,4 +296,4 @@ Never commit real policy into this directory or into these example files: no mai
no repo names, no thresholds beyond illustrative placeholders, no secrets or tokens. The
`.gittensory.yml`-named template files shipped alongside this README (see the catalog in
[TEMPLATES.md](./TEMPLATES.md)) are deliberately generic and inert — copy one into your own mounted
-`GITTENSORY_REPO_CONFIG_DIR`, name the copy `.loopover.yml`, and edit the copy, not this one.
+`LOOPOVER_REPO_CONFIG_DIR`, name the copy `.loopover.yml`, and edit the copy, not this one.
diff --git a/config/examples/TEMPLATES.md b/config/examples/TEMPLATES.md
index ecf2d31019..224a5f6faa 100644
--- a/config/examples/TEMPLATES.md
+++ b/config/examples/TEMPLATES.md
@@ -2,7 +2,7 @@
Copy-paste templates for the per-repo review manifest. Every file in this directory uses the
**same schema** whether it lives in a public repo root or a self-host private mount
-(`GITTENSORY_REPO_CONFIG_DIR`).
+(`LOOPOVER_REPO_CONFIG_DIR`).
> **Filename note:** the canonical manifest filename is **`.loopover.yml`**. The *template*
> filenames catalogued below (e.g. `loopover.minimal.yml`) are a separate, unrelated naming
@@ -29,9 +29,9 @@ in sync with those files.
| Layer | Path | Who can read it | Typical contents |
|-------|------|-----------------|------------------|
| **Public** | `.loopover.yml` or `.github/loopover.yml` in git | Contributors | `wantedPaths`, test expectations, public review presentation |
-| **Private global** | `${GITTENSORY_REPO_CONFIG_DIR}/.loopover.yml` | Operator only | Shared autonomy baseline, contributor caps, maintainer allowlists |
-| **Private per-repo** | `${GITTENSORY_REPO_CONFIG_DIR}/owner__repo/.loopover.yml` | Operator only | Repo-specific CI context names, AI mode, overrides |
-| **Private shared base** | `${GITTENSORY_REPO_CONFIG_DIR}/_shared/.loopover.yml` | Operator only | Lowest-priority cross-repo house policy for an operator running many repos (#1959) — see [README's "Shared base layer" section](./README.md#shared-base-layer-multi-repo-operators-1959) |
+| **Private global** | `${LOOPOVER_REPO_CONFIG_DIR}/.loopover.yml` | Operator only | Shared autonomy baseline, contributor caps, maintainer allowlists |
+| **Private per-repo** | `${LOOPOVER_REPO_CONFIG_DIR}/owner__repo/.loopover.yml` | Operator only | Repo-specific CI context names, AI mode, overrides |
+| **Private shared base** | `${LOOPOVER_REPO_CONFIG_DIR}/_shared/.loopover.yml` | Operator only | Lowest-priority cross-repo house policy for an operator running many repos (#1959) — see [README's "Shared base layer" section](./README.md#shared-base-layer-multi-repo-operators-1959) |
When **either** a private global or private per-repo file exists, the loader **never fetches** the
public repo file for that review — mount private policy deliberately. See [README.md](./README.md)
@@ -61,7 +61,7 @@ mkdir -p loopover-config/myorg__myrepo
cp config/examples/repo-override.loopover.yml loopover-config/myorg__myrepo/.loopover.yml
```
-Point `GITTENSORY_REPO_CONFIG_DIR` at that directory (default `/config` in `docker-compose.yml` maps
+Point `LOOPOVER_REPO_CONFIG_DIR` at that directory (default `/config` in `docker-compose.yml` maps
`./loopover-config`).
## Fleet examples (without committing private policy)
diff --git a/config/examples/global.loopover.yml b/config/examples/global.loopover.yml
index 58213e6a9b..42c7c2fe9b 100644
--- a/config/examples/global.loopover.yml
+++ b/config/examples/global.loopover.yml
@@ -2,7 +2,7 @@
# Self-host PRIVATE global default — GENERIC EXAMPLE, safe to publish
# ============================================================================
#
-# Copy this file to the ROOT of your own GITTENSORY_REPO_CONFIG_DIR mount (e.g.
+# Copy this file to the ROOT of your own LOOPOVER_REPO_CONFIG_DIR mount (e.g.
# `./loopover-config/.loopover.yml` for the default docker-compose.yml mount) and edit your
# copy — never this one. It applies to every repo that has no per-repo file of its own, and is
# deep-merged UNDER any per-repo file that does exist (see ../README.md for the precedence chain
@@ -74,7 +74,7 @@ settings:
- your-maintainer-login
# Converged-feature activation shared by every repo unless a per-repo file overrides a key (#4190). Each key
-# still needs its own deployment-wide GITTENSORY_REVIEW_* env flag ON before this block has any effect at all
+# still needs its own deployment-wide LOOPOVER_REVIEW_* env flag ON before this block has any effect at all
# — this section only controls the PER-REPO default once the operator has already turned a feature on
# fleet-wide. `e2eTests` gates the opt-in AI-generated E2E test coverage feature (epic #4189); leaving it
# false/omitted here keeps every repo dark by default until a per-repo file opts in.
diff --git a/config/examples/loopover.full.yml b/config/examples/loopover.full.yml
index 1e301c4c7b..02eed60e56 100644
--- a/config/examples/loopover.full.yml
+++ b/config/examples/loopover.full.yml
@@ -5,7 +5,7 @@
# WHERE TO COPY (pick one):
# PUBLIC REPO — repo root as `.loopover.yml` (or `.github/loopover.yml`). Use for work-area
# guidance (`wantedPaths`, test expectations) that contributors may read.
-# PRIVATE SELF-HOST — `${GITTENSORY_REPO_CONFIG_DIR}/.loopover.yml` or a per-repo file under
+# PRIVATE SELF-HOST — `${LOOPOVER_REPO_CONFIG_DIR}/.loopover.yml` or a per-repo file under
# `owner__repo/`. Use for anti-abuse thresholds, maintainer allowlists, autonomy, and anything
# contributors must not see or game. See `TEMPLATES.md` for public-vs-private split.
#
@@ -53,7 +53,7 @@
#
# NOTE: some capabilities (safety scanning, CI/full-file grounding, RAG,
# reputation control, the unified comment) are switched on at the deployment
-# level by the operator's GITTENSORY_REVIEW_* feature flags AND a per-repo
+# level by the operator's LOOPOVER_REVIEW_* feature flags AND a per-repo
# cutover allowlist. This file tunes behavior; it does not enable those
# deployment-wide capabilities.
# ============================================================================
@@ -418,8 +418,8 @@ gate:
# deterministic gate policy above. Omit the block to keep the byte-identical
# defaults.
#
-# SELF-HOST ONLY (`review.shared_config`, #2046): when `GITTENSORY_REPO_CONFIG_DIR` is mounted,
-# place a shared review base at `${GITTENSORY_REPO_CONFIG_DIR}/_shared/.loopover.yml` (see
+# SELF-HOST ONLY (`review.shared_config`, #2046): when `LOOPOVER_REPO_CONFIG_DIR` is mounted,
+# place a shared review base at `${LOOPOVER_REPO_CONFIG_DIR}/_shared/.loopover.yml` (see
# `config/examples/shared.loopover.yml`). Per-repo `review:` keys overlay it field-by-field —
# repo value wins when set, shared fills gaps, defaults stay byte-identical. Absent shared base is
# the common case and changes nothing. A malformed shared base warns and is ignored (never blocks a
@@ -486,7 +486,7 @@ review:
# When true, the unified review comment gains a compact "review effort: N/5 (~M min)" chip.
# Bool | null. Default: null/false — byte-identical (#2184, part of #1971). Also requires the operator's
- # GITTENSORY_REVIEW_IMPACT_MAP env flag to be on — this manifest field alone cannot enable it. When both are
+ # LOOPOVER_REVIEW_IMPACT_MAP env flag to be on — this manifest field alone cannot enable it. When both are
# on, a deterministic impact map (which other repo files plausibly need re-checking, from the RAG index +
# changed symbols) is computed, rendered as a compact section in the unified review comment, and fed to the
# AI reviewer as additive reference context.
@@ -494,12 +494,12 @@ review:
# When true, the AI reviewer's prompt gains an additive "repo quality-culture profile" reference block --
# typical merged-PR size + common accepted labels, derived from this repo's own merge history. Reference-only
- # grounding; never a gate/scoring input. Requires operator flag GITTENSORY_REVIEW_CULTURE_PROFILE. Bool or
+ # grounding; never a gate/scoring input. Requires operator flag LOOPOVER_REVIEW_CULTURE_PROFILE. Bool or
# null. Default: null/false — byte-identical. (#2995)
# culture_profile: false
# Per-repo FORCE-OFF for the self-improvement/auto-tune cron pass (#4104) -- `false` excludes this repo from
- # tuning even though it's otherwise agent-configured and the operator's GITTENSORY_REVIEW_SELFTUNE flag is on.
+ # tuning even though it's otherwise agent-configured and the operator's LOOPOVER_REVIEW_SELFTUNE flag is on.
# Deliberately FORCE-OFF-ONLY (no `true` override): unlike most `review.*` toggles above, this does NOT live
# under a per-repo allowlist -- selftune's own scoping is whether the repo already opted into the
# acting-autonomy surface, a separate consent boundary this key must not bypass. Bool or null. Default:
@@ -507,7 +507,7 @@ review:
# selftune: false
# Repeat-false-positive suppression (#2179, part of #1964). Bool | null. Default: null/false — byte-identical
- # (no suppression-store read, no matching). Also requires the operator's GITTENSORY_REVIEW_MEMORY env flag to
+ # (no suppression-store read, no matching). Also requires the operator's LOOPOVER_REVIEW_MEMORY env flag to
# be on -- this manifest field alone cannot enable it. When both are on, an advisory (non-blocking) AI finding
# is matched against this repo's stored review_suppression signals (a maintainer's own past false-positive
# dismissals) before it is surfaced, and demoted/dropped on a match. ADVISORY-ONLY: never applied to gate
@@ -548,7 +548,7 @@ review:
# e2e_test_auto_trigger: false
# Inline-comment layer toggles (#1956 / #1958). Bool | null. Default: null/false — byte-identical.
- # Requires operator flag GITTENSORY_REVIEW_INLINE_COMMENTS + cutover allowlist + review.inline_comments: true.
+ # Requires operator flag LOOPOVER_REVIEW_INLINE_COMMENTS + cutover allowlist + review.inline_comments: true.
# inline_comments: false
# When true, the AI reviewer ALSO leaves quiet, non-blocking inline PR comments on specific changed lines.
# suggestions: false
@@ -557,7 +557,7 @@ review:
# inline_comments_per_category: 3
# Fix-handoff blocks (#2176). Bool | null. Default: null/false — byte-identical.
- # Requires operator flag GITTENSORY_REVIEW_FIX_HANDOFF + cutover allowlist AND this toggle.
+ # Requires operator flag LOOPOVER_REVIEW_FIX_HANDOFF + cutover allowlist AND this toggle.
# fixHandoff: false
# Read-only auto-merge readiness summary (#2051). Bool | null. Default: null/false — byte-identical.
@@ -981,7 +981,7 @@ settings:
# instructions: "Flag any migration missing a matching down-path note."
# # When true, the AI reviewer ALSO leaves quiet, non-blocking inline PR comments on specific changed
# # lines, in addition to the decision summary. Bool or null. Default: null/false (no inline comments).
-# # Operator-gated too (GITTENSORY_REVIEW_INLINE_COMMENTS + allowlist).
+# # Operator-gated too (LOOPOVER_REVIEW_INLINE_COMMENTS + allowlist).
# inline_comments: false
# # When true, an inline finding whose fix is precise enough to anchor to one line is ALSO rendered as
# # a one-click GitHub suggestion block. Only takes effect when inline_comments is already on. Bool or
@@ -995,7 +995,7 @@ settings:
# # compact "review effort: N/5 (~M min)" chip -- a deterministic, no-AI complexity/time estimate from the
# # changed files' added-line volume and file-type mix. Bool or null. Default: null/false.
# effort_score: false
-# # When true (AND the operator's GITTENSORY_REVIEW_IMPACT_MAP env flag is also on), a deterministic
+# # When true (AND the operator's LOOPOVER_REVIEW_IMPACT_MAP env flag is also on), a deterministic
# # impact map -- which other repo files plausibly need re-checking, from the RAG index + changed
# # symbols -- is computed, rendered as a compact unified-comment section, and fed to the AI reviewer
# # as additive reference context. Bool or null. Default: null/false (#2184, part of #1971).
@@ -1003,14 +1003,14 @@ settings:
# # When true, the AI reviewer's prompt gains an additive "repo quality-culture profile" reference block --
# # typical merged-PR size + common accepted labels, derived from this repo's OWN recent merge history
# # (recent_merged_pull_requests). Reference-only grounding, never a gate/scoring input; requires the operator
-# # flag GITTENSORY_REVIEW_CULTURE_PROFILE. Bool or null. Default: null/false. (#2995)
+# # flag LOOPOVER_REVIEW_CULTURE_PROFILE. Bool or null. Default: null/false. (#2995)
# culture_profile: false
# # Per-repo FORCE-OFF for the self-improvement/auto-tune cron pass (#4104) -- false excludes this repo from
# # tuning even though it's otherwise agent-configured and the operator flag is on. FORCE-OFF-ONLY, no true
# # override -- selftune's own scoping is the acting-autonomy consent boundary, not a per-repo allowlist. Bool
# # or null. Default: null/true -- no change to today's agent-configured-repos-only behavior.
# selftune: false
-# # When true (AND the operator's GITTENSORY_REVIEW_MEMORY env flag is also on), an advisory (non-blocking)
+# # When true (AND the operator's LOOPOVER_REVIEW_MEMORY env flag is also on), an advisory (non-blocking)
# # AI finding is matched against this repo's stored review_suppression signals (a maintainer's own past
# # false-positive dismissals) before it is surfaced, and demoted/dropped on a match. ADVISORY-ONLY: never
# # applied to gate blockers -- it can never change the merge/close disposition. Bool or null.
@@ -1051,7 +1051,7 @@ settings:
# openai_compatible_model: null # Overrides OPENAI_COMPATIBLE_AI_MODEL for this repo. String or null. (#3902)
# anthropic_model: null # Overrides ANTHROPIC_AI_MODEL for this repo's BYOK Messages API reviewer. String or null. (#3902)
# # Per-repo before/after screenshot-capture config (#3609 preview / #3610 routes). Only takes effect when
-# # the operator has ALSO enabled GITTENSORY_REVIEW_SCREENSHOTS + this repo's cutover allowlist -- this
+# # the operator has ALSO enabled LOOPOVER_REVIEW_SCREENSHOTS + this repo's cutover allowlist -- this
# # config narrows/redirects that feature, it never turns it on by itself. All-null/empty/default ⇒
# # byte-identical to today (GitHub-native preview discovery, automatic file-to-route inference).
# visual:
@@ -1098,7 +1098,7 @@ settings:
# # renders per side, ~4s wall-clock per side measured in practice. Bool. Default: false (no scroll capture).
# gif: false
# # Config-as-code enable/disable for this repo, layered ON TOP OF (never a replacement for) the
-# # GITTENSORY_REVIEW_SCREENSHOTS + per-repo cutover-allowlist env-var gate above (#4083). Bool or null.
+# # LOOPOVER_REVIEW_SCREENSHOTS + per-repo cutover-allowlist env-var gate above (#4083). Bool or null.
# # Default: null (unset) ⇒ defers entirely to that env-var gate's own decision -- byte-identical to today.
# # Explicit `false` (set once at the global-default `.loopover.yml`, or overridden per-repo here) forces
# # capture off for this repo even when the env-var gate would otherwise allow it. Explicit `true` opts this
@@ -1142,9 +1142,9 @@ settings:
# auto_pause_after_reviewed_commits: 3
# Per-repo activation overrides for the converged review features that ship behind a deployment-wide
-# GITTENSORY_REVIEW_* env kill-switch (rag/reputation/unifiedComment/safety/grounding/e2eTests/screenshots/
+# LOOPOVER_REVIEW_* env kill-switch (rag/reputation/unifiedComment/safety/grounding/e2eTests/screenshots/
# improvementSignal). 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 --
+# enabled), `false` (force off), or omitted (falls back to the LOOPOVER_REVIEW_REPOS allowlist default --
# an operator who sets nothing keeps today's behavior). Exception: `safety` is force-on-only -- an untrusted
# repo-controlled `false` is treated as "no opinion" rather than an active force-off (#2269), since a
# lower-trust actor must never be able to silently defeat the operator's own security-hardening enablement.
@@ -1172,8 +1172,8 @@ settings:
# Optional ecosystem/network PLUGINS -- distinct from `features:` above, which only toggles gittensory's own
# converged review capabilities. Each key here couples this instance to an external system and is OFF unless
-# BOTH a deployment-wide GITTENSORY_EXPERIMENTAL_* env kill-switch AND an explicit per-repo `true` are set (no
-# GITTENSORY_REVIEW_REPOS allowlist fallback -- unlike `features:`, there is no default-on path). `gittensor`
+# BOTH a deployment-wide LOOPOVER_EXPERIMENTAL_* env kill-switch AND an explicit per-repo `true` are set (no
+# LOOPOVER_REVIEW_REPOS allowlist fallback -- unlike `features:`, there is no default-on path). `gittensor`
# is the first plugin: gittensory's original subnet mining-registry/scoring integration (per-repo emission
# share, maintainer cut, label multipliers pulled from the gittensor subnet's registry), now opt-in rather
# than a core dependency -- a self-host instance that never sets this has zero footprint from it: no fetch
@@ -1220,7 +1220,7 @@ settings:
# that folds gate-precision + outcome-calibration across every scanned repo into one report (distinct from
# the single-repo reviewRecap above). Operator-level, not per-repo -- only meaningful on the gittensory
# self-repo's own manifest (the repo this instance identifies as); a present block there wins over the
-# GITTENSORY_MAINTAINER_RECAP / GITTENSORY_RECAP_CADENCE env vars, which stay the fallback when absent.
+# LOOPOVER_MAINTAINER_RECAP / LOOPOVER_RECAP_CADENCE env vars, which stay the fallback when absent.
# maintainerRecap:
# enabled: true # Bool. Default: false (env vars decide instead).
# cadence: weekly # daily | weekly. Default: weekly. Invalid values fall back to weekly.
diff --git a/config/examples/loopover.minimal.yml b/config/examples/loopover.minimal.yml
index 670794bcdb..35a3aeec80 100644
--- a/config/examples/loopover.minimal.yml
+++ b/config/examples/loopover.minimal.yml
@@ -4,9 +4,9 @@
#
# WHERE TO COPY (pick one):
# PUBLIC REPO — repo root as `.loopover.yml` (or `.github/loopover.yml`). Contributors can read it.
-# PRIVATE SELF-HOST — `${GITTENSORY_REPO_CONFIG_DIR}/.loopover.yml` (global default),
-# `${GITTENSORY_REPO_CONFIG_DIR}/owner__repo/.loopover.yml` (per-repo override), or
-# `${GITTENSORY_REPO_CONFIG_DIR}/_shared/.loopover.yml` (lowest-priority cross-repo base, #1959).
+# PRIVATE SELF-HOST — `${LOOPOVER_REPO_CONFIG_DIR}/.loopover.yml` (global default),
+# `${LOOPOVER_REPO_CONFIG_DIR}/owner__repo/.loopover.yml` (per-repo override), or
+# `${LOOPOVER_REPO_CONFIG_DIR}/_shared/.loopover.yml` (lowest-priority cross-repo base, #1959).
# Never commit real policy here to a public repo — use the private mount for thresholds,
# allowlists, and autonomy.
#
diff --git a/config/examples/repo-override.loopover.yml b/config/examples/repo-override.loopover.yml
index 198fe90863..4d5727815f 100644
--- a/config/examples/repo-override.loopover.yml
+++ b/config/examples/repo-override.loopover.yml
@@ -2,7 +2,7 @@
# Self-host PRIVATE per-repo override — GENERIC EXAMPLE, safe to publish
# ============================================================================
#
-# Copy this file to `${GITTENSORY_REPO_CONFIG_DIR}/{owner}__{repo}/.loopover.yml` (or the bare
+# Copy this file to `${LOOPOVER_REPO_CONFIG_DIR}/{owner}__{repo}/.loopover.yml` (or the bare
# `{repo}/.loopover.yml` folder, or the flat `{owner}__{repo}.yml` file — see ../README.md) and
# edit your copy for a SPECIFIC repo. It is deep-merged OVER global.loopover.yml: any key it
# doesn't mention is inherited from the global default unchanged.
@@ -47,6 +47,6 @@ settings:
# Unlike wantedPaths above, `features:` is a nested MAPPING, not an array — it deep-merges KEY BY KEY over the
# global default, so overriding e2eTests here doesn't disturb any other converged-feature key the global file
# sets. This repo opts into AI-generated E2E test coverage (#4190/#4189); still requires the operator's
-# GITTENSORY_REVIEW_E2E_TESTS env flag to be on fleet-wide, or this override has no effect (master kill-switch).
+# LOOPOVER_REVIEW_E2E_TESTS env flag to be on fleet-wide, or this override has no effect (master kill-switch).
features:
e2eTests: true
diff --git a/config/examples/shared.loopover.yml b/config/examples/shared.loopover.yml
index 76af05b0f5..bbb73114bc 100644
--- a/config/examples/shared.loopover.yml
+++ b/config/examples/shared.loopover.yml
@@ -2,7 +2,7 @@
# Self-host PRIVATE shared base — GENERIC EXAMPLE, safe to publish (#1959)
# ============================================================================
#
-# Copy this file to `${GITTENSORY_REPO_CONFIG_DIR}/_shared/.loopover.yml` and edit your copy —
+# Copy this file to `${LOOPOVER_REPO_CONFIG_DIR}/_shared/.loopover.yml` and edit your copy —
# never this one. This is the LOWEST-priority layer: a house review policy an operator running
# MANY repos writes ONCE here instead of copy-pasting it into every repo's private config. It is
# deep-merged UNDER both the global default (global.loopover.yml) and any per-repo override
diff --git a/docker-compose.yml b/docker-compose.yml
index 900b43277f..5dcac30a98 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -806,9 +806,9 @@ services:
# Default SQLite app DB path maps app /data/loopover.sqlite to exporter /appdb/loopover.sqlite.
# If you override DATABASE_PATH, set this to the matching /appdb/ path. When DATABASE_URL points at
# Postgres, the exporter reads Postgres with psql and still writes the same redacted SQLite reporting DB
- # for Grafana. Set GITTENSORY_REPORTING_SOURCE_DATABASE_URL only if the reporting reader uses a different URL.
+ # for Grafana. Set LOOPOVER_REPORTING_SOURCE_DATABASE_URL only if the reporting reader uses a different URL.
LOOPOVER_REPORTING_SOURCE_DB: "${LOOPOVER_REPORTING_SOURCE_DB:-/appdb/loopover.sqlite}"
- GITTENSORY_REPORTING_SOURCE_DATABASE_URL: "${GITTENSORY_REPORTING_SOURCE_DATABASE_URL:-}"
+ LOOPOVER_REPORTING_SOURCE_DATABASE_URL: "${LOOPOVER_REPORTING_SOURCE_DATABASE_URL:-}"
DATABASE_URL: "${DATABASE_URL:-}"
LOOPOVER_REPORTING_DIR: /reporting
LOOPOVER_REPORTING_DB: "${LOOPOVER_REPORTING_DB:-/reporting/loopover-reporting.sqlite}"
@@ -857,11 +857,11 @@ services:
# (a single-file mount pins to the pre-`git pull` inode and silently never picks up a script update).
- ./scripts:/scripts:ro
environment:
- GITTENSORY_AMS_ATTEMPT_LOG_SOURCE_DB: "${GITTENSORY_AMS_ATTEMPT_LOG_SOURCE_DB:-/ams-ledgers/attempt-log.sqlite3}"
- GITTENSORY_AMS_PREDICTION_LEDGER_SOURCE_DB: "${GITTENSORY_AMS_PREDICTION_LEDGER_SOURCE_DB:-/ams-ledgers/prediction-ledger.sqlite3}"
+ LOOPOVER_AMS_ATTEMPT_LOG_SOURCE_DB: "${LOOPOVER_AMS_ATTEMPT_LOG_SOURCE_DB:-/ams-ledgers/attempt-log.sqlite3}"
+ LOOPOVER_AMS_PREDICTION_LEDGER_SOURCE_DB: "${LOOPOVER_AMS_PREDICTION_LEDGER_SOURCE_DB:-/ams-ledgers/prediction-ledger.sqlite3}"
LOOPOVER_REPORTING_DIR: /reporting
- GITTENSORY_AMS_ATTEMPT_LOG_REPORTING_DB: "${GITTENSORY_AMS_ATTEMPT_LOG_REPORTING_DB:-/reporting/ams-attempt-log.sqlite}"
- GITTENSORY_AMS_PREDICTION_LEDGER_REPORTING_DB: "${GITTENSORY_AMS_PREDICTION_LEDGER_REPORTING_DB:-/reporting/ams-prediction-ledger.sqlite}"
+ LOOPOVER_AMS_ATTEMPT_LOG_REPORTING_DB: "${LOOPOVER_AMS_ATTEMPT_LOG_REPORTING_DB:-/reporting/ams-attempt-log.sqlite}"
+ LOOPOVER_AMS_PREDICTION_LEDGER_REPORTING_DB: "${LOOPOVER_AMS_PREDICTION_LEDGER_REPORTING_DB:-/reporting/ams-prediction-ledger.sqlite}"
LOOPOVER_AMS_REPORTING_EXPORT_INTERVAL_SECONDS: "${LOOPOVER_AMS_REPORTING_EXPORT_INTERVAL_SECONDS:-30}"
command:
- /bin/sh
@@ -878,7 +878,7 @@ services:
test:
[
"CMD-SHELL",
- "db=\"$${GITTENSORY_AMS_ATTEMPT_LOG_REPORTING_DB:-/reporting/ams-attempt-log.sqlite}\"; test -s \"$$db\" && sqlite3 \"$$db\" 'PRAGMA quick_check;' | grep -q '^ok$'",
+ "db=\"$${LOOPOVER_AMS_ATTEMPT_LOG_REPORTING_DB:-/reporting/ams-attempt-log.sqlite}\"; test -s \"$$db\" && sqlite3 \"$$db\" 'PRAGMA quick_check;' | grep -q '^ok$'",
]
interval: 30s
timeout: 5s
@@ -1104,13 +1104,13 @@ services:
environment:
DATABASE_PATH: /data/loopover.sqlite
DATABASE_URL: "${DATABASE_URL:-}"
- GITTENSORY_BACKUP_SOURCE_DATABASE_URL: "${GITTENSORY_BACKUP_SOURCE_DATABASE_URL:-}"
+ LOOPOVER_BACKUP_SOURCE_DATABASE_URL: "${LOOPOVER_BACKUP_SOURCE_DATABASE_URL:-}"
QDRANT_URL: ${QDRANT_URL:-http://qdrant:6333}
BACKUP_RETAIN: ${BACKUP_RETAIN:-7}
# Opt-in restore drill (verify-backup.sh): restore the newest dump into a THROWAWAY database, never the
# live one. Both must be set, and the scratch URL must differ from the backup source.
VERIFY_RESTORE_SCRATCH: "${VERIFY_RESTORE_SCRATCH:-}"
- GITTENSORY_VERIFY_SCRATCH_DATABASE_URL: "${GITTENSORY_VERIFY_SCRATCH_DATABASE_URL:-}"
+ LOOPOVER_VERIFY_SCRATCH_DATABASE_URL: "${LOOPOVER_VERIFY_SCRATCH_DATABASE_URL:-}"
volumes:
# RW (not :ro) so SQLite's online-backup can use the WAL index; the script only ever reads the DB.
- loopover-data:/data
diff --git a/migrations/0051_repo_chunks.sql b/migrations/0051_repo_chunks.sql
index 445a375189..cc59f88f58 100644
--- a/migrations/0051_repo_chunks.sql
+++ b/migrations/0051_repo_chunks.sql
@@ -1,4 +1,4 @@
--- Convergence (RAG / codebase index — Layer C, flag GITTENSORY_REVIEW_RAG): the chunk-text STORE that backs
+-- Convergence (RAG / codebase index — Layer C, flag LOOPOVER_REVIEW_RAG): the chunk-text STORE that backs
-- vector retrieval. This is the missing storage half of RAG: src/review/rag.ts already reads/writes this table
-- (the retrieval path was wired in the rag-wire chunk) but no migration created it, so the index was unbacked.
-- This migration creates it so the index-population job (src/review/rag-index.ts → upsertChunks) has somewhere
@@ -22,7 +22,7 @@
--
-- Kept raw-SQL-only (matching the 0046–0050 convergence parity-store convention); deliberately NOT added to the
-- Drizzle schema. Additive + idempotent (IF NOT EXISTS): the table is only ever read/written when the
--- GITTENSORY_REVIEW_RAG flag is ON AND a Vectorize/AI binding is present, so a deploy without RAG is unaffected.
+-- LOOPOVER_REVIEW_RAG flag is ON AND a Vectorize/AI binding is present, so a deploy without RAG is unaffected.
--
-- Privacy: indexes a repo's CODE/docs for code-review context only (isIndexablePath skips the content/data
-- corpus). Internal review infrastructure — never surfaced publicly beyond the prompt the reviewer sees.
diff --git a/migrations/0143_repository_skip_automation_bot_authors.sql b/migrations/0143_repository_skip_automation_bot_authors.sql
index b62685b31b..36c139f3be 100644
--- a/migrations/0143_repository_skip_automation_bot_authors.sql
+++ b/migrations/0143_repository_skip_automation_bot_authors.sql
@@ -2,7 +2,7 @@
-- Dependabot -- settings/agent-actions.ts's PROTECTED_AUTOCLOSE_AUTHORS): skip AI review, gate evaluation,
-- and public-surface publish entirely for a PR genuinely triggered by one of these, not just suppress
-- output like review.auto_review.ignore_authors already does. 'inherit' (default) defers to the
--- GITTENSORY_SKIP_AUTOMATION_BOT_PRS global default (itself default-ON); 'off'/'enabled' fully override
+-- LOOPOVER_SKIP_AUTOMATION_BOT_PRS global default (itself default-ON); 'off'/'enabled' fully override
-- the global default in either direction for this repo. Mirrors moderation_gate_mode's inherit/off/enabled
-- shape (0105).
ALTER TABLE repository_settings ADD COLUMN skip_automation_bot_authors TEXT NOT NULL DEFAULT 'inherit';
diff --git a/packages/loopover-engine/CHANGELOG.md b/packages/loopover-engine/CHANGELOG.md
index caa3880706..7f58660f00 100644
--- a/packages/loopover-engine/CHANGELOG.md
+++ b/packages/loopover-engine/CHANGELOG.md
@@ -7,7 +7,7 @@
* **miner:** the miner's default config directory and every gittensory_miner_* Prometheus metric name changed; no dual-read/alias, per the epic's full-cutover mandate ([#5705](https://github.com/JSONbored/gittensory/issues/5705)). A self-hoster's existing ~/.config/gittensory-miner state does not migrate automatically.
* **github:** the bot no longer recognizes @gittensory as a command trigger anywhere -- only @loopover works. Any saved PR-comment template, bookmark, or muscle-memory referencing @gittensory must be updated to @loopover .
-* **miner:** every GITTENSORY_MINER_*/GITTENSORY_API_TOKEN_FILE/ GITTENSORY_MCP_TOKEN_FILE/GITTENSORY_MEM_LIMIT/GITTENSORY_REPORTING_* env var an existing self-hosted AMS/miner deployment sets must be renamed to its LOOPOVER_ equivalent -- the old names are no longer read.
+* **miner:** every LOOPOVER_MINER_*/LOOPOVER_API_TOKEN_FILE/ LOOPOVER_MCP_TOKEN_FILE/LOOPOVER_MEM_LIMIT/LOOPOVER_REPORTING_* env var an existing self-hosted AMS/miner deployment sets must be renamed to its LOOPOVER_ equivalent -- the old names are no longer read.
* remove gittensory-engine's settings.gateCheckMode yml back-compat parsing ([#5373](https://github.com/JSONbored/gittensory/issues/5373)) (#5463)
### Features
@@ -38,7 +38,7 @@
* **miner:** build a real SelfReviewContext fetcher ([#5145](https://github.com/JSONbored/gittensory/issues/5145)) ([#5235](https://github.com/JSONbored/gittensory/issues/5235)) ([7a423be](https://github.com/JSONbored/gittensory/commit/7a423be3648342232e9274e39ef39c691feca967))
* **miner:** extract and persist real coding-agent token usage ([#5658](https://github.com/JSONbored/gittensory/issues/5658)) ([1e0ac6c](https://github.com/JSONbored/gittensory/commit/1e0ac6c9b1d932f2f83b34efdfea24710988c606))
* **miner:** persist coding-agent provider + real cost on the attempt log ([#5637](https://github.com/JSONbored/gittensory/issues/5637)) ([941c300](https://github.com/JSONbored/gittensory/commit/941c300691982c65b534e86bdedf03a85f8712b4))
-* **miner:** rename GITTENSORY_MINER_*/GITTENSORY_* env vars to LOOPOVER_MINER_*/LOOPOVER_* ([#5707](https://github.com/JSONbored/gittensory/issues/5707)) ([6714f0c](https://github.com/JSONbored/gittensory/commit/6714f0cac5ab37477c7f56332cde969788c7996e)), closes [#5705](https://github.com/JSONbored/gittensory/issues/5705)
+* **miner:** rename LOOPOVER_MINER_*/LOOPOVER_* env vars to LOOPOVER_MINER_*/LOOPOVER_* ([#5707](https://github.com/JSONbored/gittensory/issues/5707)) ([6714f0c](https://github.com/JSONbored/gittensory/commit/6714f0cac5ab37477c7f56332cde969788c7996e)), closes [#5705](https://github.com/JSONbored/gittensory/issues/5705)
* **miner:** wire attempt-metering.ts into the iterate loop for a real mid-attempt budget abort ([#5437](https://github.com/JSONbored/gittensory/issues/5437)) ([30a6ffb](https://github.com/JSONbored/gittensory/commit/30a6ffbc7b7625d0fdc4ce01320ca7a5da4c986b))
* **miner:** wire claim-conflict resolution end-to-end ([#5480](https://github.com/JSONbored/gittensory/issues/5480)) ([7109bf2](https://github.com/JSONbored/gittensory/commit/7109bf267eba84733c441fa8e137c1a5d310983d))
* **miner:** wire the real runMinerAttempt call into attempt-cli.js ([#5261](https://github.com/JSONbored/gittensory/issues/5261)) ([f3f1f2b](https://github.com/JSONbored/gittensory/commit/f3f1f2b59565fc83ce2f7f84d3b186a533221087))
diff --git a/packages/loopover-engine/src/advisory/gate-advisory.ts b/packages/loopover-engine/src/advisory/gate-advisory.ts
index e0bcc94111..7d0f31a8c0 100644
--- a/packages/loopover-engine/src/advisory/gate-advisory.ts
+++ b/packages/loopover-engine/src/advisory/gate-advisory.ts
@@ -138,7 +138,7 @@ export function buildPullRequestAdvisory(
/** Duplicate-winner adjudication (#dup-winner). When true AND this PR is the cluster winner (the lowest
* open sibling number), the `duplicate_pr_risk` finding is suppressed so the winner is not gate-blocked /
* closed as a duplicate. Default/false ⇒ every duplicate sibling keeps the finding (byte-identical). The
- * caller sets this to `env.GITTENSORY_DUPLICATE_WINNER === "true"`. */
+ * caller sets this to `env.LOOPOVER_DUPLICATE_WINNER === "true"`. */
duplicateWinnerEnabled?: boolean;
/** Author logins of the linked issues (one entry per resolved issue, may be null when unknown). Used to
* surface a `self_authored_linked_issue` finding when the PR author also opened the linked issue. Absent
diff --git a/packages/loopover-engine/src/duplicate-winner.ts b/packages/loopover-engine/src/duplicate-winner.ts
index 0edb0ffef5..c853c7fed3 100644
--- a/packages/loopover-engine/src/duplicate-winner.ts
+++ b/packages/loopover-engine/src/duplicate-winner.ts
@@ -1,5 +1,5 @@
/**
- * Duplicate-winner adjudication (#dup-winner). Flag-gated by GITTENSORY_DUPLICATE_WINNER.
+ * Duplicate-winner adjudication (#dup-winner). Flag-gated by LOOPOVER_DUPLICATE_WINNER.
*
* When several OPEN PRs link the same issue (a duplicate cluster), the legacy behavior gate-blocks +
* auto-closes EVERY sibling as a duplicate — no winner survives. With the flag ON, exactly ONE winner is
diff --git a/packages/loopover-engine/src/focus-manifest.ts b/packages/loopover-engine/src/focus-manifest.ts
index 800fc57879..bdedc29155 100644
--- a/packages/loopover-engine/src/focus-manifest.ts
+++ b/packages/loopover-engine/src/focus-manifest.ts
@@ -342,7 +342,7 @@ export type FocusManifestReviewRecapConfig = {
/**
* Config-as-code override for the CROSS-repo maintainer recap digest's cron knobs (#1963, #2250), declared
* under `maintainerRecap:`. Distinct from `reviewRecap:` above (that is the single-repo digest's own window/
- * enable knob); this instead overrides the GITTENSORY_MAINTAINER_RECAP / GITTENSORY_RECAP_CADENCE env vars
+ * enable knob); this instead overrides the LOOPOVER_MAINTAINER_RECAP / LOOPOVER_RECAP_CADENCE env vars
* that gate the cron-scheduled cross-repo digest (buildMaintainerRecap, #2239 / #2248) — read from the
* gittensory self-repo's manifest (resolveLoopOverSelfRepoFullName), since the digest is an operator-level
* setting, not a per-contributor-repo one. Mirrors `reviewRecap:` exactly: no DB-backed counterpart, so the
diff --git a/packages/loopover-mcp/CHANGELOG.md b/packages/loopover-mcp/CHANGELOG.md
index d1ec1cf653..e7e750ec6c 100644
--- a/packages/loopover-mcp/CHANGELOG.md
+++ b/packages/loopover-mcp/CHANGELOG.md
@@ -232,7 +232,7 @@
- Mark gate.firstTimeContributorGrace as reserved/inert (#2411)
- Distinguish an executor error from a clean accept (#2428)
- Honor settings.commandAuthorization in .gittensory.yml (#2385)
-- Scope GITTENSORY_MCP_TOKEN read access to an operator allowlist (#2464)
+- Scope LOOPOVER_MCP_TOKEN read access to an operator allowlist (#2464)
- Cap labelPatternToRegExp wildcard groups to prevent ReDoS (#2482)
- Bound label pattern cache (#2513)
- Match label history against config labels case-insensitively (#2580)
diff --git a/packages/loopover-mcp/README.md b/packages/loopover-mcp/README.md
index 6675fd1ba6..e0240089f2 100644
--- a/packages/loopover-mcp/README.md
+++ b/packages/loopover-mcp/README.md
@@ -165,9 +165,9 @@ loopover-mcp whoami
loopover-mcp logout --profile work
```
-Use `--profile ` on `login`, `logout`, `whoami`, `config`, `status`, and `doctor`, or set `GITTENSORY_PROFILE`. `logout` only clears the selected local profile unless `--all` is passed. Profile output redacts session tokens and local config paths.
+Use `--profile ` on `login`, `logout`, `whoami`, `config`, `status`, and `doctor`, or set `LOOPOVER_PROFILE`. `logout` only clears the selected local profile unless `--all` is passed. Profile output redacts session tokens and local config paths.
-`loopover-mcp config` prints the resolved effective configuration and the source that supplied each value (`environment`, `profile`, `config`, or `default`): the active API URL and its source, active profile and profile count, whether a config file is present and which environment variable steers its location, the cache-dir source, whether a token is configured and where it came from, and whether `GITTENSORY_UPLOAD_SOURCE` has enabled the unsupported source-upload setting. It never prints token values or local absolute paths. Add `--json` for machine-readable output.
+`loopover-mcp config` prints the resolved effective configuration and the source that supplied each value (`environment`, `profile`, `config`, or `default`): the active API URL and its source, active profile and profile count, whether a config file is present and which environment variable steers its location, the cache-dir source, whether a token is configured and where it came from, and whether `LOOPOVER_UPLOAD_SOURCE` has enabled the unsupported source-upload setting. It never prints token values or local absolute paths. Add `--json` for machine-readable output.
By default `loopover-mcp doctor` always exits 0. Pass `--exit-code` to make it exit non-zero when a diagnostic check fails (`status: "needs_attention"`), so it can gate a CI step or pre-commit hook. Warnings still exit 0.
@@ -212,18 +212,18 @@ Use them when an agent should plan, explain, draft, or prepare packets from Loop
## Environment
-- `GITTENSORY_API_URL`
-- `GITTENSORY_PROFILE`
-- `GITTENSORY_CONFIG_PATH` or `GITTENSORY_CONFIG_DIR`
-- `GITTENSORY_API_TOKEN`, `GITTENSORY_MCP_TOKEN`, or `GITTENSORY_TOKEN`
+- `LOOPOVER_API_URL`
+- `LOOPOVER_PROFILE`
+- `LOOPOVER_CONFIG_PATH` or `LOOPOVER_CONFIG_DIR`
+- `LOOPOVER_API_TOKEN`, `LOOPOVER_MCP_TOKEN`, or `LOOPOVER_TOKEN`
- `GITHUB_TOKEN` for non-interactive login bootstrap
- `GITTENSOR_SCORE_PREVIEW_CMD`
- `GITTENSOR_ROOT`
- `GITTENSOR_SCORE_PREVIEW_TIMEOUT_MS` (default `15000`)
-- `GITTENSORY_UPLOAD_SOURCE=false`
-- `GITTENSORY_SKIP_NPM_VERSION_CHECK=true`
+- `LOOPOVER_UPLOAD_SOURCE=false`
+- `LOOPOVER_SKIP_NPM_VERSION_CHECK=true`
-`GITTENSORY_UPLOAD_SOURCE=true` is not supported and fails closed.
+`LOOPOVER_UPLOAD_SOURCE=true` is not supported and fails closed.
### Local score preview adapter
diff --git a/packages/loopover-mcp/bin/loopover-mcp.js b/packages/loopover-mcp/bin/loopover-mcp.js
index 05bec02377..bce2e533e5 100755
--- a/packages/loopover-mcp/bin/loopover-mcp.js
+++ b/packages/loopover-mcp/bin/loopover-mcp.js
@@ -19,7 +19,7 @@ const defaultApiUrl = "https://gittensory-api.aethereal.dev";
const legacyDefaultApiUrls = new Set(["https://gittensory-api.zeronode.workers.dev"]);
const packageName = ownPackageJson.name;
const packageVersion = ownPackageJson.version;
-const npmRegistryUrl = (process.env.GITTENSORY_NPM_REGISTRY_URL ?? "https://registry.npmjs.org").replace(/\/+$/, "");
+const npmRegistryUrl = (process.env.LOOPOVER_NPM_REGISTRY_URL ?? "https://registry.npmjs.org").replace(/\/+$/, "");
const upgradeCommand = `npm install -g ${packageName}@latest`;
const npxFallbackCommand = `npx ${packageName}@latest `;
const compatibilityPath = "/v1/mcp/compatibility";
@@ -147,18 +147,18 @@ const AGENT_PROFILES = {
},
};
const configPath =
- process.env.GITTENSORY_CONFIG_PATH ??
- (process.env.GITTENSORY_CONFIG_DIR
- ? join(process.env.GITTENSORY_CONFIG_DIR, "config.json")
+ process.env.LOOPOVER_CONFIG_PATH ??
+ (process.env.LOOPOVER_CONFIG_DIR
+ ? join(process.env.LOOPOVER_CONFIG_DIR, "config.json")
: join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "loopover", "config.json"));
-const cacheDir = process.env.GITTENSORY_CACHE_DIR ?? join(dirname(configPath), "cache");
+const cacheDir = process.env.LOOPOVER_CACHE_DIR ?? join(dirname(configPath), "cache");
const decisionPackCacheDir = join(cacheDir, "decision-packs");
const config = loadConfig();
-const requestedProfileName = cliOptionValue(cliArgs, "profile") ?? process.env.GITTENSORY_PROFILE;
+const requestedProfileName = cliOptionValue(cliArgs, "profile") ?? process.env.LOOPOVER_PROFILE;
const activeProfileName = selectProfileName(config, requestedProfileName);
const activeProfile = config.profiles?.[activeProfileName] ?? {};
const configuredApiUrl = typeof activeProfile.apiUrl === "string" ? activeProfile.apiUrl.replace(/\/+$/, "") : typeof config.apiUrl === "string" ? config.apiUrl.replace(/\/+$/, "") : undefined;
-const apiUrl = (process.env.GITTENSORY_API_URL ?? (configuredApiUrl && !legacyDefaultApiUrls.has(configuredApiUrl) ? configuredApiUrl : defaultApiUrl)).replace(/\/+$/, "");
+const apiUrl = (process.env.LOOPOVER_API_URL ?? (configuredApiUrl && !legacyDefaultApiUrls.has(configuredApiUrl) ? configuredApiUrl : defaultApiUrl)).replace(/\/+$/, "");
const ownerRepoShape = {
owner: z.string().min(1),
@@ -1852,8 +1852,8 @@ async function runCli(args) {
const suggestion = suggestCommand(command);
throw new Error(`Unknown command: ${command}.${suggestion ? ` Did you mean \`${suggestion}\`?` : ""} Run \`loopover-mcp --help\` to list commands.`);
}
- const contributorLogin = options.login ?? process.env.GITTENSORY_LOGIN ?? process.env.GITHUB_LOGIN;
- if (!contributorLogin) throw new Error("Pass --login or set GITTENSORY_LOGIN.");
+ const contributorLogin = options.login ?? process.env.LOOPOVER_LOGIN ?? process.env.GITHUB_LOGIN;
+ if (!contributorLogin) throw new Error("Pass --login or set LOOPOVER_LOGIN.");
const result = await analyzeCurrentBranch({
login: contributorLogin,
cwd: options.cwd,
@@ -1924,8 +1924,8 @@ function printReviewPrHelp() {
async function reviewPrCli(options) {
if (options.help === true) return printReviewPrHelp();
- const contributorLogin = options.login ?? process.env.GITTENSORY_LOGIN ?? process.env.GITHUB_LOGIN;
- if (!contributorLogin) throw new Error("Pass --login or set GITTENSORY_LOGIN.");
+ const contributorLogin = options.login ?? process.env.LOOPOVER_LOGIN ?? process.env.GITHUB_LOGIN;
+ if (!contributorLogin) throw new Error("Pass --login or set LOOPOVER_LOGIN.");
let prBody = options.body;
if (options.bodyFile) prBody = readCliTextFile(options.bodyFile, "Body");
const commitMessages = Array.isArray(options.commit) ? options.commit : options.commit ? [options.commit] : undefined;
@@ -2161,8 +2161,8 @@ async function issueSlopCli(args) {
}
async function decisionPackCli(options) {
- const login = options.login ?? process.env.GITTENSORY_LOGIN ?? process.env.GITHUB_LOGIN;
- if (!login) throw new Error("Pass --login or set GITTENSORY_LOGIN.");
+ const login = options.login ?? process.env.LOOPOVER_LOGIN ?? process.env.GITHUB_LOGIN;
+ if (!login) throw new Error("Pass --login or set LOOPOVER_LOGIN.");
const payload = await getDecisionPackWithCache(login);
if (options.json) {
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
@@ -2174,8 +2174,8 @@ async function decisionPackCli(options) {
}
async function repoDecisionCli(options) {
- const login = options.login ?? process.env.GITTENSORY_LOGIN ?? process.env.GITHUB_LOGIN;
- if (!login) throw new Error("Pass --login or set GITTENSORY_LOGIN.");
+ const login = options.login ?? process.env.LOOPOVER_LOGIN ?? process.env.GITHUB_LOGIN;
+ if (!login) throw new Error("Pass --login or set LOOPOVER_LOGIN.");
const repoFullName = options.repo;
if (!repoFullName || !repoFullName.includes("/")) throw new Error("Pass --repo owner/repo.");
const [owner, repo] = repoFullName.split("/", 2);
@@ -2221,8 +2221,8 @@ async function runAgentCli(args) {
if (subcommand === "--help" || subcommand === "help") return printAgentHelp();
const options = parseOptions(args.slice(1));
if (subcommand === "plan") {
- const login = options.login ?? process.env.GITTENSORY_LOGIN ?? process.env.GITHUB_LOGIN;
- if (!login) throw new Error("Pass --login or set GITTENSORY_LOGIN.");
+ const login = options.login ?? process.env.LOOPOVER_LOGIN ?? process.env.GITHUB_LOGIN;
+ if (!login) throw new Error("Pass --login or set LOOPOVER_LOGIN.");
const payload = await apiPost("/v1/agent/plan-next-work", stripUndefined({ login, repoFullName: options.repo, objective: options.objective, surface: "mcp" }));
return outputAgentPayload(payload, options, `LoopOver agent plan: ${payload.summary ?? payload.run?.status ?? "ready"}`);
}
@@ -2240,8 +2240,8 @@ async function runAgentCli(args) {
return outputAgentPayload({ ...payload, topAction }, options, topAction ? `Top action: ${topAction.recommendation}` : "No top action is available yet.");
}
if (subcommand === "packet") {
- const login = options.login ?? process.env.GITTENSORY_LOGIN ?? process.env.GITHUB_LOGIN;
- if (!login) throw new Error("Pass --login or set GITTENSORY_LOGIN.");
+ const login = options.login ?? process.env.LOOPOVER_LOGIN ?? process.env.GITHUB_LOGIN;
+ if (!login) throw new Error("Pass --login or set LOOPOVER_LOGIN.");
const payload = await agentPreparePrPacket({
login,
cwd: options.cwd,
@@ -2571,15 +2571,15 @@ function printHelp() {
loopover-mcp agent packet --login [--repo owner/repo] [--base origin/main] [--json]
Environment:
- GITTENSORY_API_URL
- GITTENSORY_PROFILE
- GITTENSORY_CONFIG_PATH or GITTENSORY_CONFIG_DIR
- GITTENSORY_API_TOKEN, GITTENSORY_MCP_TOKEN, GITTENSORY_TOKEN, or a session from loopover-mcp login
+ LOOPOVER_API_URL
+ LOOPOVER_PROFILE
+ LOOPOVER_CONFIG_PATH or LOOPOVER_CONFIG_DIR
+ LOOPOVER_API_TOKEN, LOOPOVER_MCP_TOKEN, LOOPOVER_TOKEN, or a session from loopover-mcp login
GITHUB_TOKEN for non-interactive login bootstrap
GITTENSOR_SCORE_PREVIEW_CMD
GITTENSOR_ROOT
GITTENSOR_SCORE_PREVIEW_TIMEOUT_MS
- GITTENSORY_UPLOAD_SOURCE=false
+ LOOPOVER_UPLOAD_SOURCE=false
`);
}
@@ -2613,7 +2613,7 @@ function printProfileHelp() {
loopover-mcp profile switch [--json]
loopover-mcp profile remove [--json]
-Use --profile or GITTENSORY_PROFILE to run login, logout, whoami, status, doctor, and MCP API calls with a named local session.
+Use --profile or LOOPOVER_PROFILE to run login, logout, whoami, status, doctor, and MCP API calls with a named local session.
`);
}
@@ -2871,7 +2871,7 @@ async function doctor(options) {
add("api_health", health.status === "ok" ? "pass" : "warn", `API responded from ${apiUrl}.`);
} catch (error) {
health = { status: "unreachable" };
- add("api_health", "fail", error instanceof Error ? error.message : "health_check_failed", "Check GITTENSORY_API_URL or network access.");
+ add("api_health", "fail", error instanceof Error ? error.message : "health_check_failed", "Check LOOPOVER_API_URL or network access.");
}
const compatibility = await inspectApiCompatibility(health);
@@ -2885,7 +2885,7 @@ async function doctor(options) {
} else if (pkg.state === "ahead") {
add("version", "pass", `Installed ${packageVersion} is ahead of npm latest ${pkg.latestVersion}.`);
} else if (pkg.state === "skipped") {
- add("version", "pass", "npm version check was skipped (GITTENSORY_SKIP_NPM_VERSION_CHECK).");
+ add("version", "pass", "npm version check was skipped (LOOPOVER_SKIP_NPM_VERSION_CHECK).");
} else {
add("version", "pass", `Installed ${packageVersion} matches npm latest ${pkg.latestVersion}.`);
}
@@ -2918,8 +2918,8 @@ async function doctor(options) {
}
}
- if (/^(1|true|yes)$/i.test(process.env.GITTENSORY_UPLOAD_SOURCE ?? "false")) {
- add("source_upload", "fail", "GITTENSORY_UPLOAD_SOURCE is enabled.", "Unset GITTENSORY_UPLOAD_SOURCE. Source upload is unsupported in v1.");
+ if (/^(1|true|yes)$/i.test(process.env.LOOPOVER_UPLOAD_SOURCE ?? "false")) {
+ add("source_upload", "fail", "LOOPOVER_UPLOAD_SOURCE is enabled.", "Unset LOOPOVER_UPLOAD_SOURCE. Source upload is unsupported in v1.");
} else {
add("source_upload", "pass", "Source upload is disabled and unsupported in v1.");
}
@@ -3064,7 +3064,7 @@ function doctorNextCommand(byName, context) {
const sourceUpload = byName.get("source_upload");
if (sourceUpload?.status === "fail") {
return {
- command: "unset GITTENSORY_UPLOAD_SOURCE",
+ command: "unset LOOPOVER_UPLOAD_SOURCE",
reason: "Disable source upload first; the local MCP wrapper only sends metadata.",
};
}
@@ -3184,7 +3184,7 @@ function getApiToken() {
}
function getEnvApiToken() {
- return process.env.GITTENSORY_API_TOKEN ?? process.env.GITTENSORY_TOKEN ?? process.env.GITTENSORY_MCP_TOKEN;
+ return process.env.LOOPOVER_API_TOKEN ?? process.env.LOOPOVER_TOKEN ?? process.env.LOOPOVER_MCP_TOKEN;
}
function selectedProfileName(options = {}) {
@@ -3229,7 +3229,7 @@ function selectProfileName(currentConfig, requestedName) {
}
function resolvedApiUrlSource() {
- if (process.env.GITTENSORY_API_URL) return "environment";
+ if (process.env.LOOPOVER_API_URL) return "environment";
const profileApiUrl = typeof activeProfile.apiUrl === "string" ? activeProfile.apiUrl.replace(/\/+$/, "") : undefined;
if (profileApiUrl && !legacyDefaultApiUrls.has(profileApiUrl)) return "profile";
const globalApiUrl = typeof config.apiUrl === "string" ? config.apiUrl.replace(/\/+$/, "") : undefined;
@@ -3238,8 +3238,8 @@ function resolvedApiUrlSource() {
}
function resolvedConfigPathSource() {
- if (process.env.GITTENSORY_CONFIG_PATH) return "GITTENSORY_CONFIG_PATH";
- if (process.env.GITTENSORY_CONFIG_DIR) return "GITTENSORY_CONFIG_DIR";
+ if (process.env.LOOPOVER_CONFIG_PATH) return "LOOPOVER_CONFIG_PATH";
+ if (process.env.LOOPOVER_CONFIG_DIR) return "LOOPOVER_CONFIG_DIR";
if (process.env.XDG_CONFIG_HOME) return "XDG_CONFIG_HOME";
return "default";
}
@@ -3251,11 +3251,11 @@ function resolvedTokenSource() {
}
function sourceUploadState() {
- const enabled = /^(1|true|yes)$/i.test(process.env.GITTENSORY_UPLOAD_SOURCE ?? "false");
+ const enabled = /^(1|true|yes)$/i.test(process.env.LOOPOVER_UPLOAD_SOURCE ?? "false");
return {
default: false,
enabled,
- source: enabled ? "GITTENSORY_UPLOAD_SOURCE" : "default",
+ source: enabled ? "LOOPOVER_UPLOAD_SOURCE" : "default",
supported: false,
};
}
@@ -3272,7 +3272,7 @@ function configCommand(options) {
profileCount: profileList(config).length,
configured: existsSync(configPath),
configPathSource: resolvedConfigPathSource(),
- cacheDirSource: process.env.GITTENSORY_CACHE_DIR ? "GITTENSORY_CACHE_DIR" : "default",
+ cacheDirSource: process.env.LOOPOVER_CACHE_DIR ? "LOOPOVER_CACHE_DIR" : "default",
tokenConfigured: Boolean(getApiToken()),
tokenSource: resolvedTokenSource(),
sourceUpload: sourceUploadState(),
@@ -3289,7 +3289,7 @@ function configCommand(options) {
process.stdout.write(`Token: ${payload.tokenConfigured ? `configured (${payload.tokenSource})` : "not configured"}\n`);
process.stdout.write(
payload.sourceUpload.enabled
- ? `Source upload: enabled via ${payload.sourceUpload.source} (unsupported; unset GITTENSORY_UPLOAD_SOURCE)\n`
+ ? `Source upload: enabled via ${payload.sourceUpload.source} (unsupported; unset LOOPOVER_UPLOAD_SOURCE)\n`
: "Source upload: disabled (unsupported)\n",
);
}
@@ -3839,9 +3839,9 @@ function sanitizeDiagnosticText(value, extraPaths = []) {
if (value === undefined || value === null) return value;
let text = String(value);
const sensitiveValues = [
- process.env.GITTENSORY_API_TOKEN,
- process.env.GITTENSORY_MCP_TOKEN,
- process.env.GITTENSORY_TOKEN,
+ process.env.LOOPOVER_API_TOKEN,
+ process.env.LOOPOVER_MCP_TOKEN,
+ process.env.LOOPOVER_TOKEN,
config.session?.token,
...profileSessions(config).map((entry) => entry.session.token),
].filter((candidate) => typeof candidate === "string" && candidate.length > 0);
@@ -3850,8 +3850,8 @@ function sanitizeDiagnosticText(value, extraPaths = []) {
}
const localPaths = [
configPath,
- process.env.GITTENSORY_CONFIG_PATH,
- process.env.GITTENSORY_CONFIG_DIR,
+ process.env.LOOPOVER_CONFIG_PATH,
+ process.env.LOOPOVER_CONFIG_DIR,
process.cwd(),
homedir(),
...extraPaths,
@@ -3956,13 +3956,13 @@ async function apiPost(path, body) {
async function apiFetch(path, init, options = {}) {
const token = options.token ?? getApiToken();
if (options.auth !== false && !token) {
- const error = new Error("Run `loopover-mcp login`, or set GITTENSORY_API_TOKEN, GITTENSORY_MCP_TOKEN, or GITTENSORY_TOKEN before starting the MCP wrapper.");
+ const error = new Error("Run `loopover-mcp login`, or set LOOPOVER_API_TOKEN, LOOPOVER_MCP_TOKEN, or LOOPOVER_TOKEN before starting the MCP wrapper.");
error.status = 401;
error.code = "missing_auth";
throw error;
}
const controller = new AbortController();
- const timeoutMs = Number(process.env.GITTENSORY_API_TIMEOUT_MS ?? options.timeoutMs ?? 30000);
+ const timeoutMs = Number(process.env.LOOPOVER_API_TIMEOUT_MS ?? options.timeoutMs ?? 30000);
const timeout = setTimeout(() => controller.abort(), Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 30000);
const response = await fetch(`${apiUrl}${path}`, {
...init,
@@ -3996,7 +3996,7 @@ async function apiFetch(path, init, options = {}) {
}
async function fetchLatestPackageVersion() {
- if (/^(1|true|yes)$/i.test(process.env.GITTENSORY_SKIP_NPM_VERSION_CHECK ?? "false")) return { status: "skipped" };
+ if (/^(1|true|yes)$/i.test(process.env.LOOPOVER_SKIP_NPM_VERSION_CHECK ?? "false")) return { status: "skipped" };
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
const response = await fetch(`${npmRegistryUrl}/@loopover%2fmcp/latest`, {
diff --git a/packages/loopover-mcp/lib/local-branch.js b/packages/loopover-mcp/lib/local-branch.js
index 5e1aeecb44..16e39f1813 100644
--- a/packages/loopover-mcp/lib/local-branch.js
+++ b/packages/loopover-mcp/lib/local-branch.js
@@ -555,8 +555,8 @@ function splitCommand(command) {
}
function assertSourceUploadDisabled() {
- if (/^(1|true|yes)$/i.test(process.env.GITTENSORY_UPLOAD_SOURCE ?? "false")) {
- throw new Error("GITTENSORY_UPLOAD_SOURCE=true is not supported in v1; local MCP sends metadata only.");
+ if (/^(1|true|yes)$/i.test(process.env.LOOPOVER_UPLOAD_SOURCE ?? "false")) {
+ throw new Error("LOOPOVER_UPLOAD_SOURCE=true is not supported in v1; local MCP sends metadata only.");
}
}
diff --git a/packages/loopover-miner/CHANGELOG.md b/packages/loopover-miner/CHANGELOG.md
index b4ca1dd9d8..73be4b92fd 100644
--- a/packages/loopover-miner/CHANGELOG.md
+++ b/packages/loopover-miner/CHANGELOG.md
@@ -22,7 +22,7 @@
### ⚠ BREAKING CHANGES
* **miner:** the miner's default config directory and every gittensory_miner_* Prometheus metric name changed; no dual-read/alias, per the epic's full-cutover mandate ([#5705](https://github.com/JSONbored/gittensory/issues/5705)). A self-hoster's existing ~/.config/gittensory-miner state does not migrate automatically.
-* **miner:** every GITTENSORY_MINER_*/GITTENSORY_API_TOKEN_FILE/ GITTENSORY_MCP_TOKEN_FILE/GITTENSORY_MEM_LIMIT/GITTENSORY_REPORTING_* env var an existing self-hosted AMS/miner deployment sets must be renamed to its LOOPOVER_ equivalent -- the old names are no longer read.
+* **miner:** every LOOPOVER_MINER_*/LOOPOVER_API_TOKEN_FILE/ LOOPOVER_MCP_TOKEN_FILE/LOOPOVER_MEM_LIMIT/LOOPOVER_REPORTING_* env var an existing self-hosted AMS/miner deployment sets must be renamed to its LOOPOVER_ equivalent -- the old names are no longer read.
### Features
@@ -96,7 +96,7 @@
* **miner:** persist ranked-candidates snapshots and serve them locally ([#5619](https://github.com/JSONbored/gittensory/issues/5619)) ([6553b54](https://github.com/JSONbored/gittensory/commit/6553b54d34ce005e4120e040e96e0b62119ac439))
* **miner:** persist resolved policy verdicts across discover runs ([#5516](https://github.com/JSONbored/gittensory/issues/5516)) ([0d5c340](https://github.com/JSONbored/gittensory/commit/0d5c3402a3899fcb9f8658a21472b3b0c1be8d01))
* **miner:** read loop-cli convergence history from the persisted portfolio queue ([#5679](https://github.com/JSONbored/gittensory/issues/5679)) ([66e927e](https://github.com/JSONbored/gittensory/commit/66e927e95bfbb7135494a4000a42372b336cbb86)), closes [#5677](https://github.com/JSONbored/gittensory/issues/5677)
-* **miner:** rename GITTENSORY_MINER_*/GITTENSORY_* env vars to LOOPOVER_MINER_*/LOOPOVER_* ([#5707](https://github.com/JSONbored/gittensory/issues/5707)) ([6714f0c](https://github.com/JSONbored/gittensory/commit/6714f0cac5ab37477c7f56332cde969788c7996e)), closes [#5705](https://github.com/JSONbored/gittensory/issues/5705)
+* **miner:** rename LOOPOVER_MINER_*/LOOPOVER_* env vars to LOOPOVER_MINER_*/LOOPOVER_* ([#5707](https://github.com/JSONbored/gittensory/issues/5707)) ([6714f0c](https://github.com/JSONbored/gittensory/commit/6714f0cac5ab37477c7f56332cde969788c7996e)), closes [#5705](https://github.com/JSONbored/gittensory/issues/5705)
* **miner:** resolve rejectionSignaled's AI-usage-policy-ban trigger ([#5132](https://github.com/JSONbored/gittensory/issues/5132)) ([#5241](https://github.com/JSONbored/gittensory/issues/5241)) ([e6adb43](https://github.com/JSONbored/gittensory/commit/e6adb4396f236200ca116fbb59a2c144e9053902))
* **miner:** resolve the real per-repo MinerGoalSpec from a cloned worktree ([#5255](https://github.com/JSONbored/gittensory/issues/5255)) ([28949be](https://github.com/JSONbored/gittensory/commit/28949be13b97f4eaa0d013e7b37317790833c13f))
* **miner:** respect --json on CLI error paths ([#5543](https://github.com/JSONbored/gittensory/issues/5543)) ([7cffca2](https://github.com/JSONbored/gittensory/commit/7cffca2c8893a4a7a98c79618ee635a531b6e40b))
diff --git a/packages/loopover-miner/README.md b/packages/loopover-miner/README.md
index a18afe9b29..f3878983fa 100644
--- a/packages/loopover-miner/README.md
+++ b/packages/loopover-miner/README.md
@@ -276,4 +276,4 @@ This completes the read-only AMS MCP tool surface (status, portfolio, claims, ev
## Version check
-On every invocation the CLI starts an async npm registry lookup (5s timeout). When the installed package is behind `@loopover/miner@latest`, it prints a one-line upgrade command to stderr without blocking or failing the requested command. Set `GITTENSORY_NPM_REGISTRY_URL` to point at a mirror, same as `@loopover/mcp`.
+On every invocation the CLI starts an async npm registry lookup (5s timeout). When the installed package is behind `@loopover/miner@latest`, it prints a one-line upgrade command to stderr without blocking or failing the requested command. Set `LOOPOVER_NPM_REGISTRY_URL` to point at a mirror, same as `@loopover/mcp`.
diff --git a/packages/loopover-miner/docs/discovery-plane-operator-guide.md b/packages/loopover-miner/docs/discovery-plane-operator-guide.md
index e118cdbf8e..8bf796d4d9 100644
--- a/packages/loopover-miner/docs/discovery-plane-operator-guide.md
+++ b/packages/loopover-miner/docs/discovery-plane-operator-guide.md
@@ -46,7 +46,7 @@ Until the contract issue lands, treat these as **documentation placeholders** fo
| `LOOPOVER_MINER_DISCOVERY_INDEX_URL` | unset | Hosted index base URL. Required when the plane is enabled; ignored when opt-in is off. |
| `LOOPOVER_MINER_DISCOVERY_TELEMETRY` | unset / `false` | Separate opt-in for anonymized operational telemetry ([#4301](https://github.com/JSONbored/gittensory/issues/4301)). Plane queries can stay on while telemetry stays off. |
-**Truthy-string convention** (when implemented): `/^(1|true|yes|on)$/i`, matching other `GITTENSORY_*` flags in this repo.
+**Truthy-string convention** (when implemented): `/^(1|true|yes|on)$/i`, matching other `LOOPOVER_*` flags in this repo.
**Operator checklist (enabled plane):**
diff --git a/packages/loopover-miner/lib/update-check.js b/packages/loopover-miner/lib/update-check.js
index 324c35188e..cf9417e6e8 100644
--- a/packages/loopover-miner/lib/update-check.js
+++ b/packages/loopover-miner/lib/update-check.js
@@ -12,7 +12,7 @@ function isLocalRegistryHost(hostname) {
}
export function resolveNpmRegistryUrl(env = process.env) {
- const raw = env.GITTENSORY_NPM_REGISTRY_URL?.trim();
+ const raw = env.LOOPOVER_NPM_REGISTRY_URL?.trim();
if (!raw) return defaultNpmRegistryUrl;
let url;
diff --git a/scripts/backup.sh b/scripts/backup.sh
index a7cfad4ecd..84e5c7cc5a 100644
--- a/scripts/backup.sh
+++ b/scripts/backup.sh
@@ -33,7 +33,7 @@ normalize_backup_retain() {
TS=$(date -u +%Y%m%dT%H%M%SZ)
RETAIN=$(normalize_backup_retain "${BACKUP_RETAIN:-7}")
DB=${DATABASE_PATH:-/data/loopover.sqlite}
-PG_DB="${GITTENSORY_BACKUP_SOURCE_DATABASE_URL:-${DATABASE_URL:-}}"
+PG_DB="${LOOPOVER_BACKUP_SOURCE_DATABASE_URL:-${DATABASE_URL:-}}"
OUT=${BACKUP_OUT_DIR:-/backups}
PGPASSFILE_CREATED=""
MANIFEST_TMP=""
diff --git a/scripts/check-engine-parity.ts b/scripts/check-engine-parity.ts
index 5c8cda1149..0d6cd72099 100644
--- a/scripts/check-engine-parity.ts
+++ b/scripts/check-engine-parity.ts
@@ -414,8 +414,8 @@ function defaultExecGit(args: string[], cwd: string): string {
export function listChangedEngineParityFiles({
root,
execGit = defaultExecGit,
- baseRef = process.env.GITTENSORY_ENGINE_PARITY_BASE_REF ?? process.env.GITHUB_BASE_SHA ?? "",
- headRef = process.env.GITTENSORY_ENGINE_PARITY_HEAD_REF ?? "HEAD",
+ baseRef = process.env.LOOPOVER_ENGINE_PARITY_BASE_REF ?? process.env.GITHUB_BASE_SHA ?? "",
+ headRef = process.env.LOOPOVER_ENGINE_PARITY_HEAD_REF ?? "HEAD",
}: {
root: string;
execGit?: EngineParityExecGit;
@@ -672,7 +672,7 @@ export function runEngineParityMain(root: string = process.cwd()): number {
changedFiles.length > 0
? readEnginePackageVersionAtRef({
root,
- ref: process.env.GITTENSORY_ENGINE_PARITY_BASE_REF ?? process.env.GITHUB_BASE_SHA ?? "origin/main",
+ ref: process.env.LOOPOVER_ENGINE_PARITY_BASE_REF ?? process.env.GITHUB_BASE_SHA ?? "origin/main",
}) ?? headEngineVersion
: headEngineVersion;
const { failures, pairsChecked, versionSkew } = runEngineParityChecks({
diff --git a/scripts/check-manifest-drift.mjs b/scripts/check-manifest-drift.mjs
index 5df28b07be..762d8eb610 100644
--- a/scripts/check-manifest-drift.mjs
+++ b/scripts/check-manifest-drift.mjs
@@ -1,6 +1,6 @@
#!/usr/bin/env node
// Cross-checks the bundled fallback YAML in src/config/gittensory-repo-focus-manifest.ts
-// (GITTENSORY_REPO_FOCUS_MANIFEST_YAML) against the real root .loopover.yml. The bundled string exists so
+// (LOOPOVER_REPO_FOCUS_MANIFEST_YAML) against the real root .loopover.yml. The bundled string exists so
// the focus-manifest engine still has a sane default when the live repo file is unreachable (local dev,
// pre-merge branches) -- see that file's own header comment -- but nothing in CI previously caught the two
// silently diverging once someone edited one and forgot the other. This script parses both with the `yaml`
@@ -10,7 +10,7 @@ import { readFileSync } from "node:fs";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { parse as parseYaml } from "yaml";
-import { GITTENSORY_REPO_FOCUS_MANIFEST_YAML } from "../src/config/gittensory-repo-focus-manifest.ts";
+import { LOOPOVER_REPO_FOCUS_MANIFEST_YAML } from "../src/config/gittensory-repo-focus-manifest.ts";
const ROOT_MANIFEST_PATH = ".loopover.yml";
@@ -39,7 +39,7 @@ function sortKeysDeep(value) {
* bundledManifest }` -- pure given its inputs, no process.exit/console side effects of its own (those live
* in main()).
*/
-export function checkManifestDrift({ root, readFile = defaultReadFile, bundledYaml = GITTENSORY_REPO_FOCUS_MANIFEST_YAML }) {
+export function checkManifestDrift({ root, readFile = defaultReadFile, bundledYaml = LOOPOVER_REPO_FOCUS_MANIFEST_YAML }) {
const failures = [];
const rootManifestText = readFile(root, ROOT_MANIFEST_PATH);
@@ -54,10 +54,10 @@ export function checkManifestDrift({ root, readFile = defaultReadFile, bundledYa
if (rootJson !== bundledJson) {
failures.push(
[
- `${ROOT_MANIFEST_PATH} and GITTENSORY_REPO_FOCUS_MANIFEST_YAML (src/config/gittensory-repo-focus-manifest.ts) have drifted apart.`,
+ `${ROOT_MANIFEST_PATH} and LOOPOVER_REPO_FOCUS_MANIFEST_YAML (src/config/gittensory-repo-focus-manifest.ts) have drifted apart.`,
`-- ${ROOT_MANIFEST_PATH} (parsed) --`,
rootJson,
- `-- GITTENSORY_REPO_FOCUS_MANIFEST_YAML (parsed) --`,
+ `-- LOOPOVER_REPO_FOCUS_MANIFEST_YAML (parsed) --`,
bundledJson,
].join("\n"),
);
@@ -75,7 +75,7 @@ function main() {
process.exit(1);
}
- console.log(`Manifest-drift check ok: ${ROOT_MANIFEST_PATH} and GITTENSORY_REPO_FOCUS_MANIFEST_YAML agree.`);
+ console.log(`Manifest-drift check ok: ${ROOT_MANIFEST_PATH} and LOOPOVER_REPO_FOCUS_MANIFEST_YAML agree.`);
}
// Guard so importing this module for its pure exports (tests) never triggers the file-read/exit side effects.
diff --git a/scripts/check-ui-mcp-version-copy.mjs b/scripts/check-ui-mcp-version-copy.mjs
index ea64092707..5219c5d8d5 100644
--- a/scripts/check-ui-mcp-version-copy.mjs
+++ b/scripts/check-ui-mcp-version-copy.mjs
@@ -14,9 +14,9 @@ const targets = [
].map((target) => join(root, target));
// The live npm-registry check is BEST-EFFORT: a transient registry blip must not fail CI, because a red
-// required check one-shot-closes a contributor PR. Set GITTENSORY_MCP_LATEST_VERSION to make it fully
+// required check one-shot-closes a contributor PR. Set LOOPOVER_MCP_LATEST_VERSION to make it fully
// offline/deterministic. The deterministic stale-version-string scan below always runs regardless.
-let latest = process.env.GITTENSORY_MCP_LATEST_VERSION ?? null;
+let latest = process.env.LOOPOVER_MCP_LATEST_VERSION ?? null;
let latestSkipReason = null;
if (!latest) {
try {
@@ -34,7 +34,7 @@ if (latest && sourceLatest !== latest) {
);
} else if (!latest) {
console.warn(
- `::warning::skipped the npm dist-tag drift check (registry unavailable: ${latestSkipReason}); set GITTENSORY_MCP_LATEST_VERSION to enforce it offline`,
+ `::warning::skipped the npm dist-tag drift check (registry unavailable: ${latestSkipReason}); set LOOPOVER_MCP_LATEST_VERSION to enforce it offline`,
);
}
diff --git a/scripts/deploy-selfhost-image.sh b/scripts/deploy-selfhost-image.sh
index 6214eb63cf..7c5e552737 100755
--- a/scripts/deploy-selfhost-image.sh
+++ b/scripts/deploy-selfhost-image.sh
@@ -8,7 +8,7 @@
#
# Pin production rollouts to a release tag or digest:
# ./scripts/deploy-selfhost-image.sh ghcr.io/jsonbored/loopover-selfhost:orb-v0.1.0
-# GITTENSORY_IMAGE=ghcr.io/jsonbored/loopover-selfhost@sha256:... ./scripts/deploy-selfhost-image.sh
+# LOOPOVER_IMAGE=ghcr.io/jsonbored/loopover-selfhost@sha256:... ./scripts/deploy-selfhost-image.sh
#
# The image itself carries official release metadata. Set SENTRY_RELEASE only for custom images whose
# source maps were uploaded under that exact id.
@@ -31,8 +31,8 @@ resolve_image() {
exit 1
fi
- env_file_image="$(env_get GITTENSORY_IMAGE || true)"
- printf '%s' "${1:-${GITTENSORY_IMAGE:-${env_file_image:-$DEFAULT_IMAGE}}}"
+ env_file_image="$(env_get LOOPOVER_IMAGE || true)"
+ printf '%s' "${1:-${LOOPOVER_IMAGE:-${env_file_image:-$DEFAULT_IMAGE}}}"
}
validate_inputs() {
@@ -118,6 +118,6 @@ echo "selfhost image deploy: restarting $SERVICE"
docker compose "${compose_args[@]}" up -d --no-build --no-deps "$SERVICE"
wait_for_healthy
-env_put GITTENSORY_IMAGE "$IMAGE"
+env_put LOOPOVER_IMAGE "$IMAGE"
echo "selfhost image deploy: complete ($IMAGE)"
diff --git a/scripts/export-ams-reporting-db.sh b/scripts/export-ams-reporting-db.sh
index 599f6fa4ef..db0b827079 100644
--- a/scripts/export-ams-reporting-db.sh
+++ b/scripts/export-ams-reporting-db.sh
@@ -16,13 +16,13 @@ set -eu
# Bump whenever this script's own mapping/redaction logic changes (not just when a source table gains a column):
# the incremental fast-path below only fingerprints SOURCE ROW COUNT + latest timestamp, so a logic-only edit
# would otherwise serve the previous run's output forever.
-SCRIPT_VERSION="${GITTENSORY_AMS_REPORTING_SCRIPT_VERSION:-1}"
+SCRIPT_VERSION="${LOOPOVER_AMS_REPORTING_SCRIPT_VERSION:-1}"
OUT_DIR="${LOOPOVER_REPORTING_DIR:-/reporting}"
-ATTEMPT_LOG_SOURCE_DB="${GITTENSORY_AMS_ATTEMPT_LOG_SOURCE_DB:-/ams-ledgers/attempt-log.sqlite3}"
-ATTEMPT_LOG_OUT_DB="${GITTENSORY_AMS_ATTEMPT_LOG_REPORTING_DB:-$OUT_DIR/ams-attempt-log.sqlite}"
-PREDICTION_LEDGER_SOURCE_DB="${GITTENSORY_AMS_PREDICTION_LEDGER_SOURCE_DB:-/ams-ledgers/prediction-ledger.sqlite3}"
-PREDICTION_LEDGER_OUT_DB="${GITTENSORY_AMS_PREDICTION_LEDGER_REPORTING_DB:-$OUT_DIR/ams-prediction-ledger.sqlite}"
+ATTEMPT_LOG_SOURCE_DB="${LOOPOVER_AMS_ATTEMPT_LOG_SOURCE_DB:-/ams-ledgers/attempt-log.sqlite3}"
+ATTEMPT_LOG_OUT_DB="${LOOPOVER_AMS_ATTEMPT_LOG_REPORTING_DB:-$OUT_DIR/ams-attempt-log.sqlite}"
+PREDICTION_LEDGER_SOURCE_DB="${LOOPOVER_AMS_PREDICTION_LEDGER_SOURCE_DB:-/ams-ledgers/prediction-ledger.sqlite3}"
+PREDICTION_LEDGER_OUT_DB="${LOOPOVER_AMS_PREDICTION_LEDGER_REPORTING_DB:-$OUT_DIR/ams-prediction-ledger.sqlite}"
mkdir -p "$OUT_DIR"
diff --git a/scripts/export-grafana-reporting-db.sh b/scripts/export-grafana-reporting-db.sh
index 7123b6d1bd..e2712cb7d8 100644
--- a/scripts/export-grafana-reporting-db.sh
+++ b/scripts/export-grafana-reporting-db.sh
@@ -7,10 +7,10 @@ set -eu
# would otherwise serve the previous run's output forever -- this constant, folded into the fingerprint,
# forces a full rebuild the next time this script runs after such an edit ships. Overridable via env var
# purely so a test can simulate "the script logic changed" without editing this file.
-SCRIPT_VERSION="${GITTENSORY_REPORTING_SCRIPT_VERSION:-2}"
+SCRIPT_VERSION="${LOOPOVER_REPORTING_SCRIPT_VERSION:-2}"
APP_DB="${LOOPOVER_REPORTING_SOURCE_DB:-/appdb/loopover.sqlite}"
-PG_DB="${GITTENSORY_REPORTING_SOURCE_DATABASE_URL:-${DATABASE_URL:-}}"
+PG_DB="${LOOPOVER_REPORTING_SOURCE_DATABASE_URL:-${DATABASE_URL:-}}"
OUT_DIR="${LOOPOVER_REPORTING_DIR:-/reporting}"
OUT_DB="${LOOPOVER_REPORTING_DB:-$OUT_DIR/loopover-reporting.sqlite}"
TMP_DB="${OUT_DB}.tmp"
diff --git a/scripts/gen-command-reference.mjs b/scripts/gen-command-reference.mjs
index 325159e51b..c8e3dbe8ee 100644
--- a/scripts/gen-command-reference.mjs
+++ b/scripts/gen-command-reference.mjs
@@ -13,7 +13,7 @@ export const DEFAULT_OUTPUT_PATH = "apps/loopover-ui/src/lib/command-reference.t
const PUBLIC_CATALOG_NAME = "PUBLIC_MENTION_COMMAND_CATALOG";
const MAINTAINER_CATALOG_NAME = "MAINTAINER_QUEUE_DIGEST_COMMAND_CATALOG";
-const ACTION_CATALOG_NAME = "GITTENSORY_ACTION_COMMAND_CATALOG";
+const ACTION_CATALOG_NAME = "LOOPOVER_ACTION_COMMAND_CATALOG";
// Self-defense floor: 10 public + 9 maintainer-only commands exist today. If extraction ever finds fewer
// than 15 total entries across both catalogs, the extraction regex is almost certainly broken -- fail
diff --git a/scripts/selfhost-docker-prune.sh b/scripts/selfhost-docker-prune.sh
index 1ffcd35b4e..dbc300b206 100755
--- a/scripts/selfhost-docker-prune.sh
+++ b/scripts/selfhost-docker-prune.sh
@@ -30,7 +30,7 @@
# sh scripts/selfhost-docker-prune.sh --dry-run # preview only: report disk usage, delete nothing
set -eu
-RETAIN_HOURS=${GITTENSORY_DOCKER_PRUNE_RETAIN_HOURS:-168} # 7 days
+RETAIN_HOURS=${LOOPOVER_DOCKER_PRUNE_RETAIN_HOURS:-168} # 7 days
DRY_RUN=0
for arg in "$@"; do
diff --git a/scripts/selfhost-post-update-check.sh b/scripts/selfhost-post-update-check.sh
index d6be360b4c..0adc4012e8 100755
--- a/scripts/selfhost-post-update-check.sh
+++ b/scripts/selfhost-post-update-check.sh
@@ -86,7 +86,7 @@ fi
if [ -f "$ENV_FILE" ]; then
echo "selfhost post-update check: release metadata from $ENV_FILE"
- grep -E '^(GITTENSORY_IMAGE|LOOPOVER_VERSION|SENTRY_RELEASE)=' "$ENV_FILE" || true
+ grep -E '^(LOOPOVER_IMAGE|LOOPOVER_VERSION|SENTRY_RELEASE)=' "$ENV_FILE" || true
else
echo "selfhost post-update check: warning — $ENV_FILE not found (skipping release metadata grep)" >&2
fi
diff --git a/scripts/smoke-production.mjs b/scripts/smoke-production.mjs
index 5b527083e3..a610036e30 100644
--- a/scripts/smoke-production.mjs
+++ b/scripts/smoke-production.mjs
@@ -1,5 +1,5 @@
-const siteOrigin = normalizeOrigin(process.env.GITTENSORY_SITE_ORIGIN ?? "https://loopover.ai");
-const apiOrigin = normalizeOrigin(process.env.GITTENSORY_API_ORIGIN ?? "https://api.loopover.ai");
+const siteOrigin = normalizeOrigin(process.env.LOOPOVER_SITE_ORIGIN ?? "https://loopover.ai");
+const apiOrigin = normalizeOrigin(process.env.LOOPOVER_API_ORIGIN ?? "https://api.loopover.ai");
const siteRoutes = [
"/",
diff --git a/scripts/smoke-ui-browser.mjs b/scripts/smoke-ui-browser.mjs
index ddc8813b67..6854d6deaf 100644
--- a/scripts/smoke-ui-browser.mjs
+++ b/scripts/smoke-ui-browser.mjs
@@ -1,4 +1,4 @@
-const siteOrigin = new URL(process.env.GITTENSORY_SITE_ORIGIN ?? "https://loopover.ai").origin;
+const siteOrigin = new URL(process.env.LOOPOVER_SITE_ORIGIN ?? "https://loopover.ai").origin;
const routes = ["/", "/app", "/app/workbench", "/app/repos", "/app/runs", "/app/analytics", "/app/operator", "/app/commands", "/app/digest", "/api", "/roadmap", "/changelog", "/extension", "/docs"];
const playwright = await import("playwright").catch(() => null);
diff --git a/scripts/verify-backup.sh b/scripts/verify-backup.sh
index 69207fc925..903b44aec6 100644
--- a/scripts/verify-backup.sh
+++ b/scripts/verify-backup.sh
@@ -10,7 +10,7 @@
set -eu
OUT=${BACKUP_OUT_DIR:-/backups}
-PG_DB="${GITTENSORY_BACKUP_SOURCE_DATABASE_URL:-${DATABASE_URL:-}}"
+PG_DB="${LOOPOVER_BACKUP_SOURCE_DATABASE_URL:-${DATABASE_URL:-}}"
TARGET="${1:-}"
PG_PASSFILES=""
cleanup() {
@@ -184,11 +184,11 @@ verify_postgres() {
# 2) Optional scratch restore smoke (opt-in, guarded): restore into a THROWAWAY database and sanity-check.
# Never runs against the live database — the scratch URL must be set explicitly and differ from the source.
[ "${VERIFY_RESTORE_SCRATCH:-}" = "1" ] || return 0
- scratch="${GITTENSORY_VERIFY_SCRATCH_DATABASE_URL:-}"
+ scratch="${LOOPOVER_VERIFY_SCRATCH_DATABASE_URL:-}"
case "$scratch" in
postgres://* | postgresql://*) : ;;
*)
- echo "[verify] VERIFY_RESTORE_SCRATCH=1 needs GITTENSORY_VERIFY_SCRATCH_DATABASE_URL=postgres://… (a dedicated scratch database, never the live one)" >&2
+ echo "[verify] VERIFY_RESTORE_SCRATCH=1 needs LOOPOVER_VERIFY_SCRATCH_DATABASE_URL=postgres://… (a dedicated scratch database, never the live one)" >&2
return 1
;;
esac
diff --git a/src/api/routes.ts b/src/api/routes.ts
index 59e73ad3d4..49b035446e 100644
--- a/src/api/routes.ts
+++ b/src/api/routes.ts
@@ -129,7 +129,7 @@ import { fetchPublicContributorProfile, fetchPublicRepoStats } from "../github/p
import {
buildPublicAgentCommandComment,
buildMaintainerQueueDigest,
- GITTENSORY_MENTION_COMMAND_CATALOG,
+ LOOPOVER_MENTION_COMMAND_CATALOG,
isAuthorizedCommandActor,
isMaintainerOnlyCommand,
sanitizePublicComment,
@@ -960,7 +960,7 @@ export function createApp() {
// Proof of Power (#1059): unauthenticated homepage stats counter — lifetime PRs handled / merged / closed,
// gate + slop blocks, and a reversal-grounded accuracy %. Aggregate counts only (no PR content, authors,
- // scores, or reward internals). Flag-gated: 404s when GITTENSORY_PUBLIC_STATS is off so the worker is
+ // scores, or reward internals). Flag-gated: 404s when LOOPOVER_PUBLIC_STATS is off so the worker is
// byte-identical to today. Excluded from requiresApiToken below.
app.get("/v1/public/stats", async (c) => {
if (!isPublicStatsEnabled(c.env)) return c.json({ error: "not_found" }, 404);
@@ -2528,7 +2528,7 @@ export function createApp() {
getRepositorySettings(c.env, fullName),
listPullRequests(c.env, fullName),
]);
- return c.json(buildMaintainerActivationPreview({ repoFullName: fullName, repo, settings, pullRequests, generatedAt: nowIso(), duplicateWinnerEnabled: c.env.GITTENSORY_DUPLICATE_WINNER === "true" }));
+ return c.json(buildMaintainerActivationPreview({ repoFullName: fullName, repo, settings, pullRequests, generatedAt: nowIso(), duplicateWinnerEnabled: c.env.LOOPOVER_DUPLICATE_WINNER === "true" }));
});
// #543 outcome-learning loop: is the slop score predictive, and are recommendations panning out? Read-only
@@ -4154,7 +4154,7 @@ const APP_COMMANDS = [
description: "Preview the public-safe summary that may be posted to a PR thread.",
endpoint: "/v1/app/commands/preview",
},
- ...GITTENSORY_MENTION_COMMAND_CATALOG.filter(
+ ...LOOPOVER_MENTION_COMMAND_CATALOG.filter(
(command) =>
![
"preflight",
@@ -4328,7 +4328,7 @@ function buildMaintainerSettingsPreview() {
};
}
-const PREVIEWABLE_MENTION_COMMANDS = new Set(GITTENSORY_MENTION_COMMAND_CATALOG.map((command) => command.id));
+const PREVIEWABLE_MENTION_COMMANDS = new Set(LOOPOVER_MENTION_COMMAND_CATALOG.map((command) => command.id));
type CommandPreviewDecision = {
status: "ready" | "skipped" | "missing_permission" | "private_api";
diff --git a/src/config/gittensory-repo-focus-manifest.ts b/src/config/gittensory-repo-focus-manifest.ts
index 6b26ea7c19..6ccd6a8513 100644
--- a/src/config/gittensory-repo-focus-manifest.ts
+++ b/src/config/gittensory-repo-focus-manifest.ts
@@ -2,7 +2,7 @@
* Bundled fallback for JSONbored/gittensory when the repo file is not yet reachable
* (local dev, pre-merge branches). Keep aligned with `.loopover.yml` at repo root.
*/
-export const GITTENSORY_REPO_FOCUS_MANIFEST_YAML = `# LoopOver repo focus manifest — machine-readable contributor policy for this project.
+export const LOOPOVER_REPO_FOCUS_MANIFEST_YAML = `# LoopOver repo focus manifest — machine-readable contributor policy for this project.
# Private maintainerNotes stay in authenticated API surfaces only.
source: repo_file
@@ -129,8 +129,8 @@ maintainerNotes:
export const GITTENSOR_SELF_REPO_DEFAULT = "JSONbored/gittensory";
-export function resolveLoopOverSelfRepoFullName(env: { GITTENSORY_DRIFT_ISSUE_REPO?: string }): string {
- const configured = env.GITTENSORY_DRIFT_ISSUE_REPO?.trim();
+export function resolveLoopOverSelfRepoFullName(env: { LOOPOVER_DRIFT_ISSUE_REPO?: string }): string {
+ const configured = env.LOOPOVER_DRIFT_ISSUE_REPO?.trim();
if (configured && configured.includes("/")) return configured;
return GITTENSOR_SELF_REPO_DEFAULT;
}
diff --git a/src/db/schema.ts b/src/db/schema.ts
index 06a111cada..d183e890a8 100644
--- a/src/db/schema.ts
+++ b/src/db/schema.ts
@@ -200,7 +200,7 @@ export const repositorySettings = sqliteTable("repository_settings", {
// follow-up). Nullable, same "no override configured" shape as screenshotTableGateMessage above.
screenshotTableGateSkillFileUrl: text("screenshot_table_gate_skill_file_url"),
// Waste elimination for known automation authors (settings/automation-bot-skip.ts). 'inherit' (default)
- // defers to the GITTENSORY_SKIP_AUTOMATION_BOT_PRS global default; 'off'/'enabled' force this repo
+ // defers to the LOOPOVER_SKIP_AUTOMATION_BOT_PRS global default; 'off'/'enabled' force this repo
// regardless of it -- mirrors moderationGateMode's shape above.
skipAutomationBotAuthors: text("skip_automation_bot_authors").notNull().default("inherit"),
createdAt: text("created_at").notNull().$defaultFn(() => nowIso()),
diff --git a/src/env.d.ts b/src/env.d.ts
index 1340156522..763ba4c6f8 100644
--- a/src/env.d.ts
+++ b/src/env.d.ts
@@ -198,7 +198,7 @@ declare global {
* permitted to query GET /v1/public/github/repos/:owner/:repo/stats. Default "" (unset) denies all repos
* because this unauthenticated route proxies the live GitHub API with the deployment's server-side token.
* Set this to the exact public repos that should expose website chrome stats. Mirrors
- * GITTENSORY_PUBLIC_STATS_REPOS's comma-separated shape (src/review/public-stats.ts). See
+ * LOOPOVER_PUBLIC_STATS_REPOS's comma-separated shape (src/review/public-stats.ts). See
* src/github/public.ts. */
PUBLIC_REPO_STATS_ALLOWLIST?: string;
/** #703: owner-gated global to apply upstream sigmoid time-decay in score previews. Default off. */
@@ -217,12 +217,12 @@ declare global {
* autonomy, labels, model/effort) is set privately and contributors can't read or game it. Unset ⇒ public
* fetch (cloud, or a self-host without the dir, is byte-identical to before). */
LOOPOVER_REPO_CONFIG_DIR?: string;
- GITTENSORY_AUTO_FILE_DRIFT_ISSUES?: string;
- GITTENSORY_DRIFT_ISSUE_REPO?: string;
- GITTENSORY_DRIFT_ISSUE_TOKEN?: string;
+ LOOPOVER_AUTO_FILE_DRIFT_ISSUES?: string;
+ LOOPOVER_DRIFT_ISSUE_REPO?: string;
+ LOOPOVER_DRIFT_ISSUE_TOKEN?: string;
/** Comma-separated GitHub logins assigned to filed upstream-drift issues (default: the gittensory
* maintainer). Lets a self-host operator route drift issues to their own team. */
- GITTENSORY_DRIFT_ISSUE_ASSIGNEES?: string;
+ LOOPOVER_DRIFT_ISSUE_ASSIGNEES?: string;
/** Comma-separated GitHub bot logins ADDITIONALLY trusted to author review-thread blockers via scanner
* comments (src/github/backfill.ts isTrustedScannerReviewThreadAuthor), merged with the built-in baseline
* (superagent[bot], superagent-security[bot], superagent-security-dev[bot], brin[bot]). Lets a self-host
@@ -267,7 +267,7 @@ declare global {
SENTRY_MIN_SEVERITY?: string;
/** Per-repo override map for SENTRY_MIN_SEVERITY — see its doc comment for the shape and precedence. */
SENTRY_REPO_MIN_SEVERITY?: string;
- GITTENSORY_CONTRIBUTOR_ISSUE_TOKEN?: string;
+ LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN?: string;
PRODUCT_USAGE_HASH_SALT?: string;
/** Server-to-server API bearer token — bypasses per-repo write checks (src/auth/security.ts). */
LOOPOVER_API_TOKEN?: string;
@@ -371,13 +371,13 @@ declare global {
* structured `sweep_liveness_stale` log (Sentry-visible), and re-enqueues a targeted `agent-regate-sweep`
* for just that repo. Default OFF — unset/false means the cron tick enqueues NO watchdog job (does no new
* work), so the worker is byte-identical to today. */
- GITTENSORY_SWEEP_WATCHDOG?: string;
+ LOOPOVER_SWEEP_WATCHDOG?: string;
/** Self-heal: when truthy, a short-interval cron list-diffs GitHub's open PR numbers against the local
* table for every acting-autonomy repo and catches up (fetch + upsert + regate) any PR number GitHub has
* that the local table doesn't — a silently-lost "opened" webhook, caught within minutes instead of the
* 6-hour backfillRegisteredRepositories freshness window. Default OFF — unset/false means the cron tick
* enqueues NO reconciliation job, so the worker is byte-identical to today. */
- GITTENSORY_PR_RECONCILIATION?: string;
+ LOOPOVER_PR_RECONCILIATION?: string;
/** Convergence (RAG retrieval): when truthy, the AI reviewer prompt gains a RELEVANT EXISTING CODE / DOCS
* section — at review time the codebase vector index is queried for code/docs semantically related to the
* PR's changed files (callers, related modules, existing conventions) and appended as additive reference
@@ -443,18 +443,18 @@ declare global {
* Default OFF -- flag-OFF (or every repo unset), refresh-registry is never enqueued (see src/index.ts) and
* a self-host box makes zero outbound contact with the gittensor subnet registry. See
* src/review/gittensor-wire.ts. */
- GITTENSORY_EXPERIMENTAL_GITTENSOR?: string;
+ LOOPOVER_EXPERIMENTAL_GITTENSOR?: string;
/** Maintainer recap digest (#1963, #2248): when truthy, a cross-repo RecapReport -- gittensory's OWN
* gate-precision + outcome-calibration data folded across every scanned repo (buildMaintainerRecap,
- * #2239) -- is delivered to Discord on a cron cadence. GITTENSORY_RECAP_CADENCE ("daily" | "weekly",
- * default "weekly"; an invalid value falls back to "weekly") picks how often; GITTENSORY_RECAP_HOUR
- * (0-23, default 14) and GITTENSORY_RECAP_DAY (0-6, Sunday=0, default 1/Monday, only consulted when
+ * #2239) -- is delivered to Discord on a cron cadence. LOOPOVER_RECAP_CADENCE ("daily" | "weekly",
+ * default "weekly"; an invalid value falls back to "weekly") picks how often; LOOPOVER_RECAP_HOUR
+ * (0-23, default 14) and LOOPOVER_RECAP_DAY (0-6, Sunday=0, default 1/Monday, only consulted when
* weekly) pick when, so the tick fires at most once per period. Default OFF -- unset/false means the
* cron enqueues NO recap job, byte-identical to today. See src/review/maintainer-recap-wire.ts. */
- GITTENSORY_MAINTAINER_RECAP?: string;
- GITTENSORY_RECAP_CADENCE?: string;
- GITTENSORY_RECAP_HOUR?: string;
- GITTENSORY_RECAP_DAY?: string;
+ LOOPOVER_MAINTAINER_RECAP?: string;
+ LOOPOVER_RECAP_CADENCE?: string;
+ LOOPOVER_RECAP_HOUR?: string;
+ LOOPOVER_RECAP_DAY?: string;
/** #1941: route the live CI aggregate (the gate's check/status read) through ONE GraphQL statusCheckRollup
* query instead of the paginated /check-runs + /status + /check-suites REST reads, moving that hot path onto
* the separate GraphQL rate-limit bucket. Default OFF (byte-identical, proven REST aggregate); when ON the
@@ -471,7 +471,7 @@ declare global {
* worker is byte-identical to today. Exposes review-disposition counts + a reversal-grounded accuracy
* percentage + an estimated-time-saved figure ONLY — never PR content, authors, scores, or reward internals.
* See review/public-stats.ts. */
- GITTENSORY_PUBLIC_STATS?: string;
+ LOOPOVER_PUBLIC_STATS?: string;
/** Proof of Power (#1059): comma-separated allowlist of repo full-names ("owner/repo") whose OWN historical
* review ledger (audit_events "published a review surface" + pull_requests terminal state) counts toward
* the public stats counter. DELIBERATELY SEPARATE from LOOPOVER_REVIEW_REPOS (the live per-PR-feature
@@ -482,7 +482,7 @@ declare global {
* public counter, including the unrelated cross-fleet self-hoster aggregate (getOrbGlobalStats), which does
* not depend on this var at all. Default "" (unset) → the own-ledger side reports zero (fails safe, same
* privacy stance as before) but the Orb aggregate still reports normally. See review/public-stats.ts. */
- GITTENSORY_PUBLIC_STATS_REPOS?: string;
+ LOOPOVER_PUBLIC_STATS_REPOS?: string;
/** Convergence (port): public OAuth draft-submission flow ported from reviewbot. When truthy, the
* /v1/drafts endpoints accept a contributor draft -> GitHub OAuth -> fork PR against the content repo.
* Default OFF — unset/false makes every draft endpoint 404 and writes nothing (byte-identical worker). */
@@ -521,20 +521,20 @@ declare global {
* guards short-circuit so the advisory finding, gate conclusion, close reason, slop, and panels are
* unchanged). Once a winner closes, the next-lowest OPEN sibling becomes the winner on re-eval. See
* src/signals/duplicate-winner.ts. */
- GITTENSORY_DUPLICATE_WINNER?: string;
+ LOOPOVER_DUPLICATE_WINNER?: string;
/** Open-PR file-path collision (#2653): when truthy, a live PR review enriches its own and its open
* siblings' `changedFiles` from the `pull_request_files` cache (a plain D1 read — no extra GitHub calls)
* before building the collision report, so two independently-open PRs touching the same file are flagged
* the same way two title-similar PRs already are. A contributor's own two PRs sharing a file are never
* flagged (see the same-author guard in buildCollisionReport). Default OFF — unset/false leaves every
* PullRequestRecord's changedFiles unset, byte-identical to today. See src/signals/engine.ts prItem. */
- GITTENSORY_OPEN_PR_FILE_COLLISION?: string;
+ LOOPOVER_OPEN_PR_FILE_COLLISION?: string;
/** Waste elimination for known automation authors (settings/automation-bot-skip.ts): skip AI review, gate
* evaluation, and public-surface publish entirely for a PR/event genuinely triggered by release-please's
* github-actions[bot], Renovate, or Dependabot. Default-ON, unlike most flags above — see that module's
* own doc comment for why. A repo can override via its own repository_settings.skip_automation_bot_authors
* column ("off"/"enabled"), independent of this global default. */
- GITTENSORY_SKIP_AUTOMATION_BOT_PRS?: string;
+ LOOPOVER_SKIP_AUTOMATION_BOT_PRS?: string;
/** D1 size/row-count observability probe (#3810): the Cloudflare account id that owns the D1 database to
* monitor. Presence of this AND the two vars below IS the enablement switch (see isD1SizeProbeEnabled,
* src/selfhost/d1-size-probe.ts) -- unset/blank ⇒ the probe never runs, byte-identical to today. Most
diff --git a/src/github/commands.ts b/src/github/commands.ts
index 294c187321..276b1df6aa 100644
--- a/src/github/commands.ts
+++ b/src/github/commands.ts
@@ -52,9 +52,9 @@ const MAINTAINER_QUEUE_DIGEST_COMMAND_CATALOG = [
{ id: "noise-report", title: "LoopOver noise report", description: "Highlight queue noise sources maintainers should triage first." },
] as const;
-export const GITTENSORY_MENTION_COMMAND_CATALOG = [...PUBLIC_MENTION_COMMAND_CATALOG, ...MAINTAINER_QUEUE_DIGEST_COMMAND_CATALOG] as const;
+export const LOOPOVER_MENTION_COMMAND_CATALOG = [...PUBLIC_MENTION_COMMAND_CATALOG, ...MAINTAINER_QUEUE_DIGEST_COMMAND_CATALOG] as const;
-export type LoopOverMentionCommandName = (typeof GITTENSORY_MENTION_COMMAND_CATALOG)[number]["id"];
+export type LoopOverMentionCommandName = (typeof LOOPOVER_MENTION_COMMAND_CATALOG)[number]["id"];
export type MaintainerQueueDigestCommandName = (typeof MAINTAINER_QUEUE_DIGEST_COMMAND_CATALOG)[number]["id"];
// `chat` (#4595) is excluded like help/miner-context: it renders a bespoke LLM-answer card (buildChatPublicAnswerCard),
// not the deterministic snapshot-section path, so it needs no REFRESH_/EMPTY_SECTION_TITLES entry.
@@ -84,7 +84,7 @@ export function isIntentRoutableCommand(value: unknown): value is IntentRoutable
// @loopover gate-override is not silently downgraded to "help"). #1960 adds the PR control-surface verbs
// (review, pause, resume, resolve, configuration, explain) as pure parse targets; per-command dispatch is
// wired incrementally in follow-up bounties, each mirroring maybeProcessGateOverrideCommand.
-export const GITTENSORY_ACTION_COMMAND_CATALOG = [
+export const LOOPOVER_ACTION_COMMAND_CATALOG = [
{
id: "gate-override",
title: "Gate override",
@@ -127,15 +127,15 @@ export const GITTENSORY_ACTION_COMMAND_CATALOG = [
},
] as const;
-export type LoopOverActionCommandName = (typeof GITTENSORY_ACTION_COMMAND_CATALOG)[number]["id"];
+export type LoopOverActionCommandName = (typeof LOOPOVER_ACTION_COMMAND_CATALOG)[number]["id"];
-export const GITTENSORY_ACTION_COMMANDS = GITTENSORY_ACTION_COMMAND_CATALOG.map(
+export const LOOPOVER_ACTION_COMMANDS = LOOPOVER_ACTION_COMMAND_CATALOG.map(
(command) => command.id,
) as readonly LoopOverActionCommandName[];
// Alternate spellings that resolve to a canonical action command name so both forms dispatch to the same
// handler. Only "re-review" exists today (#1960); the map stays a single source of truth for any future alias.
-const GITTENSORY_ACTION_COMMAND_ALIASES: Record = {
+const LOOPOVER_ACTION_COMMAND_ALIASES: Record = {
"re-review": "review",
};
@@ -172,13 +172,13 @@ export type AgentCommandFeedbackContext = {
command: LoopOverMentionCommandName | null;
};
-const COMMANDS = new Set(GITTENSORY_MENTION_COMMAND_CATALOG.map((command) => command.id));
-const ACTION_COMMANDS = new Set(GITTENSORY_ACTION_COMMANDS);
+const COMMANDS = new Set(LOOPOVER_MENTION_COMMAND_CATALOG.map((command) => command.id));
+const ACTION_COMMANDS = new Set(LOOPOVER_ACTION_COMMANDS);
const MAINTAINER_QUEUE_DIGEST_COMMANDS = new Set(MAINTAINER_QUEUE_DIGEST_COMMAND_CATALOG.map((command) => command.id));
const MAINTAINER_ASSOCIATIONS = new Set(["OWNER", "MEMBER", "COLLABORATOR"]);
const AGENT_COMMAND_FEEDBACK_MARKER = "gittensory-agent-command-answer";
-const COMMAND_TITLES = Object.fromEntries(GITTENSORY_MENTION_COMMAND_CATALOG.map((command) => [command.id, command.title])) as Record;
+const COMMAND_TITLES = Object.fromEntries(LOOPOVER_MENTION_COMMAND_CATALOG.map((command) => [command.id, command.title])) as Record;
const REFRESH_SECTION_TITLES: Record = {
ask: "Contribution context snapshot refresh",
@@ -260,9 +260,9 @@ const ARGUMENT_ACTION_COMMANDS = new Set(["explain"])
function commandSuggestCatalog(): CommandSuggestCatalog {
return {
- mentionCommands: GITTENSORY_MENTION_COMMAND_CATALOG.map((command) => command.id),
- actionCommands: GITTENSORY_ACTION_COMMANDS,
- actionAliases: GITTENSORY_ACTION_COMMAND_ALIASES,
+ mentionCommands: LOOPOVER_MENTION_COMMAND_CATALOG.map((command) => command.id),
+ actionCommands: LOOPOVER_ACTION_COMMANDS,
+ actionAliases: LOOPOVER_ACTION_COMMAND_ALIASES,
};
}
@@ -287,7 +287,7 @@ export function parseLoopOverMentionCommand(body: string | null | undefined): Lo
const bareTrailing = (match[2] ?? "").trim();
return { name: "help", raw: match[0].trim(), unrecognizedText: bareTrailing.length > 0 ? bareTrailing : undefined };
}
- const requested = (GITTENSORY_ACTION_COMMAND_ALIASES[rawVerbToken] ?? rawVerbToken) as LoopOverMentionCommandName | LoopOverActionCommandName;
+ const requested = (LOOPOVER_ACTION_COMMAND_ALIASES[rawVerbToken] ?? rawVerbToken) as LoopOverMentionCommandName | LoopOverActionCommandName;
if (ACTION_COMMANDS.has(requested as LoopOverActionCommandName)) {
// match[2] is captured by a `*`-quantified group outside any optional wrapper, so it always matches
// (possibly empty) and is never actually undefined; the ?? below is a noUncheckedIndexedAccess guard only.
@@ -932,7 +932,7 @@ function actionCommandHelpSections(): string[] {
"- These verbs require maintainer or collaborator authorization (per command-authorization policy).",
"- `pause` and `resume` affect only auto-review scheduling — they never change the gate disposition or make review advisory.",
"",
- ...GITTENSORY_ACTION_COMMAND_CATALOG.map(
+ ...LOOPOVER_ACTION_COMMAND_CATALOG.map(
(command) => `- \`@loopover ${command.id}\` ${sanitizePublicComment(command.description)}`,
),
];
diff --git a/src/github/footer.ts b/src/github/footer.ts
index 3cfde81773..2469a0aa3e 100644
--- a/src/github/footer.ts
+++ b/src/github/footer.ts
@@ -11,7 +11,7 @@
/** The LoopOver product site (marketing on-ramp / attribution target) -- the DEFAULT only. A
* self-hoster with `PUBLIC_SITE_ORIGIN` set gets their own domain instead, both here and in
* `gittensoryFooter` below (#4613). */
-export const GITTENSORY_SITE_URL = "https://loopover.ai";
+export const LOOPOVER_SITE_URL = "https://loopover.ai";
/** Minimal env slice `gittensoryFooter` needs, narrowed from the full `Env` the same way this file's
* `maintainerControlPanelUrl` already narrows its own `env` param inline -- so every file that renders
@@ -23,7 +23,7 @@ export type LoopOverFooterEnv = { PUBLIC_SITE_ORIGIN?: string | undefined };
* check-run `details_url` so the merge-box "Details" link lands on the repo's review panel instead of GitHub's
* generic check page, and as the in-comment control-panel link. Returns null only if URL construction throws. */
export function maintainerControlPanelUrl(env: { PUBLIC_SITE_ORIGIN?: string | undefined }, repoFullName: string): string | null {
- const origin = env.PUBLIC_SITE_ORIGIN ?? GITTENSORY_SITE_URL;
+ const origin = env.PUBLIC_SITE_ORIGIN ?? LOOPOVER_SITE_URL;
try {
const url = new URL("/app", origin);
url.searchParams.set("view", "maintainer");
@@ -42,11 +42,11 @@ export function maintainerControlPanelUrl(env: { PUBLIC_SITE_ORIGIN?: string | u
* literal path string only if origin resolution itself throws (an operator-misconfigured PUBLIC_SITE_ORIGIN
* should degrade the link, not crash comment rendering). */
export function commandReferenceUrl(env: LoopOverFooterEnv): string {
- const origin = env.PUBLIC_SITE_ORIGIN ?? GITTENSORY_SITE_URL;
+ const origin = env.PUBLIC_SITE_ORIGIN ?? LOOPOVER_SITE_URL;
try {
return new URL("/docs/gittensory-commands", origin).toString();
} catch {
- return `${GITTENSORY_SITE_URL}/docs/gittensory-commands`;
+ return `${LOOPOVER_SITE_URL}/docs/gittensory-commands`;
}
}
@@ -66,10 +66,10 @@ export function gittensorRepoEarnUrl(repoFullName: string): string {
* distinction lives in the review BODY (full panel vs. minimal), not here.
* Uses only "earn" wording — never reward/payout/score (forbidden in public comments).
* `env.PUBLIC_SITE_ORIGIN` (same resolution as `maintainerControlPanelUrl` above) lets a self-hoster's
- * own domain replace `GITTENSORY_SITE_URL` in the "Checked by LoopOver" attribution link (#4613) --
+ * own domain replace `LOOPOVER_SITE_URL` in the "Checked by LoopOver" attribution link (#4613) --
* the Gittensor register link (`GITTENSOR_HOME_URL`) is a separate, shared network and is never rebranded. */
export function gittensoryFooter(env: LoopOverFooterEnv, opts: { earnUrl?: string | undefined; customText?: string | undefined } = {}): string {
- const siteUrl = env.PUBLIC_SITE_ORIGIN ?? GITTENSORY_SITE_URL;
+ const siteUrl = env.PUBLIC_SITE_ORIGIN ?? LOOPOVER_SITE_URL;
const earnUrl = opts.earnUrl ?? GITTENSOR_HOME_URL;
// Maintainer-customized footer (via `.gittensory.yml review.footer.text`): the maintainer's public-safe
// lead replaces the default CTA copy, but the Gittensor register link + LoopOver attribution are
diff --git a/src/github/repo-doc-pr.ts b/src/github/repo-doc-pr.ts
index de9e1de48d..b236209949 100644
--- a/src/github/repo-doc-pr.ts
+++ b/src/github/repo-doc-pr.ts
@@ -27,7 +27,7 @@
// the skill from this run rather than blocking the AGENTS.md refresh it rode in with.
import { githubErrorStatus, withInstallationTokenRetry } from "./app";
import { githubRateLimitAdmissionKeyForInstallation, makeInstallationOctokit } from "./client";
-import { GITTENSORY_SITE_URL } from "./footer";
+import { LOOPOVER_SITE_URL } from "./footer";
import { getRepository } from "../db/repositories";
import { loadRepoFocusManifest } from "../signals/focus-manifest-loader";
import { extractRepoProfile } from "../review/repo-profile";
@@ -152,7 +152,7 @@ export async function openRepoDocPullRequest(env: Env, repoFullName: string, mod
// #4613: a self-hoster's own domain (env.PUBLIC_SITE_ORIGIN) reaches the generated AGENTS.md's
// attribution link instead of loopover.ai -- same fallback `maintainerControlPanelUrl`/
// `gittensoryFooter` already use.
- const generatedSection = renderRepoDocContent(profile, env.PUBLIC_SITE_ORIGIN ?? GITTENSORY_SITE_URL);
+ const generatedSection = renderRepoDocContent(profile, env.PUBLIC_SITE_ORIGIN ?? LOOPOVER_SITE_URL);
if (!generatedSection) return { opened: false, reason: "no content rendered from profile" };
if (mode !== "live") return { opened: false, reason: `repo-doc pull request not opened: action mode is "${mode}"` };
diff --git a/src/index.ts b/src/index.ts
index b0a0213985..3058957802 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -198,7 +198,7 @@ async function enqueueScheduledJobs(env: Env, controller: ScheduledController):
}
}
}
- // Self-heal (flag GITTENSORY_PR_RECONCILIATION). Every 10 minutes — see isReconciliationWindow above.
+ // Self-heal (flag LOOPOVER_PR_RECONCILIATION). Every 10 minutes — see isReconciliationWindow above.
// Enqueued ONLY when the flag is ON — flag-OFF (default) this job is never created, so the cron tick does
// ZERO new work and the enqueued set is byte-identical to today.
if (selfHostedReviews && isReconciliationWindow && isPrReconciliationEnabled(env)) jobs.push({ type: "reconcile-open-prs", requestedBy: "schedule" });
@@ -228,7 +228,7 @@ async function enqueueScheduledJobs(env: Env, controller: ScheduledController):
// review-outcome data. Enqueued ONLY when the flag is ON — flag-OFF (default) this job is never created,
// so the cron tick does ZERO new work and the enqueued set is byte-identical to today.
if (selfHostedReviews && isOpsEnabled(env)) jobs.push({ type: "ops-alerts", requestedBy: "schedule" });
- // Self-heal (flag GITTENSORY_SWEEP_WATCHDOG). Hourly liveness check over the same repo set the scheduled
+ // Self-heal (flag LOOPOVER_SWEEP_WATCHDOG). Hourly liveness check over the same repo set the scheduled
// regate sweep covers — re-enqueues a targeted sweep for any repo whose sweep marker has gone stale despite
// having open PRs to regate. Enqueued ONLY when the flag is ON — flag-OFF (default) this job is never
// created, so the cron tick does ZERO new work and the enqueued set is byte-identical to today.
@@ -254,9 +254,9 @@ async function enqueueScheduledJobs(env: Env, controller: ScheduledController):
if (isHourly && hour === 9 && selfHostedReviews) {
jobs.push({ type: "repo-doc-refresh-sweep", requestedBy: "schedule" });
}
- // Maintainer recap digest (#1963, #2248/#2250; flag GITTENSORY_MAINTAINER_RECAP). Cross-repo RecapReport
- // delivered to Discord on a configurable cadence (GITTENSORY_RECAP_CADENCE=daily|weekly, default weekly) at
- // the configured hour/day-of-week (GITTENSORY_RECAP_HOUR / GITTENSORY_RECAP_DAY). Enable/cadence can ALSO be
+ // Maintainer recap digest (#1963, #2248/#2250; flag LOOPOVER_MAINTAINER_RECAP). Cross-repo RecapReport
+ // delivered to Discord on a configurable cadence (LOOPOVER_RECAP_CADENCE=daily|weekly, default weekly) at
+ // the configured hour/day-of-week (LOOPOVER_RECAP_HOUR / LOOPOVER_RECAP_DAY). Enable/cadence can ALSO be
// set as code via the gittensory self-repo's `.gittensory.yml maintainerRecap:` block (config-as-code parity,
// #2250) -- a present manifest block wins over the env vars; absent, the env vars decide exactly as before.
// Enqueued ONLY when this tick matches the resolved cadence -- disabled (the default) this job is never
diff --git a/src/openapi/spec.ts b/src/openapi/spec.ts
index 9acfc6b10d..48ea37f095 100644
--- a/src/openapi/spec.ts
+++ b/src/openapi/spec.ts
@@ -176,7 +176,7 @@ export function buildOpenApiSpec() {
path: "/v1/public/stats",
responses: {
200: { description: "Public-safe homepage stats: lifetime PRs handled/merged/closed, gate + slop blocks, and reversal-grounded accuracy. Aggregate counts only.", content: { "application/json": { schema: PublicStatsSchema } } },
- 404: { description: "Public stats are disabled (GITTENSORY_PUBLIC_STATS off)" },
+ 404: { description: "Public stats are disabled (LOOPOVER_PUBLIC_STATS off)" },
503: { description: "Public stats are temporarily unavailable" },
},
});
diff --git a/src/orb/oauth.ts b/src/orb/oauth.ts
index ddb35cac44..47b34c9f7e 100644
--- a/src/orb/oauth.ts
+++ b/src/orb/oauth.ts
@@ -14,7 +14,7 @@
// surface).
import type { Context } from "hono";
import { PRODUCT_USER_AGENT, timeoutFetch } from "../github/client";
-import { GITTENSORY_SITE_URL } from "../github/footer";
+import { LOOPOVER_SITE_URL } from "../github/footer";
import { isOrbBrokerEnabled, issueOrbEnrollment } from "./broker";
type GitHubUser = { login: string; id?: number };
@@ -120,7 +120,7 @@ function shell(heading: string, inner: string): string {
// file-family over (github/footer.ts): env.PUBLIC_SITE_ORIGIN when set, else the public gittensory dashboard
// (#4615).
function landingPage(env: Env, heading: string, message: string): string {
- const dashboardOrigin = (env.PUBLIC_SITE_ORIGIN ?? GITTENSORY_SITE_URL).replace(/\/$/, "");
+ const dashboardOrigin = (env.PUBLIC_SITE_ORIGIN ?? LOOPOVER_SITE_URL).replace(/\/$/, "");
return shell(heading, `${message}
Open the dashboard`);
}
diff --git a/src/queue/duplicate-detection.ts b/src/queue/duplicate-detection.ts
index 49e8647328..bcbb8b2012 100644
--- a/src/queue/duplicate-detection.ts
+++ b/src/queue/duplicate-detection.ts
@@ -73,7 +73,7 @@ export async function reconcileLiveDuplicateSiblings(
pr: PullRequestRecord,
otherOpenPullRequests: PullRequestRecord[],
): Promise {
- if (env.GITTENSORY_DUPLICATE_WINNER !== "true") return otherOpenPullRequests;
+ if (env.LOOPOVER_DUPLICATE_WINNER !== "true") return otherOpenPullRequests;
const linkedIssues = new Set(pr.linkedIssues);
if (linkedIssues.size === 0) return otherOpenPullRequests;
const overlapping = otherOpenPullRequests.filter(
diff --git a/src/queue/job-dispatch.ts b/src/queue/job-dispatch.ts
index 5ce7ba6d81..255c34f114 100644
--- a/src/queue/job-dispatch.ts
+++ b/src/queue/job-dispatch.ts
@@ -200,7 +200,7 @@ export async function processJob(env: Env, message: JobMessage): Promise {
await runReviewRecapJob(env, message.repoFullName, message.windowDays);
return;
case "generate-maintainer-recap": {
- // Convergence (maintainer recap digest, flag GITTENSORY_MAINTAINER_RECAP, #1963/#2248, config-as-code
+ // Convergence (maintainer recap digest, flag LOOPOVER_MAINTAINER_RECAP, #1963/#2248, config-as-code
// override #2250). Defense-in-depth: the cron only ENQUEUES this when enabled, but a stale in-flight job
// that lands after a flag-flip (env OR manifest) must still no-op, so disabled does zero work here too.
const maintainerRecapOverride = await resolveMaintainerRecapManifestOverride(env);
@@ -275,13 +275,13 @@ export async function processJob(env: Env, message: JobMessage): Promise {
if (isOpsEnabled(env)) await runOpsAlerts(env);
return;
case "sweep-liveness-watchdog":
- // Self-heal (flag GITTENSORY_SWEEP_WATCHDOG). Defense-in-depth: the cron only ENQUEUES this when the flag
+ // Self-heal (flag LOOPOVER_SWEEP_WATCHDOG). Defense-in-depth: the cron only ENQUEUES this when the flag
// is ON, but a stale in-flight job that lands after a flag-flip must still no-op, so flag-OFF does zero
// work here too. Fails safe internally — never throws into the queue.
if (isSweepWatchdogEnabled(env)) await runSweepLivenessWatchdog(env);
return;
case "reconcile-open-prs":
- // Self-heal (flag GITTENSORY_PR_RECONCILIATION). Defense-in-depth: the cron only ENQUEUES this when the
+ // Self-heal (flag LOOPOVER_PR_RECONCILIATION). Defense-in-depth: the cron only ENQUEUES this when the
// flag is ON, but a stale in-flight job that lands after a flag-flip must still no-op, so flag-OFF does
// zero work here too. Fails safe internally — never throws into the queue.
if (isPrReconciliationEnabled(env)) await runOpenPrReconciliation(env);
diff --git a/src/queue/processors.ts b/src/queue/processors.ts
index 10e3d841e9..a5b8eb6c7e 100644
--- a/src/queue/processors.ts
+++ b/src/queue/processors.ts
@@ -1456,7 +1456,7 @@ export async function sweepRepoRegate(
const verdicts: Record = {};
const flaggedPulls: number[] = [];
const sweepInstallationId = repo?.installationId ?? null;
- const duplicateWinnerEnabled = env.GITTENSORY_DUPLICATE_WINNER === "true";
+ const duplicateWinnerEnabled = env.LOOPOVER_DUPLICATE_WINNER === "true";
// #selfhost-queue-liveness: priorityPullNumbers (surfaceRepairPriorityPullNumbers, above) are OUTAGE REPAIR --
// a PR with no current-head Gate check or an unpublished current-head surface -- not routine staleness. A
// repair candidate's fanned-out job must NOT carry the "regate-sweep:" deliveryId prefix, or
@@ -2789,7 +2789,7 @@ async function runAgentMaintenancePlanAndExecute(
const approvalsSatisfied =
autoMaintain.requireApprovals === 0 ||
(liveReviewDecision ?? pr.reviewDecision) === "APPROVED";
- const duplicateWinnerEnabled = env.GITTENSORY_DUPLICATE_WINNER === "true";
+ const duplicateWinnerEnabled = env.LOOPOVER_DUPLICATE_WINNER === "true";
const openDuplicateSiblings = linkedIssueDuplicatePullRequestRecordsForGate(pr, otherOpenPullRequests);
// AI-review low-confidence guardrail (#4603): resolved PURELY from this pass's own gate evaluation + settings
// (no extra network/DB call, unlike migrationCollisionHold/unlinkedIssueMatchHold above) -- undefined unless the
@@ -3218,7 +3218,7 @@ export async function reReviewStoredPullRequest(
const advisory = buildPullRequestAdvisory(repo, pr, {
otherOpenPullRequests,
requireLinkedIssue: shouldCollectLinkedIssueEvidence(settings),
- duplicateWinnerEnabled: env.GITTENSORY_DUPLICATE_WINNER === "true",
+ duplicateWinnerEnabled: env.LOOPOVER_DUPLICATE_WINNER === "true",
confirmedNoOpenLinkedIssue,
linkedIssueAuthorLogins,
});
@@ -5594,7 +5594,7 @@ async function handlePullRequestWebhookEvent(
const advisory = buildPullRequestAdvisory(repo, pr, {
otherOpenPullRequests,
requireLinkedIssue: shouldCollectLinkedIssueEvidence(settings),
- duplicateWinnerEnabled: env.GITTENSORY_DUPLICATE_WINNER === "true",
+ duplicateWinnerEnabled: env.LOOPOVER_DUPLICATE_WINNER === "true",
confirmedNoOpenLinkedIssue,
linkedIssueAuthorLogins,
});
@@ -8208,7 +8208,7 @@ async function maybePublishPrPublicSurface(
// Scoped to collision/preflight/queue-health inputs only — every OTHER use of repoPullRequests below (e.g. the
// duplicate-winner adjudication, which is same-linked-issue-based, not path-based) keeps reading the un-enriched array.
const collisionPullRequests =
- env.GITTENSORY_OPEN_PR_FILE_COLLISION === "true"
+ env.LOOPOVER_OPEN_PR_FILE_COLLISION === "true"
? await enrichOpenPullRequestsWithChangedFiles(env, repoFullName, repoPullRequests)
: repoPullRequests;
collisions = buildCollisionReport(
@@ -8251,7 +8251,7 @@ async function maybePublishPrPublicSurface(
pr,
otherOpenPullRequests,
);
- const duplicateWinnerEnabled = env.GITTENSORY_DUPLICATE_WINNER === "true";
+ const duplicateWinnerEnabled = env.LOOPOVER_DUPLICATE_WINNER === "true";
const isDupWinner =
duplicateWinnerEnabled &&
isDuplicateClusterWinnerByClaim(pr, linkedDuplicatePrsForGate);
@@ -9740,7 +9740,7 @@ async function maybePublishPrPublicSurface(
// Duplicate-winner adjudication (#dup-winner): thread the flag into the public panel builders so the
// winner's hard-duplicate block is suppressed (they recompute the winner from their own open-only sibling
// list). Flag-OFF (default) ⇒ false ⇒ the panels are byte-identical to today.
- const duplicateWinnerEnabled = env.GITTENSORY_DUPLICATE_WINNER === "true";
+ const duplicateWinnerEnabled = env.LOOPOVER_DUPLICATE_WINNER === "true";
// improvementSignal deterministic tier (#4742/#4744): pure/sync, no AI dependency, so it is computed
// independent of aiReview's own eligibility gates above (a paused repo, non-reviewable author, or
// aiReviewMode: "off" still gets this tier -- the two tiers are deliberately independent, epic #4737).
@@ -11875,7 +11875,7 @@ export async function buildAuthorizedPrActionAdvisory(
const advisory = buildPullRequestAdvisory(repo, pr, {
otherOpenPullRequests,
requireLinkedIssue: shouldCollectLinkedIssueEvidence(settings),
- duplicateWinnerEnabled: env.GITTENSORY_DUPLICATE_WINNER === "true",
+ duplicateWinnerEnabled: env.LOOPOVER_DUPLICATE_WINNER === "true",
confirmedNoOpenLinkedIssue,
linkedIssueAuthorLogins,
});
diff --git a/src/review/feature-activation.ts b/src/review/feature-activation.ts
index a0de83b37e..eeb77336bf 100644
--- a/src/review/feature-activation.ts
+++ b/src/review/feature-activation.ts
@@ -9,7 +9,7 @@
// inlineComments, fixHandoff, …) each re-implementing their OWN hand-rolled version of the same boolean
// arithmetic outside this file, with at least four subtly different precedence shapes and no single place
// documenting which feature uses which (#4616's config-sprawl audit finding — already the root cause of one
-// production incident, see GITTENSORY_PUBLIC_STATS_REPOS's doc comment in env.d.ts).
+// production incident, see LOOPOVER_PUBLIC_STATS_REPOS's doc comment in env.d.ts).
//
// `resolveFeatureActivation` below is now the ONE pure core every one of those precedence shapes reduces to.
// `resolveConvergedFeature` and `resolveManifestOnlyFeature` are the two thin adapters over it in actual use:
diff --git a/src/review/gittensor-wire.ts b/src/review/gittensor-wire.ts
index ea9b2febeb..29d65a37f4 100644
--- a/src/review/gittensor-wire.ts
+++ b/src/review/gittensor-wire.ts
@@ -19,15 +19,15 @@ import { resolveManifestOnlyFeature } from "./feature-activation";
* repo is ever gittensor-opted-in regardless of what any `.gittensory.yml` says, and
* {@link gittensorEnabledRepoFullNames} short-circuits before reading a single manifest. Truthy follows the
* codebase convention (`/^(1|true|yes|on)$/i`, same as isImpactMapEnabled / isSelfTuneEnabled). */
-export function isGittensorPluginEnabled(env: { GITTENSORY_EXPERIMENTAL_GITTENSOR?: string | undefined }): boolean {
- return /^(1|true|yes|on)$/i.test(env.GITTENSORY_EXPERIMENTAL_GITTENSOR ?? "");
+export function isGittensorPluginEnabled(env: { LOOPOVER_EXPERIMENTAL_GITTENSOR?: string | undefined }): boolean {
+ return /^(1|true|yes|on)$/i.test(env.LOOPOVER_EXPERIMENTAL_GITTENSOR ?? "");
}
/** Resolve whether the gittensor plugin is active for THIS repo: the operator's global env kill-switch AND an
* explicit per-repo manifest opt-in. Neither alone is sufficient -- mirrors every other manifestOnly feature
* gate in this codebase (env kill-switch first, then the manifest narrows it further). */
export function shouldEnableGittensorForRepo(
- env: { GITTENSORY_EXPERIMENTAL_GITTENSOR?: string | undefined },
+ env: { LOOPOVER_EXPERIMENTAL_GITTENSOR?: string | undefined },
manifestGittensorEnabled: boolean | null | undefined,
): boolean {
return resolveManifestOnlyFeature(isGittensorPluginEnabled(env), manifestGittensorEnabled);
@@ -45,7 +45,7 @@ export function shouldEnableGittensorForRepo(
* Flag-OFF (default) short-circuits before listing repos or loading a single manifest, so a plain self-host
* instance makes zero local reads for this and zero outbound gittensor-registry requests (see index.ts).
*/
-export async function gittensorEnabledRepoFullNames(env: Env & { GITTENSORY_EXPERIMENTAL_GITTENSOR?: string | undefined }): Promise> {
+export async function gittensorEnabledRepoFullNames(env: Env & { LOOPOVER_EXPERIMENTAL_GITTENSOR?: string | undefined }): Promise> {
if (!isGittensorPluginEnabled(env)) return new Set();
const repos = await listRepositories(env);
const enabled = new Set();
diff --git a/src/review/maintainer-recap-wire.ts b/src/review/maintainer-recap-wire.ts
index 934c462152..5bcce5b1dd 100644
--- a/src/review/maintainer-recap-wire.ts
+++ b/src/review/maintainer-recap-wire.ts
@@ -1,4 +1,4 @@
-// Maintainer recap digest scheduling (#1963, #2248; flag GITTENSORY_MAINTAINER_RECAP). The cron-driven trigger
+// Maintainer recap digest scheduling (#1963, #2248; flag LOOPOVER_MAINTAINER_RECAP). The cron-driven trigger
// for the CROSS-repo RecapReport digest (buildMaintainerRecap, #2239) -- distinct from generate-review-recap's
// single-repo ReviewRecap job, which is manually-triggerable only (review-recap.ts). Flag-gated and OFF by
// default, mirroring isOpsEnabled: flag-OFF, the cron enqueues no job and this module's exports are never
@@ -21,14 +21,14 @@ export type MaintainerRecapManifestOverride = { present: boolean; enabled: boole
/** True when the cross-repo maintainer recap digest is enabled. Config-as-code (#2250): a present
* `maintainerRecap` manifest block on the gittensory self-repo wins outright; otherwise falls back to the
- * GITTENSORY_MAINTAINER_RECAP env flag (default OFF -- the cron enqueues no job and runMaintainerRecapJob is
+ * LOOPOVER_MAINTAINER_RECAP env flag (default OFF -- the cron enqueues no job and runMaintainerRecapJob is
* never invoked). Truthy env convention matches isOpsEnabled. */
export function isRecapEnabled(
- env: { GITTENSORY_MAINTAINER_RECAP?: string | undefined },
+ env: { LOOPOVER_MAINTAINER_RECAP?: string | undefined },
manifestOverride?: MaintainerRecapManifestOverride | undefined,
): boolean {
if (manifestOverride?.present) return manifestOverride.enabled;
- return /^(1|true|yes|on)$/i.test(env.GITTENSORY_MAINTAINER_RECAP ?? "");
+ return /^(1|true|yes|on)$/i.test(env.LOOPOVER_MAINTAINER_RECAP ?? "");
}
export type RecapCadence = "daily" | "weekly";
@@ -64,10 +64,10 @@ function normalizeRecapDayOfWeek(value: string | undefined): number {
* Shared by shouldFireMaintainerRecap (gating) and runMaintainerRecapJob (audit-event metadata only, #2251)
* so there is exactly one place that resolves "what cadence is configured right now". */
function resolveRecapCadence(
- env: { GITTENSORY_RECAP_CADENCE?: string | undefined },
+ env: { LOOPOVER_RECAP_CADENCE?: string | undefined },
manifestOverride?: MaintainerRecapManifestOverride | undefined,
): RecapCadence {
- return manifestOverride?.present ? manifestOverride.cadence : normalizeRecapCadence(env.GITTENSORY_RECAP_CADENCE);
+ return manifestOverride?.present ? manifestOverride.cadence : normalizeRecapCadence(env.LOOPOVER_RECAP_CADENCE);
}
/**
@@ -76,23 +76,23 @@ function resolveRecapCadence(
* once per period. Caller passes the SAME `hour` / `dayOfWeek` enqueueScheduledJobs already derived from
* `scheduledAt` (src/index.ts) -- no new Date parsing here. The hour/day-of-week knobs are env-only (not
* manifest-overridable); ONLY the cadence itself (daily vs weekly) honors a present manifest override (#2250),
- * mirroring isRecapEnabled. An invalid GITTENSORY_RECAP_CADENCE value falls back to the "weekly" default
+ * mirroring isRecapEnabled. An invalid LOOPOVER_RECAP_CADENCE value falls back to the "weekly" default
* rather than silently firing daily, so a typo'd env var can't quietly spam the digest more often than
* intended.
*/
export function shouldFireMaintainerRecap(
env: {
- GITTENSORY_RECAP_CADENCE?: string | undefined;
- GITTENSORY_RECAP_HOUR?: string | undefined;
- GITTENSORY_RECAP_DAY?: string | undefined;
+ LOOPOVER_RECAP_CADENCE?: string | undefined;
+ LOOPOVER_RECAP_HOUR?: string | undefined;
+ LOOPOVER_RECAP_DAY?: string | undefined;
},
hour: number,
dayOfWeek: number,
manifestOverride?: MaintainerRecapManifestOverride | undefined,
): boolean {
- if (hour !== normalizeRecapHour(env.GITTENSORY_RECAP_HOUR)) return false;
+ if (hour !== normalizeRecapHour(env.LOOPOVER_RECAP_HOUR)) return false;
const cadence = resolveRecapCadence(env, manifestOverride);
- return cadence === "daily" || dayOfWeek === normalizeRecapDayOfWeek(env.GITTENSORY_RECAP_DAY);
+ return cadence === "daily" || dayOfWeek === normalizeRecapDayOfWeek(env.LOOPOVER_RECAP_DAY);
}
/** The repos this recap scans. Mirrors ops-wire.ts's opsScanRepos / pr-reconciliation.ts's watchedRepos: prefer
diff --git a/src/review/outcomes-wire.ts b/src/review/outcomes-wire.ts
index 2549c6f625..83f38efb73 100644
--- a/src/review/outcomes-wire.ts
+++ b/src/review/outcomes-wire.ts
@@ -39,7 +39,7 @@ import {
maybeAutoClearHoldOnly,
} from "./auto-tune";
import { computeGateEval } from "./parity";
-import { GITTENSORY_NATIVE_SOURCE } from "./parity-wire";
+import { LOOPOVER_NATIVE_SOURCE } from "./parity-wire";
/** PURE: parse the PR number an "Reverts #N / Reverts owner/repo#N" body refers to (GitHub's revert PRs).
* Mirrors reviewbot runtime.ts parseRevertedPrNumber. Returns undefined when the body isn't a revert. */
@@ -617,7 +617,7 @@ export async function runSelfTuneBreaker(env: Env): Promise {
const report: GateEvalReport = await computeGateEval(env, {
days: BREAKER_EVAL_WINDOW_DAYS,
nowMs,
- source: GITTENSORY_NATIVE_SOURCE,
+ source: LOOPOVER_NATIVE_SOURCE,
});
// #2352: a SEPARATE, miner-scoped pass so a miner fleet's own self-review accuracy trips the SAME breaker
// independently of the maintainer's overall (mixed) accuracy. Re-keying every row's `project` with the
@@ -626,7 +626,7 @@ export async function runSelfTuneBreaker(env: Env): Promise {
const minerReportRaw = await computeGateEval(env, {
days: BREAKER_EVAL_WINDOW_DAYS,
nowMs,
- source: GITTENSORY_NATIVE_SOURCE,
+ source: LOOPOVER_NATIVE_SOURCE,
minerOnly: true,
});
const minerReport: GateEvalReport = {
diff --git a/src/review/parity-wire.ts b/src/review/parity-wire.ts
index 1666f9559a..bc5d42a9ab 100644
--- a/src/review/parity-wire.ts
+++ b/src/review/parity-wire.ts
@@ -69,7 +69,7 @@ export function isParityAuditEnabled(env: {
/** The `source` discriminator this writer stamps on every row — the SHADOW side computeGateParity compares
* against the authoritative 'reviewbot' rows. */
-export const GITTENSORY_NATIVE_SOURCE = "gittensory-native";
+export const LOOPOVER_NATIVE_SOURCE = "gittensory-native";
/** Cutover-readiness defaults the endpoint applies before computing parity: a 90-day window (the parity
* read's own default) over all recorded sources. The agreement FLOOR (0.98) + MIN sample (30) live in
@@ -172,7 +172,7 @@ export async function recordNativeGateDecision(
VALUES (?, ?, ?, 'gate_decision', ?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET decision = excluded.decision, summary = excluded.summary, miner_authored = excluded.miner_authored, created_at = excluded.created_at`,
)
- .bind(`gate:${GITTENSORY_NATIVE_SOURCE}:${targetId}@${input.headSha}`, project, targetId, action, GITTENSORY_NATIVE_SOURCE, input.headSha, summary, minerAuthored, nowIso())
+ .bind(`gate:${LOOPOVER_NATIVE_SOURCE}:${targetId}@${input.headSha}`, project, targetId, action, LOOPOVER_NATIVE_SOURCE, input.headSha, summary, minerAuthored, nowIso())
.run();
} catch (error) {
// Telemetry must never break finalization.
@@ -217,7 +217,7 @@ export async function computeParityReadiness(
// The shadow source MUST match what recordNativeGateDecision stamps ('gittensory-native'); computeGateParity
// defaults `shadow` to 'gittensory', so pass it explicitly or the self-join would find no shadow rows. The
// authoritative side stays the default 'reviewbot' (the deploy-time dual-run writer).
- shadow: GITTENSORY_NATIVE_SOURCE,
+ shadow: LOOPOVER_NATIVE_SOURCE,
...(opts.project ? { project: opts.project } : {}),
});
return {
diff --git a/src/review/pr-reconciliation.ts b/src/review/pr-reconciliation.ts
index 4e20f4d5a1..dba36c3353 100644
--- a/src/review/pr-reconciliation.ts
+++ b/src/review/pr-reconciliation.ts
@@ -1,4 +1,4 @@
-// Self-heal (flag-gated by GITTENSORY_PR_RECONCILIATION). A "PR opened" webhook that silently vanishes (no
+// Self-heal (flag-gated by LOOPOVER_PR_RECONCILIATION). A "PR opened" webhook that silently vanishes (no
// error, no audit event, no trace anywhere — see reconcileOpenPullRequests's doc comment for the 2026-07-06
// incident that motivated this) previously wasn't caught until backfillRegisteredRepositories's opportunistic
// resync, which skips any repo whose sync is "fresh" (up to 6 hours). This module runs a much tighter, dedicated
@@ -22,8 +22,8 @@ import { isConvergenceRepoAllowed, listConvergenceRepos } from "./cutover-gate";
/** True when fast open-PR reconciliation is enabled. Flag-OFF (default) → the caller never invokes it, so the
* cron enqueues no reconciliation job and the queue processor no-ops on a stale in-flight one. */
-export function isPrReconciliationEnabled(env: { GITTENSORY_PR_RECONCILIATION?: string | undefined }): boolean {
- return /^(1|true|yes|on)$/i.test(env.GITTENSORY_PR_RECONCILIATION ?? "");
+export function isPrReconciliationEnabled(env: { LOOPOVER_PR_RECONCILIATION?: string | undefined }): boolean {
+ return /^(1|true|yes|on)$/i.test(env.LOOPOVER_PR_RECONCILIATION ?? "");
}
/** The same acting-autonomy repo set fanOutAgentRegateSweepJobs sweeps (mirrors sweep-watchdog.ts's own copy of
diff --git a/src/review/public-stats.ts b/src/review/public-stats.ts
index 4c8c5803b1..669a77b84d 100644
--- a/src/review/public-stats.ts
+++ b/src/review/public-stats.ts
@@ -1,5 +1,5 @@
// Public "proof of power" stats (#1059) — a small, public-safe aggregate of what gittensory's REVIEW SYSTEM has
-// done, powering the above-the-fold homepage counter. Flag-gated by GITTENSORY_PUBLIC_STATS (default OFF): when
+// done, powering the above-the-fold homepage counter. Flag-gated by LOOPOVER_PUBLIC_STATS (default OFF): when
// off the public endpoint 404s, so the deploy is byte-identical to today until the flag is deliberately set.
//
// REALTIME: queries the live ledger directly (no rollup/cron) so a new review shows up within the 60s HTTP cache
@@ -45,9 +45,9 @@ export const MINUTES_SAVED_PER_PR = 20;
/** Truthy-string flag check, matching ops-wire / selftune-wire. */
export function isPublicStatsEnabled(env: {
- GITTENSORY_PUBLIC_STATS?: string | undefined;
+ LOOPOVER_PUBLIC_STATS?: string | undefined;
}): boolean {
- return /^(1|true|yes|on)$/i.test(env.GITTENSORY_PUBLIC_STATS ?? "");
+ return /^(1|true|yes|on)$/i.test(env.LOOPOVER_PUBLIC_STATS ?? "");
}
/** Storage seam: gittensory's `Env` is a global ambient interface with `DB` (mirrors src/review/stats.ts). */
@@ -103,18 +103,18 @@ function accuracyPct(
}
/** The own-ledger side of public stats is intentionally constrained to an explicit allowlist (privacy: publish
- * only what's deliberately opted in). Deliberately reads GITTENSORY_PUBLIC_STATS_REPOS, NOT
+ * only what's deliberately opted in). Deliberately reads LOOPOVER_PUBLIC_STATS_REPOS, NOT
* LOOPOVER_REVIEW_REPOS (the live per-PR-feature cutover allowlist) -- the two once held the same value, but
* diverged once gittensory/awesome-claude/metagraphed moved their LIVE processing to self-host: the cutover
* allowlist correctly went empty, while the historical rows this worker already wrote for them remain real and
* safe to publish. Empty allowlist => the own-ledger side reports zero (still fails safe), but does NOT
* suppress the separately-gated Orb cross-fleet aggregate (see getPublicStats below). */
export function publicStatsProjects(env: {
- GITTENSORY_PUBLIC_STATS_REPOS?: string | undefined;
+ LOOPOVER_PUBLIC_STATS_REPOS?: string | undefined;
}): string[] {
const seen = new Set();
const projects: string[] = [];
- for (const entry of (env.GITTENSORY_PUBLIC_STATS_REPOS ?? "").split(",")) {
+ for (const entry of (env.LOOPOVER_PUBLIC_STATS_REPOS ?? "").split(",")) {
const project = entry.trim().toLowerCase();
if (!project || seen.has(project)) continue;
seen.add(project);
diff --git a/src/review/repo-doc-render.ts b/src/review/repo-doc-render.ts
index 5b1e6eef34..6e4533d756 100644
--- a/src/review/repo-doc-render.ts
+++ b/src/review/repo-doc-render.ts
@@ -65,7 +65,7 @@ function renderCiWorkflowFiles(ciWorkflowFiles: string[]): string {
* adds before the start marker or after the end marker in the delivered file is never part of this output and
* is therefore never touched.
*
- * `siteUrl` is resolved by the caller from `env.PUBLIC_SITE_ORIGIN ?? GITTENSORY_SITE_URL` (#4613), so a
+ * `siteUrl` is resolved by the caller from `env.PUBLIC_SITE_ORIGIN ?? LOOPOVER_SITE_URL` (#4613), so a
* self-hoster's own domain reaches the attribution link instead of `loopover.ai`. Threading it as a
* plain string argument (rather than an `env` slice) keeps this function's purity/determinism contract intact --
* the SAME `(profile, siteUrl)` pair always renders the SAME output, which is all the diff-aware refresh in
diff --git a/src/review/sweep-watchdog.ts b/src/review/sweep-watchdog.ts
index c5a9553aeb..7b451f9a46 100644
--- a/src/review/sweep-watchdog.ts
+++ b/src/review/sweep-watchdog.ts
@@ -1,4 +1,4 @@
-// Self-heal (flag-gated by GITTENSORY_SWEEP_WATCHDOG). The scheduled regate sweep (fanOutAgentRegateSweepJobs /
+// Self-heal (flag-gated by LOOPOVER_SWEEP_WATCHDOG). The scheduled regate sweep (fanOutAgentRegateSweepJobs /
// sweepRepoRegate, src/queue/processors.ts) advances every acting-autonomy repo's `last_regated_at` marker on
// every successful sweep tick. When the sweep stops advancing that marker for a repo — a stalled cron, a wedged
// per-repo failure that keeps recurring, a rate-limit floor that never clears — nothing in the system previously
@@ -21,8 +21,8 @@ import { isConvergenceRepoAllowed, listConvergenceRepos } from "./cutover-gate";
/** True when the sweep-liveness watchdog is enabled. Flag-OFF (default) → the caller never invokes it, so the
* cron enqueues no watchdog job and the queue processor no-ops on a stale in-flight one (defense-in-depth,
* mirrors isOpsEnabled). */
-export function isSweepWatchdogEnabled(env: { GITTENSORY_SWEEP_WATCHDOG?: string | undefined }): boolean {
- return /^(1|true|yes|on)$/i.test(env.GITTENSORY_SWEEP_WATCHDOG ?? "");
+export function isSweepWatchdogEnabled(env: { LOOPOVER_SWEEP_WATCHDOG?: string | undefined }): boolean {
+ return /^(1|true|yes|on)$/i.test(env.LOOPOVER_SWEEP_WATCHDOG ?? "");
}
/** A repo's sweep is stale when it has open PRs to regate but its last-regated marker either never advanced or
diff --git a/src/rules/advisory.ts b/src/rules/advisory.ts
index b15772db70..0731274b83 100644
--- a/src/rules/advisory.ts
+++ b/src/rules/advisory.ts
@@ -231,7 +231,7 @@ export function buildPullRequestAdvisory(
/** Duplicate-winner adjudication (#dup-winner). When true AND this PR is the cluster winner (the lowest
* open sibling number), the `duplicate_pr_risk` finding is suppressed so the winner is not gate-blocked /
* closed as a duplicate. Default/false ⇒ every duplicate sibling keeps the finding (byte-identical). The
- * caller sets this to `env.GITTENSORY_DUPLICATE_WINNER === "true"`. */
+ * caller sets this to `env.LOOPOVER_DUPLICATE_WINNER === "true"`. */
duplicateWinnerEnabled?: boolean;
/** Author logins of the linked issues (one entry per resolved issue, may be null when unknown). Used to
* surface a `self_authored_linked_issue` finding when the PR author also opened the linked issue. Absent
diff --git a/src/scenarios/input-model.ts b/src/scenarios/input-model.ts
index e1638b7eae..6ea6afa61c 100644
--- a/src/scenarios/input-model.ts
+++ b/src/scenarios/input-model.ts
@@ -187,8 +187,8 @@ export function buildScenarioInput(args: {
}
export function assertScenarioLocalBranchInputSafe(payload: Record): void {
- if (/^(1|true|yes)$/i.test(String(process.env.GITTENSORY_UPLOAD_SOURCE ?? "false"))) {
- throw new Error("GITTENSORY_UPLOAD_SOURCE=true is not supported; scenario inputs remain metadata-only.");
+ if (/^(1|true|yes)$/i.test(String(process.env.LOOPOVER_UPLOAD_SOURCE ?? "false"))) {
+ throw new Error("LOOPOVER_UPLOAD_SOURCE=true is not supported; scenario inputs remain metadata-only.");
}
for (const key of Object.keys(payload)) {
if (FORBIDDEN_SOURCE_UPLOAD_KEYS.test(key)) {
diff --git a/src/selfhost/queue-common.ts b/src/selfhost/queue-common.ts
index 294abbcfd6..d554c2eab3 100644
--- a/src/selfhost/queue-common.ts
+++ b/src/selfhost/queue-common.ts
@@ -102,7 +102,7 @@ const GITHUB_BUDGET_BACKGROUND_TYPES = new Set([
//
// runOpenPrReconciliation makes real, potentially large paginated GitHub REST calls per watched repo (up to
// RECONCILE_OPEN_PRS_MAX_PAGES per repo, plus a catch-up fetch per missing PR found). Flag-gated OFF by
- // default today (GITTENSORY_PR_RECONCILIATION) -- this closes the gap before anyone enables it.
+ // default today (LOOPOVER_PR_RECONCILIATION) -- this closes the gap before anyone enables it.
"reconcile-open-prs",
// fanOutBacklogConvergenceSweepJobs / sweepRepoBacklogConvergence both call resolveRepositorySettings per
// repo. Runs every 30 min, unconditional for self-hosted runtimes -- active in production today.
diff --git a/src/services/client-telemetry.ts b/src/services/client-telemetry.ts
index af73d697e3..a2ec952166 100644
--- a/src/services/client-telemetry.ts
+++ b/src/services/client-telemetry.ts
@@ -1,6 +1,6 @@
import {
classifyMcpClientVersion,
- GITTENSORY_MCP_PACKAGE_NAME,
+ LOOPOVER_MCP_PACKAGE_NAME,
LATEST_RECOMMENDED_MCP_VERSION,
MINIMUM_SUPPORTED_MCP_VERSION,
type McpCompatibilityStatus,
@@ -53,7 +53,7 @@ export function buildMcpClientTelemetry(headers: Headers, options: ClientTelemet
function clientNameFromPackage(packageName: string | undefined): string | undefined {
if (!packageName) return undefined;
- if (packageName === GITTENSORY_MCP_PACKAGE_NAME) return "loopover-mcp";
+ if (packageName === LOOPOVER_MCP_PACKAGE_NAME) return "loopover-mcp";
const tail = packageName.split("/").at(-1);
return safeClientHeader(tail);
}
diff --git a/src/services/contributor-issue-draft.ts b/src/services/contributor-issue-draft.ts
index 3b74a7aea7..bf6a6e4c49 100644
--- a/src/services/contributor-issue-draft.ts
+++ b/src/services/contributor-issue-draft.ts
@@ -553,7 +553,7 @@ async function loadContributorIssueDraftQueueCounts(env: Env, repoFullName: stri
}
async function createGitHubContributorIssue(env: Env, repoFullName: string, draft: ContributorIssueDraft): Promise<{ number: number; url: string } | null> {
- const token = env.GITTENSORY_CONTRIBUTOR_ISSUE_TOKEN ?? env.GITTENSORY_DRIFT_ISSUE_TOKEN ?? env.GITHUB_PUBLIC_TOKEN;
+ const token = env.LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN ?? env.LOOPOVER_DRIFT_ISSUE_TOKEN ?? env.GITHUB_PUBLIC_TOKEN;
if (!token) return null;
const { owner, name } = repoParts(repoFullName);
if (!owner || !name) return null;
diff --git a/src/services/maintainer-activation.ts b/src/services/maintainer-activation.ts
index 76ebdcd93f..da78b94ccd 100644
--- a/src/services/maintainer-activation.ts
+++ b/src/services/maintainer-activation.ts
@@ -52,7 +52,7 @@ export function buildMaintainerActivationPreview(args: {
pullRequests: PullRequestRecord[];
generatedAt: string;
sampleSize?: number;
- /** GITTENSORY_DUPLICATE_WINNER (#dup-winner): mirror the live pipeline so the demo's duplicate findings
+ /** LOOPOVER_DUPLICATE_WINNER (#dup-winner): mirror the live pipeline so the demo's duplicate findings
* match the real gate — when ON, the cluster winner is spared. Defaults to off (byte-identical preview). */
duplicateWinnerEnabled?: boolean;
}): MaintainerActivationPreview {
diff --git a/src/services/mcp-compatibility.ts b/src/services/mcp-compatibility.ts
index e19a167036..f9d722079d 100644
--- a/src/services/mcp-compatibility.ts
+++ b/src/services/mcp-compatibility.ts
@@ -5,8 +5,8 @@
// before the npm package itself is actually tagged/published.
import gittensoryMcpPackageJson from "../../packages/loopover-mcp/package.json";
-export const GITTENSORY_API_VERSION = "0.1.0";
-export const GITTENSORY_MCP_PACKAGE_NAME = "@loopover/mcp";
+export const LOOPOVER_API_VERSION = "0.1.0";
+export const LOOPOVER_MCP_PACKAGE_NAME = "@loopover/mcp";
export const MINIMUM_SUPPORTED_MCP_VERSION = "0.5.0";
export const LATEST_RECOMMENDED_MCP_VERSION: string = gittensoryMcpPackageJson.version;
@@ -45,15 +45,15 @@ export function buildMcpCompatibilityMetadata(generatedAt: string): McpCompatibi
return {
status: "ok",
service: "loopover-api",
- apiVersion: GITTENSORY_API_VERSION,
+ apiVersion: LOOPOVER_API_VERSION,
mcp: {
- packageName: GITTENSORY_MCP_PACKAGE_NAME,
+ packageName: LOOPOVER_MCP_PACKAGE_NAME,
minimumSupportedVersion: MINIMUM_SUPPORTED_MCP_VERSION,
latestRecommendedVersion: LATEST_RECOMMENDED_MCP_VERSION,
latestPackageVersion: LATEST_RECOMMENDED_MCP_VERSION,
supportedVersionRange: `>=${MINIMUM_SUPPORTED_MCP_VERSION}`,
- upgradeCommand: `npm install -g ${GITTENSORY_MCP_PACKAGE_NAME}@latest`,
- npxFallbackCommand: `npx ${GITTENSORY_MCP_PACKAGE_NAME}@latest `,
+ upgradeCommand: `npm install -g ${LOOPOVER_MCP_PACKAGE_NAME}@latest`,
+ npxFallbackCommand: `npx ${LOOPOVER_MCP_PACKAGE_NAME}@latest `,
},
compatibilityWarnings: [],
breakingChanges: [],
diff --git a/src/services/public-reuse-rate-trend.ts b/src/services/public-reuse-rate-trend.ts
index 8f41a1f9c5..d8101751ea 100644
--- a/src/services/public-reuse-rate-trend.ts
+++ b/src/services/public-reuse-rate-trend.ts
@@ -8,7 +8,7 @@
// is already durable, so a live weekly re-bucketing of the SAME rows can recompute any historical week correctly
// on every request -- no cron-miss gap risk, and no second copy of the number to keep in sync.
//
-// PUBLIC-SAFE SCOPE: only events whose target_key maps to GITTENSORY_PUBLIC_STATS_REPOS are included. Most
+// PUBLIC-SAFE SCOPE: only events whose target_key maps to LOOPOVER_PUBLIC_STATS_REPOS are included. Most
// cache keys are either a bare repoFullName or repoFullName#prNumber; anything outside that allowlist is treated
// as private operational telemetry and deliberately excluded from this unauthenticated payload.
//
diff --git a/src/services/subnet-interface.ts b/src/services/subnet-interface.ts
index feea451635..2dd4d5fb72 100644
--- a/src/services/subnet-interface.ts
+++ b/src/services/subnet-interface.ts
@@ -1,5 +1,5 @@
-import { GITTENSOR_HOME_URL, GITTENSORY_SITE_URL } from "../github/footer";
-import { GITTENSORY_MCP_PACKAGE_NAME, LATEST_RECOMMENDED_MCP_VERSION, MINIMUM_SUPPORTED_MCP_VERSION } from "./mcp-compatibility";
+import { GITTENSOR_HOME_URL, LOOPOVER_SITE_URL } from "../github/footer";
+import { LOOPOVER_MCP_PACKAGE_NAME, LATEST_RECOMMENDED_MCP_VERSION, MINIMUM_SUPPORTED_MCP_VERSION } from "./mcp-compatibility";
// Gittensor is Bittensor subnet 74 (the code subnet). LoopOver is its contribution interface.
export const GITTENSOR_NETUID = 74;
@@ -70,7 +70,7 @@ export function buildSubnetInterfaceDescriptor(args: { origin: string; generated
provider: {
name: "LoopOver",
role: "contribution_interface",
- site: GITTENSORY_SITE_URL,
+ site: LOOPOVER_SITE_URL,
summary: "Gittensor-native contribution planning layer: MCP guidance for contributors and a free anti-slop + AI second-opinion gate for maintainers.",
},
interfaces: {
@@ -78,7 +78,7 @@ export function buildSubnetInterfaceDescriptor(args: { origin: string; generated
kind: "mcp",
transport: "http",
endpoint: `${origin}/mcp`,
- package: GITTENSORY_MCP_PACKAGE_NAME,
+ package: LOOPOVER_MCP_PACKAGE_NAME,
minimumVersion: MINIMUM_SUPPORTED_MCP_VERSION,
recommendedVersion: LATEST_RECOMMENDED_MCP_VERSION,
tools: CONTRIBUTION_MCP_TOOLS.map((tool) => ({ ...tool })),
@@ -90,7 +90,7 @@ export function buildSubnetInterfaceDescriptor(args: { origin: string; generated
},
},
onboarding: {
- docs: GITTENSORY_SITE_URL,
+ docs: LOOPOVER_SITE_URL,
steps: [...ONBOARDING_STEPS],
},
};
diff --git a/src/settings/agent-actions.ts b/src/settings/agent-actions.ts
index c67d8a79ad..e8902ff2c3 100644
--- a/src/settings/agent-actions.ts
+++ b/src/settings/agent-actions.ts
@@ -145,7 +145,7 @@ export type PlannedAgentAction = {
closeRequiresDuplicateStillOpen?: boolean;
// The specific sibling PR number this close named as the duplicate-cluster winner (dupWinnerLinkedDuplicateWinnerNumber),
// persisted so the recheck above can re-verify THAT PR specifically instead of re-running the full election.
- // Absent when the election named no specific winner (GITTENSORY_DUPLICATE_WINNER off, or an ambiguous
+ // Absent when the election named no specific winner (LOOPOVER_DUPLICATE_WINNER off, or an ambiguous
// election) even though closeRequiresDuplicateStillOpen is true -- the recheck has no cheap single-PR signal
// in that case and is a no-op, matching the legacy (pre-dup-winner) behavior for that configuration.
duplicateWinnerPrNumber?: number;
diff --git a/src/settings/automation-bot-skip.ts b/src/settings/automation-bot-skip.ts
index 9d3aa6811c..6f5a95646c 100644
--- a/src/settings/automation-bot-skip.ts
+++ b/src/settings/automation-bot-skip.ts
@@ -8,8 +8,8 @@ export type AutomationBotSkipMode = "inherit" | "off" | "enabled";
* on PRs from known, maintainer-owned automation (release-please's github-actions[bot], Renovate,
* Dependabot) is safe and low-risk enough to be the sensible default -- it should not require every
* self-host operator to discover and separately opt into this. */
-export function isSkipAutomationBotPullRequestsEnabledGlobally(env: { GITTENSORY_SKIP_AUTOMATION_BOT_PRS?: string | undefined }): boolean {
- return !/^(0|false|no|off)$/i.test((env.GITTENSORY_SKIP_AUTOMATION_BOT_PRS ?? "").trim());
+export function isSkipAutomationBotPullRequestsEnabledGlobally(env: { LOOPOVER_SKIP_AUTOMATION_BOT_PRS?: string | undefined }): boolean {
+ return !/^(0|false|no|off)$/i.test((env.LOOPOVER_SKIP_AUTOMATION_BOT_PRS ?? "").trim());
}
/** Per-repo override resolved against the global default. Mirrors `ModerationGateMode`'s inherit/off/enabled
diff --git a/src/signals/engine.ts b/src/signals/engine.ts
index c871a115ca..553ddb3d70 100644
--- a/src/signals/engine.ts
+++ b/src/signals/engine.ts
@@ -4365,7 +4365,7 @@ export function buildPublicPrIntelligenceComment(args: {
* text is added, matching this epic's "degrade cleanly, never fabricate a reading" convention. */
slopBand?: SlopBand | undefined;
/** Resolved by the caller from `env.PUBLIC_SITE_ORIGIN` so a self-hoster's own domain reaches the
- * always-on footer's attribution link instead of `GITTENSORY_SITE_URL` (#4613). */
+ * always-on footer's attribution link instead of `LOOPOVER_SITE_URL` (#4613). */
env: LoopOverFooterEnv;
}): string {
const publicFindings = publicSafePreflightFindings(args.preflight, args.settings);
diff --git a/src/signals/focus-manifest-loader.ts b/src/signals/focus-manifest-loader.ts
index 5d876a1f38..1a5ae53ef3 100644
--- a/src/signals/focus-manifest-loader.ts
+++ b/src/signals/focus-manifest-loader.ts
@@ -2,7 +2,7 @@ import { listSignalSnapshots, persistSignalSnapshot } from "../db/repositories";
import type { JsonValue } from "../types";
import { nowIso } from "../utils/json";
import { contentLaneConfigToJson, experimentalConfigToJson, featuresConfigToJson, gateConfigToJson, MAX_FOCUS_MANIFEST_BYTES, parseFocusManifest, parseFocusManifestContent, repoDocGenerationConfigToJson, reviewConfigToJson, reviewRecapConfigToJson, maintainerRecapConfigToJson, settingsOverrideToJson, type FocusManifest, type FocusManifestSource, type RepoReviewContext } from "./focus-manifest";
-import { GITTENSORY_REPO_FOCUS_MANIFEST_YAML, resolveLoopOverSelfRepoFullName } from "../config/gittensory-repo-focus-manifest";
+import { LOOPOVER_REPO_FOCUS_MANIFEST_YAML, resolveLoopOverSelfRepoFullName } from "../config/gittensory-repo-focus-manifest";
import type { LocalManifestLoadResult } from "../selfhost/private-config";
export const REPO_FOCUS_MANIFEST_SIGNAL = "repo-focus-manifest";
@@ -159,7 +159,7 @@ async function loadRepoFocusManifestWithCachePolicy(
let content = await fetcher(repoFullName);
if (content !== null && typeof content === "object") content = content.content;
if ((content === null || content === undefined) && isLoopOverSelfRepo(repoFullName, env)) {
- content = GITTENSORY_REPO_FOCUS_MANIFEST_YAML;
+ content = LOOPOVER_REPO_FOCUS_MANIFEST_YAML;
}
manifest = content === null || content === undefined ? parseFocusManifest(null) : parseFocusManifestContent(content, "repo_file");
} catch {
diff --git a/src/types.ts b/src/types.ts
index 7b09e4251f..d9b0333165 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -165,7 +165,7 @@ export type JobMessage =
// Cross-repo maintainer recap digest (#1963, #2248): folds gate-precision + outcome-calibration across
// every scanned repo into ONE RecapReport (buildMaintainerRecap, #2239) and delivers it to Discord --
// distinct from "generate-review-recap" above, which is single-repo. No `repoFullName`: this is always
- // a global job, enqueued by the cron on a configurable daily/weekly cadence (GITTENSORY_RECAP_CADENCE).
+ // a global job, enqueued by the cron on a configurable daily/weekly cadence (LOOPOVER_RECAP_CADENCE).
type: "generate-maintainer-recap";
requestedBy: "schedule" | "api" | "test";
windowDays?: number;
@@ -205,7 +205,7 @@ export type JobMessage =
requestedBy: "schedule" | "api" | "test";
}
| {
- // Self-heal (flag-gated by GITTENSORY_SWEEP_WATCHDOG). Scan the SAME acting-autonomy repo set the
+ // Self-heal (flag-gated by LOOPOVER_SWEEP_WATCHDOG). Scan the SAME acting-autonomy repo set the
// scheduled regate sweep covers for a stalled per-repo sweep (open PRs present, but none regated within
// the staleness window) — emit a structured `sweep_liveness_stale` log AND re-enqueue a targeted
// `agent-regate-sweep` for just that repo. Enqueued hourly by the cron ONLY when the flag is ON
@@ -214,7 +214,7 @@ export type JobMessage =
requestedBy: "schedule" | "api" | "test";
}
| {
- // Self-heal (flag-gated by GITTENSORY_PR_RECONCILIATION). List-diff GitHub's open PR numbers against the
+ // Self-heal (flag-gated by LOOPOVER_PR_RECONCILIATION). List-diff GitHub's open PR numbers against the
// local table for every acting-autonomy repo — a much tighter cadence than backfillRegisteredRepositories's
// 6-hour freshness window — and catch up (fetch + upsert + regate) any PR number GitHub has that the local
// table doesn't (a silently-lost "opened" webhook). Enqueued on a short interval ONLY when the flag is ON
@@ -1160,7 +1160,7 @@ export type RepositorySettings = {
* Dependabot -- settings/agent-actions.ts's PROTECTED_AUTOCLOSE_AUTHORS): skip AI review, gate evaluation,
* and public-surface publish entirely for a PR/event genuinely triggered by one of these -- not just
* suppress output like {@link "./review-eligibility".ignoreAuthors}. `"inherit"` (the DB default) defers
- * to the `GITTENSORY_SKIP_AUTOMATION_BOT_PRS` global default (itself default-ON, unlike most feature
+ * to the `LOOPOVER_SKIP_AUTOMATION_BOT_PRS` global default (itself default-ON, unlike most feature
* flags -- see settings/automation-bot-skip.ts's own doc comment for why); `"off"`/`"enabled"` fully
* override the global default in either direction for this repo. Always populated by the DB layer;
* optional so existing settings fixtures/callers need not be touched. */
diff --git a/src/upstream/ruleset.ts b/src/upstream/ruleset.ts
index 4e0f421208..36e32de813 100644
--- a/src/upstream/ruleset.ts
+++ b/src/upstream/ruleset.ts
@@ -273,7 +273,7 @@ export function registryHyperparameterDriftWarningsForRepo(reports: UpstreamDrif
}
export async function fileUpstreamDriftIssues(env: Env): Promise> {
- if (!truthy(env.GITTENSORY_AUTO_FILE_DRIFT_ISSUES)) {
+ if (!truthy(env.LOOPOVER_AUTO_FILE_DRIFT_ISSUES)) {
return { status: "disabled", created: 0, updated: 0, skipped: 0 };
}
// Respect the global agent kill-switch: filing drift issues is an autonomous GitHub WRITE, so the env brake or
@@ -283,7 +283,7 @@ export async function fileUpstreamDriftIssues(env: Env): Promise = {}): Env {
GITTENSOR_UPSTREAM_REPO: "entrius/gittensor",
GITTENSOR_UPSTREAM_REF: "test",
GITTENSOR_REGISTRY_URL: "https://raw.githubusercontent.com/entrius/gittensor/test/gittensor/validator/weights/master_repositories.json",
- GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "false",
- GITTENSORY_DRIFT_ISSUE_REPO: "JSONbored/gittensory",
+ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "false",
+ LOOPOVER_DRIFT_ISSUE_REPO: "JSONbored/gittensory",
PUBLIC_API_ORIGIN: "https://api.loopover.ai",
PUBLIC_SITE_ORIGIN: "https://loopover.ai",
INTERNAL_JOB_TOKEN: "dev-internal-token",
@@ -130,11 +130,11 @@ export function createTestEnv(overrides: Partial = {}): Env {
LOOPOVER_REVIEW_REPOS: "JSONbored/gittensory,acme/widgets",
// Default-ON in production (settings/automation-bot-skip.ts); most tests don't involve a bot actor at
// all, so this default doesn't change their behavior. Tests exercising this feature override it directly.
- GITTENSORY_SKIP_AUTOMATION_BOT_PRS: "true",
+ LOOPOVER_SKIP_AUTOMATION_BOT_PRS: "true",
// Default OFF, matching wrangler.jsonc — a new required `vars` entry needs an explicit base value here
// (Partial alone leaves it optional under exactOptionalPropertyTypes, which Env's required field
// rejects). Tests exercising the experimental gittensor plugin override it directly.
- GITTENSORY_EXPERIMENTAL_GITTENSOR: "false",
+ LOOPOVER_EXPERIMENTAL_GITTENSOR: "false",
...overrides,
};
}
diff --git a/test/integration/public-stats-route-error.test.ts b/test/integration/public-stats-route-error.test.ts
index 7b38b0e7b4..c1f47ec717 100644
--- a/test/integration/public-stats-route-error.test.ts
+++ b/test/integration/public-stats-route-error.test.ts
@@ -11,7 +11,7 @@ import { createTestEnv } from "../helpers/d1";
describe("GET /v1/public/stats — error path", () => {
it("returns 503 when stats computation throws", async () => {
- const env = createTestEnv({ GITTENSORY_PUBLIC_STATS: "1" });
+ const env = createTestEnv({ LOOPOVER_PUBLIC_STATS: "1" });
const res = await createApp().request("/v1/public/stats", {}, env);
expect(res.status).toBe(503);
expect((await res.json()) as { error: string }).toEqual({
diff --git a/test/integration/public-stats-route.test.ts b/test/integration/public-stats-route.test.ts
index 254f4b2597..c827eeae7b 100644
--- a/test/integration/public-stats-route.test.ts
+++ b/test/integration/public-stats-route.test.ts
@@ -43,7 +43,7 @@ async function seed(env: Env) {
}
describe("GET /v1/public/stats (#1059)", () => {
- it("404s when GITTENSORY_PUBLIC_STATS is off (default)", async () => {
+ it("404s when LOOPOVER_PUBLIC_STATS is off (default)", async () => {
const env = createTestEnv();
const res = await createApp().request("/v1/public/stats", {}, env);
expect(res.status).toBe(404);
@@ -51,8 +51,8 @@ describe("GET /v1/public/stats (#1059)", () => {
it("serves public-safe aggregates with no auth + a cache header when enabled", async () => {
const env = createTestEnv({
- GITTENSORY_PUBLIC_STATS: "1",
- GITTENSORY_PUBLIC_STATS_REPOS: "JSONbored/gittensory,JSONbored/awesome-claude",
+ LOOPOVER_PUBLIC_STATS: "1",
+ LOOPOVER_PUBLIC_STATS_REPOS: "JSONbored/gittensory,JSONbored/awesome-claude",
});
await seed(env);
const res = await createApp().request("/v1/public/stats", {}, env);
diff --git a/test/unit/automation-bot-skip.test.ts b/test/unit/automation-bot-skip.test.ts
index 1018a4094c..42e95fcb6e 100644
--- a/test/unit/automation-bot-skip.test.ts
+++ b/test/unit/automation-bot-skip.test.ts
@@ -9,24 +9,24 @@ import {
describe("isSkipAutomationBotPullRequestsEnabledGlobally", () => {
it("defaults ON when unset (unlike most LOOPOVER_REVIEW_* flags)", () => {
expect(isSkipAutomationBotPullRequestsEnabledGlobally({})).toBe(true);
- expect(isSkipAutomationBotPullRequestsEnabledGlobally({ GITTENSORY_SKIP_AUTOMATION_BOT_PRS: undefined })).toBe(true);
- expect(isSkipAutomationBotPullRequestsEnabledGlobally({ GITTENSORY_SKIP_AUTOMATION_BOT_PRS: "" })).toBe(true);
+ expect(isSkipAutomationBotPullRequestsEnabledGlobally({ LOOPOVER_SKIP_AUTOMATION_BOT_PRS: undefined })).toBe(true);
+ expect(isSkipAutomationBotPullRequestsEnabledGlobally({ LOOPOVER_SKIP_AUTOMATION_BOT_PRS: "" })).toBe(true);
});
it("stays ON for an explicit truthy value", () => {
- expect(isSkipAutomationBotPullRequestsEnabledGlobally({ GITTENSORY_SKIP_AUTOMATION_BOT_PRS: "true" })).toBe(true);
- expect(isSkipAutomationBotPullRequestsEnabledGlobally({ GITTENSORY_SKIP_AUTOMATION_BOT_PRS: "1" })).toBe(true);
+ expect(isSkipAutomationBotPullRequestsEnabledGlobally({ LOOPOVER_SKIP_AUTOMATION_BOT_PRS: "true" })).toBe(true);
+ expect(isSkipAutomationBotPullRequestsEnabledGlobally({ LOOPOVER_SKIP_AUTOMATION_BOT_PRS: "1" })).toBe(true);
});
it("turns OFF only for an explicit falsy value, case-insensitively", () => {
for (const value of ["0", "false", "False", "FALSE", "no", "No", "off", "OFF"]) {
- expect(isSkipAutomationBotPullRequestsEnabledGlobally({ GITTENSORY_SKIP_AUTOMATION_BOT_PRS: value })).toBe(false);
+ expect(isSkipAutomationBotPullRequestsEnabledGlobally({ LOOPOVER_SKIP_AUTOMATION_BOT_PRS: value })).toBe(false);
}
});
it("stays ON for whitespace around a truthy/garbage value", () => {
- expect(isSkipAutomationBotPullRequestsEnabledGlobally({ GITTENSORY_SKIP_AUTOMATION_BOT_PRS: " false " })).toBe(false);
- expect(isSkipAutomationBotPullRequestsEnabledGlobally({ GITTENSORY_SKIP_AUTOMATION_BOT_PRS: "banana" })).toBe(true);
+ expect(isSkipAutomationBotPullRequestsEnabledGlobally({ LOOPOVER_SKIP_AUTOMATION_BOT_PRS: " false " })).toBe(false);
+ expect(isSkipAutomationBotPullRequestsEnabledGlobally({ LOOPOVER_SKIP_AUTOMATION_BOT_PRS: "banana" })).toBe(true);
});
});
diff --git a/test/unit/backfill-2.test.ts b/test/unit/backfill-2.test.ts
index 28a17a9314..9b7b638d25 100644
--- a/test/unit/backfill-2.test.ts
+++ b/test/unit/backfill-2.test.ts
@@ -2196,7 +2196,7 @@ describe("GitHub backfill", () => {
it("returns null when the live read fails, even if a stale global fallback is configured (conservative fold-all)", async () => {
const env = createTestEnv({ GITHUB_PUBLIC_TOKEN: "public-token" });
- (env as Env & { GITTENSORY_REQUIRED_CI_CONTEXTS?: string }).GITTENSORY_REQUIRED_CI_CONTEXTS = "stale-required-context";
+ (env as Env & { LOOPOVER_REQUIRED_CI_CONTEXTS?: string }).LOOPOVER_REQUIRED_CI_CONTEXTS = "stale-required-context";
vi.stubGlobal("fetch", async () => new Response("forbidden", { status: 403 }));
expect(await fetchRequiredStatusContexts(env, "JSONbored/gittensory", "main", "public-token")).toBeNull();
});
diff --git a/test/unit/backup-script.test.ts b/test/unit/backup-script.test.ts
index f64fbfc93b..b33d00d94b 100644
--- a/test/unit/backup-script.test.ts
+++ b/test/unit/backup-script.test.ts
@@ -116,7 +116,7 @@ function runBackup(
STUB_SQLITE_MODE: mode,
// Keep ambient test env from selecting a different backup branch or Qdrant target.
DATABASE_URL: options.databaseUrl ?? "",
- GITTENSORY_BACKUP_SOURCE_DATABASE_URL: "",
+ LOOPOVER_BACKUP_SOURCE_DATABASE_URL: "",
QDRANT_URL: options.qdrant ? "http://qdrant.test" : "",
},
});
diff --git a/test/unit/change-guardrail.test.ts b/test/unit/change-guardrail.test.ts
index 49812e4362..bec01e6fe5 100644
--- a/test/unit/change-guardrail.test.ts
+++ b/test/unit/change-guardrail.test.ts
@@ -147,7 +147,7 @@ describe("change-guardrail glob matching", () => {
// Operator-configured hard-guardrail globs can still cover sensitive files outside broad dir-prefix guards,
// while leaving ordinary paths auto-mergeable. These examples are not runtime defaults.
describe("configured hard-guardrail glob examples", () => {
- const GITTENSORY_GLOBS = [
+ const LOOPOVER_GLOBS = [
".github/**", "scripts/**", "packages/**", "apps/loopover-ui/**",
"src/scoring/**", "src/signals/**", "src/rules/**", "src/gittensor/**", "src/auth/**",
"src/upstream/**", "src/settings/**", "src/review/**", "src/services/**", "src/github/**", "src/config/**",
@@ -165,13 +165,13 @@ describe("configured hard-guardrail glob examples", () => {
"src/github/backfill.ts", // CI aggregation that gates merges
"src/config/gittensory-repo-focus-manifest.ts", // scoring focus config
]) {
- expect(changedPathsHittingGuardrail([p], GITTENSORY_GLOBS)).toEqual([p]);
+ expect(changedPathsHittingGuardrail([p], LOOPOVER_GLOBS)).toEqual([p]);
}
});
it("still lets clean non-crucial PRs auto-merge (infra/data/registry/docs/tests)", () => {
const nonCrucial = ["src/utils/json.ts", "src/db/repositories.ts", "src/registry/normalize.ts", "src/mcp/server.ts", "README.md", "docs/x.md", "test/unit/foo.test.ts"];
- expect(changedPathsHittingGuardrail(nonCrucial, GITTENSORY_GLOBS)).toEqual([]);
+ expect(changedPathsHittingGuardrail(nonCrucial, LOOPOVER_GLOBS)).toEqual([]);
});
it("the hold-all sentinel ['**'] guards every path", () => {
diff --git a/test/unit/check-manifest-drift-script.test.ts b/test/unit/check-manifest-drift-script.test.ts
index 5727da8d3c..231b7aaf9e 100644
--- a/test/unit/check-manifest-drift-script.test.ts
+++ b/test/unit/check-manifest-drift-script.test.ts
@@ -2,7 +2,7 @@ import { execFileSync } from "node:child_process";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { checkManifestDrift } from "../../scripts/check-manifest-drift.mjs";
-import { GITTENSORY_REPO_FOCUS_MANIFEST_YAML } from "../../src/config/gittensory-repo-focus-manifest";
+import { LOOPOVER_REPO_FOCUS_MANIFEST_YAML } from "../../src/config/gittensory-repo-focus-manifest";
// The script imports src/config/gittensory-repo-focus-manifest.ts (a .ts module), so -- like
// check-schema-drift.mjs, check-migrations.mjs, and check-openapi-settings-parity.mjs -- it must run via
@@ -51,7 +51,7 @@ describe("check-manifest-drift script", () => {
expect(result.failures).toHaveLength(1);
expect(result.failures[0]).toContain(".loopover.yml");
- expect(result.failures[0]).toContain("GITTENSORY_REPO_FOCUS_MANIFEST_YAML");
+ expect(result.failures[0]).toContain("LOOPOVER_REPO_FOCUS_MANIFEST_YAML");
expect(result.failures[0]).toContain("linkedIssuePolicy");
});
@@ -70,7 +70,7 @@ describe("check-manifest-drift script", () => {
});
// Most important regression test in this file: proves the REAL current repo state (root .loopover.yml
- // vs. the real bundled GITTENSORY_REPO_FOCUS_MANIFEST_YAML constant) agrees, using the real filesystem
+ // vs. the real bundled LOOPOVER_REPO_FOCUS_MANIFEST_YAML constant) agrees, using the real filesystem
// reader against the real repo root. If this fails, the two have genuinely drifted apart -- either way,
// the check must not be weakened to make this test pass.
it("the real repo's root .loopover.yml and the bundled fallback agree (regression guard)", () => {
@@ -83,13 +83,13 @@ describe("check-manifest-drift script", () => {
// A structural guard on the constant itself: an accidental empty-string or truncated bundle would
// otherwise parse to `undefined`/{} and could pass a deep-equal check against an equally-broken root
// read, so assert the constant looks like real manifest YAML independent of the comparison above.
- expect(GITTENSORY_REPO_FOCUS_MANIFEST_YAML.length).toBeGreaterThan(100);
- expect(GITTENSORY_REPO_FOCUS_MANIFEST_YAML).toContain("source: repo_file");
+ expect(LOOPOVER_REPO_FOCUS_MANIFEST_YAML.length).toBeGreaterThan(100);
+ expect(LOOPOVER_REPO_FOCUS_MANIFEST_YAML).toContain("source: repo_file");
});
it("prints a clean summary and exits 0 for the real repo state when run as a subprocess", () => {
const output = execFileSync(TSX_BIN, ["scripts/check-manifest-drift.mjs"], { encoding: "utf8" });
- expect(output).toMatch(/Manifest-drift check ok: \.loopover\.yml and GITTENSORY_REPO_FOCUS_MANIFEST_YAML agree\./);
+ expect(output).toMatch(/Manifest-drift check ok: \.loopover\.yml and LOOPOVER_REPO_FOCUS_MANIFEST_YAML agree\./);
});
});
diff --git a/test/unit/contributor-issue-draft.test.ts b/test/unit/contributor-issue-draft.test.ts
index 5e3ddeb2ed..cf087afdf8 100644
--- a/test/unit/contributor-issue-draft.test.ts
+++ b/test/unit/contributor-issue-draft.test.ts
@@ -22,7 +22,7 @@ import { buildLaneAdvice, buildConfigQuality, buildContributorIntakeHealth, buil
const FORBIDDEN = /wallet|hotkey|raw trust score|payout|reward estimate|farming|private reviewability|public score estimate/i;
-const GITTENSORY_MANIFEST = parseFocusManifestContent(
+const LOOPOVER_MANIFEST = parseFocusManifestContent(
JSON.stringify({
wantedPaths: ["src/", "apps/loopover-ui/", "packages/loopover-mcp/"],
testExpectations: ["npm run test:ci"],
@@ -113,7 +113,7 @@ describe("contributor issue drafts", () => {
const configQuality = buildConfigQuality(repo, issues, pullRequests, repoFullName);
const labelAudit = buildLabelAudit(repo, [], issues, pullRequests, repoFullName);
const contributorIntakeHealth = buildContributorIntakeHealth(repo, issues, pullRequests, repoFullName, collisions, queueCounts);
- const manifest = { ...GITTENSORY_MANIFEST, present: false, source: "none" as const, warnings: [] };
+ const manifest = { ...LOOPOVER_MANIFEST, present: false, source: "none" as const, warnings: [] };
const candidates = buildContributorIssueDraftCandidates({
repoFullName,
repo,
@@ -162,9 +162,9 @@ describe("contributor issue drafts", () => {
}),
),
);
- const env = createTestEnv({ GITTENSORY_CONTRIBUTOR_ISSUE_TOKEN: "token" });
+ const env = createTestEnv({ LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN: "token" });
const manifest = {
- ...GITTENSORY_MANIFEST,
+ ...LOOPOVER_MANIFEST,
wantedPaths: ["src/unique-path-119/"],
testExpectations: ["npm run test:ci"],
};
@@ -292,7 +292,7 @@ describe("contributor issue drafts", () => {
}),
),
);
- const env = createTestEnv({ GITTENSORY_CONTRIBUTOR_ISSUE_TOKEN: "token" });
+ const env = createTestEnv({ LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN: "token" });
const repoFullName = "other-owner/other-repo";
const fingerprint = await contributorIssueDraftFingerprint(repoFullName, "policy:focus_policy_missing", "policy:focus_policy_missing");
const marker = contributorIssueDraftMarker(fingerprint);
@@ -330,7 +330,7 @@ describe("contributor issue drafts", () => {
};
// DB-freeze arm: a frozen agent forces dryRun even though the caller asked to create.
- const frozenEnv = createTestEnv({ GITTENSORY_CONTRIBUTOR_ISSUE_TOKEN: "token" });
+ const frozenEnv = createTestEnv({ LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN: "token" });
await repositories.setGlobalAgentFrozen(frozenEnv, true);
seedCandidate();
const frozen = await generateContributorIssueDrafts(frozenEnv, repoFullName, { dryRun: false, create: true, limit: 1 });
@@ -339,7 +339,7 @@ describe("contributor issue drafts", () => {
expect(calls.some((c) => c.startsWith("POST"))).toBe(false); // no issue POST reached the network
// env-brake arm: AGENT_ACTIONS_PAUSED short-circuits before the DB freeze read.
- const pausedEnv = createTestEnv({ GITTENSORY_CONTRIBUTOR_ISSUE_TOKEN: "token", AGENT_ACTIONS_PAUSED: "true" });
+ const pausedEnv = createTestEnv({ LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN: "token", AGENT_ACTIONS_PAUSED: "true" });
seedCandidate();
const paused = await generateContributorIssueDrafts(pausedEnv, repoFullName, { dryRun: false, create: true, limit: 1 });
expect(paused.dryRun).toBe(true);
@@ -362,7 +362,7 @@ describe("contributor issue drafts", () => {
it("records skipped_create_failed when GitHub returns a non-ok response", async () => {
vi.stubGlobal("fetch", vi.fn(async () => new Response("nope", { status: 403 })));
- const env = createTestEnv({ GITTENSORY_CONTRIBUTOR_ISSUE_TOKEN: "token" });
+ const env = createTestEnv({ LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN: "token" });
vi.spyOn(repositories, "listOpenIssues").mockResolvedValue([]);
const result = await generateContributorIssueDrafts(env, "JSONbored/gittensory", {
@@ -386,7 +386,7 @@ describe("contributor issue drafts", () => {
);
const auditSpy = vi.spyOn(repositories, "recordAuditEvent").mockResolvedValue(undefined);
vi.spyOn(repositories, "listOpenIssues").mockResolvedValue([]);
- const env = createTestEnv({ GITTENSORY_CONTRIBUTOR_ISSUE_TOKEN: "token" });
+ const env = createTestEnv({ LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN: "token" });
const result = await generateContributorIssueDrafts(env, "JSONbored/gittensory", {
dryRun: false,
create: true,
@@ -481,7 +481,7 @@ describe("contributor issue drafts", () => {
it("returns null for invalid repo names when creating GitHub issues", async () => {
vi.stubGlobal("fetch", vi.fn(async () => Response.json({ number: 1, html_url: "https://example.com/1" })));
- const env = createTestEnv({ GITTENSORY_CONTRIBUTOR_ISSUE_TOKEN: "token" });
+ const env = createTestEnv({ LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN: "token" });
vi.spyOn(repositories, "listOpenIssues").mockResolvedValue([]);
const result = await generateContributorIssueDrafts(env, "invalid", {
dryRun: false,
@@ -493,7 +493,7 @@ describe("contributor issue drafts", () => {
it("treats malformed GitHub create responses as skipped_create_failed", async () => {
vi.stubGlobal("fetch", vi.fn(async () => Response.json({ html_url: "https://github.com/x/y/issues/1" })));
- const env = createTestEnv({ GITTENSORY_CONTRIBUTOR_ISSUE_TOKEN: "token" });
+ const env = createTestEnv({ LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN: "token" });
vi.spyOn(repositories, "listOpenIssues").mockResolvedValue([]);
const result = await generateContributorIssueDrafts(env, "JSONbored/gittensory", {
dryRun: false,
@@ -536,7 +536,7 @@ describe("contributor issue drafts", () => {
);
const auditSpy = vi.spyOn(repositories, "recordAuditEvent").mockResolvedValue(undefined);
vi.spyOn(repositories, "listOpenIssues").mockResolvedValue([]);
- const env = createTestEnv({ GITTENSORY_CONTRIBUTOR_ISSUE_TOKEN: "token" });
+ const env = createTestEnv({ LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN: "token" });
const result = await generateContributorIssueDrafts(env, "JSONbored/gittensory", {
dryRun: false,
create: true,
diff --git a/test/unit/docs-miner-quickstart.test.ts b/test/unit/docs-miner-quickstart.test.ts
index 166c398857..1fbe39b9ea 100644
--- a/test/unit/docs-miner-quickstart.test.ts
+++ b/test/unit/docs-miner-quickstart.test.ts
@@ -64,7 +64,7 @@ describe("docs miner quickstart page", () => {
it("documents the local privacy boundary and packet redaction", () => {
expect(normalizedSource).toMatch(/source never leaves your machine/i);
- expect(source).toMatch(/GITTENSORY_UPLOAD_SOURCE=false/);
+ expect(source).toMatch(/LOOPOVER_UPLOAD_SOURCE=false/);
expect(source).toMatch(/local absolute paths are redacted/i);
expect(source).toMatch(/public-safe/i);
expect(normalizedSource).toMatch(
diff --git a/test/unit/docs-selfhost-update-rollback.test.ts b/test/unit/docs-selfhost-update-rollback.test.ts
index 8fe559c987..f3745313f8 100644
--- a/test/unit/docs-selfhost-update-rollback.test.ts
+++ b/test/unit/docs-selfhost-update-rollback.test.ts
@@ -45,7 +45,7 @@ describe("self-host update + rollback docs (#1823)", () => {
it("post-update script probes /ready without mutating operator-owned state", () => {
expect(postUpdateScript).toContain("/ready");
- expect(postUpdateScript).toContain("GITTENSORY_IMAGE");
+ expect(postUpdateScript).toContain("LOOPOVER_IMAGE");
expect(postUpdateScript).toContain("LOOPOVER_VERSION");
expect(postUpdateScript).toContain("SENTRY_RELEASE");
expect(postUpdateScript).not.toContain("env_put");
diff --git a/test/unit/e2e-test-gen-render.test.ts b/test/unit/e2e-test-gen-render.test.ts
index c17e880035..b1c4c24a7b 100644
--- a/test/unit/e2e-test-gen-render.test.ts
+++ b/test/unit/e2e-test-gen-render.test.ts
@@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import { buildE2eTestGenCommentBody } from "../../src/review/e2e-test-gen-render";
import { PR_PANEL_COMMENT_MARKER } from "../../src/github/comments";
-import { GITTENSORY_SITE_URL } from "../../src/github/footer";
+import { LOOPOVER_SITE_URL } from "../../src/github/footer";
describe("buildE2eTestGenCommentBody", () => {
it("renders the generated test source in a fenced code block, defaulting the framework to Playwright", () => {
@@ -88,15 +88,15 @@ describe("buildE2eTestGenCommentBody", () => {
});
// #4613: the footer's attribution link honors a self-hoster's PUBLIC_SITE_ORIGIN instead of always
- // pointing at GITTENSORY_SITE_URL.
+ // pointing at LOOPOVER_SITE_URL.
it("#4613: honors env.PUBLIC_SITE_ORIGIN in the footer attribution link", () => {
const selfHosted = buildE2eTestGenCommentBody({ env: { PUBLIC_SITE_ORIGIN: "https://gittensory.example.org" }, actor: "maintainer", testSource: "test('x', () => {});" });
expect(selfHosted).toContain("Checked by [LoopOver](https://gittensory.example.org)");
- expect(selfHosted).not.toContain(GITTENSORY_SITE_URL);
+ expect(selfHosted).not.toContain(LOOPOVER_SITE_URL);
});
- it("#4613: falls back to GITTENSORY_SITE_URL when PUBLIC_SITE_ORIGIN is unset", () => {
+ it("#4613: falls back to LOOPOVER_SITE_URL when PUBLIC_SITE_ORIGIN is unset", () => {
const defaultHosted = buildE2eTestGenCommentBody({ env: {}, actor: "maintainer", testSource: "test('x', () => {});" });
- expect(defaultHosted).toContain(`Checked by [LoopOver](${GITTENSORY_SITE_URL})`);
+ expect(defaultHosted).toContain(`Checked by [LoopOver](${LOOPOVER_SITE_URL})`);
});
});
diff --git a/test/unit/extension-content.test.ts b/test/unit/extension-content.test.ts
index 00c7b777fe..b3db53fa43 100644
--- a/test/unit/extension-content.test.ts
+++ b/test/unit/extension-content.test.ts
@@ -74,7 +74,7 @@ describe("extension content script", () => {
function loadContentInternals() {
const context: Record = {
- __GITTENSORY_EXTENSION_TEST__: true,
+ __LOOPOVER_EXTENSION_TEST__: true,
location: { pathname: "/JSONbored/gittensory/issues/146" },
document: {
querySelector: vi.fn(() => null),
diff --git a/test/unit/focus-manifest-loader.test.ts b/test/unit/focus-manifest-loader.test.ts
index e3909e6b32..94b0f7c9d7 100644
--- a/test/unit/focus-manifest-loader.test.ts
+++ b/test/unit/focus-manifest-loader.test.ts
@@ -376,7 +376,7 @@ describe("focus-manifest loader", () => {
});
it("falls back to bundled YAML for a configured self-repo alias when fetch is unavailable", async () => {
- const env = createTestEnv({ GITTENSORY_DRIFT_ISSUE_REPO: "fork/gittensory" });
+ const env = createTestEnv({ LOOPOVER_DRIFT_ISSUE_REPO: "fork/gittensory" });
const manifest = await loadRepoFocusManifest(env, "fork/gittensory", { fetcher: async () => null });
expect(manifest.present).toBe(true);
expect(manifest.wantedPaths).toContain("apps/loopover-ui/");
diff --git a/test/unit/footer.test.ts b/test/unit/footer.test.ts
index 6e0a6b3285..01566215da 100644
--- a/test/unit/footer.test.ts
+++ b/test/unit/footer.test.ts
@@ -1,10 +1,10 @@
import { describe, expect, it } from "vitest";
-import { gittensoryFooter, gittensorRepoEarnUrl, GITTENSOR_HOME_URL, GITTENSORY_SITE_URL, maintainerControlPanelUrl } from "../../src/github/footer";
+import { gittensoryFooter, gittensorRepoEarnUrl, GITTENSOR_HOME_URL, LOOPOVER_SITE_URL, maintainerControlPanelUrl } from "../../src/github/footer";
import { FORBIDDEN_PUBLIC_COMMENT_WORDS } from "../../src/queue-intelligence";
describe("maintainerControlPanelUrl", () => {
it("builds the repo maintainer panel URL on the default site origin", () => {
- expect(maintainerControlPanelUrl({}, "owner/repo")).toBe(`${GITTENSORY_SITE_URL}/app?view=maintainer&repo=owner%2Frepo`);
+ expect(maintainerControlPanelUrl({}, "owner/repo")).toBe(`${LOOPOVER_SITE_URL}/app?view=maintainer&repo=owner%2Frepo`);
});
it("uses a configured PUBLIC_SITE_ORIGIN when present", () => {
@@ -22,7 +22,7 @@ describe("gittensory public-comment footer", () => {
expect(footer).toMatch(/earn/i);
expect(footer).toContain("register to start earning");
expect(footer).toContain(GITTENSOR_HOME_URL);
- expect(footer).toContain(GITTENSORY_SITE_URL);
+ expect(footer).toContain(LOOPOVER_SITE_URL);
});
it("points the CTA at a specific repo's public miner page when given an earnUrl", () => {
@@ -47,27 +47,27 @@ describe("gittensory public-comment footer", () => {
expect(footer.startsWith("Thanks for contributing to LoopOver!")).toBe(true);
expect(footer).toContain("register to start earning");
expect(footer).toContain(earnUrl);
- expect(footer).toContain(GITTENSORY_SITE_URL);
+ expect(footer).toContain(LOOPOVER_SITE_URL);
expect(footer.toLowerCase()).not.toMatch(/reward|payout|score/);
});
- // #4613: a self-hoster's PUBLIC_SITE_ORIGIN replaces GITTENSORY_SITE_URL in the "Checked by LoopOver"
+ // #4613: a self-hoster's PUBLIC_SITE_ORIGIN replaces LOOPOVER_SITE_URL in the "Checked by LoopOver"
// attribution link -- both the default-copy branch and the maintainer-customText branch splice it in,
// and the Gittensor register link (a separate, shared network) is never rebranded.
it("#4613: uses PUBLIC_SITE_ORIGIN in the attribution link when configured", () => {
const footer = gittensoryFooter({ PUBLIC_SITE_ORIGIN: "https://gittensory.example.org" });
expect(footer).toContain("Checked by [LoopOver](https://gittensory.example.org)");
- expect(footer).not.toContain(GITTENSORY_SITE_URL);
+ expect(footer).not.toContain(LOOPOVER_SITE_URL);
expect(footer).toContain(GITTENSOR_HOME_URL); // the network link is never rebranded
});
- it("#4613: falls back to GITTENSORY_SITE_URL when PUBLIC_SITE_ORIGIN is unset", () => {
- expect(gittensoryFooter({})).toContain(`Checked by [LoopOver](${GITTENSORY_SITE_URL})`);
+ it("#4613: falls back to LOOPOVER_SITE_URL when PUBLIC_SITE_ORIGIN is unset", () => {
+ expect(gittensoryFooter({})).toContain(`Checked by [LoopOver](${LOOPOVER_SITE_URL})`);
});
it("#4613: uses PUBLIC_SITE_ORIGIN in the attribution link on the customText branch too", () => {
const footer = gittensoryFooter({ PUBLIC_SITE_ORIGIN: "https://gittensory.example.org" }, { customText: "Thanks for contributing!" });
expect(footer).toContain("Checked by [LoopOver](https://gittensory.example.org)");
- expect(footer).not.toContain(GITTENSORY_SITE_URL);
+ expect(footer).not.toContain(LOOPOVER_SITE_URL);
});
});
diff --git a/test/unit/gen-command-reference-script.test.ts b/test/unit/gen-command-reference-script.test.ts
index 2b45e6d9a8..40496e5806 100644
--- a/test/unit/gen-command-reference-script.test.ts
+++ b/test/unit/gen-command-reference-script.test.ts
@@ -83,7 +83,7 @@ describe("gen-command-reference script (#3046)", () => {
const MAINTAINER_QUEUE_DIGEST_COMMAND_CATALOG = [
${maintainerIds}
] as const;
- const GITTENSORY_ACTION_COMMAND_CATALOG = [
+ const LOOPOVER_ACTION_COMMAND_CATALOG = [
${actionIds}
] as const;
`);
diff --git a/test/unit/generated-doc-refresh.test.ts b/test/unit/generated-doc-refresh.test.ts
index 9a4be4e4cb..15150060a6 100644
--- a/test/unit/generated-doc-refresh.test.ts
+++ b/test/unit/generated-doc-refresh.test.ts
@@ -3,7 +3,7 @@ import { refreshGeneratedDoc } from "../../src/review/generated-doc-refresh";
import { REPO_DOC_MARKERS, renderRepoDocContent } from "../../src/review/repo-doc-render";
import { REPO_PROFILE_SCHEMA_VERSION } from "../../src/review/repo-profile";
import type { RepoProfile } from "../../src/review/repo-profile";
-import { GITTENSORY_SITE_URL } from "../../src/github/footer";
+import { LOOPOVER_SITE_URL } from "../../src/github/footer";
const MARKERS = { start: "", end: "" };
const SECTION = `${MARKERS.start}\ngenerated body v1\n${MARKERS.end}\n`;
@@ -114,7 +114,7 @@ describe("refreshGeneratedDoc (#3004)", () => {
});
it("REGRESSION: a real renderRepoDocContent() output round-trips as no-change against itself, with or without surrounding manual content", () => {
- const rendered = renderRepoDocContent(fixtureProfile(), GITTENSORY_SITE_URL)!;
+ const rendered = renderRepoDocContent(fixtureProfile(), LOOPOVER_SITE_URL)!;
expect(refreshGeneratedDoc(rendered, rendered, REPO_DOC_MARKERS)).toEqual({ action: "no-change" });
const withManualContent = `\n\n${rendered}\n\n`;
diff --git a/test/unit/github-commands.test.ts b/test/unit/github-commands.test.ts
index bfe5013211..835e358406 100644
--- a/test/unit/github-commands.test.ts
+++ b/test/unit/github-commands.test.ts
@@ -12,9 +12,9 @@ import {
parseLoopOverMentionCommand,
sanitizePublicComment,
suggestCommand,
- GITTENSORY_ACTION_COMMAND_CATALOG,
- GITTENSORY_ACTION_COMMANDS,
- GITTENSORY_MENTION_COMMAND_CATALOG,
+ LOOPOVER_ACTION_COMMAND_CATALOG,
+ LOOPOVER_ACTION_COMMANDS,
+ LOOPOVER_MENTION_COMMAND_CATALOG,
INTENT_ROUTABLE_COMMANDS,
githubCommandsInternals,
} from "../../src/github/commands";
@@ -85,12 +85,12 @@ describe("GitHub mention commands", () => {
}
// Every action command (the actual write-capable surface a prompt-injection attempt would target) is
// rejected — this is the exact adversarial case req 3 calls out.
- for (const actionCommand of GITTENSORY_ACTION_COMMANDS) {
+ for (const actionCommand of LOOPOVER_ACTION_COMMANDS) {
expect(isIntentRoutableCommand(actionCommand)).toBe(false);
}
// Every OTHER cataloged Q&A command not in the closed set (help/miner-context/every maintainer-queue-digest
// command) is also rejected — the closed set is a strict subset of the full Q&A catalog, not the whole thing.
- const nonRoutableCatalogNames = GITTENSORY_MENTION_COMMAND_CATALOG.map((c) => c.id).filter((id) => !(INTENT_ROUTABLE_COMMANDS as readonly string[]).includes(id));
+ const nonRoutableCatalogNames = LOOPOVER_MENTION_COMMAND_CATALOG.map((c) => c.id).filter((id) => !(INTENT_ROUTABLE_COMMANDS as readonly string[]).includes(id));
expect(nonRoutableCatalogNames).toEqual(expect.arrayContaining(["help", "miner-context", "queue-summary", "confirmed-miners"]));
for (const name of nonRoutableCatalogNames) {
expect(isIntentRoutableCommand(name)).toBe(false);
@@ -195,10 +195,10 @@ describe("GitHub mention commands", () => {
expect(bare.join("\n")).toContain("**Commands**");
});
- it("keeps GITTENSORY_ACTION_COMMAND_CATALOG in sync with GITTENSORY_ACTION_COMMANDS (#2167)", () => {
- expect(GITTENSORY_ACTION_COMMAND_CATALOG.map((command) => command.id)).toEqual([...GITTENSORY_ACTION_COMMANDS]);
- expect(GITTENSORY_ACTION_COMMAND_CATALOG).toHaveLength(GITTENSORY_ACTION_COMMANDS.length);
- for (const command of GITTENSORY_ACTION_COMMAND_CATALOG) {
+ it("keeps LOOPOVER_ACTION_COMMAND_CATALOG in sync with LOOPOVER_ACTION_COMMANDS (#2167)", () => {
+ expect(LOOPOVER_ACTION_COMMAND_CATALOG.map((command) => command.id)).toEqual([...LOOPOVER_ACTION_COMMANDS]);
+ expect(LOOPOVER_ACTION_COMMAND_CATALOG).toHaveLength(LOOPOVER_ACTION_COMMANDS.length);
+ for (const command of LOOPOVER_ACTION_COMMAND_CATALOG) {
expect(command.title.trim().length).toBeGreaterThan(0);
expect(command.description.trim().length).toBeGreaterThan(0);
}
@@ -209,7 +209,7 @@ describe("GitHub mention commands", () => {
expect(body).toContain("**PR action commands**");
expect(body).toContain("maintainer or collaborator authorization");
expect(body).toContain("pause` and `resume` affect only auto-review");
- for (const action of GITTENSORY_ACTION_COMMANDS) {
+ for (const action of LOOPOVER_ACTION_COMMANDS) {
expect(body).toContain(`@loopover ${action}`);
}
const rendered = githubCommandsInternals.actionCommandHelpSections().join("\n");
diff --git a/test/unit/gittensor-wire.test.ts b/test/unit/gittensor-wire.test.ts
index 515648b952..c18aeaad20 100644
--- a/test/unit/gittensor-wire.test.ts
+++ b/test/unit/gittensor-wire.test.ts
@@ -32,27 +32,27 @@ function poisonDbPrepare(env: Env, pattern: RegExp): void {
describe("isGittensorPluginEnabled", () => {
it("is OFF for unset/false and ON for the truthy convention", () => {
expect(isGittensorPluginEnabled({})).toBe(false);
- expect(isGittensorPluginEnabled({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "false" })).toBe(false);
- expect(isGittensorPluginEnabled({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "true" })).toBe(true);
- expect(isGittensorPluginEnabled({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "1" })).toBe(true);
- expect(isGittensorPluginEnabled({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "on" })).toBe(true);
- expect(isGittensorPluginEnabled({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "yes" })).toBe(true);
+ expect(isGittensorPluginEnabled({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "false" })).toBe(false);
+ expect(isGittensorPluginEnabled({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "true" })).toBe(true);
+ expect(isGittensorPluginEnabled({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "1" })).toBe(true);
+ expect(isGittensorPluginEnabled({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "on" })).toBe(true);
+ expect(isGittensorPluginEnabled({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "yes" })).toBe(true);
});
});
describe("shouldEnableGittensorForRepo", () => {
it("requires BOTH the operator env flag AND the per-repo manifest opt-in", () => {
- expect(shouldEnableGittensorForRepo({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "true" }, true)).toBe(true);
+ expect(shouldEnableGittensorForRepo({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "true" }, true)).toBe(true);
});
it("is OFF when the operator flag is on but the manifest didn't opt in", () => {
- expect(shouldEnableGittensorForRepo({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "true" }, false)).toBe(false);
- expect(shouldEnableGittensorForRepo({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "true" }, null)).toBe(false);
- expect(shouldEnableGittensorForRepo({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "true" }, undefined)).toBe(false);
+ expect(shouldEnableGittensorForRepo({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "true" }, false)).toBe(false);
+ expect(shouldEnableGittensorForRepo({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "true" }, null)).toBe(false);
+ expect(shouldEnableGittensorForRepo({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "true" }, undefined)).toBe(false);
});
it("is OFF when the manifest opted in but the operator flag is off (repo cannot self-enable)", () => {
- expect(shouldEnableGittensorForRepo({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "false" }, true)).toBe(false);
+ expect(shouldEnableGittensorForRepo({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "false" }, true)).toBe(false);
expect(shouldEnableGittensorForRepo({}, true)).toBe(false);
});
@@ -71,26 +71,26 @@ describe("gittensorEnabledRepoFullNames", () => {
});
it("is empty when the flag is on but no repos are locally known", async () => {
- const env = createTestEnv({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "true" });
+ const env = createTestEnv({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "true" });
await expect(gittensorEnabledRepoFullNames(env)).resolves.toEqual(new Set());
});
it("excludes a repo whose manifest never sets experimental.gittensor", async () => {
- const env = createTestEnv({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "true" });
+ const env = createTestEnv({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "true" });
await seedRegisteredRepo(env, "owner/unopted");
await upsertRepoFocusManifest(env, "owner/unopted", { wantedPaths: ["src/"] });
await expect(gittensorEnabledRepoFullNames(env)).resolves.toEqual(new Set());
});
it("excludes a repo that explicitly sets experimental.gittensor: false", async () => {
- const env = createTestEnv({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "true" });
+ const env = createTestEnv({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "true" });
await seedRegisteredRepo(env, "owner/optedout");
await upsertRepoFocusManifest(env, "owner/optedout", { experimental: { gittensor: false } });
await expect(gittensorEnabledRepoFullNames(env)).resolves.toEqual(new Set());
});
it("includes only the repos that explicitly opt in, lowercased, out of a mixed set", async () => {
- const env = createTestEnv({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "true" });
+ const env = createTestEnv({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "true" });
await seedRegisteredRepo(env, "Owner/OptedIn");
await upsertRepoFocusManifest(env, "Owner/OptedIn", { experimental: { gittensor: true } });
await seedRegisteredRepo(env, "owner/other");
@@ -99,21 +99,21 @@ describe("gittensorEnabledRepoFullNames", () => {
});
it("is not gated by isRegistered — a not-yet-registered but opted-in repo is still included (avoids the chicken-and-egg deadlock)", async () => {
- const env = createTestEnv({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "true" });
+ const env = createTestEnv({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "true" });
await seedRegisteredRepo(env, "owner/notyetregistered"); // seeded with is_registered=0
await upsertRepoFocusManifest(env, "owner/notyetregistered", { experimental: { gittensor: true } });
await expect(gittensorEnabledRepoFullNames(env)).resolves.toEqual(new Set(["owner/notyetregistered"]));
});
it("excludes registry-only repos whose cached manifests opt in (regression for the unscoped registry activation bypass)", async () => {
- const env = createTestEnv({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "true" });
+ const env = createTestEnv({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "true" });
await seedRegistryOnlyRepo(env, "attacker/external");
await upsertRepoFocusManifest(env, "attacker/external", { experimental: { gittensor: true } });
await expect(gittensorEnabledRepoFullNames(env)).resolves.toEqual(new Set());
});
it("fails safe per-repo: a manifest-load error is swallowed and the pass still resolves", async () => {
- const env = createTestEnv({ GITTENSORY_EXPERIMENTAL_GITTENSOR: "true" });
+ const env = createTestEnv({ LOOPOVER_EXPERIMENTAL_GITTENSOR: "true" });
await seedRegisteredRepo(env, "owner/blip");
// loadRepoFocusManifest's cache read hits signal_snapshots; poison it so the per-repo try/catch fires.
poisonDbPrepare(env, /"signal_snapshots"/i);
diff --git a/test/unit/gittensory-focus-manifest.test.ts b/test/unit/gittensory-focus-manifest.test.ts
index 869c539d9c..4a64065bad 100644
--- a/test/unit/gittensory-focus-manifest.test.ts
+++ b/test/unit/gittensory-focus-manifest.test.ts
@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { describe, expect, it } from "vitest";
import {
- GITTENSORY_REPO_FOCUS_MANIFEST_YAML,
+ LOOPOVER_REPO_FOCUS_MANIFEST_YAML,
GITTENSOR_SELF_REPO_DEFAULT,
resolveLoopOverSelfRepoFullName,
} from "../../src/config/gittensory-repo-focus-manifest";
@@ -21,24 +21,24 @@ const FORBIDDEN_PUBLIC_LANGUAGE =
describe("resolveLoopOverSelfRepoFullName", () => {
it("defaults to the LoopOver repo when drift issue repo is unset or invalid", () => {
expect(resolveLoopOverSelfRepoFullName({})).toBe(GITTENSOR_SELF_REPO_DEFAULT);
- expect(resolveLoopOverSelfRepoFullName({ GITTENSORY_DRIFT_ISSUE_REPO: "invalid" })).toBe(GITTENSOR_SELF_REPO_DEFAULT);
- expect(resolveLoopOverSelfRepoFullName({ GITTENSORY_DRIFT_ISSUE_REPO: " " })).toBe(GITTENSOR_SELF_REPO_DEFAULT);
+ expect(resolveLoopOverSelfRepoFullName({ LOOPOVER_DRIFT_ISSUE_REPO: "invalid" })).toBe(GITTENSOR_SELF_REPO_DEFAULT);
+ expect(resolveLoopOverSelfRepoFullName({ LOOPOVER_DRIFT_ISSUE_REPO: " " })).toBe(GITTENSOR_SELF_REPO_DEFAULT);
});
it("returns a configured owner/repo drift target when present", () => {
- expect(resolveLoopOverSelfRepoFullName({ GITTENSORY_DRIFT_ISSUE_REPO: "acme/widget" })).toBe("acme/widget");
- expect(resolveLoopOverSelfRepoFullName({ GITTENSORY_DRIFT_ISSUE_REPO: " org/repo " })).toBe("org/repo");
+ expect(resolveLoopOverSelfRepoFullName({ LOOPOVER_DRIFT_ISSUE_REPO: "acme/widget" })).toBe("acme/widget");
+ expect(resolveLoopOverSelfRepoFullName({ LOOPOVER_DRIFT_ISSUE_REPO: " org/repo " })).toBe("org/repo");
});
});
describe("LoopOver repo focus manifest", () => {
it("keeps bundled YAML aligned with the committed .loopover.yml file", () => {
const onDisk = readFileSync(resolve(process.cwd(), ".loopover.yml"), "utf8").trim();
- expect(GITTENSORY_REPO_FOCUS_MANIFEST_YAML.trim()).toBe(onDisk);
+ expect(LOOPOVER_REPO_FOCUS_MANIFEST_YAML.trim()).toBe(onDisk);
});
it("valid manifest fixture parses and compiles policy", () => {
- const manifest = parseFocusManifestContent(GITTENSORY_REPO_FOCUS_MANIFEST_YAML, "repo_file");
+ const manifest = parseFocusManifestContent(LOOPOVER_REPO_FOCUS_MANIFEST_YAML, "repo_file");
expect(manifest.present).toBe(true);
expect(manifest.wantedPaths).toContain("src/");
expect(manifest.wantedPaths).toContain("review-enrichment/");
@@ -60,7 +60,7 @@ describe("LoopOver repo focus manifest", () => {
});
it("retired static-site surfaces no longer create manifest path holds", () => {
- const manifest = parseFocusManifestContent(GITTENSORY_REPO_FOCUS_MANIFEST_YAML, "repo_file");
+ const manifest = parseFocusManifestContent(LOOPOVER_REPO_FOCUS_MANIFEST_YAML, "repo_file");
const guidance = buildFocusManifestGuidance({
manifest,
changedPaths: ["site/docs/index.html"],
@@ -70,7 +70,7 @@ describe("LoopOver repo focus manifest", () => {
});
it("focused control-panel UI changes align with wanted paths", () => {
- const manifest = parseFocusManifestContent(GITTENSORY_REPO_FOCUS_MANIFEST_YAML, "repo_file");
+ const manifest = parseFocusManifestContent(LOOPOVER_REPO_FOCUS_MANIFEST_YAML, "repo_file");
const guidance = buildFocusManifestGuidance({
manifest,
changedPaths: ["apps/loopover-ui/src/routes/app.operator.tsx"],
@@ -79,7 +79,7 @@ describe("LoopOver repo focus manifest", () => {
});
it("recommendation influence prefers in-scope backend and UI paths without legacy blocked surfaces", () => {
- const manifest = parseFocusManifestContent(GITTENSORY_REPO_FOCUS_MANIFEST_YAML, "repo_file");
+ const manifest = parseFocusManifestContent(LOOPOVER_REPO_FOCUS_MANIFEST_YAML, "repo_file");
const backend = buildFocusManifestGuidance({ manifest, changedPaths: ["src/api/routes.ts"] });
const controlPanel = buildFocusManifestGuidance({ manifest, changedPaths: ["apps/loopover-ui/src/app.tsx"] });
const retiredSite = buildFocusManifestGuidance({ manifest, changedPaths: ["site/index.html"] });
@@ -90,7 +90,7 @@ describe("LoopOver repo focus manifest", () => {
});
it("public/private boundary regression keeps maintainer notes out of public guidance", () => {
- const manifest = parseFocusManifestContent(GITTENSORY_REPO_FOCUS_MANIFEST_YAML, "repo_file");
+ const manifest = parseFocusManifestContent(LOOPOVER_REPO_FOCUS_MANIFEST_YAML, "repo_file");
const guidance = buildFocusManifestGuidance({ manifest, changedPaths: ["src/x.ts"] });
for (const step of guidance.publicNextSteps) {
expect(isFocusManifestPublicSafe(step)).toBe(true);
@@ -100,7 +100,7 @@ describe("LoopOver repo focus manifest", () => {
});
it("enables linked-issue label propagation with bug/feature relaxed and priority strict (#priority-linked-issue-gate-ownership)", () => {
- const manifest = parseFocusManifestContent(GITTENSORY_REPO_FOCUS_MANIFEST_YAML, "repo_file");
+ const manifest = parseFocusManifestContent(LOOPOVER_REPO_FOCUS_MANIFEST_YAML, "repo_file");
const propagation = manifest.settings.linkedIssueLabelPropagation;
expect(propagation?.enabled).toBe(true);
expect(propagation?.mode).toBe("exclusive_type_label");
@@ -112,7 +112,7 @@ describe("LoopOver repo focus manifest", () => {
});
it("loads bundled manifest for the LoopOver repo when fetch is unavailable", async () => {
- const env = createTestEnv({ GITTENSORY_DRIFT_ISSUE_REPO: "JSONbored/gittensory" });
+ const env = createTestEnv({ LOOPOVER_DRIFT_ISSUE_REPO: "JSONbored/gittensory" });
const manifest = await loadRepoFocusManifest(env, "JSONbored/gittensory", { fetcher: async () => null });
expect(manifest.present).toBe(true);
expect(manifest.wantedPaths).toContain("packages/");
@@ -120,7 +120,7 @@ describe("LoopOver repo focus manifest", () => {
});
it("does not treat legacy lovable-only and CNAME paths as manifest holds", () => {
- const manifest = parseFocusManifestContent(GITTENSORY_REPO_FOCUS_MANIFEST_YAML, "repo_file");
+ const manifest = parseFocusManifestContent(LOOPOVER_REPO_FOCUS_MANIFEST_YAML, "repo_file");
for (const changedPath of ["CNAME", "vendor/lovable/widget.ts"]) {
const guidance = buildFocusManifestGuidance({ manifest, changedPaths: [changedPath] });
expect(guidance.findings.map((finding) => finding.code)).not.toContain("manifest_malformed");
diff --git a/test/unit/index.test.ts b/test/unit/index.test.ts
index 299e4ef8cb..06dd5a6870 100644
--- a/test/unit/index.test.ts
+++ b/test/unit/index.test.ts
@@ -590,7 +590,7 @@ describe("worker entrypoint", () => {
it("re-includes refresh-registry once a repo opts into the experimental gittensor plugin", async () => {
const sent: Array = [];
const env = createTestEnv({
- GITTENSORY_EXPERIMENTAL_GITTENSOR: "true",
+ LOOPOVER_EXPERIMENTAL_GITTENSOR: "true",
JOBS: {
async send(message: import("../../src/types").JobMessage) {
sent.push(message);
@@ -798,11 +798,11 @@ describe("worker entrypoint", () => {
expect(sent.some((m) => m.type === "sync-brokered-installed-repos")).toBe(false);
});
- it("enqueues the sweep-liveness-watchdog job hourly ONLY when GITTENSORY_SWEEP_WATCHDOG is ON (flag-OFF is byte-identical)", async () => {
+ it("enqueues the sweep-liveness-watchdog job hourly ONLY when LOOPOVER_SWEEP_WATCHDOG is ON (flag-OFF is byte-identical)", async () => {
const sentFor = async (watchdogFlag?: string): Promise> => {
const sent: Array = [];
const env = createTestEnv({
- ...(watchdogFlag === undefined ? {} : { GITTENSORY_SWEEP_WATCHDOG: watchdogFlag }),
+ ...(watchdogFlag === undefined ? {} : { LOOPOVER_SWEEP_WATCHDOG: watchdogFlag }),
JOBS: {
async send(message: import("../../src/types").JobMessage) {
sent.push(message);
@@ -823,10 +823,10 @@ describe("worker entrypoint", () => {
expect(on.filter((m) => m.type === "sweep-liveness-watchdog")).toEqual([{ type: "sweep-liveness-watchdog", requestedBy: "schedule" }]);
});
- it("does NOT enqueue sweep-liveness-watchdog outside the hourly window even when GITTENSORY_SWEEP_WATCHDOG is ON", async () => {
+ it("does NOT enqueue sweep-liveness-watchdog outside the hourly window even when LOOPOVER_SWEEP_WATCHDOG is ON", async () => {
const sent: Array = [];
const env = createTestEnv({
- GITTENSORY_SWEEP_WATCHDOG: "true",
+ LOOPOVER_SWEEP_WATCHDOG: "true",
JOBS: {
async send(message: import("../../src/types").JobMessage) {
sent.push(message);
@@ -839,11 +839,11 @@ describe("worker entrypoint", () => {
expect(sent.some((m) => m.type === "sweep-liveness-watchdog")).toBe(false);
});
- it("enqueues the reconcile-open-prs job every 10 minutes ONLY when GITTENSORY_PR_RECONCILIATION is ON (flag-OFF is byte-identical)", async () => {
+ it("enqueues the reconcile-open-prs job every 10 minutes ONLY when LOOPOVER_PR_RECONCILIATION is ON (flag-OFF is byte-identical)", async () => {
const sentFor = async (flag?: string, isoTime = "2026-05-25T05:10:00.000Z"): Promise> => {
const sent: Array = [];
const env = createTestEnv({
- ...(flag === undefined ? {} : { GITTENSORY_PR_RECONCILIATION: flag }),
+ ...(flag === undefined ? {} : { LOOPOVER_PR_RECONCILIATION: flag }),
JOBS: {
async send(message: import("../../src/types").JobMessage) {
sent.push(message);
@@ -864,10 +864,10 @@ describe("worker entrypoint", () => {
expect(on.filter((m) => m.type === "reconcile-open-prs")).toEqual([{ type: "reconcile-open-prs", requestedBy: "schedule" }]);
});
- it("does NOT enqueue reconcile-open-prs outside the 10-minute window even when GITTENSORY_PR_RECONCILIATION is ON", async () => {
+ it("does NOT enqueue reconcile-open-prs outside the 10-minute window even when LOOPOVER_PR_RECONCILIATION is ON", async () => {
const sent: Array = [];
const env = createTestEnv({
- GITTENSORY_PR_RECONCILIATION: "true",
+ LOOPOVER_PR_RECONCILIATION: "true",
JOBS: {
async send(message: import("../../src/types").JobMessage) {
sent.push(message);
@@ -1011,11 +1011,11 @@ describe("worker entrypoint", () => {
);
});
- it("enqueues the maintainer recap digest on the default weekly cadence (Monday 14:00 UTC) ONLY when GITTENSORY_MAINTAINER_RECAP is ON (#2248, flag-OFF is byte-identical)", async () => {
+ it("enqueues the maintainer recap digest on the default weekly cadence (Monday 14:00 UTC) ONLY when LOOPOVER_MAINTAINER_RECAP is ON (#2248, flag-OFF is byte-identical)", async () => {
const sentFor = async (recapFlag?: string): Promise> => {
const sent: Array = [];
const env = createTestEnv({
- ...(recapFlag === undefined ? {} : { GITTENSORY_MAINTAINER_RECAP: recapFlag }),
+ ...(recapFlag === undefined ? {} : { LOOPOVER_MAINTAINER_RECAP: recapFlag }),
JOBS: {
async send(message: import("../../src/types").JobMessage) {
sent.push(message);
@@ -1036,10 +1036,10 @@ describe("worker entrypoint", () => {
expect(on.filter((m) => m.type === "generate-maintainer-recap")).toEqual([{ type: "generate-maintainer-recap", requestedBy: "schedule" }]);
});
- it("does NOT enqueue the maintainer recap digest outside its configured cadence even when GITTENSORY_MAINTAINER_RECAP is ON", async () => {
+ it("does NOT enqueue the maintainer recap digest outside its configured cadence even when LOOPOVER_MAINTAINER_RECAP is ON", async () => {
const sent: Array = [];
const env = createTestEnv({
- GITTENSORY_MAINTAINER_RECAP: "true",
+ LOOPOVER_MAINTAINER_RECAP: "true",
JOBS: {
async send(message: import("../../src/types").JobMessage) {
sent.push(message);
@@ -1052,11 +1052,11 @@ describe("worker entrypoint", () => {
expect(sent.some((m) => m.type === "generate-maintainer-recap")).toBe(false);
});
- it("honors a custom GITTENSORY_RECAP_CADENCE=daily, firing every day at the configured hour", async () => {
+ it("honors a custom LOOPOVER_RECAP_CADENCE=daily, firing every day at the configured hour", async () => {
const sent: Array = [];
const env = createTestEnv({
- GITTENSORY_MAINTAINER_RECAP: "true",
- GITTENSORY_RECAP_CADENCE: "daily",
+ LOOPOVER_MAINTAINER_RECAP: "true",
+ LOOPOVER_RECAP_CADENCE: "daily",
JOBS: {
async send(message: import("../../src/types").JobMessage) {
sent.push(message);
diff --git a/test/unit/local-branch.test.ts b/test/unit/local-branch.test.ts
index b6e47ddbb3..fe55a3abf4 100644
--- a/test/unit/local-branch.test.ts
+++ b/test/unit/local-branch.test.ts
@@ -1690,7 +1690,7 @@ describe("local MCP git metadata collection", () => {
afterEach(() => {
if (tempDir) rmSync(tempDir, { recursive: true, force: true });
tempDir = null;
- delete process.env.GITTENSORY_UPLOAD_SOURCE;
+ delete process.env.LOOPOVER_UPLOAD_SOURCE;
});
it("counts pending commits, emits CI hints, and tracks deleted or renamed paths", async () => {
@@ -1923,7 +1923,7 @@ describe("local MCP git metadata collection", () => {
);
expect(JSON.stringify(metadata)).not.toMatch(/export const cache/);
- process.env.GITTENSORY_UPLOAD_SOURCE = "true";
+ process.env.LOOPOVER_UPLOAD_SOURCE = "true";
expect(() => collectLocalBranchMetadata({ cwd: tempDir, baseRef: "HEAD", login: "oktofeesh1" })).toThrow(/not supported/);
});
diff --git a/test/unit/local-scorer-adapter.test.ts b/test/unit/local-scorer-adapter.test.ts
index e957f33e8a..4a74f40fc3 100644
--- a/test/unit/local-scorer-adapter.test.ts
+++ b/test/unit/local-scorer-adapter.test.ts
@@ -89,9 +89,9 @@ describe("local scorer adapter", () => {
expect(payload).not.toHaveProperty("repoRoot");
expect(JSON.stringify(payload)).not.toMatch(/BEGIN (RSA )?PRIVATE KEY/);
- process.env.GITTENSORY_UPLOAD_SOURCE = "true";
+ process.env.LOOPOVER_UPLOAD_SOURCE = "true";
expect(() => collectLocalBranchMetadata({ cwd: process.cwd(), repoFullName: "JSONbored/gittensory", login: "local" })).toThrow(/not supported/);
- delete process.env.GITTENSORY_UPLOAD_SOURCE;
+ delete process.env.LOOPOVER_UPLOAD_SOURCE;
});
it("runs the packaged reference scorer against metadata only", async () => {
diff --git a/test/unit/maintainer-activation.test.ts b/test/unit/maintainer-activation.test.ts
index 13d328b4e4..4fd847741c 100644
--- a/test/unit/maintainer-activation.test.ts
+++ b/test/unit/maintainer-activation.test.ts
@@ -184,7 +184,7 @@ describe("buildMaintainerActivationPreview", () => {
expect(undatedPreview.evaluatedCount).toBe(2);
});
- it("spares the duplicate-cluster winner when GITTENSORY_DUPLICATE_WINNER is on, flagging only the losers", () => {
+ it("spares the duplicate-cluster winner when LOOPOVER_DUPLICATE_WINNER is on, flagging only the losers", () => {
const preview = buildMaintainerActivationPreview({
repoFullName: repo.fullName,
repo,
@@ -203,7 +203,7 @@ describe("buildMaintainerActivationPreview", () => {
expect(loser.findings.map((finding) => finding.code)).toContain("duplicate_pr_risk");
});
- it("flags every duplicate-cluster member when GITTENSORY_DUPLICATE_WINNER is off (default)", () => {
+ it("flags every duplicate-cluster member when LOOPOVER_DUPLICATE_WINNER is off (default)", () => {
const preview = buildMaintainerActivationPreview({
repoFullName: repo.fullName,
repo,
diff --git a/test/unit/maintainer-recap-wire.test.ts b/test/unit/maintainer-recap-wire.test.ts
index bc444203bd..c16023126a 100644
--- a/test/unit/maintainer-recap-wire.test.ts
+++ b/test/unit/maintainer-recap-wire.test.ts
@@ -64,18 +64,18 @@ afterEach(() => {
describe("isRecapEnabled — default OFF, truthy convention", () => {
it("is OFF for unset / false / empty, ON for 1/true/yes/on", () => {
- for (const off of [undefined, "", "false", "no", "0", "off"]) expect(isRecapEnabled({ GITTENSORY_MAINTAINER_RECAP: off })).toBe(false);
- for (const on of ["1", "true", "yes", "on", "TRUE", "On"]) expect(isRecapEnabled({ GITTENSORY_MAINTAINER_RECAP: on })).toBe(true);
+ for (const off of [undefined, "", "false", "no", "0", "off"]) expect(isRecapEnabled({ LOOPOVER_MAINTAINER_RECAP: off })).toBe(false);
+ for (const on of ["1", "true", "yes", "on", "TRUE", "On"]) expect(isRecapEnabled({ LOOPOVER_MAINTAINER_RECAP: on })).toBe(true);
});
it("a present manifest override wins outright over the env flag, in both directions (#2250)", () => {
- expect(isRecapEnabled({ GITTENSORY_MAINTAINER_RECAP: "false" }, { present: true, enabled: true, cadence: "weekly" })).toBe(true);
- expect(isRecapEnabled({ GITTENSORY_MAINTAINER_RECAP: "true" }, { present: true, enabled: false, cadence: "weekly" })).toBe(false);
+ expect(isRecapEnabled({ LOOPOVER_MAINTAINER_RECAP: "false" }, { present: true, enabled: true, cadence: "weekly" })).toBe(true);
+ expect(isRecapEnabled({ LOOPOVER_MAINTAINER_RECAP: "true" }, { present: true, enabled: false, cadence: "weekly" })).toBe(false);
});
it("falls back to the env flag when the manifest override is not present", () => {
- expect(isRecapEnabled({ GITTENSORY_MAINTAINER_RECAP: "true" }, { present: false, enabled: false, cadence: "weekly" })).toBe(true);
- expect(isRecapEnabled({ GITTENSORY_MAINTAINER_RECAP: "false" }, undefined)).toBe(false);
+ expect(isRecapEnabled({ LOOPOVER_MAINTAINER_RECAP: "true" }, { present: false, enabled: false, cadence: "weekly" })).toBe(true);
+ expect(isRecapEnabled({ LOOPOVER_MAINTAINER_RECAP: "false" }, undefined)).toBe(false);
});
});
@@ -87,12 +87,12 @@ describe("shouldFireMaintainerRecap — cadence gate (#2248)", () => {
});
it("an explicit weekly cadence behaves exactly like the default", () => {
- expect(shouldFireMaintainerRecap({ GITTENSORY_RECAP_CADENCE: "weekly" }, 14, 1)).toBe(true);
- expect(shouldFireMaintainerRecap({ GITTENSORY_RECAP_CADENCE: "weekly" }, 14, 2)).toBe(false);
+ expect(shouldFireMaintainerRecap({ LOOPOVER_RECAP_CADENCE: "weekly" }, 14, 1)).toBe(true);
+ expect(shouldFireMaintainerRecap({ LOOPOVER_RECAP_CADENCE: "weekly" }, 14, 2)).toBe(false);
});
it("daily cadence fires every day at the configured hour, ignoring day-of-week", () => {
- const env = { GITTENSORY_RECAP_CADENCE: "daily" };
+ const env = { LOOPOVER_RECAP_CADENCE: "daily" };
expect(shouldFireMaintainerRecap(env, 14, 1)).toBe(true);
expect(shouldFireMaintainerRecap(env, 14, 3)).toBe(true);
expect(shouldFireMaintainerRecap(env, 14, 6)).toBe(true);
@@ -100,42 +100,42 @@ describe("shouldFireMaintainerRecap — cadence gate (#2248)", () => {
});
it("an invalid cadence value falls back to weekly (not daily), so a typo can't quietly fire more often", () => {
- const env = { GITTENSORY_RECAP_CADENCE: "biweekly" };
+ const env = { LOOPOVER_RECAP_CADENCE: "biweekly" };
expect(shouldFireMaintainerRecap(env, 14, 1)).toBe(true); // Monday still fires (weekly default)
expect(shouldFireMaintainerRecap(env, 14, 2)).toBe(false); // Tuesday does not — proves it is NOT daily
});
it("respects a custom configured hour and day-of-week", () => {
- const env = { GITTENSORY_RECAP_CADENCE: "weekly", GITTENSORY_RECAP_HOUR: "3", GITTENSORY_RECAP_DAY: "5" };
+ const env = { LOOPOVER_RECAP_CADENCE: "weekly", LOOPOVER_RECAP_HOUR: "3", LOOPOVER_RECAP_DAY: "5" };
expect(shouldFireMaintainerRecap(env, 3, 5)).toBe(true);
expect(shouldFireMaintainerRecap(env, 3, 1)).toBe(false); // the default Monday no longer applies
expect(shouldFireMaintainerRecap(env, 14, 5)).toBe(false); // the default hour no longer applies
});
it("clamps an out-of-range (but finite) hour/day to the nearest bound", () => {
- const env = { GITTENSORY_RECAP_HOUR: "99", GITTENSORY_RECAP_DAY: "-3" };
+ const env = { LOOPOVER_RECAP_HOUR: "99", LOOPOVER_RECAP_DAY: "-3" };
expect(shouldFireMaintainerRecap(env, 23, 0)).toBe(true); // 99 → 23 (MAX_HOUR), -3 → 0 (MIN_DAY_OF_WEEK)
expect(shouldFireMaintainerRecap(env, 14, 1)).toBe(false); // the (unclamped) default no longer matches
});
it("falls back to the default hour/day on a non-finite value", () => {
- const env = { GITTENSORY_RECAP_HOUR: "not-a-number", GITTENSORY_RECAP_DAY: "nope" };
+ const env = { LOOPOVER_RECAP_HOUR: "not-a-number", LOOPOVER_RECAP_DAY: "nope" };
expect(shouldFireMaintainerRecap(env, 14, 1)).toBe(true); // falls back to 14 / Monday
});
it("a present manifest override's cadence wins over the env cadence, in both directions (#2250)", () => {
const dailyOverride = { present: true, enabled: true, cadence: "daily" } as const;
// env says weekly, manifest says daily -> fires on a non-Monday too (hour still gates).
- expect(shouldFireMaintainerRecap({ GITTENSORY_RECAP_CADENCE: "weekly" }, 14, 3, dailyOverride)).toBe(true);
+ expect(shouldFireMaintainerRecap({ LOOPOVER_RECAP_CADENCE: "weekly" }, 14, 3, dailyOverride)).toBe(true);
const weeklyOverride = { present: true, enabled: true, cadence: "weekly" } as const;
// env says daily, manifest says weekly -> does NOT fire on a non-Monday.
- expect(shouldFireMaintainerRecap({ GITTENSORY_RECAP_CADENCE: "daily" }, 14, 3, weeklyOverride)).toBe(false);
- expect(shouldFireMaintainerRecap({ GITTENSORY_RECAP_CADENCE: "daily" }, 14, 1, weeklyOverride)).toBe(true);
+ expect(shouldFireMaintainerRecap({ LOOPOVER_RECAP_CADENCE: "daily" }, 14, 3, weeklyOverride)).toBe(false);
+ expect(shouldFireMaintainerRecap({ LOOPOVER_RECAP_CADENCE: "daily" }, 14, 1, weeklyOverride)).toBe(true);
});
it("falls back to the env cadence when the manifest override is not present", () => {
const notPresent = { present: false, enabled: false, cadence: "daily" } as const;
- expect(shouldFireMaintainerRecap({ GITTENSORY_RECAP_CADENCE: "weekly" }, 14, 3, notPresent)).toBe(false);
+ expect(shouldFireMaintainerRecap({ LOOPOVER_RECAP_CADENCE: "weekly" }, 14, 3, notPresent)).toBe(false);
});
});
@@ -353,7 +353,7 @@ describe("runMaintainerRecapJob — cross-repo digest (#1963, #2248)", () => {
it("records a maintainer_recap_generated audit event with cadence/windowDays/repoCount/sectionCount/channelsAttempted metadata (#2251)", async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-07-09T14:00:00.000Z"));
- const env = createTestEnv({ DISCORD_WEBHOOK_URL: HOOK, GITTENSORY_RECAP_CADENCE: "daily" });
+ const env = createTestEnv({ DISCORD_WEBHOOK_URL: HOOK, LOOPOVER_RECAP_CADENCE: "daily" });
await seedRegisteredRepo(env, "owner/alpha");
await seedMergedPr(env, "owner/alpha", 1);
stubDiscordFetch();
@@ -373,7 +373,7 @@ describe("runMaintainerRecapJob — cross-repo digest (#1963, #2248)", () => {
it("a present manifest override's cadence is reflected in the maintainer_recap_generated audit metadata, not the env value", async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-07-09T14:00:00.000Z"));
- const env = createTestEnv({ DISCORD_WEBHOOK_URL: HOOK, GITTENSORY_RECAP_CADENCE: "weekly" });
+ const env = createTestEnv({ DISCORD_WEBHOOK_URL: HOOK, LOOPOVER_RECAP_CADENCE: "weekly" });
stubDiscordFetch();
await runMaintainerRecapJob(env, undefined, { present: true, enabled: true, cadence: "daily" });
diff --git a/test/unit/mcp-cli-analyze-branch.test.ts b/test/unit/mcp-cli-analyze-branch.test.ts
index c53836439e..4c38622e8f 100644
--- a/test/unit/mcp-cli-analyze-branch.test.ts
+++ b/test/unit/mcp-cli-analyze-branch.test.ts
@@ -22,7 +22,7 @@ describe("gittensory-mcp CLI — analyze-branch --format table", () => {
scoreBlockers: ["Add a linked issue.", "Increase test evidence."],
});
- const env = (url: string) => ({ GITTENSORY_API_URL: url, GITTENSORY_TOKEN: "session-token", GITTENSORY_SKIP_NPM_VERSION_CHECK: "true" });
+ const env = (url: string) => ({ LOOPOVER_API_URL: url, LOOPOVER_TOKEN: "session-token", LOOPOVER_SKIP_NPM_VERSION_CHECK: "true" });
const args = (extra: string[]) => ["analyze-branch", "--login", "JSONbored", "--cwd", tempDir as string, "--repo", "JSONbored/gittensory", ...extra];
it("renders next actions and score blockers as aligned tables", async () => {
diff --git a/test/unit/mcp-cli-basics.test.ts b/test/unit/mcp-cli-basics.test.ts
index 9738e89788..014722f4a8 100644
--- a/test/unit/mcp-cli-basics.test.ts
+++ b/test/unit/mcp-cli-basics.test.ts
@@ -142,9 +142,9 @@ describe("loopover-mcp CLI — basics", () => {
const url = await startFixtureServer();
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
};
const jsonOutput = await runAsync(["preflight", "--login", "JSONbored", "--cwd", tempDir, "--repo", "JSONbored/gittensory", "--json"], env);
const payload = JSON.parse(jsonOutput) as { workspaceIntelligence: { blockers: { accountState: string[] }; rerunWhen: string } };
@@ -227,13 +227,13 @@ describe("loopover-mcp CLI — basics", () => {
tokenSource: string;
sourceUpload: { default: boolean; enabled: boolean; source: string; supported: boolean };
};
- // The run() harness sets GITTENSORY_CONFIG_DIR but no API URL or token.
+ // The run() harness sets LOOPOVER_CONFIG_DIR but no API URL or token.
expect(payload.apiUrl).toBe("https://gittensory-api.aethereal.dev");
expect(payload.apiUrlSource).toBe("default");
expect(payload.activeProfile).toBe("default");
expect(payload.profileCount).toBeGreaterThanOrEqual(1);
expect(payload.configured).toBe(false);
- expect(payload.configPathSource).toBe("GITTENSORY_CONFIG_DIR");
+ expect(payload.configPathSource).toBe("LOOPOVER_CONFIG_DIR");
expect(payload.cacheDirSource).toBe("default");
expect(payload.tokenConfigured).toBe(false);
expect(payload.tokenSource).toBe("none");
@@ -244,14 +244,14 @@ describe("loopover-mcp CLI — basics", () => {
const secretDir = mkdtempSync(join(tmpdir(), "loopover-config-secret-"));
try {
const out = run(["config"], {
- GITTENSORY_API_URL: "https://example.test",
- GITTENSORY_TOKEN: "super-secret-token",
- GITTENSORY_CACHE_DIR: join(secretDir, "cache"),
- GITTENSORY_CONFIG_DIR: secretDir,
+ LOOPOVER_API_URL: "https://example.test",
+ LOOPOVER_TOKEN: "super-secret-token",
+ LOOPOVER_CACHE_DIR: join(secretDir, "cache"),
+ LOOPOVER_CONFIG_DIR: secretDir,
});
expect(out).toContain("API URL: https://example.test (environment)");
expect(out).toContain("Token: configured (environment)");
- expect(out).toContain("Cache dir: GITTENSORY_CACHE_DIR");
+ expect(out).toContain("Cache dir: LOOPOVER_CACHE_DIR");
expect(out).toContain("Source upload: disabled (unsupported)");
// No token value or local absolute path may appear in output.
expect(out).not.toContain("super-secret-token");
@@ -262,13 +262,13 @@ describe("loopover-mcp CLI — basics", () => {
});
it("reports enabled unsupported source upload environment settings via config", () => {
- const payload = JSON.parse(run(["config", "--json"], { GITTENSORY_UPLOAD_SOURCE: "true" })) as {
+ const payload = JSON.parse(run(["config", "--json"], { LOOPOVER_UPLOAD_SOURCE: "true" })) as {
sourceUpload: { default: boolean; enabled: boolean; source: string; supported: boolean };
};
- expect(payload.sourceUpload).toEqual({ default: false, enabled: true, source: "GITTENSORY_UPLOAD_SOURCE", supported: false });
+ expect(payload.sourceUpload).toEqual({ default: false, enabled: true, source: "LOOPOVER_UPLOAD_SOURCE", supported: false });
- const out = run(["config"], { GITTENSORY_UPLOAD_SOURCE: "true" });
- expect(out).toContain("Source upload: enabled via GITTENSORY_UPLOAD_SOURCE (unsupported; unset GITTENSORY_UPLOAD_SOURCE)");
+ const out = run(["config"], { LOOPOVER_UPLOAD_SOURCE: "true" });
+ expect(out).toContain("Source upload: enabled via LOOPOVER_UPLOAD_SOURCE (unsupported; unset LOOPOVER_UPLOAD_SOURCE)");
});
it("attributes API URL and token to a named profile from the config file", () => {
@@ -282,7 +282,7 @@ describe("loopover-mcp CLI — basics", () => {
}),
{ mode: 0o600 },
);
- const payload = JSON.parse(run(["config", "--json"], { GITTENSORY_CONFIG_DIR: configDir })) as {
+ const payload = JSON.parse(run(["config", "--json"], { LOOPOVER_CONFIG_DIR: configDir })) as {
apiUrl: string;
apiUrlSource: string;
activeProfile: string;
@@ -308,7 +308,7 @@ describe("loopover-mcp CLI — basics", () => {
const file = join(dir, "custom-config.json");
try {
writeFileSync(file, JSON.stringify({ apiUrl: "https://global.example" }), { mode: 0o600 });
- const payload = JSON.parse(run(["config", "--json"], { GITTENSORY_CONFIG_PATH: file, GITTENSORY_CONFIG_DIR: "" })) as {
+ const payload = JSON.parse(run(["config", "--json"], { LOOPOVER_CONFIG_PATH: file, LOOPOVER_CONFIG_DIR: "" })) as {
apiUrl: string;
apiUrlSource: string;
configPathSource: string;
@@ -316,7 +316,7 @@ describe("loopover-mcp CLI — basics", () => {
};
expect(payload.apiUrl).toBe("https://global.example");
expect(payload.apiUrlSource).toBe("config");
- expect(payload.configPathSource).toBe("GITTENSORY_CONFIG_PATH");
+ expect(payload.configPathSource).toBe("LOOPOVER_CONFIG_PATH");
expect(payload.configured).toBe(true);
} finally {
rmSync(dir, { recursive: true, force: true });
diff --git a/test/unit/mcp-cli-burden-forecast.test.ts b/test/unit/mcp-cli-burden-forecast.test.ts
index 3f7b149f85..eb8d2b3bfe 100644
--- a/test/unit/mcp-cli-burden-forecast.test.ts
+++ b/test/unit/mcp-cli-burden-forecast.test.ts
@@ -30,10 +30,10 @@ async function connect() {
args: [bin, "--stdio"],
env: {
...process.env,
- GITTENSORY_CONFIG_DIR: configDir,
- GITTENSORY_API_URL: apiUrl,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_API_TIMEOUT_MS: "5000",
+ LOOPOVER_CONFIG_DIR: configDir,
+ LOOPOVER_API_URL: apiUrl,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_API_TIMEOUT_MS: "5000",
},
});
client = new Client({ name: "burden-forecast-test", version: "0.0.1" });
diff --git a/test/unit/mcp-cli-doctor.test.ts b/test/unit/mcp-cli-doctor.test.ts
index 5fda310852..25cc55d8f2 100644
--- a/test/unit/mcp-cli-doctor.test.ts
+++ b/test/unit/mcp-cli-doctor.test.ts
@@ -32,12 +32,12 @@ describe("loopover-mcp CLI — doctor", () => {
writeFileSync(join(secretConfigDir, "config.json"), JSON.stringify({ apiUrl: url }), { mode: 0o600 });
const payload = JSON.parse(
await runAsync(["doctor", "--cwd", tempDir, "--repo", "JSONbored/gittensory", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: secretConfigDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: secretConfigDir,
GITTENSOR_ROOT: secretRoot,
GITTENSOR_SCORE_PREVIEW_CMD: `node ${join(process.cwd(), "test/fixtures/local-scorer/scorer-malformed.mjs")}`,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
}),
) as {
status: string;
@@ -87,11 +87,11 @@ describe("loopover-mcp CLI — doctor", () => {
git(tempDir, "remote", "set-url", "origin", "git@github.com:owner/repo$(touch /tmp/av_pwned).git");
const url = await startFixtureServer();
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
GITTENSOR_SCORE_PREVIEW_CMD: `node ${join(process.cwd(), "test/fixtures/local-scorer/scorer-success.mjs")}`,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
};
const payload = JSON.parse(await runAsync(["doctor", "--cwd", tempDir, "--json"], env)) as { nextCommand: { command: string } };
@@ -108,12 +108,12 @@ describe("loopover-mcp CLI — doctor", () => {
const url = await startFixtureServer();
const payload = JSON.parse(
await runAsync(["doctor", "--cwd", tempDir, "--repo", "JSONbored/gittensory", "--json"], {
- GITTENSORY_API_URL: url,
+ LOOPOVER_API_URL: url,
LOOPOVER_API_TOKEN: "",
- GITTENSORY_TOKEN: "",
+ LOOPOVER_TOKEN: "",
LOOPOVER_MCP_TOKEN: "",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
}),
) as {
status: string;
@@ -137,10 +137,10 @@ describe("loopover-mcp CLI — doctor", () => {
const url = await startFixtureServer({ latestVersion: "9.9.9" });
const payload = JSON.parse(
await runAsync(["status", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_NPM_REGISTRY_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_NPM_REGISTRY_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
}),
) as { package: { state: string; latestVersion: string; updateAvailable: boolean; upgradeCommand: string; npxFallback: string } };
@@ -158,10 +158,10 @@ describe("loopover-mcp CLI — doctor", () => {
const url = await startFixtureServer({ latestVersion: mcpPackageJson.version, minMcpVersion: "0.5.0" });
const payload = JSON.parse(
await runAsync(["status", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_NPM_REGISTRY_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_NPM_REGISTRY_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
}),
) as {
package: { state: string; updateAvailable: boolean; upgradeCommand?: string };
@@ -186,10 +186,10 @@ describe("loopover-mcp CLI — doctor", () => {
const aheadUrl = await startFixtureServer({ latestVersion: "0.5.0-rc.1" });
const ahead = JSON.parse(
await runAsync(["status", "--json"], {
- GITTENSORY_API_URL: aheadUrl,
- GITTENSORY_NPM_REGISTRY_URL: aheadUrl,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: aheadUrl,
+ LOOPOVER_NPM_REGISTRY_URL: aheadUrl,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
}),
) as { package: { state: string; updateAvailable: boolean } };
expect(ahead.package).toMatchObject({ state: "ahead", updateAvailable: false });
@@ -199,10 +199,10 @@ describe("loopover-mcp CLI — doctor", () => {
const staleUrl = await startFixtureServer({ latestVersion: `${oneMinorAboveLocal}-rc.1` });
const stale = JSON.parse(
await runAsync(["status", "--json"], {
- GITTENSORY_API_URL: staleUrl,
- GITTENSORY_NPM_REGISTRY_URL: staleUrl,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: staleUrl,
+ LOOPOVER_NPM_REGISTRY_URL: staleUrl,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
}),
) as { package: { state: string } };
expect(stale.package.state).toBe("stale");
@@ -213,10 +213,10 @@ describe("loopover-mcp CLI — doctor", () => {
const url = await startFixtureServer({ npmStatus: 500, compatibilityStatus: 404 });
const status = JSON.parse(
await runAsync(["status", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_NPM_REGISTRY_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_NPM_REGISTRY_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
}),
) as { package: { state: string; updateAvailable: boolean } };
expect(status.package.state).toBe("unavailable");
@@ -224,10 +224,10 @@ describe("loopover-mcp CLI — doctor", () => {
const doctor = JSON.parse(
await runAsync(["doctor", "--cwd", tempDir, "--repo", "JSONbored/gittensory", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_NPM_REGISTRY_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_NPM_REGISTRY_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
}),
) as { status: string; checks: Array<{ name: string; status: string; remediation?: string }> };
expect(doctor.checks).toEqual(expect.arrayContaining([expect.objectContaining({ name: "version", status: "warn" })]));
@@ -239,10 +239,10 @@ describe("loopover-mcp CLI — doctor", () => {
const url = await startFixtureServer({ latestVersion: oneMinorAboveLocal });
const payload = JSON.parse(
await runAsync(["doctor", "--cwd", tempDir, "--repo", "JSONbored/gittensory", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_NPM_REGISTRY_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_NPM_REGISTRY_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
}),
) as { checks: Array<{ name: string; status: string; remediation?: string }> };
const version = payload.checks.find((check) => check.name === "version");
@@ -256,20 +256,20 @@ describe("loopover-mcp CLI — doctor", () => {
const url = await startFixtureServer({ compatibilityStatus: 404 });
const payload = JSON.parse(
await runAsync(["status", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
}),
) as { apiCompatibility: { status: string } };
expect(payload.apiCompatibility.status).toBe("unavailable");
const doctor = JSON.parse(
await runAsync(["doctor", "--cwd", tempDir, "--repo", "JSONbored/gittensory", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
}),
) as { checks: Array<{ name: string; status: string }> };
expect(doctor.checks).toEqual(expect.arrayContaining([expect.objectContaining({ name: "api_compatibility", status: "warn" })]));
@@ -280,10 +280,10 @@ describe("loopover-mcp CLI — doctor", () => {
const url = await startFixtureServer({ compatibilityStatus: 503, minMcpVersion: "0.4.0" });
const payload = JSON.parse(
await runAsync(["status", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
}),
) as { apiCompatibility: { status: string; source: string; minVersion: string } };
expect(payload.apiCompatibility).toMatchObject({ status: "compatible", source: "health", minVersion: "0.4.0" });
@@ -294,10 +294,10 @@ describe("loopover-mcp CLI — doctor", () => {
const url = await startFixtureServer({ npmStatus: 500, latestRecommendedMcpVersion: oneMinorAboveLocal });
const payload = JSON.parse(
await runAsync(["status", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_NPM_REGISTRY_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_NPM_REGISTRY_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
}),
) as { package: { state: string; latestStatus: string; latestVersion: string; upgradeCommand: string } };
expect(payload.package).toMatchObject({
@@ -312,10 +312,10 @@ describe("loopover-mcp CLI — doctor", () => {
tempDir = mkdtempSync(join(tmpdir(), "gittensory-cli-"));
const url = await startFixtureServer({ minMcpVersion: "9.0.0" });
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
};
const status = JSON.parse(await runAsync(["status", "--json"], env)) as { apiCompatibility: { status: string; minVersion: string; upgradeCommand: string } };
expect(status.apiCompatibility).toMatchObject({
@@ -350,11 +350,11 @@ describe("loopover-mcp CLI — doctor", () => {
const url = await startFixtureServer();
const payload = JSON.parse(
await runAsync(["doctor", "--cwd", tempDir, "--repo", "JSONbored/gittensory", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
- GITTENSORY_UPLOAD_SOURCE: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_UPLOAD_SOURCE: "true",
}),
) as {
sourceUploadSupported: boolean;
@@ -367,7 +367,7 @@ describe("loopover-mcp CLI — doctor", () => {
expect(outputSafety).toMatchObject({ status: "fail" });
expect(outputSafety?.checks).toEqual(expect.arrayContaining([expect.objectContaining({ name: "source_upload", status: "fail" })]));
expect(payload.nextCommand).toMatchObject({
- command: "unset GITTENSORY_UPLOAD_SOURCE",
+ command: "unset LOOPOVER_UPLOAD_SOURCE",
reason: expect.stringContaining("metadata"),
});
});
@@ -377,10 +377,10 @@ describe("loopover-mcp CLI — doctor", () => {
const url = await startFixtureServer();
const payload = JSON.parse(
await runAsync(["doctor", "--cwd", tempDir, "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
}),
) as {
checklist: Array<{ id: string; status: string; checks?: Array<{ name: string; status: string; detail: string }> }>;
@@ -401,10 +401,10 @@ describe("loopover-mcp CLI — doctor", () => {
tempDir = mkdtempSync(join(tmpdir(), "gittensory-cli-"));
const url = await startFixtureServer({ latestVersion: "9.9.9", minMcpVersion: "9.0.0" });
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_NPM_REGISTRY_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_NPM_REGISTRY_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
};
const statusOutput = await runAsync(["status"], env);
const statusJsonOutput = await runAsync(["status", "--json"], env);
@@ -426,9 +426,9 @@ describe("loopover-mcp CLI — doctor", () => {
// No token configured -> the auth check fails -> status "needs_attention".
const payload = JSON.parse(
await runAsync(["doctor", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
}),
) as { status: string; checks: Array<{ name: string; status: string }> };
expect(payload.status).toBe("needs_attention");
@@ -445,10 +445,10 @@ describe("loopover-mcp CLI — doctor", () => {
encoding: "utf8",
env: {
...process.env,
- GITTENSORY_API_TIMEOUT_MS: "1000",
- GITTENSORY_API_URL: url,
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_TIMEOUT_MS: "1000",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
},
stdio: ["ignore", "pipe", "pipe"],
});
@@ -468,10 +468,10 @@ describe("loopover-mcp CLI — doctor", () => {
// runAsync resolves only on a zero exit code, so reaching the assertion proves exit 0.
const payload = JSON.parse(
await runAsync(["doctor", "--exit-code", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
}),
) as { status: string };
expect(payload.status).toMatch(/ok|warnings/);
diff --git a/test/unit/mcp-cli-find-opportunities.test.ts b/test/unit/mcp-cli-find-opportunities.test.ts
index 8219ef08fa..a3b5791098 100644
--- a/test/unit/mcp-cli-find-opportunities.test.ts
+++ b/test/unit/mcp-cli-find-opportunities.test.ts
@@ -38,10 +38,10 @@ async function connect() {
args: [bin, "--stdio"],
env: {
...process.env,
- GITTENSORY_CONFIG_DIR: configDir,
- GITTENSORY_API_URL: apiUrl,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_API_TIMEOUT_MS: "5000",
+ LOOPOVER_CONFIG_DIR: configDir,
+ LOOPOVER_API_URL: apiUrl,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_API_TIMEOUT_MS: "5000",
},
});
client = new Client({ name: "find-opportunities-test", version: "0.0.1" });
diff --git a/test/unit/mcp-cli-issue-rag.test.ts b/test/unit/mcp-cli-issue-rag.test.ts
index 2f7a9d1093..79b220de01 100644
--- a/test/unit/mcp-cli-issue-rag.test.ts
+++ b/test/unit/mcp-cli-issue-rag.test.ts
@@ -38,10 +38,10 @@ async function connect() {
args: [bin, "--stdio"],
env: {
...process.env,
- GITTENSORY_CONFIG_DIR: configDir,
- GITTENSORY_API_URL: apiUrl,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_API_TIMEOUT_MS: "5000",
+ LOOPOVER_CONFIG_DIR: configDir,
+ LOOPOVER_API_URL: apiUrl,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_API_TIMEOUT_MS: "5000",
},
});
client = new Client({ name: "issue-rag-test", version: "0.0.1" });
diff --git a/test/unit/mcp-cli-issue-slop.test.ts b/test/unit/mcp-cli-issue-slop.test.ts
index ba05f0d6c6..5ea398a7b7 100644
--- a/test/unit/mcp-cli-issue-slop.test.ts
+++ b/test/unit/mcp-cli-issue-slop.test.ts
@@ -16,7 +16,7 @@ describe("loopover-mcp CLI — issue-slop", () => {
async function env() {
tempDir = mkdtempSync(join(tmpdir(), "gittensory-cli-"));
const url = await startFixtureServer();
- return { GITTENSORY_API_URL: url, GITTENSORY_TOKEN: "session-token", GITTENSORY_CONFIG_DIR: tempDir, GITTENSORY_API_TIMEOUT_MS: "1000" };
+ return { LOOPOVER_API_URL: url, LOOPOVER_TOKEN: "session-token", LOOPOVER_CONFIG_DIR: tempDir, LOOPOVER_API_TIMEOUT_MS: "1000" };
}
it("assesses issue slop via the API and prints plain or json output", async () => {
diff --git a/test/unit/mcp-cli-label-audit.test.ts b/test/unit/mcp-cli-label-audit.test.ts
index b98452e926..55c1621d8b 100644
--- a/test/unit/mcp-cli-label-audit.test.ts
+++ b/test/unit/mcp-cli-label-audit.test.ts
@@ -30,10 +30,10 @@ async function connect() {
args: [bin, "--stdio"],
env: {
...process.env,
- GITTENSORY_CONFIG_DIR: configDir,
- GITTENSORY_API_URL: apiUrl,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_API_TIMEOUT_MS: "5000",
+ LOOPOVER_CONFIG_DIR: configDir,
+ LOOPOVER_API_URL: apiUrl,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_API_TIMEOUT_MS: "5000",
},
});
client = new Client({ name: "label-audit-test", version: "0.0.1" });
diff --git a/test/unit/mcp-cli-lint-pr-text.test.ts b/test/unit/mcp-cli-lint-pr-text.test.ts
index 4e0f8bc6ec..d855e98075 100644
--- a/test/unit/mcp-cli-lint-pr-text.test.ts
+++ b/test/unit/mcp-cli-lint-pr-text.test.ts
@@ -16,7 +16,7 @@ describe("loopover-mcp CLI — lint-pr-text", () => {
async function env() {
tempDir = mkdtempSync(join(tmpdir(), "gittensory-cli-"));
const url = await startFixtureServer();
- return { GITTENSORY_API_URL: url, GITTENSORY_TOKEN: "session-token", GITTENSORY_CONFIG_DIR: tempDir, GITTENSORY_API_TIMEOUT_MS: "1000" };
+ return { LOOPOVER_API_URL: url, LOOPOVER_TOKEN: "session-token", LOOPOVER_CONFIG_DIR: tempDir, LOOPOVER_API_TIMEOUT_MS: "1000" };
}
it("lints commit + body via the API and prints plain or json output", async () => {
diff --git a/test/unit/mcp-cli-maintain.test.ts b/test/unit/mcp-cli-maintain.test.ts
index 6f709d9d96..35892a874e 100644
--- a/test/unit/mcp-cli-maintain.test.ts
+++ b/test/unit/mcp-cli-maintain.test.ts
@@ -16,7 +16,7 @@ describe("loopover-mcp CLI — maintain (#784)", () => {
async function env() {
tempDir = mkdtempSync(join(tmpdir(), "gittensory-cli-"));
const url = await startFixtureServer();
- return { GITTENSORY_API_URL: url, GITTENSORY_TOKEN: "session-token", GITTENSORY_CONFIG_DIR: tempDir, GITTENSORY_API_TIMEOUT_MS: "1000" };
+ return { LOOPOVER_API_URL: url, LOOPOVER_TOKEN: "session-token", LOOPOVER_CONFIG_DIR: tempDir, LOOPOVER_API_TIMEOUT_MS: "1000" };
}
it("status lists the agent approval queue (plain + json)", async () => {
diff --git a/test/unit/mcp-cli-maintainer-noise.test.ts b/test/unit/mcp-cli-maintainer-noise.test.ts
index d7163cad1c..229a86bd28 100644
--- a/test/unit/mcp-cli-maintainer-noise.test.ts
+++ b/test/unit/mcp-cli-maintainer-noise.test.ts
@@ -30,10 +30,10 @@ async function connect() {
args: [bin, "--stdio"],
env: {
...process.env,
- GITTENSORY_CONFIG_DIR: configDir,
- GITTENSORY_API_URL: apiUrl,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_API_TIMEOUT_MS: "5000",
+ LOOPOVER_CONFIG_DIR: configDir,
+ LOOPOVER_API_URL: apiUrl,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_API_TIMEOUT_MS: "5000",
},
});
client = new Client({ name: "maintainer-noise-test", version: "0.0.1" });
diff --git a/test/unit/mcp-cli-packets.test.ts b/test/unit/mcp-cli-packets.test.ts
index 630f74299d..5749e00c57 100644
--- a/test/unit/mcp-cli-packets.test.ts
+++ b/test/unit/mcp-cli-packets.test.ts
@@ -27,10 +27,10 @@ describe("loopover-mcp CLI — packets", () => {
tempDir = mkdtempSync(join(tmpdir(), "gittensory-cli-"));
const url = await startFixtureServer();
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_API_TIMEOUT_MS: "1000",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_API_TIMEOUT_MS: "1000",
};
const online = JSON.parse(await runAsync(["decision-pack", "--login", "JSONbored", "--json"], env)) as { status: string; source: string };
@@ -77,10 +77,10 @@ describe("loopover-mcp CLI — packets", () => {
tempDir = mkdtempSync(join(tmpdir(), "gittensory-cli-"));
const url = await startFixtureServer();
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_API_TIMEOUT_MS: "1000",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_API_TIMEOUT_MS: "1000",
};
await runAsync(["decision-pack", "--login", "JSONbored", "--json"], env);
@@ -102,10 +102,10 @@ describe("loopover-mcp CLI — packets", () => {
tempDir = mkdtempSync(join(tmpdir(), "gittensory-cli-"));
const url = await startFixtureServer();
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_API_TIMEOUT_MS: "1000",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_API_TIMEOUT_MS: "1000",
};
const empty = JSON.parse(run(["cache", "list", "--json"], env)) as { count: number; entries: unknown[] };
@@ -135,10 +135,10 @@ describe("loopover-mcp CLI — packets", () => {
tempDir = mkdtempSync(join(tmpdir(), "gittensory-cli-"));
const url = await startFixtureServer();
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_API_TIMEOUT_MS: "1000",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_API_TIMEOUT_MS: "1000",
};
// Empty cache → zero ndjson lines (not a wrapper object).
@@ -160,9 +160,9 @@ describe("loopover-mcp CLI — packets", () => {
const fixtureOptions: { decisionPackStatus?: number } = {};
const url = await startFixtureServer(fixtureOptions);
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
};
await runAsync(["decision-pack", "--login", "JSONbored", "--json"], env);
@@ -171,7 +171,7 @@ describe("loopover-mcp CLI — packets", () => {
await expect(
runAsync(["decision-pack", "--login", "JSONbored", "--json"], {
...env,
- GITTENSORY_TOKEN: "different-session-token",
+ LOOPOVER_TOKEN: "different-session-token",
}),
).rejects.toThrow(/LoopOver API 429/);
});
@@ -181,9 +181,9 @@ describe("loopover-mcp CLI — packets", () => {
const fixtureOptions: { decisionPackStatus?: number } = {};
const url = await startFixtureServer(fixtureOptions);
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
};
await runAsync(["decision-pack", "--login", "JSONbored", "--json"], env);
@@ -204,9 +204,9 @@ describe("loopover-mcp CLI — packets", () => {
} = {};
const url = await startFixtureServer(fixtureOptions);
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
};
await runAsync(["decision-pack", "--login", "JSONbored", "--json"], env);
@@ -225,16 +225,16 @@ describe("loopover-mcp CLI — packets", () => {
tempDir = mkdtempSync(join(tmpdir(), "gittensory-cli-"));
const url = await startFixtureServer();
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
};
await runAsync(["decision-pack", "--login", "JSONbored", "--json"], env);
const withoutToken = {
...env,
LOOPOVER_API_TOKEN: "",
- GITTENSORY_TOKEN: "",
+ LOOPOVER_TOKEN: "",
LOOPOVER_MCP_TOKEN: "",
};
@@ -248,9 +248,9 @@ describe("loopover-mcp CLI — packets", () => {
tempDir = mkdtempSync(join(tmpdir(), "gittensory-cli-"));
const url = await startFixtureServer();
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
};
const plan = JSON.parse(await runAsync(["agent", "plan", "--login", "JSONbored", "--repo", "JSONbored/gittensory", "--json"], env)) as {
@@ -290,9 +290,9 @@ describe("loopover-mcp CLI — packets", () => {
const output = await runAsync(
["agent", "packet", "--login", "oktofeesh1", "--cwd", tempDir, "--base", "HEAD", "--body", "Closes #39", "--validation", "passed|npm test|packet tests passed"],
{
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
},
);
@@ -332,10 +332,10 @@ describe("loopover-mcp CLI — packets", () => {
runAsync(
["agent", "packet", "--login", "oktofeesh1", "--cwd", tempDir, "--base", "HEAD"],
{
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_API_TIMEOUT_MS: "3000",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_API_TIMEOUT_MS: "3000",
},
),
).rejects.toThrow("Refusing to print unsafe public packet markdown from the server.");
@@ -377,9 +377,9 @@ describe("loopover-mcp CLI — packets", () => {
"--json",
],
{
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
},
);
@@ -424,9 +424,9 @@ describe("loopover-mcp CLI — packets", () => {
"--json",
],
{
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
},
);
@@ -465,9 +465,9 @@ describe("loopover-mcp CLI — packets", () => {
"--json",
],
{
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
},
);
diff --git a/test/unit/mcp-cli-profiles.test.ts b/test/unit/mcp-cli-profiles.test.ts
index bf4b2cf4dd..747aad9b36 100644
--- a/test/unit/mcp-cli-profiles.test.ts
+++ b/test/unit/mcp-cli-profiles.test.ts
@@ -22,9 +22,9 @@ describe("gittensory-mcp CLI — profiles", () => {
onApiRequest: (request) => requests.push({ url: request.url, authorization: request.headers.authorization }),
});
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
};
const firstLogin = JSON.parse(await runAsync(["login", "--profile", "JSONbored", "--github-token", "github-jsonbored", "--json"], env)) as { profile: string; login: string };
@@ -77,7 +77,7 @@ describe("gittensory-mcp CLI — profiles", () => {
2,
),
);
- const env = { GITTENSORY_CONFIG_DIR: tempDir, GITTENSORY_SKIP_NPM_VERSION_CHECK: "true" };
+ const env = { LOOPOVER_CONFIG_DIR: tempDir, LOOPOVER_SKIP_NPM_VERSION_CHECK: "true" };
const lines = run(["profile", "list", "--format", "ndjson"], env).trim().split("\n");
expect(lines).toHaveLength(2);
@@ -100,15 +100,15 @@ describe("gittensory-mcp CLI — profiles", () => {
onApiRequest: (request) => requests.push({ url: request.url, authorization: request.headers.authorization }),
});
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
};
await runAsync(["login", "--profile", "jsonbored", "--github-token", "github-jsonbored", "--json"], env);
await runAsync(["profile", "switch", "jsonbored", "--json"], env);
- const whoami = JSON.parse(await runAsync(["whoami", "--json"], { ...env, GITTENSORY_TOKEN: "session-okto" })) as { profile: string; login: string };
- const status = JSON.parse(await runAsync(["status", "--json"], { ...env, GITTENSORY_TOKEN: "session-okto" })) as { profile: { tokenSource: string }; auth: { login: string } };
+ const whoami = JSON.parse(await runAsync(["whoami", "--json"], { ...env, LOOPOVER_TOKEN: "session-okto" })) as { profile: string; login: string };
+ const status = JSON.parse(await runAsync(["status", "--json"], { ...env, LOOPOVER_TOKEN: "session-okto" })) as { profile: { tokenSource: string }; auth: { login: string } };
expect(whoami).toMatchObject({ profile: "jsonbored", login: "oktofeesh1" });
expect(status).toMatchObject({ auth: { login: "oktofeesh1" }, profile: { tokenSource: "environment" } });
@@ -135,7 +135,7 @@ describe("gittensory-mcp CLI — profiles", () => {
),
);
- const removed = JSON.parse(await runAsync(["profile", "remove", "default", "--json"], { GITTENSORY_CONFIG_DIR: tempDir })) as { status: string; removedProfile: string; activeProfile: string };
+ const removed = JSON.parse(await runAsync(["profile", "remove", "default", "--json"], { LOOPOVER_CONFIG_DIR: tempDir })) as { status: string; removedProfile: string; activeProfile: string };
const saved = JSON.parse(readFileSync(configPath, "utf8")) as { activeProfile: string; profiles?: Record; session?: unknown };
expect(removed).toMatchObject({ status: "removed", removedProfile: "default", activeProfile: "beta" });
@@ -154,9 +154,9 @@ describe("gittensory-mcp CLI — profiles", () => {
onApiRequest: (request) => requests.push({ url: request.url, authorization: request.headers.authorization }),
});
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
};
await runAsync(["login", "--profile", "alpha", "--github-token", "github-jsonbored", "--json"], env);
@@ -187,10 +187,10 @@ describe("gittensory-mcp CLI — profiles", () => {
const url = await startFixtureServer();
const status = JSON.parse(
await runAsync(["status", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
}),
) as { package: { name: string; version: string; latestStatus: string }; api: { status: string }; auth: { login: string } };
@@ -209,10 +209,10 @@ describe("gittensory-mcp CLI — profiles", () => {
const url = await startFixtureServer({ onApiRequest: (request) => requests.push({ url: request.url, headers: request.headers }) });
await runAsync(["status", "--json"], {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
});
const sessionRequest = requests.find((request) => request.url === "/v1/auth/session");
diff --git a/test/unit/mcp-cli-review-pr.test.ts b/test/unit/mcp-cli-review-pr.test.ts
index 93cb7cd82e..a81b5b89ac 100644
--- a/test/unit/mcp-cli-review-pr.test.ts
+++ b/test/unit/mcp-cli-review-pr.test.ts
@@ -24,9 +24,9 @@ describe("loopover-mcp CLI — review-pr", () => {
tempDir = createPacketRepo();
const url = await startFixtureServer();
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
};
const json = JSON.parse(
@@ -104,9 +104,9 @@ describe("loopover-mcp CLI — review-pr", () => {
tempDir = createPacketRepo();
const url = await startFixtureServer();
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
};
const json = JSON.parse(
@@ -153,9 +153,9 @@ describe("loopover-mcp CLI — review-pr", () => {
},
});
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
};
const json = JSON.parse(
@@ -209,9 +209,9 @@ describe("loopover-mcp CLI — review-pr", () => {
},
});
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
};
const json = JSON.parse(
@@ -251,9 +251,9 @@ describe("loopover-mcp CLI — review-pr", () => {
tempDir = createPacketRepo();
const url = await startFixtureServer();
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
};
const bodyPath = join(tempDir, "pr-body.md");
writeFileSync(bodyPath, "Fixes #1968\n\nValidated with npm test.", "utf8");
@@ -284,9 +284,9 @@ describe("loopover-mcp CLI — review-pr", () => {
tempDir = createPacketRepo();
const url = await startFixtureServer({ slopRiskStatus: 500 });
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
};
const json = JSON.parse(
@@ -347,9 +347,9 @@ describe("loopover-mcp CLI — review-pr", () => {
tempDir = createPacketRepo();
const url = await startFixtureServer({ prTextLintStatus: 503 });
const env = {
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_SKIP_NPM_VERSION_CHECK: "true",
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_SKIP_NPM_VERSION_CHECK: "true",
};
const json = JSON.parse(
diff --git a/test/unit/mcp-cli-slop-risk.test.ts b/test/unit/mcp-cli-slop-risk.test.ts
index be80260b23..8b9503e30d 100644
--- a/test/unit/mcp-cli-slop-risk.test.ts
+++ b/test/unit/mcp-cli-slop-risk.test.ts
@@ -16,7 +16,7 @@ describe("loopover-mcp CLI — slop-risk", () => {
async function env() {
tempDir = mkdtempSync(join(tmpdir(), "gittensory-cli-"));
const url = await startFixtureServer();
- return { GITTENSORY_API_URL: url, GITTENSORY_TOKEN: "session-token", GITTENSORY_CONFIG_DIR: tempDir, GITTENSORY_API_TIMEOUT_MS: "1000" };
+ return { LOOPOVER_API_URL: url, LOOPOVER_TOKEN: "session-token", LOOPOVER_CONFIG_DIR: tempDir, LOOPOVER_API_TIMEOUT_MS: "1000" };
}
it("assesses slop risk via the API and prints plain or json output", async () => {
diff --git a/test/unit/mcp-cli-tools.test.ts b/test/unit/mcp-cli-tools.test.ts
index 43ddaf9cd5..e74ad26a9d 100644
--- a/test/unit/mcp-cli-tools.test.ts
+++ b/test/unit/mcp-cli-tools.test.ts
@@ -30,10 +30,10 @@ describe("loopover-mcp CLI — tools", () => {
args: [bin, "--stdio"],
env: {
...process.env,
- GITTENSORY_CONFIG_DIR: configDir,
- GITTENSORY_API_URL: apiUrl,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_API_TIMEOUT_MS: "5000",
+ LOOPOVER_CONFIG_DIR: configDir,
+ LOOPOVER_API_URL: apiUrl,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_API_TIMEOUT_MS: "5000",
},
});
client = new Client({ name: "tools-cli-test", version: "0.0.1" });
diff --git a/test/unit/mcp-cli-upstream-drift.test.ts b/test/unit/mcp-cli-upstream-drift.test.ts
index b8f6495115..6a7d88052d 100644
--- a/test/unit/mcp-cli-upstream-drift.test.ts
+++ b/test/unit/mcp-cli-upstream-drift.test.ts
@@ -30,10 +30,10 @@ async function connect() {
args: [bin, "--stdio"],
env: {
...process.env,
- GITTENSORY_CONFIG_DIR: configDir,
- GITTENSORY_API_URL: apiUrl,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_API_TIMEOUT_MS: "5000",
+ LOOPOVER_CONFIG_DIR: configDir,
+ LOOPOVER_API_URL: apiUrl,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_API_TIMEOUT_MS: "5000",
},
});
client = new Client({ name: "upstream-drift-test", version: "0.0.1" });
diff --git a/test/unit/mcp-cli-validate-config.test.ts b/test/unit/mcp-cli-validate-config.test.ts
index 7ef27887cd..4ccb4f4e18 100644
--- a/test/unit/mcp-cli-validate-config.test.ts
+++ b/test/unit/mcp-cli-validate-config.test.ts
@@ -16,7 +16,7 @@ describe("loopover-mcp CLI — validate-config", () => {
async function env(options: Parameters[0] = {}) {
tempDir = mkdtempSync(join(tmpdir(), "gittensory-cli-"));
const url = await startFixtureServer(options);
- return { GITTENSORY_API_URL: url, GITTENSORY_TOKEN: "session-token", GITTENSORY_CONFIG_DIR: tempDir, GITTENSORY_API_TIMEOUT_MS: "1000" };
+ return { LOOPOVER_API_URL: url, LOOPOVER_TOKEN: "session-token", LOOPOVER_CONFIG_DIR: tempDir, LOOPOVER_API_TIMEOUT_MS: "1000" };
}
it("validates a manifest file via the API and prints plain or json output", async () => {
diff --git a/test/unit/mcp-discovery.test.ts b/test/unit/mcp-discovery.test.ts
index 032e2ec02f..d1a4e6ca5a 100644
--- a/test/unit/mcp-discovery.test.ts
+++ b/test/unit/mcp-discovery.test.ts
@@ -23,8 +23,8 @@ async function connect() {
args: [bin, "--stdio"],
env: {
...process.env,
- GITTENSORY_CONFIG_DIR: configDir,
- GITTENSORY_API_TIMEOUT_MS: "1000",
+ LOOPOVER_CONFIG_DIR: configDir,
+ LOOPOVER_API_TIMEOUT_MS: "1000",
},
});
client = new Client({ name: "discovery-test", version: "0.0.1" });
@@ -55,8 +55,8 @@ describe("MCP workspace root boundaries", () => {
args: [bin, "--stdio"],
env: {
...process.env,
- GITTENSORY_CONFIG_DIR: localConfigDir,
- GITTENSORY_API_TIMEOUT_MS: "1000",
+ LOOPOVER_CONFIG_DIR: localConfigDir,
+ LOOPOVER_API_TIMEOUT_MS: "1000",
},
});
const rootedClient = new Client({ name: "roots-boundary-test", version: "0.0.1" }, { capabilities: { roots: {} } });
diff --git a/test/unit/mcp-feasibility-gate.test.ts b/test/unit/mcp-feasibility-gate.test.ts
index 770d1fb0be..6bfd4630eb 100644
--- a/test/unit/mcp-feasibility-gate.test.ts
+++ b/test/unit/mcp-feasibility-gate.test.ts
@@ -20,7 +20,7 @@ async function connect() {
args: [bin, "--stdio"],
env: {
...process.env,
- GITTENSORY_CONFIG_DIR: configDir,
+ LOOPOVER_CONFIG_DIR: configDir,
},
});
client = new Client({ name: "feasibility-gate-test", version: "0.0.1" });
@@ -108,7 +108,7 @@ describe("loopover_feasibility_gate: local claim-ledger sourcing (#5157)", () =>
async function connectWithLedgerDb(dbPath: string | undefined) {
ledgerConfigDir = mkdtempSync(join(tmpdir(), "gittensory-feasibility-gate-ledger-"));
- const env: Record = { ...(process.env as Record), GITTENSORY_CONFIG_DIR: ledgerConfigDir };
+ const env: Record = { ...(process.env as Record), LOOPOVER_CONFIG_DIR: ledgerConfigDir };
if (dbPath !== undefined) env.LOOPOVER_MINER_CLAIM_LEDGER_DB = dbPath;
else delete env.LOOPOVER_MINER_CLAIM_LEDGER_DB;
ledgerTransport = new StdioClientTransport({ command: "node", args: [bin, "--stdio"], env });
diff --git a/test/unit/mcp-tool-rename-aliases.test.ts b/test/unit/mcp-tool-rename-aliases.test.ts
index 41a2a78a92..10b96491e6 100644
--- a/test/unit/mcp-tool-rename-aliases.test.ts
+++ b/test/unit/mcp-tool-rename-aliases.test.ts
@@ -24,9 +24,9 @@ async function connect(apiUrl: string) {
args: [bin, "--stdio"],
env: {
...process.env,
- GITTENSORY_CONFIG_DIR: configDir,
- GITTENSORY_API_URL: apiUrl,
- GITTENSORY_API_TIMEOUT_MS: "5000",
+ LOOPOVER_CONFIG_DIR: configDir,
+ LOOPOVER_API_URL: apiUrl,
+ LOOPOVER_API_TIMEOUT_MS: "5000",
},
});
client = new Client({ name: "rename-alias-test", version: "0.0.1" });
diff --git a/test/unit/miner-cli.test.ts b/test/unit/miner-cli.test.ts
index 43e4b890eb..0dcc05522a 100644
--- a/test/unit/miner-cli.test.ts
+++ b/test/unit/miner-cli.test.ts
@@ -109,7 +109,7 @@ describe("loopover-miner startup update check (#2331)", () => {
expect(resolveNpmRegistryUrl({})).toBe("https://registry.npmjs.org");
expect(
resolveNpmRegistryUrl({
- GITTENSORY_NPM_REGISTRY_URL: "https://registry.example.com/",
+ LOOPOVER_NPM_REGISTRY_URL: "https://registry.example.com/",
}),
).toBe("https://registry.example.com");
expect(resolveUpgradeCommand("@loopover/miner")).toBe(
@@ -120,22 +120,22 @@ describe("loopover-miner startup update check (#2331)", () => {
it("falls back to the default npm registry for unsafe or invalid registry URLs", () => {
expect(
resolveNpmRegistryUrl({
- GITTENSORY_NPM_REGISTRY_URL: "file:///etc/passwd",
+ LOOPOVER_NPM_REGISTRY_URL: "file:///etc/passwd",
}),
).toBe("https://registry.npmjs.org");
expect(
resolveNpmRegistryUrl({
- GITTENSORY_NPM_REGISTRY_URL: "http://169.254.169.254/",
+ LOOPOVER_NPM_REGISTRY_URL: "http://169.254.169.254/",
}),
).toBe("https://registry.npmjs.org");
expect(
resolveNpmRegistryUrl({
- GITTENSORY_NPM_REGISTRY_URL: "https://user:pass@registry.example.com/",
+ LOOPOVER_NPM_REGISTRY_URL: "https://user:pass@registry.example.com/",
}),
).toBe("https://registry.npmjs.org");
expect(
resolveNpmRegistryUrl({
- GITTENSORY_NPM_REGISTRY_URL: "not-a-url",
+ LOOPOVER_NPM_REGISTRY_URL: "not-a-url",
}),
).toBe("https://registry.npmjs.org");
});
@@ -143,12 +143,12 @@ describe("loopover-miner startup update check (#2331)", () => {
it("allows http registry URLs only on local loopback hosts", () => {
expect(
resolveNpmRegistryUrl({
- GITTENSORY_NPM_REGISTRY_URL: "http://127.0.0.1:4873/",
+ LOOPOVER_NPM_REGISTRY_URL: "http://127.0.0.1:4873/",
}),
).toBe("http://127.0.0.1:4873");
expect(
resolveNpmRegistryUrl({
- GITTENSORY_NPM_REGISTRY_URL: "http://localhost:4873/",
+ LOOPOVER_NPM_REGISTRY_URL: "http://localhost:4873/",
}),
).toBe("http://localhost:4873");
});
@@ -257,7 +257,7 @@ describe("loopover-miner startup update check (#2331)", () => {
await startUpdateCheck(["--version"], {
packageName: "@loopover/miner",
packageVersion: "0.1.0",
- env: { GITTENSORY_NPM_REGISTRY_URL: registryUrl },
+ env: { LOOPOVER_NPM_REGISTRY_URL: registryUrl },
});
expect(stderr).toHaveBeenCalledWith(
"npm install -g @loopover/miner@latest\n",
@@ -272,7 +272,7 @@ describe("loopover-miner startup update check (#2331)", () => {
await startUpdateCheck(["--version"], {
packageName: "@loopover/miner",
packageVersion: "0.1.0",
- env: { GITTENSORY_NPM_REGISTRY_URL: registryUrl },
+ env: { LOOPOVER_NPM_REGISTRY_URL: registryUrl },
});
expect(stderr).not.toHaveBeenCalled();
});
@@ -283,7 +283,7 @@ describe("loopover-miner startup update check (#2331)", () => {
startUpdateCheck(["--version"], {
packageName: "@loopover/miner",
packageVersion: "0.1.0",
- env: { GITTENSORY_NPM_REGISTRY_URL: registryUrl },
+ env: { LOOPOVER_NPM_REGISTRY_URL: registryUrl },
}),
).resolves.toBeUndefined();
});
@@ -309,7 +309,7 @@ describe("loopover-miner startup update check (#2331)", () => {
const updateCheck = startUpdateCheck(["mystery"], {
packageName: "@loopover/miner",
packageVersion: "0.1.0",
- env: { GITTENSORY_NPM_REGISTRY_URL: registryUrl },
+ env: { LOOPOVER_NPM_REGISTRY_URL: registryUrl },
});
await awaitOpportunisticUpdateCheck(updateCheck);
expect(stderr).toHaveBeenCalledWith(
@@ -329,7 +329,7 @@ describe("loopover-miner startup update check (#2331)", () => {
});
const startedAt = Date.now();
const output = runCapture(["--help"], {
- GITTENSORY_NPM_REGISTRY_URL: registryUrl,
+ LOOPOVER_NPM_REGISTRY_URL: registryUrl,
});
expect(Date.now() - startedAt).toBeLessThan(2000);
expect(output).toContain("loopover-miner --help");
@@ -348,7 +348,7 @@ describe("loopover-miner startup update check (#2331)", () => {
encoding: "utf8",
env: {
...process.env,
- GITTENSORY_NPM_REGISTRY_URL: registryUrl,
+ LOOPOVER_NPM_REGISTRY_URL: registryUrl,
},
});
expect(Date.now() - startedAt).toBeLessThan(2000);
diff --git a/test/unit/parity-wire.test.ts b/test/unit/parity-wire.test.ts
index a74d0dae9f..dc2c55929f 100644
--- a/test/unit/parity-wire.test.ts
+++ b/test/unit/parity-wire.test.ts
@@ -12,7 +12,7 @@ import {
} from "../../src/db/repositories";
import { upsertRepoFocusManifest } from "../../src/signals/focus-manifest-loader";
import {
- GITTENSORY_NATIVE_SOURCE,
+ LOOPOVER_NATIVE_SOURCE,
computeParityReadiness,
isParityAuditEnabled,
nativeGateActionFromConclusion,
@@ -111,7 +111,7 @@ describe("recordNativeGateDecision — flag-gated SHADOW recording into review_a
target_id: "owner/repo#7",
event_type: "gate_decision",
decision: "merge", // success → merge
- source: GITTENSORY_NATIVE_SOURCE,
+ source: LOOPOVER_NATIVE_SOURCE,
head_sha: "abc123",
summary: "all_clear",
});
@@ -163,7 +163,7 @@ describe("recordNativeGateDecision — flag-gated SHADOW recording into review_a
const rows = await rawAll(env, "SELECT * FROM review_audit");
expect(rows.length).toBe(1);
- expect(rows[0]).toMatchObject({ decision: "close", summary: "ci_failed", source: GITTENSORY_NATIVE_SOURCE });
+ expect(rows[0]).toMatchObject({ decision: "close", summary: "ci_failed", source: LOOPOVER_NATIVE_SOURCE });
});
it("a new commit gets its OWN row", async () => {
@@ -212,14 +212,14 @@ describe("recordNativeGateDecision — flag-gated SHADOW recording into review_a
await recordNativeGateDecision(env, { project: "owner/repo", pullNumber: 7, headSha: "abc123", conclusion: "success", reasonCode: "all_clear" });
const rows = await rawAll(env, "SELECT * FROM review_audit");
expect(rows.length).toBe(1);
- expect(rows[0]).toMatchObject({ decision: "merge", source: GITTENSORY_NATIVE_SOURCE, summary: "all_clear" });
+ expect(rows[0]).toMatchObject({ decision: "merge", source: LOOPOVER_NATIVE_SOURCE, summary: "all_clear" });
// ...and an explicit "false" flag value doesn't override the self-host signal either.
const envFalse = createTestEnv({ LOOPOVER_REVIEW_PARITY_AUDIT: "false" });
await recordNativeGateDecision(envFalse, { project: "owner/repo", pullNumber: 8, headSha: "def456", conclusion: "failure", reasonCode: "slop_risk" });
const falseRows = await rawAll(envFalse, "SELECT * FROM review_audit");
expect(falseRows.length).toBe(1);
- expect(falseRows[0]).toMatchObject({ decision: "hold", source: GITTENSORY_NATIVE_SOURCE, summary: "slop_risk" });
+ expect(falseRows[0]).toMatchObject({ decision: "hold", source: LOOPOVER_NATIVE_SOURCE, summary: "slop_risk" });
});
it("fails safe: a D1 write error is swallowed + logged (telemetry never breaks finalization)", async () => {
@@ -250,7 +250,7 @@ describe("computeParityReadiness — runs computeGateParity / isParityCutoverRea
await recordNativeGateDecision(env, { project: "owner/repo", pullNumber: i, headSha: `sha${i}`, conclusion: "success" });
}
const report = await computeParityReadiness(env, { nowMs: Date.now() });
- expect(report.shadow).toBe(GITTENSORY_NATIVE_SOURCE);
+ expect(report.shadow).toBe(LOOPOVER_NATIVE_SOURCE);
expect(report.authoritative).toBe("reviewbot");
expect(report.rows).toEqual([]); // nothing to pair against → no rows
expect(report.hasSignal).toBe(false);
@@ -326,7 +326,7 @@ describe("GET /v1/internal/parity — bearer-gated, flag-gated endpoint", () =>
expect(res.status).toBe(200);
const body = (await res.json()) as { authoritative: string; shadow: string; hasSignal: boolean; rows: Array<{ project: string; pairedSamples: number; cutoverReady: boolean }> };
expect(body.authoritative).toBe("reviewbot");
- expect(body.shadow).toBe(GITTENSORY_NATIVE_SOURCE);
+ expect(body.shadow).toBe(LOOPOVER_NATIVE_SOURCE);
const row = body.rows.find((r) => r.project === "owner/repo");
expect(row?.pairedSamples).toBe(35);
expect(row?.cutoverReady).toBe(true);
@@ -433,7 +433,7 @@ function prWebhook(deliveryId: string, author: string) {
}
async function nativeRows(env: Env): Promise> {
- const res = await env.DB.prepare("SELECT decision, summary, source, miner_authored FROM review_audit WHERE source = ? AND event_type = 'gate_decision'").bind(GITTENSORY_NATIVE_SOURCE).all<{ decision: string; summary: string; source: string; miner_authored: number }>();
+ const res = await env.DB.prepare("SELECT decision, summary, source, miner_authored FROM review_audit WHERE source = ? AND event_type = 'gate_decision'").bind(LOOPOVER_NATIVE_SOURCE).all<{ decision: string; summary: string; source: string; miner_authored: number }>();
return res.results;
}
@@ -451,7 +451,7 @@ describe("recordNativeGateDecision wired into the review FINALIZE path (LOOPOVER
const rows = await nativeRows(env);
// gateEvaluation.conclusion === "failure" → native action "hold"; reasonCode = blockers[0].code.
expect(rows.length).toBe(1);
- expect(rows[0]).toMatchObject({ decision: "hold", source: GITTENSORY_NATIVE_SOURCE });
+ expect(rows[0]).toMatchObject({ decision: "hold", source: LOOPOVER_NATIVE_SOURCE });
expect(rows[0]!.summary).toBe("missing_linked_issue");
});
@@ -468,7 +468,7 @@ describe("recordNativeGateDecision wired into the review FINALIZE path (LOOPOVER
// "hold", which IS comparable → recordNativeGateDecision writes one row with the blocker code as the reason.
const rows = await nativeRows(env);
expect(rows.length).toBe(1);
- expect(rows[0]).toMatchObject({ decision: "hold", source: GITTENSORY_NATIVE_SOURCE });
+ expect(rows[0]).toMatchObject({ decision: "hold", source: LOOPOVER_NATIVE_SOURCE });
expect(rows[0]!.summary).toBe("missing_linked_issue");
});
@@ -500,7 +500,7 @@ describe("recordNativeGateDecision wired into the review FINALIZE path (LOOPOVER
}
const rows = await nativeRows(env);
expect(rows.length).toBe(1);
- expect(rows[0]).toMatchObject({ decision: "hold", source: GITTENSORY_NATIVE_SOURCE, summary: "missing_linked_issue" });
+ expect(rows[0]).toMatchObject({ decision: "hold", source: LOOPOVER_NATIVE_SOURCE, summary: "missing_linked_issue" });
});
it("#2352: a confirmed-miner author's gate decision is recorded with miner_authored = 1", async () => {
diff --git a/test/unit/pr-reconciliation.test.ts b/test/unit/pr-reconciliation.test.ts
index a5e7f91cba..27b7ab4800 100644
--- a/test/unit/pr-reconciliation.test.ts
+++ b/test/unit/pr-reconciliation.test.ts
@@ -8,8 +8,8 @@ import { createTestEnv } from "../helpers/d1";
describe("isPrReconciliationEnabled — default OFF, truthy convention", () => {
it("matches the codebase's shared truthy-string convention", () => {
- for (const off of [undefined, "", "false", "no", "0", "off"]) expect(isPrReconciliationEnabled({ GITTENSORY_PR_RECONCILIATION: off })).toBe(false);
- for (const on of ["1", "true", "yes", "on", "TRUE", "On"]) expect(isPrReconciliationEnabled({ GITTENSORY_PR_RECONCILIATION: on })).toBe(true);
+ for (const off of [undefined, "", "false", "no", "0", "off"]) expect(isPrReconciliationEnabled({ LOOPOVER_PR_RECONCILIATION: off })).toBe(false);
+ for (const on of ["1", "true", "yes", "on", "TRUE", "On"]) expect(isPrReconciliationEnabled({ LOOPOVER_PR_RECONCILIATION: on })).toBe(true);
});
});
diff --git a/test/unit/public-accuracy-trend.test.ts b/test/unit/public-accuracy-trend.test.ts
index 7763f4632a..a57ab36993 100644
--- a/test/unit/public-accuracy-trend.test.ts
+++ b/test/unit/public-accuracy-trend.test.ts
@@ -89,7 +89,7 @@ describe("buildPublicAccuracyTrend", () => {
describe("loadPublicAccuracyTrend — end-to-end over the real live tables", () => {
it("combines own-ledger merged/closed/reversed and Orb-fleet merged/closed into a consistent weekly trend", async () => {
- const env = createTestEnv({ GITTENSORY_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
+ const env = createTestEnv({ LOOPOVER_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
const thisMonday = isoWeekStart(NOW);
const thisWeekIso = `${thisMonday}T09:00:00.000Z`;
const laterInWeekIso = new Date(Date.parse(thisWeekIso) + 86_400_000).toISOString();
@@ -130,8 +130,8 @@ describe("loadPublicAccuracyTrend — end-to-end over the real live tables", ()
expect(currentWeek?.reversed).toBe(1);
});
- it("redacts a sparse Orb-fleet week when GITTENSORY_PUBLIC_STATS_REPOS is empty (no own-ledger allowlist)", async () => {
- const env = createTestEnv({ GITTENSORY_PUBLIC_STATS_REPOS: "" });
+ it("redacts a sparse Orb-fleet week when LOOPOVER_PUBLIC_STATS_REPOS is empty (no own-ledger allowlist)", async () => {
+ const env = createTestEnv({ LOOPOVER_PUBLIC_STATS_REPOS: "" });
const thisMonday = isoWeekStart(NOW);
const thisWeekIso = `${thisMonday}T09:00:00.000Z`;
await env.DB.prepare("INSERT INTO orb_github_installations (installation_id, registered) VALUES (?, 1)").bind(9002).run();
diff --git a/test/unit/public-reuse-rate-trend.test.ts b/test/unit/public-reuse-rate-trend.test.ts
index fc57742e65..8e4ded106f 100644
--- a/test/unit/public-reuse-rate-trend.test.ts
+++ b/test/unit/public-reuse-rate-trend.test.ts
@@ -70,7 +70,7 @@ describe("buildPublicReuseRateTrend", () => {
describe("loadPublicReuseRateTrend — end-to-end over the real live audit_events ledger", () => {
it("counts every github_app.*_cache_hit / *_cache_miss event, plus ai_review's three non-suffix reuse variants, as hits/misses", async () => {
- const env = createTestEnv({ GITTENSORY_PUBLIC_STATS_REPOS: "owner/repo" });
+ const env = createTestEnv({ LOOPOVER_PUBLIC_STATS_REPOS: "owner/repo" });
const thisMonday = isoWeekStart(NOW);
const thisWeekIso = `${thisMonday}T09:00:00.000Z`;
@@ -94,13 +94,13 @@ describe("loadPublicReuseRateTrend — end-to-end over the real live audit_event
});
it("returns all-zero buckets when no instrumented events exist yet", async () => {
- const env = createTestEnv({ GITTENSORY_PUBLIC_STATS_REPOS: "owner/repo" });
+ const env = createTestEnv({ LOOPOVER_PUBLIC_STATS_REPOS: "owner/repo" });
const trend = await loadPublicReuseRateTrend(env, NOW);
for (const week of trend) expect(week).toMatchObject({ hits: 0, misses: 0, reuseRatePct: null });
});
it("REGRESSION: excludes cache activity outside the public stats repo allowlist", async () => {
- const env = createTestEnv({ GITTENSORY_PUBLIC_STATS_REPOS: "owner/repo" });
+ const env = createTestEnv({ LOOPOVER_PUBLIC_STATS_REPOS: "owner/repo" });
const thisMonday = isoWeekStart(NOW);
const thisWeekIso = `${thisMonday}T09:00:00.000Z`;
diff --git a/test/unit/public-review-volume-trend.test.ts b/test/unit/public-review-volume-trend.test.ts
index 8fc70135ce..5ab706a7de 100644
--- a/test/unit/public-review-volume-trend.test.ts
+++ b/test/unit/public-review-volume-trend.test.ts
@@ -82,7 +82,7 @@ describe("buildPublicReviewVolumeTrend", () => {
describe("loadPublicReviewVolumeTrend — end-to-end over the real live tables", () => {
it("credits a PR's week by its FIRST-PUBLISHED day, not its (possibly later) merge day, and folds in the Orb fleet", async () => {
- const env = createTestEnv({ GITTENSORY_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
+ const env = createTestEnv({ LOOPOVER_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
const thisMonday = isoWeekStart(NOW);
const thisWeekIso = `${thisMonday}T09:00:00.000Z`;
const laterInWeekIso = new Date(Date.parse(thisWeekIso) + 86_400_000).toISOString();
@@ -133,7 +133,7 @@ describe("loadPublicReviewVolumeTrend — end-to-end over the real live tables",
// trend's own window, so the PR must be excluded entirely, not misattributed to the recent event's week.
// A naive single-pass query that filtered raw events by `created_at >= sinceIso` BEFORE taking MIN() would
// get this wrong: it would see only the recent event and wrongly count the PR in the current week.
- const env = createTestEnv({ GITTENSORY_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
+ const env = createTestEnv({ LOOPOVER_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
const thisMonday = isoWeekStart(NOW);
const thisWeekIso = `${thisMonday}T09:00:00.000Z`;
// 20 weeks ago: well outside the 8-week trend window, but still a real, storable timestamp.
@@ -155,8 +155,8 @@ describe("loadPublicReviewVolumeTrend — end-to-end over the real live tables",
expect(trend.every((week) => week.reviewed === 0)).toBe(true);
});
- it("still reports the Orb-fleet side when GITTENSORY_PUBLIC_STATS_REPOS is empty (no own-ledger allowlist)", async () => {
- const env = createTestEnv({ GITTENSORY_PUBLIC_STATS_REPOS: "" });
+ it("still reports the Orb-fleet side when LOOPOVER_PUBLIC_STATS_REPOS is empty (no own-ledger allowlist)", async () => {
+ const env = createTestEnv({ LOOPOVER_PUBLIC_STATS_REPOS: "" });
const thisMonday = isoWeekStart(NOW);
const thisWeekIso = `${thisMonday}T09:00:00.000Z`;
await env.DB.prepare("INSERT INTO orb_github_installations (installation_id, registered) VALUES (?, 1)").bind(9102).run();
diff --git a/test/unit/public-stats.test.ts b/test/unit/public-stats.test.ts
index e63fd51315..8a491ba175 100644
--- a/test/unit/public-stats.test.ts
+++ b/test/unit/public-stats.test.ts
@@ -20,7 +20,7 @@ function stubEnv(handler: (sql: string, args: unknown[]) => Row[]): Env {
});
return {
DB: { prepare: (sql: string) => make(sql, []) },
- GITTENSORY_PUBLIC_STATS_REPOS:
+ LOOPOVER_PUBLIC_STATS_REPOS:
"JSONbored/gittensory,JSONbored/awesome-claude,JSONbored/metagraphed",
} as unknown as Env;
}
@@ -56,9 +56,9 @@ function isReversal(sql: string): boolean {
describe("isPublicStatsEnabled", () => {
it("is truthy only for 1/true/yes/on (case-insensitive)", () => {
for (const v of ["1", "true", "TRUE", "yes", "on"])
- expect(isPublicStatsEnabled({ GITTENSORY_PUBLIC_STATS: v })).toBe(true);
+ expect(isPublicStatsEnabled({ LOOPOVER_PUBLIC_STATS: v })).toBe(true);
for (const v of ["", "0", "false", "off", "no", undefined])
- expect(isPublicStatsEnabled({ GITTENSORY_PUBLIC_STATS: v })).toBe(false);
+ expect(isPublicStatsEnabled({ LOOPOVER_PUBLIC_STATS: v })).toBe(false);
});
});
@@ -154,7 +154,7 @@ describe("getPublicStats — live aggregate over the review ledger", () => {
// #2070: mixed ledgers must COALESCE missing per-PR estimates to MINUTES_SAVED_PER_PR, not AVG-skip them.
it("sums mixed per-PR effort with fallback when one published PR lacks reviewEffortMinutes", async () => {
- const env = createTestEnv({ GITTENSORY_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
+ const env = createTestEnv({ LOOPOVER_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
const db = env.DB;
await db
@@ -329,7 +329,7 @@ describe("getPublicStats — live aggregate over the review ledger", () => {
});
it("excludes dry-run terminal actions from live reversal counts", async () => {
- const env = createTestEnv({ GITTENSORY_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
+ const env = createTestEnv({ LOOPOVER_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
const db = env.DB;
await db
@@ -392,7 +392,7 @@ describe("getPublicStats — live aggregate over the review ledger", () => {
// #1955/#2070: end-to-end over REAL D1/SQLite — published reviewEffortMinutes round-trip through
// json_extract/SUM(COALESCE(...)) into minutesSaved.
it("averages a real reviewEffortMinutes value out of metadata_json via json_extract (real D1)", async () => {
- const env = createTestEnv({ GITTENSORY_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
+ const env = createTestEnv({ LOOPOVER_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
const db = env.DB;
await db
@@ -443,7 +443,7 @@ describe("getPublicStats — live aggregate over the review ledger", () => {
});
it("deduplicates repeated public-surface publishes before averaging reviewEffortMinutes (real D1)", async () => {
- const env = createTestEnv({ GITTENSORY_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
+ const env = createTestEnv({ LOOPOVER_PUBLIC_STATS_REPOS: "JSONbored/gittensory" });
const db = env.DB;
await db
@@ -515,7 +515,7 @@ describe("getPublicStats — live aggregate over the review ledger", () => {
throw new Error("public stats must not query an unscoped own-ledger");
},
},
- GITTENSORY_PUBLIC_STATS_REPOS: "",
+ LOOPOVER_PUBLIC_STATS_REPOS: "",
} as unknown as Env;
const out = await getPublicStats(env, NOW);
expect(out.totals.handled).toBe(0);
@@ -536,7 +536,7 @@ describe("getPublicStats — live aggregate over the review ledger", () => {
throw new Error("public stats must not query an unscoped own-ledger");
},
},
- GITTENSORY_PUBLIC_STATS_REPOS: "",
+ LOOPOVER_PUBLIC_STATS_REPOS: "",
} as unknown as Env;
const out = await getPublicStats(env, NOW);
expect(out.totals.merged).toBe(12);
diff --git a/test/unit/queue-2.test.ts b/test/unit/queue-2.test.ts
index 5aa9eea1a9..2ec6cb3f22 100644
--- a/test/unit/queue-2.test.ts
+++ b/test/unit/queue-2.test.ts
@@ -456,10 +456,10 @@ describe("queue processors", () => {
expect(stickyComment.current?.body).not.toContain("is reviewing");
});
- it("flags an open-PR file-path collision against a sibling PR when GITTENSORY_OPEN_PR_FILE_COLLISION is on (#2653)", async () => {
+ it("flags an open-PR file-path collision against a sibling PR when LOOPOVER_OPEN_PR_FILE_COLLISION is on (#2653)", async () => {
const env = createTestEnv({
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
- GITTENSORY_OPEN_PR_FILE_COLLISION: "true",
+ LOOPOVER_OPEN_PR_FILE_COLLISION: "true",
});
await upsertRepositoryFromGitHub(env, { name: "gittensory", full_name: "JSONbored/gittensory", private: false, owner: { login: "JSONbored" } }, 123);
await upsertRepositorySettings(env, {
@@ -528,7 +528,7 @@ describe("queue processors", () => {
expect(stickyComment.current?.body).toContain("#8");
});
- it("does NOT flag an open-PR file-path collision when GITTENSORY_OPEN_PR_FILE_COLLISION is unset (byte-identical default)", async () => {
+ it("does NOT flag an open-PR file-path collision when LOOPOVER_OPEN_PR_FILE_COLLISION is unset (byte-identical default)", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
await upsertRepositoryFromGitHub(env, { name: "gittensory", full_name: "JSONbored/gittensory", private: false, owner: { login: "JSONbored" } }, 123);
await upsertRepositorySettings(env, {
diff --git a/test/unit/queue-5.test.ts b/test/unit/queue-5.test.ts
index 92ddd89942..d3abfd39a3 100644
--- a/test/unit/queue-5.test.ts
+++ b/test/unit/queue-5.test.ts
@@ -2563,11 +2563,11 @@ describe("queue processors", () => {
});
it("#dup-winner: flag ON spares the lowest open sibling — no duplicate block, slop not penalized for the cluster", async () => {
- // GITTENSORY_DUPLICATE_WINNER ON. A same-issue cluster of OPEN PRs (#91 winner, #92 loser) under
+ // LOOPOVER_DUPLICATE_WINNER ON. A same-issue cluster of OPEN PRs (#91 winner, #92 loser) under
// duplicatePrGateMode: block. The winner (#91, lowest open number) must NOT be gate-blocked or slop-
// penalized as a duplicate — it is judged on its own merits. This drives the flag-ON branch of the
// processors gate path (isDupWinner) + the advisory duplicate-finding suppression.
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), GITTENSORY_DUPLICATE_WINNER: "true" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_DUPLICATE_WINNER: "true" });
await persistRegistrySnapshot(
env,
normalizeRegistryPayload({ "JSONbored/gittensory": { emission_share: 0.01, issue_discovery_share: 0 } }, { kind: "raw-github", url: "https://example.test" }, "2026-05-23T00:00:00.000Z"),
@@ -2686,7 +2686,7 @@ describe("queue processors", () => {
});
it("REGRESSION (#dup-winner-slop-drift): maybePublishPrPublicSurface's slop penalty uses the LIVE-reconciled siblings, not a raw stale-cached read — a stale-cached-open lower sibling that is actually CLOSED on GitHub must not deny this PR winner status / slop-penalize it for the cluster", async () => {
- // GITTENSORY_DUPLICATE_WINNER ON. PR #95 (this PR, being reviewed) links issue #1; PR #90 (LOWER-numbered,
+ // LOOPOVER_DUPLICATE_WINNER ON. PR #95 (this PR, being reviewed) links issue #1; PR #90 (LOWER-numbered,
// same linked issue) is cached `open` in the DB (a missed/delayed `closed` webhook), but GitHub's LIVE state
// for #90 is actually `closed`. Before the fix, maybePublishPrPublicSurface's own duplicate-winner election
// read the raw, un-reconciled `listPullRequests` result (still showing #90 as open) and so wrongly denied
@@ -2694,7 +2694,7 @@ describe("queue processors", () => {
// "low") even though the gate's OWN reconciled otherOpenPullRequests (used to build the advisory/gate
// disposition) had already correctly dropped #90. After the fix, both paths agree: #95 is the winner (no
// open siblings once reconciled) and carries NO duplicate-cluster slop penalty (slop_band "clean").
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), GITTENSORY_DUPLICATE_WINNER: "true" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_DUPLICATE_WINNER: "true" });
await persistRegistrySnapshot(
env,
normalizeRegistryPayload({ "JSONbored/gittensory": { emission_share: 0.01, issue_discovery_share: 0 } }, { kind: "raw-github", url: "https://example.test" }, "2026-05-23T00:00:00.000Z"),
@@ -5714,7 +5714,7 @@ describe("queue processors", () => {
errors.mockRestore();
});
- it("sweep-liveness-watchdog job no-ops when GITTENSORY_SWEEP_WATCHDOG is OFF (does no scan, no re-enqueue)", async () => {
+ it("sweep-liveness-watchdog job no-ops when LOOPOVER_SWEEP_WATCHDOG is OFF (does no scan, no re-enqueue)", async () => {
const sent: import("../../src/types").JobMessage[] = [];
const env = createTestEnv({ JOBS: { async send(m: import("../../src/types").JobMessage) { sent.push(m); } } as unknown as Queue }); // flag unset → OFF
await upsertRepositoryFromGitHub(env, { name: "stale-repo", full_name: "owner/stale-repo", private: false, owner: { login: "owner" } }, 9310);
@@ -5726,9 +5726,9 @@ describe("queue processors", () => {
expect(sent).toEqual([]);
});
- it("sweep-liveness-watchdog job runs the liveness scan and re-enqueues a stale repo when GITTENSORY_SWEEP_WATCHDOG is ON", async () => {
+ it("sweep-liveness-watchdog job runs the liveness scan and re-enqueues a stale repo when LOOPOVER_SWEEP_WATCHDOG is ON", async () => {
const sent: import("../../src/types").JobMessage[] = [];
- const env = createTestEnv({ GITTENSORY_SWEEP_WATCHDOG: "true", JOBS: { async send(m: import("../../src/types").JobMessage) { sent.push(m); } } as unknown as Queue });
+ const env = createTestEnv({ LOOPOVER_SWEEP_WATCHDOG: "true", JOBS: { async send(m: import("../../src/types").JobMessage) { sent.push(m); } } as unknown as Queue });
await upsertRepositoryFromGitHub(env, { name: "stale-repo", full_name: "owner/stale-repo", private: false, owner: { login: "owner" } }, 9311);
await upsertRepositorySettings(env, { repoFullName: "owner/stale-repo", autonomy: { merge: "auto" } });
await upsertPullRequestFromGitHub(env, "owner/stale-repo", { number: 1, title: "PR1", state: "open", user: { login: "c" }, head: { sha: "a1" }, labels: [], body: "" });
@@ -5738,7 +5738,7 @@ describe("queue processors", () => {
expect(sent).toEqual([expect.objectContaining({ type: "agent-regate-sweep", repoFullName: "owner/stale-repo", installationId: 9311 })]);
});
- it("reconcile-open-prs job no-ops when GITTENSORY_PR_RECONCILIATION is OFF (does no scan)", async () => {
+ it("reconcile-open-prs job no-ops when LOOPOVER_PR_RECONCILIATION is OFF (does no scan)", async () => {
const env = createTestEnv(); // flag unset → OFF
await upsertRepositoryFromGitHub(env, { name: "stale-repo", full_name: "owner/stale-repo", private: false, owner: { login: "owner" } }, 9410);
await upsertRepositorySettings(env, { repoFullName: "owner/stale-repo", autonomy: { merge: "auto" } });
@@ -5749,8 +5749,8 @@ describe("queue processors", () => {
expect(reconcileSpy).not.toHaveBeenCalled();
});
- it("reconcile-open-prs job runs the reconciliation scan when GITTENSORY_PR_RECONCILIATION is ON", async () => {
- const env = createTestEnv({ GITTENSORY_PR_RECONCILIATION: "true" });
+ it("reconcile-open-prs job runs the reconciliation scan when LOOPOVER_PR_RECONCILIATION is ON", async () => {
+ const env = createTestEnv({ LOOPOVER_PR_RECONCILIATION: "true" });
await upsertRepositoryFromGitHub(env, { name: "stale-repo", full_name: "owner/stale-repo", private: false, owner: { login: "owner" } }, 9411);
await upsertRepositorySettings(env, { repoFullName: "owner/stale-repo", autonomy: { merge: "auto" } });
const reconcileSpy = vi.spyOn(backfillModule, "reconcileOpenPullRequests").mockResolvedValue({ repoFullName: "owner/stale-repo", remoteOpenCount: 0, localOpenCount: 0, missingNumbers: [] });
@@ -6275,7 +6275,7 @@ describe("queue processors", () => {
},
});
- // JSONbored/gittensory falls back to its own bundled manifest (GITTENSORY_REPO_FOCUS_MANIFEST_YAML) when
+ // JSONbored/gittensory falls back to its own bundled manifest (LOOPOVER_REPO_FOCUS_MANIFEST_YAML) when
// no other manifest source responds, which REPLACES this test's DB-configured single-mapping override
// with its own bug/feature (exclusive) + priority (additive) mapping list -- so the linked issue's
// gittensor:priority label composes with the title-derived "fix" -> gittensor:bug, rather than replacing
@@ -6640,7 +6640,7 @@ describe("queue processors", () => {
// Deliberately NOT "JSONbored/gittensory" (unlike its two sibling tests above): this repo's own
// `.loopover.yml` now enables propagation for itself (#priority-linked-issue-gate-ownership
// dogfooding), and `resolveRepositorySettings` falls back to the bundled
- // `GITTENSORY_REPO_FOCUS_MANIFEST_YAML` copy of it whenever a live manifest fetch is unavailable
+ // `LOOPOVER_REPO_FOCUS_MANIFEST_YAML` copy of it whenever a live manifest fetch is unavailable
// (`isLoopOverSelfRepo`, `src/signals/focus-manifest-loader.ts`) -- exactly the case in this test's
// stubbed fetch. Using gittensory's own literal repo name here would make this "propagation is off by
// DEFAULT" test silently stop being a default-behavior test at all.
diff --git a/test/unit/queue-lifecycle-guards.test.ts b/test/unit/queue-lifecycle-guards.test.ts
index 9c7186d7ed..11da307252 100644
--- a/test/unit/queue-lifecycle-guards.test.ts
+++ b/test/unit/queue-lifecycle-guards.test.ts
@@ -3675,7 +3675,7 @@ describe("installation app_id capture + dual-app webhook filter (#selfhost-app-i
});
it("REGRESSION (#2537, gate-flagged): reconcileLiveDuplicateSiblings must NOT serve a warm durable PR-state cache row — a cached 'open' read up to PR_STATE_CACHE_MAX_AGE_MS stale after a missed closed webhook would keep an already-closed sibling eligible as the duplicate-cluster winner, wrongly closing the CURRENT PR as the loser", async () => {
- const env = createTestEnv({ GITTENSORY_DUPLICATE_WINNER: "true" });
+ const env = createTestEnv({ LOOPOVER_DUPLICATE_WINNER: "true" });
// Seed a WARM cache row claiming the sibling is still open, but the live GitHub state below says CLOSED —
// proving the cache is never consulted: only a genuine live read can discover this and correctly reconcile it.
await seedWarmPrStateCache(env, "owner/repo", 5);
@@ -4830,7 +4830,7 @@ describe("automation-bot-skip: end-to-end webhook + re-entry wiring (#automation
});
it("a per-repo 'enabled' override skips a genuine bot-triggered PR even when the global default is OFF", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), GITTENSORY_SKIP_AUTOMATION_BOT_PRS: "false" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_SKIP_AUTOMATION_BOT_PRS: "false" });
const calls = await fetchCallTracker();
await upsertRepositorySettings(env, { repoFullName: "owner/bot-skip-repo", skipAutomationBotAuthors: "enabled" });
diff --git a/test/unit/queue.test.ts b/test/unit/queue.test.ts
index 70da84fdcd..067ce7ee95 100644
--- a/test/unit/queue.test.ts
+++ b/test/unit/queue.test.ts
@@ -620,8 +620,8 @@ describe("queue processors", () => {
vi.unstubAllGlobals();
});
- it("runs the maintainer recap job through the queue processor when GITTENSORY_MAINTAINER_RECAP is ON (#1963, #2248)", async () => {
- const env = createTestEnv({ DISCORD_WEBHOOK_URL: "https://discord.com/api/webhooks/123/abc", GITTENSORY_MAINTAINER_RECAP: "true" });
+ it("runs the maintainer recap job through the queue processor when LOOPOVER_MAINTAINER_RECAP is ON (#1963, #2248)", async () => {
+ const env = createTestEnv({ DISCORD_WEBHOOK_URL: "https://discord.com/api/webhooks/123/abc", LOOPOVER_MAINTAINER_RECAP: "true" });
await env.DB.prepare("INSERT INTO repositories (full_name, owner, name, is_installed, is_registered) VALUES (?, ?, ?, 1, 1)").bind("JSONbored/gittensory", "JSONbored", "gittensory").run();
vi.stubGlobal("fetch", async () => new Response(null, { status: 204 }));
@@ -632,7 +632,7 @@ describe("queue processors", () => {
vi.unstubAllGlobals();
});
- it("skips the maintainer recap job as a no-op when GITTENSORY_MAINTAINER_RECAP is OFF (default, #2248)", async () => {
+ it("skips the maintainer recap job as a no-op when LOOPOVER_MAINTAINER_RECAP is OFF (default, #2248)", async () => {
const env = createTestEnv({ DISCORD_WEBHOOK_URL: "https://discord.com/api/webhooks/123/abc" });
let discordFetchCalled = false;
// The disabled-check ALSO resolves the self-repo's manifest override (#2250), which may fall through to a
diff --git a/test/unit/reconcile-live-duplicate-siblings.test.ts b/test/unit/reconcile-live-duplicate-siblings.test.ts
index 164720b155..f8036cd6b1 100644
--- a/test/unit/reconcile-live-duplicate-siblings.test.ts
+++ b/test/unit/reconcile-live-duplicate-siblings.test.ts
@@ -38,7 +38,7 @@ describe("reconcileLiveDuplicateSiblings (#dup-winner / audit #15)", () => {
it("flag OFF ⇒ returns the cached list untouched and never hits GitHub", async () => {
const env = createTestEnv();
- env.GITTENSORY_DUPLICATE_WINNER = "false";
+ env.LOOPOVER_DUPLICATE_WINNER = "false";
vi.stubGlobal("fetch", async () => {
throw new Error("fetch must not be called when the flag is off");
});
@@ -49,7 +49,7 @@ describe("reconcileLiveDuplicateSiblings (#dup-winner / audit #15)", () => {
it("flag ON but the PR links no issue ⇒ unchanged (no cluster to adjudicate)", async () => {
const env = createTestEnv();
- env.GITTENSORY_DUPLICATE_WINNER = "true";
+ env.LOOPOVER_DUPLICATE_WINNER = "true";
const siblings = [makePr(5, "open", [1])];
const pr = makePr(9, "open", []);
expect(await reconcileLiveDuplicateSiblings(env, null, "owner/repo", pr, siblings)).toBe(siblings);
@@ -57,7 +57,7 @@ describe("reconcileLiveDuplicateSiblings (#dup-winner / audit #15)", () => {
it("flag ON, a higher overlapping sibling LIVE-closed ⇒ dropped because claim-time election lets higher numbers demote", async () => {
const env = createTestEnv();
- env.GITTENSORY_DUPLICATE_WINNER = "true";
+ env.LOOPOVER_DUPLICATE_WINNER = "true";
stubLiveStates({ 12: "closed" });
const siblings = [makePr(12, "open", [1])];
const pr = makePr(9, "open", [1]);
@@ -67,7 +67,7 @@ describe("reconcileLiveDuplicateSiblings (#dup-winner / audit #15)", () => {
it("flag ON, a higher overlapping sibling LIVE-open ⇒ kept (claim-time winner selection decides later)", async () => {
const env = createTestEnv();
- env.GITTENSORY_DUPLICATE_WINNER = "true";
+ env.LOOPOVER_DUPLICATE_WINNER = "true";
stubLiveStates({ 12: "open" });
const siblings = [makePr(12, "open", [1])];
const pr = makePr(9, "open", [1]);
@@ -77,7 +77,7 @@ describe("reconcileLiveDuplicateSiblings (#dup-winner / audit #15)", () => {
it("flag ON, a sibling that does NOT overlap the issue set ⇒ unchanged (not a cluster member)", async () => {
const env = createTestEnv();
- env.GITTENSORY_DUPLICATE_WINNER = "true";
+ env.LOOPOVER_DUPLICATE_WINNER = "true";
const siblings = [makePr(12, "open", [2])];
const pr = makePr(9, "open", [1]);
expect(await reconcileLiveDuplicateSiblings(env, null, "owner/repo", pr, siblings)).toBe(siblings);
@@ -85,7 +85,7 @@ describe("reconcileLiveDuplicateSiblings (#dup-winner / audit #15)", () => {
it("flag ON, a sibling already cached non-open ⇒ unchanged (the cache already excludes it)", async () => {
const env = createTestEnv();
- env.GITTENSORY_DUPLICATE_WINNER = "true";
+ env.LOOPOVER_DUPLICATE_WINNER = "true";
const siblings = [makePr(5, "closed", [1])];
const pr = makePr(9, "open", [1]);
expect(await reconcileLiveDuplicateSiblings(env, null, "owner/repo", pr, siblings)).toBe(siblings);
@@ -93,7 +93,7 @@ describe("reconcileLiveDuplicateSiblings (#dup-winner / audit #15)", () => {
it("flag ON, a lower overlapping sibling LIVE-closed ⇒ dropped so the remaining open cluster is adjudicated from live members", async () => {
const env = createTestEnv();
- env.GITTENSORY_DUPLICATE_WINNER = "true";
+ env.LOOPOVER_DUPLICATE_WINNER = "true";
stubLiveStates({ 5: "closed" });
const siblings = [makePr(5, "open", [1])];
const pr = makePr(9, "open", [1]);
@@ -103,7 +103,7 @@ describe("reconcileLiveDuplicateSiblings (#dup-winner / audit #15)", () => {
it("flag ON, a lower overlapping sibling LIVE-open ⇒ kept (this PR is genuinely a loser)", async () => {
const env = createTestEnv();
- env.GITTENSORY_DUPLICATE_WINNER = "true";
+ env.LOOPOVER_DUPLICATE_WINNER = "true";
stubLiveStates({ 5: "open" });
const siblings = [makePr(5, "open", [1])];
const pr = makePr(9, "open", [1]);
@@ -113,7 +113,7 @@ describe("reconcileLiveDuplicateSiblings (#dup-winner / audit #15)", () => {
it("flag ON, an unreadable live fetch ⇒ fails OPEN (sibling kept; no new spare on a transient hiccup)", async () => {
const env = createTestEnv();
- env.GITTENSORY_DUPLICATE_WINNER = "true";
+ env.LOOPOVER_DUPLICATE_WINNER = "true";
stubLiveStates({}); // every /pulls/{n} 404s ⇒ undefined live state
const siblings = [makePr(5, "open", [1])];
const pr = makePr(9, "open", [1]);
@@ -123,7 +123,7 @@ describe("reconcileLiveDuplicateSiblings (#dup-winner / audit #15)", () => {
it("flag ON, two overlapping siblings (one live-closed, one live-open) ⇒ only the closed one is dropped", async () => {
const env = createTestEnv();
- env.GITTENSORY_DUPLICATE_WINNER = "true";
+ env.LOOPOVER_DUPLICATE_WINNER = "true";
stubLiveStates({ 4: "closed", 12: "open" });
const siblings = [makePr(4, "open", [1]), makePr(12, "open", [1])];
const pr = makePr(9, "open", [1]);
@@ -133,7 +133,7 @@ describe("reconcileLiveDuplicateSiblings (#dup-winner / audit #15)", () => {
it("flag ON, a numeric installation whose token mint THROWS ⇒ falls back to the public token and still reconciles", async () => {
const env = createTestEnv();
- env.GITTENSORY_DUPLICATE_WINNER = "true";
+ env.LOOPOVER_DUPLICATE_WINNER = "true";
env.GITHUB_PUBLIC_TOKEN = "public-tok";
stubLiveStates({ 5: "closed" });
const siblings = [makePr(5, "open", [1])];
@@ -145,7 +145,7 @@ describe("reconcileLiveDuplicateSiblings (#dup-winner / audit #15)", () => {
it("flag ON, a numeric installation whose token mint SUCCEEDS ⇒ uses the installation token to reconcile", async () => {
const env = createTestEnv();
- env.GITTENSORY_DUPLICATE_WINNER = "true";
+ env.LOOPOVER_DUPLICATE_WINNER = "true";
mockedToken.mockResolvedValue("inst-tok");
stubLiveStates({ 5: "closed" });
const siblings = [makePr(5, "open", [1])];
diff --git a/test/unit/repo-doc-pr.test.ts b/test/unit/repo-doc-pr.test.ts
index 742519b619..109a026ffe 100644
--- a/test/unit/repo-doc-pr.test.ts
+++ b/test/unit/repo-doc-pr.test.ts
@@ -9,7 +9,7 @@ import { renderRepoSkillContent, repoSkillFilePath } from "../../src/review/repo
import { extractRepoProfile } from "../../src/review/repo-profile";
import { upsertRepoFocusManifest } from "../../src/signals/focus-manifest-loader";
import { createTestEnv } from "../helpers/d1";
-import { GITTENSORY_SITE_URL } from "../../src/github/footer";
+import { LOOPOVER_SITE_URL } from "../../src/github/footer";
function base64Utf8(text: string): string {
return Buffer.from(text, "utf8").toString("base64");
@@ -213,8 +213,8 @@ describe("openRepoDocPullRequest (#3000)", () => {
});
// #4613: a self-hoster's PUBLIC_SITE_ORIGIN reaches the generated AGENTS.md's attribution link instead
- // of GITTENSORY_SITE_URL. NOTE: createTestEnv() defaults PUBLIC_SITE_ORIGIN to a truthy value (matching
- // GITTENSORY_SITE_URL's own string), so envWithKey() alone does NOT exercise the `??` fallback's nullish
+ // of LOOPOVER_SITE_URL. NOTE: createTestEnv() defaults PUBLIC_SITE_ORIGIN to a truthy value (matching
+ // LOOPOVER_SITE_URL's own string), so envWithKey() alone does NOT exercise the `??` fallback's nullish
// side -- the paired test below overrides it to `undefined` explicitly to cover that branch too.
it("#4613: uses env.PUBLIC_SITE_ORIGIN in the generated AGENTS.md attribution link when configured", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: generateRsaPrivateKeyPem(), PUBLIC_SITE_ORIGIN: "https://gittensory.example.org" });
@@ -244,13 +244,13 @@ describe("openRepoDocPullRequest (#3000)", () => {
const tree = treeCall?.body.tree as Array<{ path: string; content: string }>;
const agentsContent = tree.find((entry) => entry.path === "AGENTS.md")?.content;
expect(agentsContent).toContain("This file is generated by [LoopOver](https://gittensory.example.org)");
- expect(agentsContent).not.toContain(GITTENSORY_SITE_URL);
+ expect(agentsContent).not.toContain(LOOPOVER_SITE_URL);
});
// #4613: the true nullish case -- PUBLIC_SITE_ORIGIN explicitly absent (unlike envWithKey()'s default,
- // which sets it) -- must still fall back to GITTENSORY_SITE_URL, matching the acceptance criterion that
+ // which sets it) -- must still fall back to LOOPOVER_SITE_URL, matching the acceptance criterion that
// default behavior for deployments without PUBLIC_SITE_ORIGIN configured is unchanged.
- it("#4613: falls back to GITTENSORY_SITE_URL when env.PUBLIC_SITE_ORIGIN is genuinely unset", async () => {
+ it("#4613: falls back to LOOPOVER_SITE_URL when env.PUBLIC_SITE_ORIGIN is genuinely unset", async () => {
const env = envWithKey();
delete env.PUBLIC_SITE_ORIGIN; // exactOptionalPropertyTypes forbids passing `undefined` via createTestEnv's overrides -- delete instead
await seedInstalledRepo(env, { defaultBranch: "main" });
@@ -278,7 +278,7 @@ describe("openRepoDocPullRequest (#3000)", () => {
const treeCall = calls.find((c) => c.url.endsWith("/git/trees"));
const tree = treeCall?.body.tree as Array<{ path: string; content: string }>;
const agentsContent = tree.find((entry) => entry.path === "AGENTS.md")?.content;
- expect(agentsContent).toContain(`This file is generated by [LoopOver](${GITTENSORY_SITE_URL})`);
+ expect(agentsContent).toContain(`This file is generated by [LoopOver](${LOOPOVER_SITE_URL})`);
});
it("falls back to a byte-identical CLAUDE.md copy when the target repo rejects a symlink tree entry", async () => {
@@ -366,7 +366,7 @@ describe("openRepoDocPullRequest (#3000)", () => {
await seedProfileData(env);
await seedRepoDocGenerationConfig(env, REPO);
const profile = await extractRepoProfile(env, REPO);
- const currentContent = renderRepoDocContent(profile, GITTENSORY_SITE_URL)!;
+ const currentContent = renderRepoDocContent(profile, LOOPOVER_SITE_URL)!;
let wroteAnything = false;
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -481,7 +481,7 @@ describe("openRepoDocPullRequest (#3000)", () => {
await seedProfileData(env);
await seedRepoDocGenerationConfig(env, REPO);
const profile = await extractRepoProfile(env, REPO);
- const staleGeneratedSection = renderRepoDocContent(profile, GITTENSORY_SITE_URL)!.replace("`npm run lint`", "an older lint command");
+ const staleGeneratedSection = renderRepoDocContent(profile, LOOPOVER_SITE_URL)!.replace("`npm run lint`", "an older lint command");
const currentContent = `# Preamble the maintainer added.\n\n${staleGeneratedSection}\nAn appendix the maintainer added.\n`;
const calls: Array<{ method: string; url: string; body: Record }> = [];
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
@@ -600,7 +600,7 @@ describe("openRepoDocPullRequest (#3000)", () => {
await seedInstalledRepo(env, { defaultBranch: "main" });
await seedSkillTriggerRepo(env, REPO);
const profile = await extractRepoProfile(env, REPO);
- const currentAgentsContent = renderRepoDocContent(profile, GITTENSORY_SITE_URL)!;
+ const currentAgentsContent = renderRepoDocContent(profile, LOOPOVER_SITE_URL)!;
const calls: Array<{ method: string; url: string; body: Record }> = [];
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
@@ -632,7 +632,7 @@ describe("openRepoDocPullRequest (#3000)", () => {
await seedInstalledRepo(env, { defaultBranch: "main" });
await seedSkillTriggerRepo(env, REPO);
const profile = await extractRepoProfile(env, REPO);
- const currentAgentsContent = renderRepoDocContent(profile, GITTENSORY_SITE_URL)!;
+ const currentAgentsContent = renderRepoDocContent(profile, LOOPOVER_SITE_URL)!;
const currentSkillContent = renderRepoSkillContent(profile)!;
let wroteAnything = false;
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
diff --git a/test/unit/repo-doc-render.test.ts b/test/unit/repo-doc-render.test.ts
index 17b8f376e9..c8a42a2612 100644
--- a/test/unit/repo-doc-render.test.ts
+++ b/test/unit/repo-doc-render.test.ts
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
import { REPO_DOC_MARKER_END, REPO_DOC_MARKER_START, renderRepoDocContent } from "../../src/review/repo-doc-render";
import type { RepoProfile, RepoProfileFileNamingStyle, RepoProfileTestFileConvention } from "../../src/review/repo-profile";
import { REPO_PROFILE_SCHEMA_VERSION } from "../../src/review/repo-profile";
-import { GITTENSORY_SITE_URL } from "../../src/github/footer";
+import { LOOPOVER_SITE_URL } from "../../src/github/footer";
function presentProfile(overrides: Partial> = {}): RepoProfile {
return {
@@ -21,11 +21,11 @@ function presentProfile(overrides: Partial {
it("renders null for an absent profile, never a placeholder document", () => {
const profile: RepoProfile = { version: REPO_PROFILE_SCHEMA_VERSION, present: false, repoFullName: "owner/widgets", generatedAt: "now", reason: "no RAG index configured or populated for this repo yet" };
- expect(renderRepoDocContent(profile, GITTENSORY_SITE_URL)).toBeNull();
+ expect(renderRepoDocContent(profile, LOOPOVER_SITE_URL)).toBeNull();
});
it("renders the marker, architecture, conventions, commands, and workflow sections for a full profile", () => {
- const content = renderRepoDocContent(presentProfile(), GITTENSORY_SITE_URL);
+ const content = renderRepoDocContent(presentProfile(), LOOPOVER_SITE_URL);
expect(content).not.toBeNull();
expect(content!.startsWith(REPO_DOC_MARKER_START)).toBe(true);
expect(content!.trimEnd().endsWith(REPO_DOC_MARKER_END)).toBe(true);
@@ -44,17 +44,17 @@ describe("renderRepoDocContent (#3000)", () => {
expect(content).toContain("Requires a linked issue: no");
expect(content).toContain("- `.github/workflows/ci.yml`");
expect(content).toContain("Generated by LoopOver from this repository's own indexed code.");
- expect(content).toContain(`This file is generated by [LoopOver](${GITTENSORY_SITE_URL})`);
+ expect(content).toContain(`This file is generated by [LoopOver](${LOOPOVER_SITE_URL})`);
});
// #4613: a self-hoster's own domain (resolved by the caller from PUBLIC_SITE_ORIGIN) replaces
- // GITTENSORY_SITE_URL in the attribution link -- the SAME (profile, siteUrl) pair still renders
+ // LOOPOVER_SITE_URL in the attribution link -- the SAME (profile, siteUrl) pair still renders
// identically on repeat calls, preserving the purity/determinism the diff-aware refresh depends on.
- it("#4613: renders the caller-provided siteUrl in the attribution link instead of GITTENSORY_SITE_URL", () => {
+ it("#4613: renders the caller-provided siteUrl in the attribution link instead of LOOPOVER_SITE_URL", () => {
const selfHostedUrl = "https://gittensory.example.org";
const content = renderRepoDocContent(presentProfile(), selfHostedUrl);
expect(content).toContain(`This file is generated by [LoopOver](${selfHostedUrl})`);
- expect(content).not.toContain(GITTENSORY_SITE_URL);
+ expect(content).not.toContain(LOOPOVER_SITE_URL);
});
it("#4613: renders byte-identical output for the same (profile, siteUrl) pair on repeat calls", () => {
@@ -65,67 +65,67 @@ describe("renderRepoDocContent (#3000)", () => {
});
it("renders byte-identical output for the same profile facts regardless of generatedAt, so refresh's no-change check is meaningful", () => {
- const a = renderRepoDocContent(presentProfile({ generatedAt: "2026-01-01T00:00:00.000Z" }), GITTENSORY_SITE_URL);
- const b = renderRepoDocContent(presentProfile({ generatedAt: "2026-12-31T23:59:59.000Z" }), GITTENSORY_SITE_URL);
+ const a = renderRepoDocContent(presentProfile({ generatedAt: "2026-01-01T00:00:00.000Z" }), LOOPOVER_SITE_URL);
+ const b = renderRepoDocContent(presentProfile({ generatedAt: "2026-12-31T23:59:59.000Z" }), LOOPOVER_SITE_URL);
expect(a).toEqual(b);
});
it("uses singular wording for exactly one indexed file and one top-level directory", () => {
- const content = renderRepoDocContent(presentProfile({ architecture: { indexedFileCount: 1, topLevelDirectories: [{ path: "src", fileCount: 1 }] } }), GITTENSORY_SITE_URL);
+ const content = renderRepoDocContent(presentProfile({ architecture: { indexedFileCount: 1, topLevelDirectories: [{ path: "src", fileCount: 1 }] } }), LOOPOVER_SITE_URL);
expect(content).toContain("1 indexed source file across 1 top-level directory:");
expect(content).toContain("- `src` -- 1 file");
});
it("caps the rendered top-level directory list and reports how many were omitted", () => {
const topLevelDirectories = Array.from({ length: 15 }, (_, i) => ({ path: `dir${i}`, fileCount: 15 - i }));
- const content = renderRepoDocContent(presentProfile({ architecture: { indexedFileCount: 200, topLevelDirectories } }), GITTENSORY_SITE_URL);
+ const content = renderRepoDocContent(presentProfile({ architecture: { indexedFileCount: 200, topLevelDirectories } }), LOOPOVER_SITE_URL);
expect(content).toContain("- `dir11` -- 4 files");
expect(content).not.toContain("`dir12`");
expect(content).toContain("- (3 more, not shown)");
});
it("degrades to 'none detected' for empty build/test/lint command lists", () => {
- const content = renderRepoDocContent(presentProfile({ commands: { packageManager: "npm", buildCommands: [], testCommands: [], lintCommands: [] } }), GITTENSORY_SITE_URL);
+ const content = renderRepoDocContent(presentProfile({ commands: { packageManager: "npm", buildCommands: [], testCommands: [], lintCommands: [] } }), LOOPOVER_SITE_URL);
expect(content).toContain("- Build: none detected");
expect(content).toContain("- Test: none detected");
expect(content).toContain("- Lint: none detected");
});
it("falls back to npm as the runner prefix and 'not detected' label when no package manager is known", () => {
- const content = renderRepoDocContent(presentProfile({ commands: { packageManager: null, buildCommands: ["build"], testCommands: [], lintCommands: [] } }), GITTENSORY_SITE_URL);
+ const content = renderRepoDocContent(presentProfile({ commands: { packageManager: null, buildCommands: ["build"], testCommands: [], lintCommands: [] } }), LOOPOVER_SITE_URL);
expect(content).toContain("Package manager: not detected");
expect(content).toContain("- Build: `npm run build`");
});
it("renders each non-npm package manager as its own run-command prefix", () => {
for (const packageManager of ["yarn", "pnpm", "bun"] as const) {
- const content = renderRepoDocContent(presentProfile({ commands: { packageManager, buildCommands: ["build"], testCommands: [], lintCommands: [] } }), GITTENSORY_SITE_URL);
+ const content = renderRepoDocContent(presentProfile({ commands: { packageManager, buildCommands: ["build"], testCommands: [], lintCommands: [] } }), LOOPOVER_SITE_URL);
expect(content).toContain(`Package manager: ${packageManager}`);
expect(content).toContain(`- Build: \`${packageManager} run build\``);
}
});
it("lists multiple commands for the same category as a comma-separated set", () => {
- const content = renderRepoDocContent(presentProfile({ commands: { packageManager: "npm", buildCommands: [], testCommands: ["test", "test:watch"], lintCommands: [] } }), GITTENSORY_SITE_URL);
+ const content = renderRepoDocContent(presentProfile({ commands: { packageManager: "npm", buildCommands: [], testCommands: ["test", "test:watch"], lintCommands: [] } }), LOOPOVER_SITE_URL);
expect(content).toContain("- Test: `npm run test`, `npm run test:watch`");
});
it("renders 'none indexed' when no CI workflow files were found", () => {
- const content = renderRepoDocContent(presentProfile({ contributionWorkflow: { gatePublishesCheck: false, linkedIssuePolicy: "optional", requireLinkedIssue: false, linkedIssueGateMode: "advisory", ciWorkflowFiles: [] } }), GITTENSORY_SITE_URL);
+ const content = renderRepoDocContent(presentProfile({ contributionWorkflow: { gatePublishesCheck: false, linkedIssuePolicy: "optional", requireLinkedIssue: false, linkedIssueGateMode: "advisory", ciWorkflowFiles: [] } }), LOOPOVER_SITE_URL);
expect(content).toContain("CI publishes a required check: no");
expect(content).toContain("Requires a linked issue: no");
expect(content).toContain("- none indexed");
});
it("renders 'yes' for requireLinkedIssue only when linkedIssueGateMode is 'block'", () => {
- const content = renderRepoDocContent(presentProfile({ contributionWorkflow: { gatePublishesCheck: true, linkedIssuePolicy: "required", requireLinkedIssue: true, linkedIssueGateMode: "block", ciWorkflowFiles: [] } }), GITTENSORY_SITE_URL);
+ const content = renderRepoDocContent(presentProfile({ contributionWorkflow: { gatePublishesCheck: true, linkedIssuePolicy: "required", requireLinkedIssue: true, linkedIssueGateMode: "block", ciWorkflowFiles: [] } }), LOOPOVER_SITE_URL);
expect(content).toContain("Requires a linked issue: yes");
});
it("does not claim a linked issue is required when requireLinkedIssue is on but linkedIssueGateMode is the advisory default (#5287)", () => {
// requireLinkedIssue alone only surfaces an advisory finding -- it never blocks on its own, so the
// generated doc must not claim "yes" unless linkedIssueGateMode is actually "block".
- const content = renderRepoDocContent(presentProfile({ contributionWorkflow: { gatePublishesCheck: true, linkedIssuePolicy: "required", requireLinkedIssue: true, linkedIssueGateMode: "advisory", ciWorkflowFiles: [] } }), GITTENSORY_SITE_URL);
+ const content = renderRepoDocContent(presentProfile({ contributionWorkflow: { gatePublishesCheck: true, linkedIssuePolicy: "required", requireLinkedIssue: true, linkedIssueGateMode: "advisory", ciWorkflowFiles: [] } }), LOOPOVER_SITE_URL);
expect(content).toContain("Requires a linked issue: no");
expect(content).not.toContain("Requires a linked issue: yes");
});
@@ -139,7 +139,7 @@ describe("renderRepoDocContent (#3000)", () => {
["unknown", "not detected"],
];
it.each(namingStyles)("renders the file-naming label for %s", (style, label) => {
- const content = renderRepoDocContent(presentProfile({ conventions: { fileNamingStyle: style, testFileConvention: "none-detected" } }), GITTENSORY_SITE_URL);
+ const content = renderRepoDocContent(presentProfile({ conventions: { fileNamingStyle: style, testFileConvention: "none-detected" } }), LOOPOVER_SITE_URL);
expect(content).toContain(`File naming: ${label}`);
});
@@ -150,7 +150,7 @@ describe("renderRepoDocContent (#3000)", () => {
["none-detected", "not detected"],
];
it.each(testConventions)("renders the test-file-convention label for %s", (convention, label) => {
- const content = renderRepoDocContent(presentProfile({ conventions: { fileNamingStyle: "unknown", testFileConvention: convention } }), GITTENSORY_SITE_URL);
+ const content = renderRepoDocContent(presentProfile({ conventions: { fileNamingStyle: "unknown", testFileConvention: convention } }), LOOPOVER_SITE_URL);
expect(content).toContain(`Test files: ${label}`);
});
});
diff --git a/test/unit/routes-contributor-issue-draft.test.ts b/test/unit/routes-contributor-issue-draft.test.ts
index d81defefa8..af3b0aef1c 100644
--- a/test/unit/routes-contributor-issue-draft.test.ts
+++ b/test/unit/routes-contributor-issue-draft.test.ts
@@ -97,7 +97,7 @@ describe("contributor-issue-drafts route auth", () => {
it("requires live GitHub write permission before session issue creation", async () => {
const app = createApp();
- const env = createTestEnv({ ADMIN_GITHUB_LOGINS: "", GITTENSORY_CONTRIBUTOR_ISSUE_TOKEN: "service-token" });
+ const env = createTestEnv({ ADMIN_GITHUB_LOGINS: "", LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN: "service-token" });
await seedRegisteredInstalledRepo(env, 201, "repo-owner", "owned-repo");
await upsertPullRequestFromGitHub(env, "repo-owner/owned-repo", {
number: 5,
@@ -185,7 +185,7 @@ describe("contributor-issue-drafts route auth", () => {
it("returns dry-run drafts for authorized static-token callers", async () => {
const app = createApp();
- const env = createTestEnv({ GITTENSORY_DRIFT_ISSUE_REPO: "JSONbored/gittensory" });
+ const env = createTestEnv({ LOOPOVER_DRIFT_ISSUE_REPO: "JSONbored/gittensory" });
const response = await app.request(
DRAFTS_PATH,
{ method: "POST", headers: apiHeaders(env), body: JSON.stringify({ dryRun: true, limit: 2 }) },
diff --git a/test/unit/routes-focus-manifest.test.ts b/test/unit/routes-focus-manifest.test.ts
index fbbc65e162..3928d8e58b 100644
--- a/test/unit/routes-focus-manifest.test.ts
+++ b/test/unit/routes-focus-manifest.test.ts
@@ -273,14 +273,14 @@ describe("focus-manifest route auth", () => {
it("returns bundled manifest and policy for authorized static-token callers", async () => {
const app = createApp();
- const env = createTestEnv({ GITTENSORY_DRIFT_ISSUE_REPO: "JSONbored/gittensory" });
+ const env = createTestEnv({ LOOPOVER_DRIFT_ISSUE_REPO: "JSONbored/gittensory" });
const response = await app.request(FOCUS_MANIFEST_PATH, { headers: apiHeaders(env) }, env);
expect(response.status).toBe(200);
await expect(response.json()).resolves.toMatchObject({
repoFullName: "JSONbored/gittensory",
manifest: {
present: true,
- wantedPaths: expect.arrayContaining(["apps/gittensory-ui/"]),
+ wantedPaths: expect.arrayContaining(["apps/loopover-ui/"]),
},
policy: {
present: true,
@@ -298,7 +298,7 @@ describe("focus-manifest route auth", () => {
it("does not refresh cached manifests from GET query parameters", async () => {
const app = createApp();
- const env = createTestEnv({ GITTENSORY_DRIFT_ISSUE_REPO: "JSONbored/gittensory" });
+ const env = createTestEnv({ LOOPOVER_DRIFT_ISSUE_REPO: "JSONbored/gittensory" });
const headers = apiHeaders(env);
const putResponse = await app.request(
FOCUS_MANIFEST_PATH,
@@ -316,7 +316,7 @@ describe("focus-manifest route auth", () => {
it("refreshes cached manifests from an unsafe POST endpoint", async () => {
const app = createApp();
- const env = createTestEnv({ GITTENSORY_DRIFT_ISSUE_REPO: "JSONbored/gittensory" });
+ const env = createTestEnv({ LOOPOVER_DRIFT_ISSUE_REPO: "JSONbored/gittensory" });
const headers = apiHeaders(env);
const putResponse = await app.request(
FOCUS_MANIFEST_PATH,
@@ -328,7 +328,7 @@ describe("focus-manifest route auth", () => {
const refreshed = await app.request(`${FOCUS_MANIFEST_PATH}/refresh`, { method: "POST", headers }, env);
expect(refreshed.status).toBe(200);
await expect(refreshed.json()).resolves.toMatchObject({
- manifest: { present: true, source: "repo_file", wantedPaths: expect.arrayContaining(["apps/gittensory-ui/"]) },
+ manifest: { present: true, source: "repo_file", wantedPaths: expect.arrayContaining(["apps/loopover-ui/"]) },
});
});
diff --git a/test/unit/scenario-input-model.test.ts b/test/unit/scenario-input-model.test.ts
index cda368e924..fc27fbfbc6 100644
--- a/test/unit/scenario-input-model.test.ts
+++ b/test/unit/scenario-input-model.test.ts
@@ -258,10 +258,10 @@ describe("source-upload safety", () => {
});
it("rejects source-upload env flag and forbidden local branch keys", () => {
- const previous = process.env.GITTENSORY_UPLOAD_SOURCE;
- process.env.GITTENSORY_UPLOAD_SOURCE = "true";
+ const previous = process.env.LOOPOVER_UPLOAD_SOURCE;
+ process.env.LOOPOVER_UPLOAD_SOURCE = "true";
expect(() => assertScenarioLocalBranchInputSafe({ login: "miner", repoFullName: "octo/demo" })).toThrow(/metadata-only/i);
- process.env.GITTENSORY_UPLOAD_SOURCE = previous;
+ process.env.LOOPOVER_UPLOAD_SOURCE = previous;
expect(() => assertScenarioLocalBranchInputSafe({ fileContent: "secret" })).toThrow(/never uploaded/i);
expect(() => assertScenarioLocalBranchInputSafe({ changedFiles: [{ path: "a.ts", diff: "code" }] })).toThrow(/never uploaded/i);
});
diff --git a/test/unit/self-dogfood-registration-pack.test.ts b/test/unit/self-dogfood-registration-pack.test.ts
index f9a9d59519..8c34ff58ec 100644
--- a/test/unit/self-dogfood-registration-pack.test.ts
+++ b/test/unit/self-dogfood-registration-pack.test.ts
@@ -226,15 +226,15 @@ function packFromRepo(
describe("resolveSelfDogfoodRepoFullName", () => {
it("defaults to the LoopOver repo when drift issue repo is unset", () => {
expect(resolveSelfDogfoodRepoFullName({})).toBe(DEFAULT_SELF_DOGFOOD_REPO);
- expect(resolveSelfDogfoodRepoFullName({ GITTENSORY_DRIFT_ISSUE_REPO: "" })).toBe(DEFAULT_SELF_DOGFOOD_REPO);
+ expect(resolveSelfDogfoodRepoFullName({ LOOPOVER_DRIFT_ISSUE_REPO: "" })).toBe(DEFAULT_SELF_DOGFOOD_REPO);
});
it("uses the configured drift issue repo when valid", () => {
- expect(resolveSelfDogfoodRepoFullName({ GITTENSORY_DRIFT_ISSUE_REPO: "acme/widget" })).toBe("acme/widget");
+ expect(resolveSelfDogfoodRepoFullName({ LOOPOVER_DRIFT_ISSUE_REPO: "acme/widget" })).toBe("acme/widget");
});
it("falls back when drift issue repo is missing a slash", () => {
- expect(resolveSelfDogfoodRepoFullName({ GITTENSORY_DRIFT_ISSUE_REPO: "invalid" })).toBe(DEFAULT_SELF_DOGFOOD_REPO);
+ expect(resolveSelfDogfoodRepoFullName({ LOOPOVER_DRIFT_ISSUE_REPO: "invalid" })).toBe(DEFAULT_SELF_DOGFOOD_REPO);
});
});
diff --git a/test/unit/selfhost-ams-reporting.test.ts b/test/unit/selfhost-ams-reporting.test.ts
index 2af211762d..f2d8519627 100644
--- a/test/unit/selfhost-ams-reporting.test.ts
+++ b/test/unit/selfhost-ams-reporting.test.ts
@@ -39,10 +39,10 @@ function runExporter(
cwd: process.cwd(),
env: {
...process.env,
- GITTENSORY_AMS_ATTEMPT_LOG_SOURCE_DB: overrides.attemptLogSource ?? join(root, "attempt-log.sqlite3"),
- GITTENSORY_AMS_PREDICTION_LEDGER_SOURCE_DB: overrides.predictionLedgerSource ?? join(root, "prediction-ledger.sqlite3"),
+ LOOPOVER_AMS_ATTEMPT_LOG_SOURCE_DB: overrides.attemptLogSource ?? join(root, "attempt-log.sqlite3"),
+ LOOPOVER_AMS_PREDICTION_LEDGER_SOURCE_DB: overrides.predictionLedgerSource ?? join(root, "prediction-ledger.sqlite3"),
LOOPOVER_REPORTING_DIR: reportingDir,
- ...(overrides.scriptVersion ? { GITTENSORY_AMS_REPORTING_SCRIPT_VERSION: overrides.scriptVersion } : {}),
+ ...(overrides.scriptVersion ? { LOOPOVER_AMS_REPORTING_SCRIPT_VERSION: overrides.scriptVersion } : {}),
},
stdio: "pipe",
encoding: "utf8",
diff --git a/test/unit/selfhost-backup-script.test.ts b/test/unit/selfhost-backup-script.test.ts
index 0374d8c012..f0a624b09e 100644
--- a/test/unit/selfhost-backup-script.test.ts
+++ b/test/unit/selfhost-backup-script.test.ts
@@ -95,7 +95,7 @@ function runBackup(root: string, env: Record): string {
...process.env,
BACKUP_OUT_DIR: join(root, "backups"),
BACKUP_RETAIN: "7",
- GITTENSORY_BACKUP_SOURCE_DATABASE_URL: "",
+ LOOPOVER_BACKUP_SOURCE_DATABASE_URL: "",
QDRANT_URL: "",
...env,
},
diff --git a/test/unit/selfhost-docker-prune-script.test.ts b/test/unit/selfhost-docker-prune-script.test.ts
index 76153ad044..07b2faa6df 100644
--- a/test/unit/selfhost-docker-prune-script.test.ts
+++ b/test/unit/selfhost-docker-prune-script.test.ts
@@ -160,8 +160,8 @@ describe("selfhost-docker-prune.sh", () => {
expect(calls).not.toContain("container prune");
});
- it("honors GITTENSORY_DOCKER_PRUNE_RETAIN_HOURS to widen or narrow the safety window", () => {
- const calls = runPruneScript(tmpRoot(), { GITTENSORY_DOCKER_PRUNE_RETAIN_HOURS: "24" });
+ it("honors LOOPOVER_DOCKER_PRUNE_RETAIN_HOURS to widen or narrow the safety window", () => {
+ const calls = runPruneScript(tmpRoot(), { LOOPOVER_DOCKER_PRUNE_RETAIN_HOURS: "24" });
expect(calls).toContain("image prune -af --filter until=24h");
expect(calls).toContain("builder prune -af --filter until=24h");
@@ -190,12 +190,12 @@ describe("selfhost-docker-prune.sh", () => {
expect(calls).not.toMatch(/\bvolume\b/);
});
- it("--dry-run still honors GITTENSORY_DOCKER_PRUNE_RETAIN_HOURS in its preview output", () => {
+ it("--dry-run still honors LOOPOVER_DOCKER_PRUNE_RETAIN_HOURS in its preview output", () => {
const root = tmpRoot();
const { binDir } = stubDocker(root);
const stdout = execFileSync("sh", ["scripts/selfhost-docker-prune.sh", "--dry-run"], {
cwd: process.cwd(),
- env: { ...process.env, PATH: `${binDir}:${process.env.PATH ?? ""}`, GITTENSORY_DOCKER_PRUNE_RETAIN_HOURS: "48" },
+ env: { ...process.env, PATH: `${binDir}:${process.env.PATH ?? ""}`, LOOPOVER_DOCKER_PRUNE_RETAIN_HOURS: "48" },
}).toString();
expect(stdout).toContain("until=48h");
@@ -242,7 +242,7 @@ describe("selfhost-docker-prune.sh", () => {
const root = tmpRoot();
const calls = runPruneScript(
root,
- { GITTENSORY_DOCKER_PRUNE_RETAIN_HOURS: "24" },
+ { LOOPOVER_DOCKER_PRUNE_RETAIN_HOURS: "24" },
[],
[
{ id: "just-under", finishedAt: isoHoursAgo(23) },
diff --git a/test/unit/selfhost-grafana-reporting.test.ts b/test/unit/selfhost-grafana-reporting.test.ts
index c83e11fecc..9bd17f2952 100644
--- a/test/unit/selfhost-grafana-reporting.test.ts
+++ b/test/unit/selfhost-grafana-reporting.test.ts
@@ -722,17 +722,17 @@ esac
);
`);
- const first = runExporter(root, appDb, outDb, { GITTENSORY_REPORTING_SCRIPT_VERSION: "test-v1" });
+ const first = runExporter(root, appDb, outDb, { LOOPOVER_REPORTING_SCRIPT_VERSION: "test-v1" });
expect(first).toContain("reporting export complete");
// Same source data, same script version -- the normal incremental fast-path applies.
- const second = runExporter(root, appDb, outDb, { GITTENSORY_REPORTING_SCRIPT_VERSION: "test-v1" });
+ const second = runExporter(root, appDb, outDb, { LOOPOVER_REPORTING_SCRIPT_VERSION: "test-v1" });
expect(second).toContain("reporting export skipped: source unchanged since last export");
// Same source data, but the script's own logic "changed" (simulated by a different version string) --
// this must NOT skip, exactly the gap that let the dead 'ignored'/'ignore' values survive a real mapping
// migration for years without the reporting DB ever refreshing.
- const third = runExporter(root, appDb, outDb, { GITTENSORY_REPORTING_SCRIPT_VERSION: "test-v2" });
+ const third = runExporter(root, appDb, outDb, { LOOPOVER_REPORTING_SCRIPT_VERSION: "test-v2" });
expect(third).toContain("reporting export complete");
});
diff --git a/test/unit/selfhost-image-deploy.test.ts b/test/unit/selfhost-image-deploy.test.ts
index 8e04e5e813..3ae62f1621 100644
--- a/test/unit/selfhost-image-deploy.test.ts
+++ b/test/unit/selfhost-image-deploy.test.ts
@@ -68,7 +68,7 @@ if [ "$1" = "compose" ]; then
fi
if [ "$1" = "inspect" ]; then
- if grep -q '^GITTENSORY_IMAGE=' "$SELFHOST_ENV_FILE" 2>/dev/null; then
+ if grep -q '^LOOPOVER_IMAGE=' "$SELFHOST_ENV_FILE" 2>/dev/null; then
printf '%s\\n' persisted-before-health >> "$DOCKER_CALLS"
else
printf '%s\\n' not-persisted-before-health >> "$DOCKER_CALLS"
@@ -129,19 +129,19 @@ describe("self-host image deploy script", () => {
{
name: "CLI argument",
args: ["ghcr.io/jsonbored/gittensory-selfhost:cli"],
- env: { GITTENSORY_IMAGE: "ghcr.io/jsonbored/gittensory-selfhost:env" },
- envFile: "GITTENSORY_IMAGE=ghcr.io/jsonbored/gittensory-selfhost:file\n",
+ env: { LOOPOVER_IMAGE: "ghcr.io/jsonbored/gittensory-selfhost:env" },
+ envFile: "LOOPOVER_IMAGE=ghcr.io/jsonbored/gittensory-selfhost:file\n",
expected: "ghcr.io/jsonbored/gittensory-selfhost:cli",
},
{
name: "environment variable",
- env: { GITTENSORY_IMAGE: "ghcr.io/jsonbored/gittensory-selfhost:env" },
- envFile: "GITTENSORY_IMAGE=ghcr.io/jsonbored/gittensory-selfhost:file\n",
+ env: { LOOPOVER_IMAGE: "ghcr.io/jsonbored/gittensory-selfhost:env" },
+ envFile: "LOOPOVER_IMAGE=ghcr.io/jsonbored/gittensory-selfhost:file\n",
expected: "ghcr.io/jsonbored/gittensory-selfhost:env",
},
{
name: ".env value",
- envFile: "GITTENSORY_IMAGE=ghcr.io/jsonbored/gittensory-selfhost:file\n",
+ envFile: "LOOPOVER_IMAGE=ghcr.io/jsonbored/gittensory-selfhost:file\n",
expected: "ghcr.io/jsonbored/gittensory-selfhost:file",
},
{
@@ -156,7 +156,7 @@ describe("self-host image deploy script", () => {
const { harness, result } = runHarness(options);
try {
expect(result.status, result.stderr).toBe(0);
- expect(readFileSync(harness.envPath, "utf8")).toContain(`GITTENSORY_IMAGE=${expected}`);
+ expect(readFileSync(harness.envPath, "utf8")).toContain(`LOOPOVER_IMAGE=${expected}`);
expect(harness.readImages()).toContain(`image: "${expected}"`);
// REGRESSION: without this reset, an operator's own docker-compose.override.yml build: block for this
// service silently wins over the pulled image at `up --no-build` time (found deploying live).
@@ -169,7 +169,7 @@ describe("self-host image deploy script", () => {
});
// REGRESSION (#4777): DEFAULT_IMAGE moved to the new "loopover-selfhost" name, but a self-hoster who
- // explicitly pinned the pre-rename "gittensory-selfhost" image (as a CLI argument, GITTENSORY_IMAGE env
+ // explicitly pinned the pre-rename "gittensory-selfhost" image (as a CLI argument, LOOPOVER_IMAGE env
// var, or .env value) must keep resolving to that exact string, unmodified -- GHCR keeps already-published
// tags/digests resolving forever with no server-side alias needed, and this script must not rewrite an
// operator's existing pin either way.
@@ -177,7 +177,7 @@ describe("self-host image deploy script", () => {
const { harness, result } = runHarness({ args: [legacyPinnedImage] });
try {
expect(result.status, result.stderr).toBe(0);
- expect(readFileSync(harness.envPath, "utf8")).toContain(`GITTENSORY_IMAGE=${legacyPinnedImage}`);
+ expect(readFileSync(harness.envPath, "utf8")).toContain(`LOOPOVER_IMAGE=${legacyPinnedImage}`);
expect(harness.readImages()).toContain(`image: "${legacyPinnedImage}"`);
} finally {
harness.cleanup();
@@ -192,7 +192,7 @@ describe("self-host image deploy script", () => {
const calls = harness.readCalls().trim().split("\n");
expect(calls).toContain("not-persisted-before-health");
expect(calls).not.toContain("persisted-before-health");
- expect(readFileSync(harness.envPath, "utf8")).toContain(`GITTENSORY_IMAGE=${image}`);
+ expect(readFileSync(harness.envPath, "utf8")).toContain(`LOOPOVER_IMAGE=${image}`);
} finally {
harness.cleanup();
}
diff --git a/test/unit/selfhost-queue-common.test.ts b/test/unit/selfhost-queue-common.test.ts
index 1dae96f4df..5a67e7ffd1 100644
--- a/test/unit/selfhost-queue-common.test.ts
+++ b/test/unit/selfhost-queue-common.test.ts
@@ -1654,7 +1654,7 @@ describe("self-host queue common helpers", () => {
});
describe("parsePositiveIntEnv", () => {
- const KNOB = "GITTENSORY_TEST_ENV_KNOB";
+ const KNOB = "LOOPOVER_TEST_ENV_KNOB";
const saved = process.env[KNOB];
afterEach(() => {
diff --git a/test/unit/selfhost-verify-backup-script.test.ts b/test/unit/selfhost-verify-backup-script.test.ts
index 3a537a461c..cadc135f4e 100644
--- a/test/unit/selfhost-verify-backup-script.test.ts
+++ b/test/unit/selfhost-verify-backup-script.test.ts
@@ -170,10 +170,10 @@ function runVerify(
...process.env,
PATH: `${bin}:${process.env.PATH ?? ""}`,
BACKUP_OUT_DIR: join(root, "backups"),
- GITTENSORY_BACKUP_SOURCE_DATABASE_URL: "",
+ LOOPOVER_BACKUP_SOURCE_DATABASE_URL: "",
DATABASE_URL: "",
VERIFY_RESTORE_SCRATCH: "",
- GITTENSORY_VERIFY_SCRATCH_DATABASE_URL: "",
+ LOOPOVER_VERIFY_SCRATCH_DATABASE_URL: "",
...env,
},
});
@@ -189,7 +189,7 @@ describe("self-host verify-backup script", () => {
const root = tmpRoot();
writePgDump(root, "gittensory-20240101T000000Z.dump", true);
- const r = runVerify(root, [], { GITTENSORY_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/db" }, { pg_restore: PG_RESTORE });
+ const r = runVerify(root, [], { LOOPOVER_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/db" }, { pg_restore: PG_RESTORE });
expect(r.status).toBe(0);
expect(r.out).toContain("postgres archive OK");
@@ -201,7 +201,7 @@ describe("self-host verify-backup script", () => {
const root = tmpRoot();
writePgDump(root, "gittensory-bad.dump", false);
- const r = runVerify(root, [], { GITTENSORY_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/db" }, { pg_restore: PG_RESTORE });
+ const r = runVerify(root, [], { LOOPOVER_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/db" }, { pg_restore: PG_RESTORE });
expect(r.status).toBe(1);
expect(r.out).toContain("pg_restore --list failed");
@@ -211,7 +211,7 @@ describe("self-host verify-backup script", () => {
const root = tmpRoot();
mkdirSync(join(root, "backups", "postgres"), { recursive: true });
- const r = runVerify(root, [], { GITTENSORY_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/db" }, { pg_restore: PG_RESTORE });
+ const r = runVerify(root, [], { LOOPOVER_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/db" }, { pg_restore: PG_RESTORE });
expect(r.status).toBe(1);
expect(r.out).toContain("no Postgres .dump found");
@@ -224,12 +224,12 @@ describe("self-host verify-backup script", () => {
const r = runVerify(
root,
[],
- { GITTENSORY_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/live", VERIFY_RESTORE_SCRATCH: "1" },
+ { LOOPOVER_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/live", VERIFY_RESTORE_SCRATCH: "1" },
{ pg_restore: PG_RESTORE, psql: fakePsql({}) },
);
expect(r.status).toBe(1);
- expect(r.out).toContain("needs GITTENSORY_VERIFY_SCRATCH_DATABASE_URL");
+ expect(r.out).toContain("needs LOOPOVER_VERIFY_SCRATCH_DATABASE_URL");
});
it("refuses the scratch restore when the scratch URL is byte-for-byte the live database", () => {
@@ -241,9 +241,9 @@ describe("self-host verify-backup script", () => {
root,
[],
{
- GITTENSORY_BACKUP_SOURCE_DATABASE_URL: live,
+ LOOPOVER_BACKUP_SOURCE_DATABASE_URL: live,
VERIFY_RESTORE_SCRATCH: "1",
- GITTENSORY_VERIFY_SCRATCH_DATABASE_URL: live,
+ LOOPOVER_VERIFY_SCRATCH_DATABASE_URL: live,
},
{ pg_restore: PG_RESTORE, psql: fakePsql({ [sanitizedUrl(live)]: "same-cluster@10.0.0.5:5432/live" }) },
);
@@ -265,9 +265,9 @@ describe("self-host verify-backup script", () => {
root,
[],
{
- GITTENSORY_BACKUP_SOURCE_DATABASE_URL: live,
+ LOOPOVER_BACKUP_SOURCE_DATABASE_URL: live,
VERIFY_RESTORE_SCRATCH: "1",
- GITTENSORY_VERIFY_SCRATCH_DATABASE_URL: scratch,
+ LOOPOVER_VERIFY_SCRATCH_DATABASE_URL: scratch,
},
{
pg_restore: PG_RESTORE,
@@ -292,9 +292,9 @@ describe("self-host verify-backup script", () => {
root,
[],
{
- GITTENSORY_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/live",
+ LOOPOVER_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/live",
VERIFY_RESTORE_SCRATCH: "1",
- GITTENSORY_VERIFY_SCRATCH_DATABASE_URL: "postgres://u:p@h/scratch",
+ LOOPOVER_VERIFY_SCRATCH_DATABASE_URL: "postgres://u:p@h/scratch",
},
// No identity entries at all: the scratch identity query fails, so the guard must abort rather than
// silently assume the databases differ.
@@ -314,9 +314,9 @@ describe("self-host verify-backup script", () => {
root,
[],
{
- GITTENSORY_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/live",
+ LOOPOVER_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/live",
VERIFY_RESTORE_SCRATCH: "1",
- GITTENSORY_VERIFY_SCRATCH_DATABASE_URL: scratch,
+ LOOPOVER_VERIFY_SCRATCH_DATABASE_URL: scratch,
},
// Scratch resolves fine, but the live URL has no mapping — its identity query fails.
{ pg_restore: PG_RESTORE, psql: fakePsql({ [sanitizedUrl(scratch)]: "gittensory@10.0.0.9:5432/scratch" }) },
@@ -336,9 +336,9 @@ describe("self-host verify-backup script", () => {
root,
[],
{
- GITTENSORY_BACKUP_SOURCE_DATABASE_URL: live,
+ LOOPOVER_BACKUP_SOURCE_DATABASE_URL: live,
VERIFY_RESTORE_SCRATCH: "1",
- GITTENSORY_VERIFY_SCRATCH_DATABASE_URL: scratch,
+ LOOPOVER_VERIFY_SCRATCH_DATABASE_URL: scratch,
},
{
pg_restore: PG_RESTORE,
@@ -367,9 +367,9 @@ describe("self-host verify-backup script", () => {
root,
[],
{
- GITTENSORY_BACKUP_SOURCE_DATABASE_URL: live,
+ LOOPOVER_BACKUP_SOURCE_DATABASE_URL: live,
VERIFY_RESTORE_SCRATCH: "1",
- GITTENSORY_VERIFY_SCRATCH_DATABASE_URL: scratch,
+ LOOPOVER_VERIFY_SCRATCH_DATABASE_URL: scratch,
PG_CAPTURE_FILE: captureFile,
},
{
@@ -422,9 +422,9 @@ describe("self-host verify-backup script", () => {
root,
[],
{
- GITTENSORY_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/live",
+ LOOPOVER_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/live",
VERIFY_RESTORE_SCRATCH: "1",
- GITTENSORY_VERIFY_SCRATCH_DATABASE_URL: scratch,
+ LOOPOVER_VERIFY_SCRATCH_DATABASE_URL: scratch,
PG_CAPTURE_FILE: captureFile,
},
// No identity entries: db_identity(scratch) fails before ever reaching db_identity(live) or the
@@ -454,9 +454,9 @@ describe("self-host verify-backup script", () => {
root,
[],
{
- GITTENSORY_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/live",
+ LOOPOVER_BACKUP_SOURCE_DATABASE_URL: "postgres://u:p@h/live",
VERIFY_RESTORE_SCRATCH: "1",
- GITTENSORY_VERIFY_SCRATCH_DATABASE_URL: scratch,
+ LOOPOVER_VERIFY_SCRATCH_DATABASE_URL: scratch,
PG_CAPTURE_FILE: captureFile,
},
{ pg_restore: PG_RESTORE, psql: fakePsql({}) },
@@ -485,9 +485,9 @@ describe("self-host verify-backup script", () => {
root,
[],
{
- GITTENSORY_BACKUP_SOURCE_DATABASE_URL: live,
+ LOOPOVER_BACKUP_SOURCE_DATABASE_URL: live,
VERIFY_RESTORE_SCRATCH: "1",
- GITTENSORY_VERIFY_SCRATCH_DATABASE_URL: scratch,
+ LOOPOVER_VERIFY_SCRATCH_DATABASE_URL: scratch,
PG_CAPTURE_FILE: captureFile,
},
{
diff --git a/test/unit/support/mcp-cli-harness.ts b/test/unit/support/mcp-cli-harness.ts
index 85535bf53d..2239d74fdf 100644
--- a/test/unit/support/mcp-cli-harness.ts
+++ b/test/unit/support/mcp-cli-harness.ts
@@ -18,8 +18,8 @@ export function run(args: string[], env: Record = {}) {
encoding: "utf8",
env: {
...process.env,
- GITTENSORY_API_TIMEOUT_MS: "1000",
- GITTENSORY_CONFIG_DIR: mkdtempSync(join(tmpdir(), "gittensory-cli-config-")),
+ LOOPOVER_API_TIMEOUT_MS: "1000",
+ LOOPOVER_CONFIG_DIR: mkdtempSync(join(tmpdir(), "gittensory-cli-config-")),
...env,
},
stdio: ["ignore", "pipe", "pipe"],
@@ -35,8 +35,8 @@ export function runAsync(args: string[], env: Record = {}) {
encoding: "utf8",
env: {
...process.env,
- GITTENSORY_API_TIMEOUT_MS: "1000",
- GITTENSORY_CONFIG_DIR: mkdtempSync(join(tmpdir(), "gittensory-cli-config-")),
+ LOOPOVER_API_TIMEOUT_MS: "1000",
+ LOOPOVER_CONFIG_DIR: mkdtempSync(join(tmpdir(), "gittensory-cli-config-")),
...env,
},
},
@@ -74,9 +74,9 @@ export async function capturePacketValidation(tempDir: string, validationArgs: s
await runAsync(
["agent", "packet", "--login", "oktofeesh1", "--cwd", tempDir, "--base", "HEAD", ...validationArgs, "--json"],
{
- GITTENSORY_API_URL: url,
- GITTENSORY_TOKEN: "session-token",
- GITTENSORY_CONFIG_DIR: tempDir,
+ LOOPOVER_API_URL: url,
+ LOOPOVER_TOKEN: "session-token",
+ LOOPOVER_CONFIG_DIR: tempDir,
},
);
return (requests[0] as { validation: Array<{ command: string; status: string; exitCode?: number; summary?: string }> }).validation;
diff --git a/test/unit/sweep-watchdog.test.ts b/test/unit/sweep-watchdog.test.ts
index 7e21205cb1..8d81b820c0 100644
--- a/test/unit/sweep-watchdog.test.ts
+++ b/test/unit/sweep-watchdog.test.ts
@@ -6,8 +6,8 @@ import { createTestEnv } from "../helpers/d1";
describe("isSweepWatchdogEnabled — default OFF, truthy convention", () => {
it("matches the codebase's shared truthy-string convention", () => {
- for (const off of [undefined, "", "false", "no", "0", "off"]) expect(isSweepWatchdogEnabled({ GITTENSORY_SWEEP_WATCHDOG: off })).toBe(false);
- for (const on of ["1", "true", "yes", "on", "TRUE", "On"]) expect(isSweepWatchdogEnabled({ GITTENSORY_SWEEP_WATCHDOG: on })).toBe(true);
+ for (const off of [undefined, "", "false", "no", "0", "off"]) expect(isSweepWatchdogEnabled({ LOOPOVER_SWEEP_WATCHDOG: off })).toBe(false);
+ for (const on of ["1", "true", "yes", "on", "TRUE", "On"]) expect(isSweepWatchdogEnabled({ LOOPOVER_SWEEP_WATCHDOG: on })).toBe(true);
});
});
diff --git a/test/unit/unified-comment-parity.test.ts b/test/unit/unified-comment-parity.test.ts
index 95dda7b69e..b8adb31e4a 100644
--- a/test/unit/unified-comment-parity.test.ts
+++ b/test/unit/unified-comment-parity.test.ts
@@ -10,7 +10,7 @@ import {
detectGittensorContributor,
} from "../../src/signals/engine";
import { buildUnifiedCommentBody } from "../../src/review/unified-comment-bridge";
-import { GITTENSORY_SITE_URL } from "../../src/github/footer";
+import { LOOPOVER_SITE_URL } from "../../src/github/footer";
import type { GateCheckEvaluation } from "../../src/rules/advisory";
import type { IssueRecord, PullRequestRecord, RepositoryRecord, RepositorySettings } from "../../src/types";
@@ -254,7 +254,7 @@ describe("converged comment ↔ legacy panel parity (#unified-comment)", () => {
});
const beta = collapsibles.find((section) => section.title === "[BETA] Chat with LoopOver");
expect(beta?.body).toContain("routed to the closest matching read-only command");
- expect(beta?.body).toContain(GITTENSORY_SITE_URL);
+ expect(beta?.body).toContain(LOOPOVER_SITE_URL);
});
});
});
diff --git a/test/unit/upstream-ruleset.test.ts b/test/unit/upstream-ruleset.test.ts
index ec19b79dbe..6261cf81dc 100644
--- a/test/unit/upstream-ruleset.test.ts
+++ b/test/unit/upstream-ruleset.test.ts
@@ -844,7 +844,7 @@ describe("upstream ruleset drift tracking", () => {
it("REGRESSION (#audit-rawfetch-pause): the global agent brake / freeze halts drift-issue filing (raw PAT writes outside the chokepoint)", async () => {
// DB freeze arm: enabled + token + a real report, but a global freeze must skip ALL GitHub writes.
- const frozenEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const frozenEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(frozenEnv, driftReport("frozen-fingerprint"));
await repositories.setGlobalAgentFrozen(frozenEnv, true);
const calls: string[] = [];
@@ -856,26 +856,26 @@ describe("upstream ruleset drift tracking", () => {
expect(calls.some((c) => c.startsWith("POST") || c.startsWith("PATCH"))).toBe(false); // no GitHub issue write reached the network
// env brake arm: AGENT_ACTIONS_PAUSED short-circuits before the DB freeze read.
- const pausedEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token", AGENT_ACTIONS_PAUSED: "true" });
+ const pausedEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token", AGENT_ACTIONS_PAUSED: "true" });
await upsertUpstreamDriftReport(pausedEnv, driftReport("paused-fingerprint"));
await expect(fileUpstreamDriftIssues(pausedEnv)).resolves.toMatchObject({ status: "paused", created: 0, updated: 0, skipped: 0 });
});
it("files or reuses upstream drift issues only when explicitly enabled", async () => {
- const missingTokenEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true" });
+ const missingTokenEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true" });
await expect(fileUpstreamDriftIssues(missingTokenEnv)).resolves.toMatchObject({ status: "skipped", reason: "missing_issue_token" });
- const invalidRepoEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token", GITTENSORY_DRIFT_ISSUE_REPO: "bad-repo-name" });
+ const invalidRepoEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token", LOOPOVER_DRIFT_ISSUE_REPO: "bad-repo-name" });
await upsertUpstreamDriftReport(invalidRepoEnv, driftReport("invalid-repo"));
await expect(fileUpstreamDriftIssues(invalidRepoEnv)).resolves.toMatchObject({ status: "completed", created: 0, updated: 0, skipped: 1 });
- const createEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const createEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(createEnv, driftReport("create-fingerprint"));
vi.stubGlobal("fetch", githubIssueFetch({ create: { number: 77, url: "https://github.com/JSONbored/gittensory/issues/77" } }));
await expect(fileUpstreamDriftIssues(createEnv)).resolves.toMatchObject({ status: "completed", created: 1, updated: 0, skipped: 0 });
await expect(listUpstreamDriftReports(createEnv)).resolves.toEqual([expect.objectContaining({ issueNumber: 77 })]);
- const updateEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "yes", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const updateEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "yes", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(updateEnv, driftReport("existing-fingerprint"));
const updateCalls: GitHubIssueFetchCall[] = [];
vi.stubGlobal(
@@ -904,14 +904,14 @@ describe("upstream ruleset drift tracking", () => {
expect(String(updateBody?.body)).toContain("gittensor/constants.py");
expect(String(updateBody?.body)).not.toMatch(/wallet|hotkey|raw trust score|payout|reward estimate|farming|private reviewability|public score estimate/i);
- const failingEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "on", GITHUB_PUBLIC_TOKEN: "token" });
+ const failingEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "on", GITHUB_PUBLIC_TOKEN: "token" });
await upsertUpstreamDriftReport(failingEnv, driftReport("failing-fingerprint"));
vi.stubGlobal("fetch", githubIssueFetch({ createStatus: 500, listStatus: 500 }));
await expect(fileUpstreamDriftIssues(failingEnv)).resolves.toMatchObject({ status: "completed", created: 0, updated: 0, skipped: 1 });
});
it("INVARIANT (#4503): a second run against an UNCHANGED open drift issue makes zero PATCH calls", async () => {
- const env = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const env = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(env, driftReport("stable-fingerprint"));
const createCalls: GitHubIssueFetchCall[] = [];
vi.stubGlobal("fetch", githubIssueFetch({ create: { number: 101, url: "https://github.com/JSONbored/gittensory/issues/101" }, calls: createCalls }));
@@ -939,7 +939,7 @@ describe("upstream ruleset drift tracking", () => {
});
it("REGRESSION (#4503): two consecutive cycles against an unchanged report only PATCH on the first", async () => {
- const env = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const env = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(env, driftReport("cycle-fingerprint", { issueNumber: 202, issueUrl: "https://github.com/JSONbored/gittensory/issues/202" }));
// Cycle 1: the recorded issue's live body/labels are STALE (drift from before the report's current content) --
@@ -976,7 +976,7 @@ describe("upstream ruleset drift tracking", () => {
});
it("negative-path (#4503): a genuinely changed report (severity escalation) still triggers a fresh PATCH", async () => {
- const env = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const env = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(env, driftReport("escalating-fingerprint", { severity: "low" }));
const createCalls: GitHubIssueFetchCall[] = [];
vi.stubGlobal("fetch", githubIssueFetch({ create: { number: 303, url: "https://github.com/JSONbored/gittensory/issues/303" }, calls: createCalls }));
@@ -1007,7 +1007,7 @@ describe("upstream ruleset drift tracking", () => {
});
it("REGRESSION (#4503): the list-search fallback tolerates malformed label/assignee shapes (missing name/login) without crashing", async () => {
- const env = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const env = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(env, driftReport("malformed-shape-fingerprint"));
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit): Promise => {
const url = input.toString();
@@ -1034,7 +1034,7 @@ describe("upstream ruleset drift tracking", () => {
});
it("REGRESSION (#4503): validateRecordedGitHubIssue's fast path tolerates a malformed (loginless) assignee without crashing", async () => {
- const env = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const env = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(env, driftReport("malformed-assignee-fingerprint", { issueNumber: 505, issueUrl: "https://github.com/JSONbored/gittensory/issues/505" }));
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit): Promise => {
const url = input.toString();
@@ -1060,7 +1060,7 @@ describe("upstream ruleset drift tracking", () => {
});
it("REGRESSION (#4503): validateRecordedGitHubIssue tolerates an issue response with the assignees field entirely absent", async () => {
- const env = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const env = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(env, driftReport("no-assignees-field-fingerprint", { issueNumber: 606, issueUrl: "https://github.com/JSONbored/gittensory/issues/606" }));
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit): Promise => {
const url = input.toString();
@@ -1076,8 +1076,8 @@ describe("upstream ruleset drift tracking", () => {
await expect(fileUpstreamDriftIssues(env)).resolves.toMatchObject({ status: "completed", created: 0, updated: 1, skipped: 0 });
});
- it("assigns filed drift issues to GITTENSORY_DRIFT_ISSUE_ASSIGNEES when a self-host operator sets it", async () => {
- const env = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token", GITTENSORY_DRIFT_ISSUE_ASSIGNEES: "alice, ,bob" });
+ it("assigns filed drift issues to LOOPOVER_DRIFT_ISSUE_ASSIGNEES when a self-host operator sets it", async () => {
+ const env = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token", LOOPOVER_DRIFT_ISSUE_ASSIGNEES: "alice, ,bob" });
await upsertUpstreamDriftReport(env, driftReport("assignee-override"));
const calls: GitHubIssueFetchCall[] = [];
vi.stubGlobal("fetch", githubIssueFetch({ create: { number: 70, url: "https://github.com/acme/widgets/issues/70" }, calls }));
@@ -1090,12 +1090,12 @@ describe("upstream ruleset drift tracking", () => {
// 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: "" });
+ const defaultRepoEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "1", LOOPOVER_DRIFT_ISSUE_TOKEN: "token", LOOPOVER_DRIFT_ISSUE_REPO: "" });
await upsertUpstreamDriftReport(defaultRepoEnv, driftReport("source-fingerprint", { severity: "medium", affectedAreas: [] }));
vi.stubGlobal("fetch", githubIssueFetch({ create: { number: 91, url: "https://github.com/JSONbored/gittensory/issues/91" } }));
await expect(fileUpstreamDriftIssues(defaultRepoEnv)).resolves.toMatchObject({ status: "completed", created: 1, updated: 0, skipped: 0 });
- const areaSourceEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const areaSourceEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(
areaSourceEnv,
driftReport("area-source-paths", { severity: "medium", affectedAreas: ["registry", "issue_discovery", "mirror_linkage", "language_weights"] }),
@@ -1109,17 +1109,17 @@ describe("upstream ruleset drift tracking", () => {
expect(areaSourceBody).toContain("gittensor/utils/mirror/models.py");
expect(areaSourceBody).toContain("gittensor/validator/weights/programming_languages.json");
- const missingPayloadEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const missingPayloadEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(missingPayloadEnv, driftReport("missing-payload", { currentRulesetId: null, previousRulesetId: null }));
vi.stubGlobal("fetch", githubIssueFetch({ createPayload: {} }));
await expect(fileUpstreamDriftIssues(missingPayloadEnv)).resolves.toMatchObject({ status: "completed", created: 0, updated: 0, skipped: 1 });
- const throwingListEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const throwingListEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(throwingListEnv, driftReport("throwing-list"));
vi.stubGlobal("fetch", githubIssueFetch({ throwOnList: true, create: { number: 92, url: "https://github.com/JSONbored/gittensory/issues/92" } }));
await expect(fileUpstreamDriftIssues(throwingListEnv)).resolves.toMatchObject({ status: "completed", created: 1, updated: 0, skipped: 0 });
- const linkedEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const linkedEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(linkedEnv, driftReport("linked-fingerprint", { issueNumber: 93, issueUrl: "https://github.com/JSONbored/gittensory/issues/93" }));
const linkedCalls: GitHubIssueFetchCall[] = [];
vi.stubGlobal(
@@ -1136,7 +1136,7 @@ describe("upstream ruleset drift tracking", () => {
expect.objectContaining({ method: "PATCH", url: "https://api.github.com/repos/JSONbored/gittensory/issues/93" }),
]);
- const objectLabelLinkedEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const objectLabelLinkedEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(objectLabelLinkedEnv, driftReport("object-label-linked", { issueNumber: 129, issueUrl: "https://github.com/JSONbored/gittensory/issues/129" }));
vi.stubGlobal(
"fetch",
@@ -1149,7 +1149,7 @@ describe("upstream ruleset drift tracking", () => {
// GitHub labels are case-insensitive: a repo whose "signals" label is stored with different casing
// (e.g. "Signals") must still be recognized as the recorded drift issue, so it is updated, not duplicated.
- const caseVariantLabelEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const caseVariantLabelEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(caseVariantLabelEnv, driftReport("case-variant-label-linked", { issueNumber: 139, issueUrl: "https://github.com/JSONbored/gittensory/issues/139" }));
vi.stubGlobal(
"fetch",
@@ -1160,7 +1160,7 @@ describe("upstream ruleset drift tracking", () => {
);
await expect(fileUpstreamDriftIssues(caseVariantLabelEnv)).resolves.toMatchObject({ status: "completed", created: 0, updated: 1, skipped: 0 });
- const staleLinkedEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token", GITTENSORY_DRIFT_ISSUE_REPO: "victim/current-repo" });
+ const staleLinkedEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token", LOOPOVER_DRIFT_ISSUE_REPO: "victim/current-repo" });
await upsertUpstreamDriftReport(staleLinkedEnv, driftReport("stale-linked", { issueNumber: 123, issueUrl: "https://github.com/other-owner/old-repo/issues/123" }));
const staleLinkedCalls: GitHubIssueFetchCall[] = [];
vi.stubGlobal("fetch", githubIssueFetch({ create: { number: 124, url: "https://github.com/victim/current-repo/issues/124" }, calls: staleLinkedCalls }));
@@ -1169,7 +1169,7 @@ describe("upstream ruleset drift tracking", () => {
expect.not.arrayContaining([expect.objectContaining({ method: "PATCH", url: "https://api.github.com/repos/victim/current-repo/issues/123" })]),
);
- const invalidLinkedEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const invalidLinkedEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(invalidLinkedEnv, driftReport("invalid-linked", { issueNumber: 125, issueUrl: "not a github issue url" }));
const invalidLinkedCalls: GitHubIssueFetchCall[] = [];
vi.stubGlobal("fetch", githubIssueFetch({ create: { number: 126, url: "https://github.com/JSONbored/gittensory/issues/126" }, calls: invalidLinkedCalls }));
@@ -1178,7 +1178,7 @@ describe("upstream ruleset drift tracking", () => {
expect.not.arrayContaining([expect.objectContaining({ method: "PATCH", url: "https://api.github.com/repos/JSONbored/gittensory/issues/125" })]),
);
- const throwingLinkedEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const throwingLinkedEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(throwingLinkedEnv, driftReport("throwing-linked", { issueNumber: 127, issueUrl: "https://github.com/JSONbored/gittensory/issues/127" }));
const throwingLinkedCalls: GitHubIssueFetchCall[] = [];
vi.stubGlobal("fetch", githubIssueFetch({ throwOnIssueGet: true, create: { number: 128, url: "https://github.com/JSONbored/gittensory/issues/128" }, calls: throwingLinkedCalls }));
@@ -1198,7 +1198,7 @@ describe("upstream ruleset drift tracking", () => {
{ fingerprint: "nameless-label-linked", issueNumber: 141, issueUrl: "https://github.com/JSONbored/gittensory/issues/141", issue: { number: 141, url: "https://github.com/JSONbored/gittensory/issues/141", fingerprint: "nameless-label-linked", labels: [{}] } },
{ fingerprint: "returned-url-linked", issueNumber: 138, issueUrl: "https://github.com/JSONbored/gittensory/issues/138", issue: { number: 138, url: "https://github.com/other/repo/issues/138", fingerprint: "returned-url-linked" } },
]) {
- const rejectedLinkedEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const rejectedLinkedEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(rejectedLinkedEnv, driftReport(scenario.fingerprint, { issueNumber: scenario.issueNumber, issueUrl: scenario.issueUrl }));
const rejectedLinkedCalls: GitHubIssueFetchCall[] = [];
vi.stubGlobal(
@@ -1216,23 +1216,23 @@ describe("upstream ruleset drift tracking", () => {
);
}
- const failingLinkedEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const failingLinkedEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(failingLinkedEnv, driftReport("failing-linked", { issueNumber: 94, issueUrl: "https://github.com/JSONbored/gittensory/issues/94" }));
vi.stubGlobal("fetch", githubIssueFetch({ issue: { number: 94, url: "https://github.com/JSONbored/gittensory/issues/94", fingerprint: "failing-linked" }, updateStatus: 500 }));
await expect(fileUpstreamDriftIssues(failingLinkedEnv)).resolves.toMatchObject({ status: "completed", created: 0, updated: 0, skipped: 1 });
- const missingUpdatePayloadEnv = createTestEnv({ GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "true", GITTENSORY_DRIFT_ISSUE_TOKEN: "token" });
+ const missingUpdatePayloadEnv = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(missingUpdatePayloadEnv, driftReport("missing-update-payload", { issueNumber: 96, issueUrl: "https://github.com/JSONbored/gittensory/issues/96" }));
vi.stubGlobal("fetch", githubIssueFetch({ issue: { number: 96, url: "https://github.com/JSONbored/gittensory/issues/96", fingerprint: "missing-update-payload" }, updatePayload: {} }));
await expect(fileUpstreamDriftIssues(missingUpdatePayloadEnv)).resolves.toMatchObject({ status: "completed", created: 0, updated: 0, skipped: 1 });
const disabledEnv = createTestEnv();
- delete (disabledEnv as Partial).GITTENSORY_AUTO_FILE_DRIFT_ISSUES;
+ delete (disabledEnv as Partial).LOOPOVER_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" });
+ const env = createTestEnv({ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "true", LOOPOVER_DRIFT_ISSUE_TOKEN: "token" });
await upsertUpstreamDriftReport(env, driftReport("page-2-fingerprint"));
const calls: string[] = [];
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
@@ -1703,12 +1703,12 @@ function driftReport(
describe("resolveDriftAssignees", () => {
it("defaults to the gittensory maintainer when unset, empty, or whitespace-only", () => {
expect(resolveDriftAssignees(createTestEnv())).toEqual(["jsonbored"]);
- expect(resolveDriftAssignees(createTestEnv({ GITTENSORY_DRIFT_ISSUE_ASSIGNEES: "" }))).toEqual(["jsonbored"]);
- expect(resolveDriftAssignees(createTestEnv({ GITTENSORY_DRIFT_ISSUE_ASSIGNEES: " " }))).toEqual(["jsonbored"]);
+ expect(resolveDriftAssignees(createTestEnv({ LOOPOVER_DRIFT_ISSUE_ASSIGNEES: "" }))).toEqual(["jsonbored"]);
+ expect(resolveDriftAssignees(createTestEnv({ LOOPOVER_DRIFT_ISSUE_ASSIGNEES: " " }))).toEqual(["jsonbored"]);
});
it("parses a comma-separated list, trimming and dropping empty segments", () => {
- expect(resolveDriftAssignees(createTestEnv({ GITTENSORY_DRIFT_ISSUE_ASSIGNEES: "alice" }))).toEqual(["alice"]);
- expect(resolveDriftAssignees(createTestEnv({ GITTENSORY_DRIFT_ISSUE_ASSIGNEES: " alice , ,bob " }))).toEqual(["alice", "bob"]);
+ expect(resolveDriftAssignees(createTestEnv({ LOOPOVER_DRIFT_ISSUE_ASSIGNEES: "alice" }))).toEqual(["alice"]);
+ expect(resolveDriftAssignees(createTestEnv({ LOOPOVER_DRIFT_ISSUE_ASSIGNEES: " alice , ,bob " }))).toEqual(["alice", "bob"]);
});
});
diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts
index 4e70391eb0..b52c809f16 100644
--- a/worker-configuration.d.ts
+++ b/worker-configuration.d.ts
@@ -1,5 +1,5 @@
/* eslint-disable */
-// Generated by Wrangler by running `wrangler types` (hash: a410b7776c54af3156c9fe07cfa264d1)
+// Generated by Wrangler by running `wrangler types` (hash: 4d6b19cd1b49c2d246210b2839cf1394)
// Runtime types generated with workerd@1.20260701.1 2026-05-28 nodejs_compat
interface __BaseEnv_Env {
REVIEW_AUDIT: R2Bucket;
@@ -12,8 +12,8 @@ interface __BaseEnv_Env {
GITTENSOR_UPSTREAM_REF: "test";
GITTENSOR_REGISTRY_URL: "https://raw.githubusercontent.com/entrius/gittensor/test/gittensor/validator/weights/master_repositories.json";
SCORING_TIME_DECAY_ENABLED: "true";
- GITTENSORY_AUTO_FILE_DRIFT_ISSUES: "false";
- GITTENSORY_DRIFT_ISSUE_REPO: "JSONbored/gittensory";
+ LOOPOVER_AUTO_FILE_DRIFT_ISSUES: "false";
+ LOOPOVER_DRIFT_ISSUE_REPO: "JSONbored/gittensory";
PUBLIC_API_ORIGIN: "https://api.loopover.ai";
PUBLIC_SITE_ORIGIN: "https://loopover.ai";
PUBLIC_SITE_ORIGIN_ALIASES: "https://gittensory.aethereal.dev";
@@ -28,27 +28,27 @@ interface __BaseEnv_Env {
LOOPOVER_REVIEW_CONTINUOUS: "false";
LOOPOVER_REVIEW_REPUTATION: "false";
LOOPOVER_REVIEW_OPS: "false";
- GITTENSORY_SWEEP_WATCHDOG: "false";
- GITTENSORY_PR_RECONCILIATION: "false";
+ LOOPOVER_SWEEP_WATCHDOG: "false";
+ LOOPOVER_PR_RECONCILIATION: "false";
LOOPOVER_REVIEW_RAG: "false";
LOOPOVER_REVIEW_IMPACT_MAP: "false";
LOOPOVER_REVIEW_CULTURE_PROFILE: "false";
LOOPOVER_REVIEW_MEMORY: "false";
LOOPOVER_REVIEW_CONTENT_LANE: "false";
LOOPOVER_REVIEW_SELFTUNE: "false";
- GITTENSORY_EXPERIMENTAL_GITTENSOR: "false";
- GITTENSORY_MAINTAINER_RECAP: "false";
+ LOOPOVER_EXPERIMENTAL_GITTENSOR: "false";
+ LOOPOVER_MAINTAINER_RECAP: "false";
GITHUB_STATUS_ROLLUP_GRAPHQL: "false";
LOOPOVER_REVIEW_PLANNER: "false";
LOOPOVER_REVIEW_DRAFT: "false";
LOOPOVER_REVIEW_PARITY_AUDIT: "false";
LOOPOVER_REVIEW_REPOS: "";
- GITTENSORY_PUBLIC_STATS: "true";
- GITTENSORY_PUBLIC_STATS_REPOS: "JSONbored/gittensory,JSONbored/awesome-claude,JSONbored/metagraphed";
+ LOOPOVER_PUBLIC_STATS: "true";
+ LOOPOVER_PUBLIC_STATS_REPOS: "JSONbored/gittensory,JSONbored/awesome-claude,JSONbored/metagraphed";
PUBLIC_REPO_STATS_ALLOWLIST: "JSONbored/gittensory";
- GITTENSORY_DUPLICATE_WINNER: "true";
- GITTENSORY_OPEN_PR_FILE_COLLISION: "true";
- GITTENSORY_SKIP_AUTOMATION_BOT_PRS: "true";
+ LOOPOVER_DUPLICATE_WINNER: "true";
+ LOOPOVER_OPEN_PR_FILE_COLLISION: "true";
+ LOOPOVER_SKIP_AUTOMATION_BOT_PRS: "true";
RATE_LIMITER: DurableObjectNamespace;
}
declare namespace Cloudflare {
@@ -71,17 +71,15 @@ declare namespace NodeJS {
| "GITTENSOR_REGISTRY_URL"
| "GITTENSOR_UPSTREAM_REF"
| "GITTENSOR_UPSTREAM_REPO"
- | "GITTENSORY_AUTO_FILE_DRIFT_ISSUES"
- | "GITTENSORY_DRIFT_ISSUE_REPO"
- | "GITTENSORY_DUPLICATE_WINNER"
- | "GITTENSORY_EXPERIMENTAL_GITTENSOR"
- | "GITTENSORY_MAINTAINER_RECAP"
- | "GITTENSORY_OPEN_PR_FILE_COLLISION"
- | "GITTENSORY_PR_RECONCILIATION"
- | "GITTENSORY_PUBLIC_STATS"
- | "GITTENSORY_PUBLIC_STATS_REPOS"
- | "GITTENSORY_SKIP_AUTOMATION_BOT_PRS"
- | "GITTENSORY_SWEEP_WATCHDOG"
+ | "LOOPOVER_AUTO_FILE_DRIFT_ISSUES"
+ | "LOOPOVER_DRIFT_ISSUE_REPO"
+ | "LOOPOVER_DUPLICATE_WINNER"
+ | "LOOPOVER_EXPERIMENTAL_GITTENSOR"
+ | "LOOPOVER_MAINTAINER_RECAP"
+ | "LOOPOVER_OPEN_PR_FILE_COLLISION"
+ | "LOOPOVER_PR_RECONCILIATION"
+ | "LOOPOVER_PUBLIC_STATS"
+ | "LOOPOVER_PUBLIC_STATS_REPOS"
| "LOOPOVER_REVIEW_CONTENT_LANE"
| "LOOPOVER_REVIEW_CONTINUOUS"
| "LOOPOVER_REVIEW_CULTURE_PROFILE"
@@ -102,6 +100,8 @@ declare namespace NodeJS {
| "LOOPOVER_REVIEW_SCREENSHOTS"
| "LOOPOVER_REVIEW_SELFTUNE"
| "LOOPOVER_REVIEW_UNIFIED_COMMENT"
+ | "LOOPOVER_SKIP_AUTOMATION_BOT_PRS"
+ | "LOOPOVER_SWEEP_WATCHDOG"
| "PUBLIC_API_ORIGIN"
| "PUBLIC_REPO_STATS_ALLOWLIST"
| "PUBLIC_SITE_ORIGIN"
diff --git a/wrangler.jsonc b/wrangler.jsonc
index b3beabea57..0f24b51c25 100644
--- a/wrangler.jsonc
+++ b/wrangler.jsonc
@@ -36,8 +36,8 @@
// hyperparameters (registry scoring.time_decay overlaid on the upstream defaults). A fresh PR is
// unaffected (decay 1.0); only aged-PR projections decay. Owner-reviewed; this is the go-live switch.
"SCORING_TIME_DECAY_ENABLED": "true",
- "GITTENSORY_AUTO_FILE_DRIFT_ISSUES": "false",
- "GITTENSORY_DRIFT_ISSUE_REPO": "JSONbored/gittensory",
+ "LOOPOVER_AUTO_FILE_DRIFT_ISSUES": "false",
+ "LOOPOVER_DRIFT_ISSUE_REPO": "JSONbored/gittensory",
// LoopOver rebrand (issue #4765, domain flip): loopover.ai is now the canonical site origin. The old
// gittensory.aethereal.dev domain stays live (still resolves, still valid as a post-OAuth-login redirect
// target via PUBLIC_SITE_ORIGIN_ALIASES below) -- no redirect rule, no sunset date, both domains keep
@@ -107,13 +107,13 @@
// targeted `agent-regate-sweep` re-enqueue for just that repo, so a stalled sweep recovers on its own instead
// of requiring a human to notice and manually trigger it. Default OFF — flag-OFF the cron enqueues no
// watchdog job, byte-identical to today.
- "GITTENSORY_SWEEP_WATCHDOG": "false",
+ "LOOPOVER_SWEEP_WATCHDOG": "false",
// Self-heal: a short-interval cron list-diffs GitHub's open PR numbers against the local table for every
// acting-autonomy repo, and catches up (fetch + upsert + regate) any PR number GitHub has that the local
// table doesn't — a silently-lost "opened" webhook, caught within minutes instead of the 6-hour
// backfillRegisteredRepositories freshness window. Default OFF — flag-OFF the cron enqueues no
// reconciliation job, byte-identical to today.
- "GITTENSORY_PR_RECONCILIATION": "false",
+ "LOOPOVER_PR_RECONCILIATION": "false",
// Convergence (RAG retrieval): at review time, query the codebase vector index for code/docs semantically
// related to the PR's changed files and append a RELEVANT EXISTING CODE / DOCS section to the reviewer
// prompt — additive reference context (callers, related modules, conventions), exactly like grounding.
@@ -162,12 +162,12 @@
// Default OFF — flag-OFF (or every repo unset), refresh-registry is never enqueued (see src/index.ts),
// registry/sync.ts persists nothing for this instance, and a self-host box makes zero outbound contact
// with the gittensor subnet registry. See src/review/gittensor-wire.ts.
- "GITTENSORY_EXPERIMENTAL_GITTENSOR": "false",
+ "LOOPOVER_EXPERIMENTAL_GITTENSOR": "false",
// Maintainer recap digest (#1963, #2248): deliver a cross-repo RecapReport -- gittensory's own gate-
// precision + outcome-calibration data folded across every scanned repo -- to Discord on a cron cadence
- // (GITTENSORY_RECAP_CADENCE=daily|weekly, default weekly; GITTENSORY_RECAP_HOUR / GITTENSORY_RECAP_DAY
+ // (LOOPOVER_RECAP_CADENCE=daily|weekly, default weekly; LOOPOVER_RECAP_HOUR / LOOPOVER_RECAP_DAY
// pick when). Default OFF — flag-OFF the cron enqueues no recap job, byte-identical to today.
- "GITTENSORY_MAINTAINER_RECAP": "false",
+ "LOOPOVER_MAINTAINER_RECAP": "false",
// #1941: route the live CI aggregate (the gate's check/status read) through ONE GraphQL statusCheckRollup query
// instead of the paginated /check-runs + /status + /check-suites REST reads — moving that hot path onto the
// SEPARATE GraphQL rate-limit bucket. Default OFF: the gate uses the proven REST aggregate, byte-identical. When
@@ -200,12 +200,12 @@
// shows PRs reviewed / filtered-without-merge % / maintainer time saved / decision accuracy for the
// reviewed repos. Flag-OFF the endpoint 404s and the homepage band renders nothing (counts only, no PR
// content/authors/scores/rewards). See src/review/public-stats.ts.
- "GITTENSORY_PUBLIC_STATS": "true",
+ "LOOPOVER_PUBLIC_STATS": "true",
// Proof of Power (#1059): the own-ledger allowlist for the public stats counter (DELIBERATELY separate from
// LOOPOVER_REVIEW_REPOS above, which went empty when these repos' LIVE processing moved to self-host — see
// src/env.d.ts for the full split rationale). These three repos' historical audit_events rows remain real and
// safe to publish even though the cutover allowlist above no longer lists them.
- "GITTENSORY_PUBLIC_STATS_REPOS": "JSONbored/gittensory,JSONbored/awesome-claude,JSONbored/metagraphed",
+ "LOOPOVER_PUBLIC_STATS_REPOS": "JSONbored/gittensory,JSONbored/awesome-claude,JSONbored/metagraphed",
// Website chrome repo stats are public and proxied through the server-side GitHub token, so require
// an explicit narrow allowlist instead of letting unset config proxy arbitrary owner/repo pairs.
"PUBLIC_REPO_STATS_ALLOWLIST": "JSONbored/gittensory",
@@ -216,7 +216,7 @@
// dry-run audit found the flag-OFF behavior closing EVERY sibling of a duplicate cluster (e.g. #1648 AND
// #1695 both closed) — the opposite of "spare one good PR". With it ON, the earliest open PR in a cluster
// is judged on its merits and only the true duplicates close.
- "GITTENSORY_DUPLICATE_WINNER": "true",
+ "LOOPOVER_DUPLICATE_WINNER": "true",
// Open-PR file-path collision (#2653): enrich changedFiles on the reviewed PR and its open siblings from
// the pull_request_files cache before building the collision report, so two independently-open PRs on the
// same file get flagged the way two title-similar PRs already are. ENABLED: a repo-wide shadow test against
@@ -225,13 +225,13 @@
// fixed by requiring an actual shared, non-lockfile file path before a path-driven match clusters (see the
// sharesMeaningfulFile guard in buildCollisionReport). Re-validated post-fix: every remaining flagged pair is
// backed by a real shared file.
- "GITTENSORY_OPEN_PR_FILE_COLLISION": "true",
+ "LOOPOVER_OPEN_PR_FILE_COLLISION": "true",
// Waste elimination for known automation authors (settings/automation-bot-skip.ts): skip AI review, gate
// evaluation, and public-surface publish entirely for a PR/event genuinely triggered by github-actions[bot]
// (release-please), Renovate, or Dependabot. Default-ON, unlike most flags above -- see that module's own
// doc comment for why. A repo can override via its own repository_settings.skip_automation_bot_authors
// column ("off"/"enabled"), independent of this global default.
- "GITTENSORY_SKIP_AUTOMATION_BOT_PRS": "true",
+ "LOOPOVER_SKIP_AUTOMATION_BOT_PRS": "true",
},
"routes": [
{
From d811b2219faf2f2ce2f69954c9ac53d23e13fd2a Mon Sep 17 00:00:00 2001
From: JSONbored <49853598+JSONbored@users.noreply.github.com>
Date: Tue, 14 Jul 2026 04:27:23 -0700
Subject: [PATCH 2/2] fix(docs)!: fix stale .gittensory.yml doc-comment
references + finish remaining camelCase identifiers
- 95 files: JSDoc/comment references to the config filename still said
.gittensory.yml throughout src/env.d.ts, src/types.ts, and elsewhere, even
though the actual file was already renamed to .loopover.yml by an earlier
(pre-epic) migration. Purely cosmetic to a reader who trusts the code, but
actively wrong -- pointed at a file that no longer exists.
- Finished 5 camelCase identifiers (gittensoryFooter, gittensoryMcpPackageJson,
gittensoryMinerOpportunityBadge, gittensoryPings, gittensoryPrContext) that
an earlier commit in this same phase found but never actually renamed --
the rename command silently no-op'd (BSD sed's `sed 's/\bfoo\b/bar/'` does
not support \b word-boundary the way GNU sed/perl do), redone with perl.
Advances #5705
---
apps/loopover-extension/content.js | 2 +-
apps/loopover-miner-extension/content.js | 2 +-
config/examples/README.md | 2 +-
.../src/advisory/gate-advisory.ts | 2 +-
.../src/finding-severity-calibration.ts | 4 +-
.../loopover-engine/src/focus-manifest.ts | 26 ++++-----
.../src/gate-verdict-calibration.ts | 4 +-
.../loopover-engine/src/miner-goal-spec.ts | 6 +-
.../loopover-engine/src/predicted-gate.ts | 6 +-
.../src/review/advisory-ai-routing-config.ts | 2 +-
.../loopover-engine/src/review/cla-check.ts | 2 +-
.../src/review/enrichment-analyzer-names.ts | 2 +-
.../src/review/guardrail-config.ts | 4 +-
.../src/review/pre-merge-checks.ts | 2 +-
.../src/review/screenshot-table-gate.ts | 4 +-
.../review/unlinked-issue-guardrail-config.ts | 2 +-
.../src/reviewer-consensus-calibration.ts | 4 +-
.../src/scoring/label-match.ts | 2 +-
.../loopover-engine/src/scoring/preview.ts | 2 +-
.../src/settings/auto-close-exempt.ts | 4 +-
.../loopover-engine/src/settings/autonomy.ts | 2 +-
.../src/settings/contributor-blacklist.ts | 4 +-
.../src/settings/moderation-rules.ts | 4 +-
.../src/settings/pr-type-label.ts | 2 +-
.../src/signals/change-guardrail.ts | 2 +-
.../src/types/manifest-deps-types.ts | 44 +++++++--------
packages/loopover-mcp/CHANGELOG.md | 24 ++++----
src/api/routes.ts | 8 +--
src/db/repositories.ts | 4 +-
src/db/schema.ts | 4 +-
src/env.d.ts | 20 +++----
src/github/backfill.ts | 2 +-
src/github/commands.ts | 6 +-
src/github/footer.ts | 8 +--
src/github/repo-doc-pr.ts | 2 +-
src/index.ts | 2 +-
src/queue/ai-review-orchestration.ts | 28 +++++-----
src/queue/gate-checks.ts | 6 +-
src/queue/processors.ts | 44 +++++++--------
src/review/advisory-ai-routing-config.ts | 2 +-
src/review/ai-review-cache-input.ts | 2 +-
src/review/cla-check.ts | 2 +-
src/review/content-lane-wire.ts | 12 ++--
src/review/content-lane/registry-logic.ts | 2 +-
src/review/content-lane/spec-resolver.ts | 10 ++--
src/review/e2e-test-gen-render.ts | 10 ++--
src/review/e2e-test-gen-wire.ts | 2 +-
src/review/enrichment-analyzer-names.ts | 2 +-
src/review/feature-activation.ts | 8 +--
src/review/fix-handoff.ts | 4 +-
src/review/gittensor-wire.ts | 4 +-
src/review/guardrail-config.ts | 4 +-
src/review/impact-map-wire.ts | 2 +-
src/review/improvement-signal-wire.ts | 2 +-
src/review/inline-comments.ts | 4 +-
src/review/maintainer-recap-wire.ts | 4 +-
src/review/planner.ts | 4 +-
src/review/pre-merge-checks.ts | 2 +-
src/review/repo-culture-profile-wire.ts | 2 +-
src/review/review-memory-wire.ts | 2 +-
src/review/screenshot-table-gate.ts | 4 +-
src/review/selftune-wire.ts | 2 +-
src/review/unified-comment-bridge.ts | 10 ++--
src/review/unlinked-issue-guardrail-config.ts | 2 +-
src/rules/advisory.ts | 2 +-
src/selfhost/ai.ts | 6 +-
src/server.ts | 6 +-
src/services/agent-approval-queue.ts | 2 +-
src/services/ai-review.ts | 36 ++++++------
src/services/label-audit.ts | 2 +-
src/services/maintainer-activation.ts | 2 +-
src/services/mcp-compatibility.ts | 4 +-
src/settings/agent-actions.ts | 16 +++---
src/settings/auto-close-exempt.ts | 4 +-
src/settings/autonomy.ts | 2 +-
src/settings/contributor-blacklist.ts | 4 +-
src/settings/effective-config-summary.ts | 2 +-
src/settings/global-contributor-cap.ts | 2 +-
src/settings/moderation-rules.ts | 4 +-
src/settings/pr-type-label.ts | 2 +-
src/settings/repository-settings.ts | 2 +-
src/signals/change-guardrail.ts | 2 +-
src/signals/engine.ts | 18 +++---
src/signals/focus-manifest.ts | 6 +-
src/signals/settings-preview.ts | 2 +-
src/types.ts | 56 +++++++++----------
test/contract/live-gate-parity.test.ts | 2 +-
test/integration/api.test.ts | 2 +-
test/unit/agent-approval-queue.test.ts | 2 +-
test/unit/ai-review-advisory.test.ts | 2 +-
test/unit/content-lane-wire.test.ts | 2 +-
test/unit/footer.test.ts | 18 +++---
test/unit/gate-check-policy.test.ts | 8 +--
.../linked-issue-satisfaction-run.test.ts | 2 +-
test/unit/maintainer-recap-wire.test.ts | 2 +-
test/unit/parity-wire.test.ts | 2 +-
test/unit/prompt-injection.test.ts | 2 +-
test/unit/queue-3.test.ts | 6 +-
test/unit/queue-5.test.ts | 4 +-
test/unit/queue-lifecycle-guards.test.ts | 10 ++--
test/unit/queue.test.ts | 18 +++---
test/unit/repo-profile.test.ts | 2 +-
test/unit/selfhost-config-examples.test.ts | 2 +-
test/unit/signals-coverage.test.ts | 2 +-
test/unit/signals.test.ts | 2 +-
test/unit/slop.test.ts | 2 +-
106 files changed, 339 insertions(+), 339 deletions(-)
diff --git a/apps/loopover-extension/content.js b/apps/loopover-extension/content.js
index 2bf81d96bb..acb8734920 100644
--- a/apps/loopover-extension/content.js
+++ b/apps/loopover-extension/content.js
@@ -23,7 +23,7 @@ function mountOverlay(target) {
const container = document.createElement("aside");
const host = findPullRequestSidebar();
container.className = `gittensory-overlay ${host ? "gittensory-overlay--sidebar" : "gittensory-overlay--floating"}`;
- container.dataset.gittensoryPrContext = "true";
+ container.dataset.loopoverPrContext = "true";
container.innerHTML = `