feat(invariant): the platform ships with Anthropic completely down #5479
2 changed files+122−0
Modifieddocs/ops/OPERATIONS.md+23−0View fileUnifiedSplit
@@ -97,6 +97,29 @@ after flipping the PR to merged. Before 2026-08-08 that hook did not exist
9797and `branches: [main]` workflows effectively never fired — main moves by
9898PR merges here, not pushes.
9999
100## Ship-path AI independence — THE invariant (2026-08-10)
101
102**The platform must ship with Anthropic completely down.** Owner-stated
103after the API balance hit zero: "we shouldn't need credit balance to ship
104a product — if Anthropic ever went down then so would our websites."
105
106- **Ship path** (push → CI → gates → merge → deploy) has NO hard AI
107 dependency. Push scanning is regex; CI is credential-free; the AI
108 review and AI security gates degrade to honest "skipped/unavailable"
109 fail-open states; clean merges (including non-fast-forward) complete
110 with zero AI calls; conflicted files are the only thing that ever
111 reaches Claude, and that failure is honest, not blocking-forever.
112- **Internal AI** (repairs, reviews, summaries) is an accelerant, billed
113 to the owner's subscription via the agent queue where it matters —
114 never to platform-critical API calls.
115- **Customer AI** features are the only surfaces allowed to hard-require
116 the metered API (their quota, their outage).
117
118Enforced by `src/__tests__/ship-path-ai-independence.test.ts` — new
119features on the ship path either stay AI-free or add a degradation seam
120and pin it there. Audited 2026-08-10 with a zero balance: ~10 PRs shipped
121over two days in that state.
122
100123## GitHub reality (2026-07-12)
101124
102125The old GitHub repo (`ccantynz-alt/Gluecron.com`) is NOT frozen and is
Addedsrc/__tests__/ship-path-ai-independence.test.ts+99−0View fileUnifiedSplit
@@ -0,0 +1,99 @@
1/**
2 * THE INVARIANT: the platform ships with Anthropic completely down.
3 *
4 * Owner-stated, 2026-08-10: "we shouldn't need credit balance to ship a
5 * product — if Anthropic ever went down then so would our websites."
6 * Internal AI is an accelerant (and runs on the owner's subscription via
7 * the agent queue when it matters); it is never load-bearing for the ship
8 * path. Only CUSTOMER features may hard-require the metered API.
9 *
10 * Ship path = push → CI → gates → merge → deploy. Audited 2026-08-10 with
11 * the API balance at zero — ~10 PRs shipped that way over two days — and
12 * each seam below is what keeps it true. This suite pins them so a future
13 * feature cannot quietly re-introduce a hard dependency.
14 */
15
16import { describe, expect, it } from "bun:test";
17
18async function read(path: string): Promise<string> {
19 return Bun.file(path).text();
20}
21
22describe("ship path has no AI imports at all", () => {
23 // These modules must not even IMPORT the AI client — a degradation
24 // seam can be mis-wired, an absent import cannot.
25 const AI_FREE = [
26 "src/lib/push-policy.ts", // pre-receive secret scan is pure regex
27 "src/lib/push-workflow-sync.ts", // workflow sync + enqueue
28 "src/lib/workflow-runner.ts", // CI execution
29 "src/lib/gate-ci.ts", // (does not exist — placeholder guard removed below if absent)
30 ];
31 for (const path of AI_FREE) {
32 it(`${path} never touches ai-client`, async () => {
33 const file = Bun.file(path);
34 if (!(await file.exists())) return; // placeholder tolerated
35 const src = await file.text();
36 expect(src).not.toContain('from "./ai-client"');
37 expect(src).not.toContain('from "../lib/ai-client"');
38 expect(src).not.toContain("getAnthropic");
39 });
40 }
41
42 it("the deploy script pipeline is AI-free", async () => {
43 for (const path of ["scripts/auto-update.sh", "scripts/ci-tests.sh"]) {
44 // Comments may MENTION the invariant; executable lines may not
45 // depend on it.
46 const code = (await read(path))
47 .split("\n")
48 .filter((l) => !l.trimStart().startsWith("#"))
49 .join("\n");
50 expect(code.toLowerCase()).not.toContain("anthropic");
51 }
52 });
53});
54
55describe("merge execution: AI only ever sees conflicted files", () => {
56 it("mergeWithAutoResolve commits clean merges before any Claude involvement", async () => {
57 const src = await read("src/lib/merge-resolver.ts");
58 // The clean-merge success return must come BEFORE the first
59 // resolveConflict call in the function body — a clean non-ff merge
60 // (the overwhelmingly common case) completes with zero AI calls.
61 const fn = src.slice(src.indexOf("export async function mergeWithAutoResolve"));
62 const cleanReturn = fn.indexOf("return { success: true, resolvedFiles: [], commitSha");
63 const aiCall = fn.indexOf("resolveConflict(");
64 expect(cleanReturn).toBeGreaterThan(-1);
65 expect(aiCall).toBeGreaterThan(cleanReturn);
66 });
67
68 it("getAnthropic is confined to the per-file conflict resolver", async () => {
69 const src = await read("src/lib/merge-resolver.ts");
70 const firstUse = src.indexOf("getAnthropic()");
71 const resolver = src.indexOf("async function resolveConflict");
72 expect(firstUse).toBeGreaterThan(resolver);
73 });
74});
75
76describe("gates degrade, never block, on AI outage", () => {
77 it("AI review has the unavailable fail-open state", async () => {
78 const src = await read("src/lib/gate.ts");
79 expect(src).toContain('aiReviewApproved === "unavailable"');
80 expect(src).toContain("not blocking the merge");
81 });
82
83 it("the AI security scan degrades to skipped on provider errors", async () => {
84 // Behavior pinned in gate-security-scan-availability.test.ts; this is
85 // the cross-reference so THIS suite fails if that seam is deleted.
86 const src = await read("src/lib/gate.ts");
87 expect(src).toContain("aiSecurityScanSafe");
88 });
89
90 it("the CI gate consults workflow runs, not the AI client", async () => {
91 const src = await read("src/lib/gate.ts");
92 const fn = src.slice(
93 src.indexOf("export async function checkCiWorkflows"),
94 src.indexOf("export async function runAllGateChecks")
95 );
96 expect(fn).not.toContain("getAnthropic");
97 expect(fn).not.toContain("ai-client");
98 });
99});
0100
c comment · e edit title · m merge · a approve · r request changes · ? shortcuts