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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .loopover.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ review:
# gittensor:priority at once); resolvePrTypeLabel composes every additive match alongside the one exclusive
# winner, rather than the two categories competing for a single slot.
#
# Review-evasion protection: closing or converting-to-draft your OWN PR while gittensory has an active
# Review-evasion protection: closing or converting-to-draft your OWN PR while loopover has an active
# review pass running, a prior recorded gate failure, or a repeated ready<->draft cycle on this PR, is
# treated as dodging the one-shot review rather than an ordinary action (layered OVER the dashboard's
# own default of "off").
Expand Down
2 changes: 1 addition & 1 deletion apps/loopover-ui/public/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -16944,7 +16944,7 @@
{
"schema": {
"type": "string",
"example": "JSONbored/gittensory"
"example": "JSONbored/loopover"
},
"required": false,
"description": "Optional repository filter. Browser sessions must have control-panel access to this repo.",
Expand Down
4 changes: 2 additions & 2 deletions packages/loopover-engine/src/settings/autonomy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { AgentActionClass, AutoMaintainPolicy, AutoMergeMethod, AutonomyLevel, AutonomyPolicy } from "../types/manifest-deps-types.js";

// The graduated autonomy dial (#773), ordered least → most autonomous. Every later agent-layer phase reads
// this BEFORE acting. `observe` is the deny-by-default floor — gittensory watches but never takes an action.
// this BEFORE acting. `observe` is the deny-by-default floor — loopover watches but never takes an action.
// (#4620: `suggest`/`propose` removed -- both were 100% behaviorally identical to `observe`, see
// AutonomyLevel's own doc comment.)
export const AUTONOMY_LEVELS = ["observe", "auto_with_approval", "auto"] as const;
Expand All @@ -22,7 +22,7 @@ const AUTONOMY_LEVEL_SET = new Set<string>(AUTONOMY_LEVELS);
/**
* Resolve the configured autonomy level for one action class on a repo. THE single gate the action layer
* (#778) consults before any write action. Deny-by-default: an unset (or malformed) action class is
* `observe` — gittensory observes but never acts. Pure.
* `observe` — loopover observes but never acts. Pure.
*/
export function resolveAutonomy(autonomy: AutonomyPolicy | null | undefined, actionClass: AgentActionClass): AutonomyLevel {
return autonomy?.[actionClass] ?? DEFAULT_AUTONOMY_LEVEL;
Expand Down
6 changes: 3 additions & 3 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2463,7 +2463,7 @@ export function createApp() {
);
});

// Repo gittensory settings (gate config, AI-review mode/provider/model — NON-secret; the BYOK key is
// Repo loopover settings (gate config, AI-review mode/provider/model — NON-secret; the BYOK key is
// never here). Maintainer DATA: session callers must be a verified maintainer of THIS repo (per-repo
// scope), so a maintainer of repo A cannot read repo B's config. Server-to-server tokens are exempt.
app.get("/v1/repos/:owner/:repo/settings", async (c) => {
Expand Down Expand Up @@ -3691,7 +3691,7 @@ export function createApp() {

// #predicted-live-gate-agreement (maintainer review-stack x AMS integration audit, 2026-07-09): how often the
// MCP predict_gate/explain_gate_disposition verdict agrees with the REAL gate decision a contributor's PR
// later receives -- a DIFFERENT question than /v1/internal/parity's reviewbot-vs-gittensory migration parity
// later receives -- a DIFFERENT question than /v1/internal/parity's reviewbot-vs-loopover migration parity
// (see src/review/predicted-gate-agreement.ts's module header). Same gate/auth contract as /v1/internal/parity:
// bearer-gated by the `/v1/internal/*` middleware, 404 when LOOPOVER_REVIEW_PARITY_AUDIT is off so the
// endpoint does not exist on a deploy not running this telemetry family. Aggregate counts only — no PR
Expand Down Expand Up @@ -5843,7 +5843,7 @@ const DEFAULT_CORS_ORIGINS = [
"http://localhost:3000",
"http://localhost:4173",
"http://localhost:5173",
// gittensory-ui's dev server (@lovable.dev/vite-tanstack-config) binds 8080, not Vite's 5173 default —
// loopover-ui's dev server (@lovable.dev/vite-tanstack-config) binds 8080, not Vite's 5173 default —
// without this, every local/preview dev server is CORS-blocked from /health and shows a false "API unreachable" banner.
"http://localhost:8080",
"http://127.0.0.1:3000",
Expand Down
2 changes: 1 addition & 1 deletion src/auth/github-oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export async function createSessionFromGitHubToken(
): Promise<{ token: string; login: string; expiresAt: string; scopes: string[] }> {
// A caller-supplied token (the github_token_exchange route) carries no proof it was minted for THIS
// OAuth app. Without an audience check, any token a victim issued to an unrelated app would mint a
// gittensory session as that login. The device/web flows skip this — they minted the token themselves.
// loopover session as that login. The device/web flows skip this — they minted the token themselves.
if (options.verifyAppAudience && !(await verifyTokenBelongsToApp(env, githubToken))) {
await recordAuditEvent(env, {
eventType: "auth.github_session",
Expand Down
6 changes: 3 additions & 3 deletions src/config/gittensory-repo-focus-manifest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Bundled fallback for JSONbored/gittensory when the repo file is not yet reachable
* Bundled fallback for JSONbored/loopover when the repo file is not yet reachable
* (local dev, pre-merge branches). Keep aligned with `.loopover.yml` at repo root.
*/
export const LOOPOVER_REPO_FOCUS_MANIFEST_YAML = `# LoopOver repo focus manifest — machine-readable contributor policy for this project.
Expand Down Expand Up @@ -87,7 +87,7 @@ review:
# gittensor:priority at once); resolvePrTypeLabel composes every additive match alongside the one exclusive
# winner, rather than the two categories competing for a single slot.
#
# Review-evasion protection: closing or converting-to-draft your OWN PR while gittensory has an active
# Review-evasion protection: closing or converting-to-draft your OWN PR while loopover has an active
# review pass running, a prior recorded gate failure, or a repeated ready<->draft cycle on this PR, is
# treated as dodging the one-shot review rather than an ordinary action (layered OVER the dashboard's
# own default of "off").
Expand Down Expand Up @@ -127,7 +127,7 @@ maintainerNotes:
- Cosmetic UI-only polish without API wiring or maintainer-approved issue context should be redirected to backend or operator-facing work.
`;

export const GITTENSOR_SELF_REPO_DEFAULT = "JSONbored/gittensory";
export const GITTENSOR_SELF_REPO_DEFAULT = "JSONbored/loopover";

export function resolveLoopOverSelfRepoFullName(env: { LOOPOVER_DRIFT_ISSUE_REPO?: string }): string {
const configured = env.LOOPOVER_DRIFT_ISSUE_REPO?.trim();
Expand Down
10 changes: 5 additions & 5 deletions src/db/repositories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3551,7 +3551,7 @@ export async function countByokAiEventsForRepoSince(env: Env, repoFullName: stri
}

/**
* #hosted-ai-usage-observability: the ONLY AI activity the HOSTED gittensory-api Worker can ever have is a
* #hosted-ai-usage-observability: the ONLY AI activity the HOSTED loopover-api Worker can ever have is a
* maintainer's own BYOK call (the legacy Workers-AI-binding path is retired; `env.AI` is undefined there) --
* yet nothing previously read back the real token/cost columns migration 0109 added to `ai_usage_events` for
* the hosted deployment specifically (the one dashboard built for this, orb-ai-usage.json, is wired
Expand Down Expand Up @@ -5413,7 +5413,7 @@ export async function getGateBlockOutcome(
return { headSha: row.headSha, blockerCodes: parseJson<string[]>(row.blockerCodesJson, []), overridden: row.overridden };
}

// Review-evasion protection (#review-evasion-protection): idempotently mark that gittensory started a fresh
// Review-evasion protection (#review-evasion-protection): idempotently mark that loopover started a fresh
// review pass for repoFullName#pullNumber at headSha, BEFORE any cost-bearing AI-review work begins. A
// redelivery/retry for the SAME headSha while the row is still active is a true no-op (startedAt/deliveryId
// are preserved); a NEW headSha (a fresh commit) or a previously-terminalized row is overwritten with fresh
Expand Down Expand Up @@ -5449,7 +5449,7 @@ export async function startActiveReviewTracking(
});
}

// Review-evasion protection: whether gittensory has an ACTIVE review pass recorded for this EXACT
// Review-evasion protection: whether loopover has an ACTIVE review pass recorded for this EXACT
// repo/PR/headSha -- the read side the closed/converted_to_draft evasion guards check before treating a
// contributor's action as evasion. A row for a DIFFERENT headSha (or a terminalized row) does not count --
// the active window is scoped to the specific commit under review.
Expand Down Expand Up @@ -6202,7 +6202,7 @@ function toPullRequestRecordFromRow(row: typeof pullRequests.$inferSelect): Pull
mergeBlockedSha: row.mergeBlockedSha,
mergeBlockedReason: row.mergeBlockedReason,
approvedHeadSha: row.approvedHeadSha,
// Read straight from the row, NEVER the GitHub payload — this is a gittensory-internal sweep marker.
// Read straight from the row, NEVER the GitHub payload — this is a loopover-internal sweep marker.
lastRegatedAt: row.lastRegatedAt,
lastPublishedSurfaceSha: row.lastPublishedSurfaceSha,
linkedIssueHardRuleViolatedAt: row.linkedIssueHardRuleViolatedAt,
Expand Down Expand Up @@ -7593,7 +7593,7 @@ function normalizeReviewNagPolicy(value: string | null | undefined): "off" | "ho
// #4011: default-ON, the deliberate exception to every other field in this file defaulting conservatively
// (off/false/advisory). A repo that hasn't discovered and explicitly set this field got ZERO self-close/
// draft-dodge/repeated-cycling protection under the old "off" default -- a real, already-exploited gaming
// vector (see gittensory-ai-review-repeat-spend-and-draft-gaming-fix). Any value other than the explicit
// vector (see loopover-ai-review-repeat-spend-and-draft-gaming-fix). Any value other than the explicit
// opt-out "off" (including undefined/garbage) now resolves to "close": protected unless a repo deliberately
// turns it off, not unprotected unless a repo discovers and turns it on. This is the ONLY reachable default
// for this field -- the raw schema.ts column-level DEFAULT and the SQLite DDL default are never reached by
Expand Down
24 changes: 12 additions & 12 deletions src/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const installations = sqliteTable("installations", {
accountId: integer("account_id").notNull(),
// The GitHub App this installation belongs to (#selfhost-app-id). Nullable: only `installation` events (and
// the App-installation API refresh) carry it, so existing rows backfill lazily. Lets a backend tell its OWN
// installations from a SECOND gittensory App installed on the same account (cloud + self-host side by side).
// installations from a SECOND loopover App installed on the same account (cloud + self-host side by side).
appId: integer("app_id"),
targetType: text("target_type").notNull(),
repositorySelection: text("repository_selection"),
Expand Down Expand Up @@ -80,7 +80,7 @@ export const repositorySettings = sqliteTable("repository_settings", {
// Linked-issue satisfaction gate (#1961/#3906). off = the assessment never runs (byte-identical to today,
// and the default); advisory = it runs and renders in the comment but never blocks; block = an above-
// confidence-floor "unaddressed" verdict additionally becomes a hard blocker. See src/rules/advisory.ts's
// isConfiguredGateBlocker (linked_issue_scope_mismatch) and gittensory-gate-setting-wiring for the pattern.
// isConfiguredGateBlocker (linked_issue_scope_mismatch) and loopover-gate-setting-wiring for the pattern.
linkedIssueSatisfactionGateMode: text("linked_issue_satisfaction_gate_mode").notNull().default("off"),
firstTimeContributorGrace: integer("first_time_contributor_grace", { mode: "boolean" }).notNull().default(false),
slopGateMinScore: integer("slop_gate_min_score"),
Expand Down Expand Up @@ -453,38 +453,38 @@ export const pullRequests = sqliteTable(
linkedIssueClaimedAt: text("linked_issue_claimed_at"),
lastSeenOpenAt: text("last_seen_open_at"),
payloadJson: text("payload_json").notNull().default("{}"),
// Latest deterministic slop assessment (gittensory-computed; written separately from the GitHub sync).
// Latest deterministic slop assessment (loopover-computed; written separately from the GitHub sync).
slopRisk: integer("slop_risk"),
slopBand: text("slop_band"),
// RC3 terminal-fail merges: failed-merge attempt count + the head SHA at which the merge is terminally
// blocked (perms/required-check/conflict) so the planner stops planning a merge. Keyed to head SHA → a new
// commit auto-clears it. gittensory-computed (executor-written), omitted from the GitHub-sync SET clause.
// commit auto-clears it. loopover-computed (executor-written), omitted from the GitHub-sync SET clause.
mergeAttemptCount: integer("merge_attempt_count").notNull().default(0),
mergeBlockedSha: text("merge_blocked_sha"),
mergeBlockedReason: text("merge_blocked_reason"),
// Review-evasion: repeated ready<->draft cycling (#gaming-tactic-draft-cycle). Counts every converted_to_draft
// webhook ever processed for this PR NUMBER -- deliberately NOT scoped to head SHA like mergeAttemptCount,
// since cycling back to draft after a fresh push is exactly the same evasion shape a new commit must not
// reset. gittensory-computed (webhook-written), omitted from the GitHub-sync SET clause.
// reset. loopover-computed (webhook-written), omitted from the GitHub-sync SET clause.
draftConversionCount: integer("draft_conversion_count").notNull().default(0),
// Re-approval idempotency: the head SHA the bot last auto-approved. The planner skips the `approve`
// disposition while approved_head_sha == headSha (this commit is already approved). Keyed to head SHA → a
// new commit makes the bot re-approve the new code. gittensory-computed (executor-written), omitted from
// new commit makes the bot re-approve the new code. loopover-computed (executor-written), omitted from
// the GitHub-sync SET clause so a later sync cannot clobber it. (Mirrors merge_blocked_sha.)
approvedHeadSha: text("approved_head_sha"),
// Sweep convergence: the timestamp the scheduled re-gate sweep last recomputed this PR. selectRegateCandidates
// orders the sweep by THIS marker (not GitHub's updated_at) so it advances through all open PRs even when the
// review WRITE that would bump updated_at is suppressed (dry-run / paused). gittensory-computed (sweep-written),
// review WRITE that would bump updated_at is suppressed (dry-run / paused). loopover-computed (sweep-written),
// omitted from the GitHub-sync SET clause so a later sync cannot clobber it. (Mirrors approved_head_sha.)
lastRegatedAt: text("last_regated_at"),
// Draining guard for backlog-convergence-sweep (#4502), mirroring lastRegatedAt but scoped to THIS sweep --
// stamped at dispatch by sweepRepoBacklogConvergence, read by fanOutBacklogConvergenceSweepJobs to skip a
// repo whose prior fan-out is still draining. Kept separate from lastRegatedAt so the two differently-cadenced
// sweeps' in-flight signals never conflate. gittensory-computed, omitted from the GitHub-sync SET clause.
// sweeps' in-flight signals never conflate. loopover-computed, omitted from the GitHub-sync SET clause.
lastBacklogConvergenceRegatedAt: text("last_backlog_convergence_regated_at"),
// Public-surface marker: the head SHA at which the public surface (comment/label/check-run) was LAST published.
// Used for reporting and stale-surface diagnostics, not as a hard sweep skip; GitHub comments/checks can still
// be stale or partial while this marker matches headSha. gittensory-computed (publish-written), omitted from
// be stale or partial while this marker matches headSha. loopover-computed (publish-written), omitted from
// the GitHub-sync SET clause so a later sync cannot clobber it. (Mirrors approved_head_sha.)
lastPublishedSurfaceSha: text("last_published_surface_sha"),
// Linked-issue hard-rule violation memory (#linked-issue-hard-rule-persistence). The FIRST time this PR NUMBER
Expand All @@ -494,7 +494,7 @@ export const pullRequests = sqliteTable(
// ADDITIONALLY alongside resolveLinkedIssueHardRule's own live re-parse so a contributor cannot dodge the
// flag-then-close verification window by stripping the closing reference from the body, or by the linked
// issue's live state changing (e.g. unassigned), between the flagging pass and the verification pass.
// gittensory-computed (planner-written), omitted from the GitHub-sync SET clause so a later sync cannot clobber
// loopover-computed (planner-written), omitted from the GitHub-sync SET clause so a later sync cannot clobber
// it.
linkedIssueHardRuleViolatedAt: text("linked_issue_hard_rule_violated_at"),
// The specific rule reason text captured at the moment of the FIRST violation (mirrors merge_blocked_reason's
Expand All @@ -506,7 +506,7 @@ export const pullRequests = sqliteTable(
// shot) for this PR. Lets the deterministic screenshotTableGate treat a successful automated capture as
// equivalent evidence to a hand-authored before/after table. Keyed to head SHA (mirrors approved_head_sha /
// last_published_surface_sha) -- a new commit re-arms the requirement until capture succeeds again for the
// new head. gittensory-computed (publish-written), omitted from the GitHub-sync SET clause so a later sync
// new head. loopover-computed (publish-written), omitted from the GitHub-sync SET clause so a later sync
// cannot clobber it.
visualCaptureSatisfiedSha: text("visual_capture_satisfied_sha"),
createdAt: text("created_at").notNull().$defaultFn(() => nowIso()),
Expand Down Expand Up @@ -819,7 +819,7 @@ export const gateOutcomes = sqliteTable(
);

// Review-evasion active-review tracking (#review-evasion-protection): one row per (repo, PR), recording that
// gittensory started a fresh review pass against a specific headSha before any cost-bearing AI-review work
// loopover started a fresh review pass against a specific headSha before any cost-bearing AI-review work
// begins. Read by the closed/converted_to_draft webhook handlers to tell a contributor evading the one-shot
// review mid-pass apart from an ordinary close/draft conversion after the review already concluded. `status`
// flips 'active' -> 'terminal' once the pass concludes (published, PR closed/merged, head moved, or evasion
Expand Down
Loading
Loading