diff --git a/config/examples/gittensory.full.yml b/config/examples/gittensory.full.yml index 6d595f4e93..8497fd698c 100644 --- a/config/examples/gittensory.full.yml +++ b/config/examples/gittensory.full.yml @@ -355,6 +355,13 @@ gate: # Review output controls. These tune review output without changing the # 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/.gittensory.yml` (see +# `config/examples/shared.gittensory.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 +# review). The loader records provenance at runtime in `review.sharedConfigSource` (not a YAML key). review: # Deterministic AI review eligibility filters (`review.auto_review`, #1954 / #2038–#2065). Each knob quietly # skips the advisory AI review for matching PRs — never a gate failure. When the Orb review check is enabled, diff --git a/test/unit/predicted-gate-engine-coverage.test.ts b/test/unit/predicted-gate-engine-coverage.test.ts index 0e42644720..85b0e57cbe 100644 --- a/test/unit/predicted-gate-engine-coverage.test.ts +++ b/test/unit/predicted-gate-engine-coverage.test.ts @@ -106,7 +106,10 @@ describe("predicted-gate engine module coverage (#2283)", () => { }); it("exercises duplicate-winner election helpers", () => { - expect(isDuplicateClusterWinnerByClaim({ number: 1, createdAt: "2026-01-01T00:00:00.000Z" }, [{ number: 2, createdAt: "2026-01-02T00:00:00.000Z" }])).toBe(true); + // #dup-winner anti-backdating: createdAt is deliberately NOT an ordering signal (a contributor can edit an + // old placeholder PR to add a linked issue later), so a pair with only createdAt and no linkedIssueClaimedAt + // has no comparable claim time and fails closed, regardless of which createdAt is earlier. + expect(isDuplicateClusterWinnerByClaim({ number: 1, createdAt: "2026-01-01T00:00:00.000Z" }, [{ number: 2, createdAt: "2026-01-02T00:00:00.000Z" }])).toBe(false); expect( isDuplicateClusterWinnerByClaim( { number: 2, linkedIssueClaimedAt: "2026-01-02T00:00:00.000Z" }, @@ -124,8 +127,10 @@ describe("predicted-gate engine module coverage (#2283)", () => { { number: 1, createdAt: "2026-01-01T00:00:00.000Z" }, [{ number: 2, createdAt: "2026-01-01T00:00:00.000Z" }], ), - ).toBe(true); - expect(resolveDuplicateClusterWinnerNumber({ number: 2, createdAt: "2026-01-02T00:00:00.000Z" }, [{ number: 1, createdAt: "2026-01-01T00:00:00.000Z" }])).toBe(1); + ).toBe(false); + // Same fail-closed reasoning: resolveDuplicateClusterWinnerNumber mirrors isDuplicateClusterWinnerByClaim, + // so a createdAt-only pair with no linkedIssueClaimedAt is not a determinable election either. + expect(resolveDuplicateClusterWinnerNumber({ number: 2, createdAt: "2026-01-02T00:00:00.000Z" }, [{ number: 1, createdAt: "2026-01-01T00:00:00.000Z" }])).toBeNull(); expect(resolveDuplicateClusterWinnerNumber({ number: 1, createdAt: null }, [{ number: 2, createdAt: null }])).toBeNull(); }); diff --git a/test/unit/queue.test.ts b/test/unit/queue.test.ts index a9cf622176..8e895a2ad9 100644 --- a/test/unit/queue.test.ts +++ b/test/unit/queue.test.ts @@ -27223,7 +27223,7 @@ describe("auto-action convergence: end-to-end plan+execute for the general heuri // #terminal-outcome-audit: the disposition counter's "close" action_class, with the actual gate-blocker // code (missing_linked_issue, from the default linkedIssueGateMode:block + no-linked-issue body) as the // bounded blocker_class -- proof this reaches the real gate.blockers, not just a hardcoded label. - expect(await renderMetrics()).toContain('gittensory_agent_disposition_total{action_class="close",autonomy_level="auto",blocker_class="missing_linked_issue"} 1'); + expect(await renderMetrics()).toContain('gittensory_agent_disposition_total{action_class="close",autonomy_level="auto",blocker_class="missing_linked_issue",repo="redacted-1"} 1'); const nativeDecision = await env.DB.prepare("select decision, summary, source from review_audit where event_type = 'gate_decision' and target_id = ?").bind(`${REPO}#60`).first<{ decision: string; summary: string; source: string }>(); expect(nativeDecision).toMatchObject({ decision: "close", summary: "missing_linked_issue", source: "gittensory-native" }); }); @@ -27266,7 +27266,7 @@ describe("auto-action convergence: end-to-end plan+execute for the general heuri expect(seen.merged).toBe(false); expect(seen.closed).toBe(false); - expect(await renderMetrics()).toContain('gittensory_agent_disposition_total{action_class="hold",autonomy_level="auto",blocker_class="guardrail_hold"} 1'); + expect(await renderMetrics()).toContain('gittensory_agent_disposition_total{action_class="hold",autonomy_level="auto",blocker_class="guardrail_hold",repo="redacted-1"} 1'); const holdAudit = await env.DB.prepare("select metadata_json from audit_events where event_type = 'agent.action.hold' order by created_at desc limit 1").first<{ metadata_json: string }>(); expect(JSON.parse(holdAudit?.metadata_json ?? "{}")).toMatchObject({ repoFullName: REPO, @@ -27394,7 +27394,7 @@ describe("auto-action convergence: end-to-end plan+execute for the general heuri const mergeAudit = await env.DB.prepare("select count(*) as n from audit_events where event_type = 'agent.action.merge'").first<{ n: number }>(); expect(mergeAudit?.n).toBeGreaterThanOrEqual(1); // #terminal-outcome-audit: the disposition counter's "merge" action_class, on the actual live call site. - expect(await renderMetrics()).toContain('gittensory_agent_disposition_total{action_class="merge",autonomy_level="auto",blocker_class="none"} 1'); + expect(await renderMetrics()).toContain('gittensory_agent_disposition_total{action_class="merge",autonomy_level="auto",blocker_class="none",repo="redacted-1"} 1'); }); // #terminal-outcome-audit: end-to-end proof that the LIVE runAgentMaintenancePlanAndExecute call site (not just @@ -27468,7 +27468,7 @@ describe("auto-action convergence: end-to-end plan+execute for the general heuri // early return -- this is the exact "hold, but no audit_events row at all" shape (the breaker downgrade // leaves no merge/close action) that previously had zero aggregate signal. close autonomy is unset in this // repo's settings (only merge/approve are configured), so it resolves to the default "observe". - expect(await renderMetrics()).toContain('gittensory_agent_disposition_total{action_class="hold",autonomy_level="observe",blocker_class="none"} 1'); + expect(await renderMetrics()).toContain('gittensory_agent_disposition_total{action_class="hold",autonomy_level="observe",blocker_class="none",repo="redacted-1"} 1'); const holdAudit = await env.DB.prepare("select detail, metadata_json from audit_events where event_type = 'agent.action.hold' order by created_at desc limit 1").first<{ detail: string; metadata_json: string }>(); expect(holdAudit?.detail).toBe("auto-action held by precision circuit breaker"); expect(JSON.parse(holdAudit?.metadata_json ?? "{}")).toMatchObject({