fix: GitHub OAuth handle private emails - #1
Merged
Conversation
GitHub's /user endpoint returns email: null for accounts that mark their primary email private (the default). resolveOAuthProfile() then threw OAUTH_PROFILE_INCOMPLETE and the callback bounced users back to /login with detail=OAUTH_PROFILE_INCOMPLETE even though the OAuth handshake succeeded. The fix falls back to /user/emails (granted by the user:email scope we already request) and picks the primary verified address. Also adds the User-Agent and vendor accept headers GitHub's REST API requires, and coerces profile.id to a string so numeric GitHub IDs survive the externalId round-trip. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
openaxcloud
pushed a commit
that referenced
this pull request
Jun 1, 2026
…oy, add invoices endpoint (audit #1, #4, #8) #1: call deployProviderConfigError before creating a deploy so non-static providers without a configured hook return 400 PROVIDER_NOT_CONFIGURED instead of a fake *.vibecore.local URL marked READY. #4: the redeploy route now actually re-runs the build (static build + snapshot, or provider hook) instead of cloning the previous READY row. #8 (backend): add GET /orgs/:orgId/billing/invoices backed by a new StripeBillingClient.listInvoices(); returns [] when no customer/Stripe. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 10, 2026
…/collab-atomicity + 18 more CRITICAL #0: SAML ACS now binds the asserted email to a domain the org has verified ownership of (store.listDomainVerifications), blocking an org owner from minting a self-signed assertion for an arbitrary victim (cross-org / platform-admin account takeover). HIGH/MED/LOW (api, CD-deployed): - #5 platform-admin bootstrap deferred from registration to email-verify (no pre-verification admin squatting); /auth/reauth added to MFA-exempt paths. - #1 redeploy route gets the create route's in-flight concurrency guard. - #2/#19 file-search: default node_modules/.git/build ignores, file-count + file-size + per-line-length caps (bounds ReDoS worst-case; re2 still tracked). - #3/#4/#17/#21 collaboration edit/terminal-perms/ai-conversation now do an atomic optimistic-concurrency RMW (mutateProjectIdeState) so concurrent writes can't clobber peers or resurrect a revoked terminal grant. - #14/#40 git-remote SSRF: canonicalize IPv4-mapped IPv6 + decimal/hex/octal IP literals before the blocklist; reject embedded URL credentials. - #18 workspace export skips dependency/build trees + caps bytes/file-count. - #20 native WS keep-alive ping on logs/files.watch/ports.watch (LB idle flap). - #22 Stripe webhook: don't swallow transient org-resolution errors (roll back dedup row + rethrow so Stripe retries); #23 resolve org via subscription external id so invoice.payment_failed can downgrade. - #24 MFA setup + #47 recovery-codes now require recent re-auth. - #25 admin workspace delete actually reclaims pod/PVC via the manager. - #26 IP allowlist also resolves org/project from the request body. - #27 add revoke for project share-links + chat-shares (revokedAt was dead) with DELETE routes; #45 WS comment.create records activity+audit like the HTTP path. - #46 enforce snapshots.sizeMb quota; #48 last-platform-admin guard on revoke/suspend; #49 honest rate-limit key (drop dead currentUser ref); #50 enforce agent-memory opt-out; #52 dedup probe no longer inflates accessCount. All 347 api tests pass; typecheck clean. Tests updated for the new verify-before-admin / reauth-before-MFA / SAML-domain behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 10, 2026
…ards (TOCTOU) Adds store.withSerializedMutation(key, fn): a Postgres transaction-scoped advisory-lock primitive that serializes a check-then-mutate critical section across all pods. Applied to the four TOCTOU races where a non-atomic read-check-write let concurrent privileged requests defeat a guard: - #1 PATCH /orgs/:orgId/memberships/:userId — owner demotion last-owner check. - #2 DELETE /orgs/:orgId/memberships/:userId — owner removal last-owner check. - #6 PATCH /admin/users/:userId/platform-admin — last-admin revoke check. - #7 POST /projects/:projectId/deployments — in-flight + quota guard (two concurrent creates could both queue + double-consume deploy quota). Each previously read the count, passed the >1 guard, then mutated as separate DB round-trips, so two simultaneous requests could each pass and leave the org owner-less / platform admin-less, or double-queue a deploy. Now the count + mutate run under one advisory lock keyed by org/platform/project. 347 api tests pass; typecheck clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 10, 2026
7 confirmed new findings (deduped vs 191 prior across waves 14-17), all fixed: - #0 HIGH SIEM webhook URL SSRF: siemWebhookSchema.url now refined with isSafeWebhookUrl (https + non-internal host, reusing the IP-literal-aware git host blocklist) — the stored URL is fetched server-side on abuse/delivery. - #1 /commands/stream missing terminal-permission check: extracted a shared denyTerminalSocketIfRevoked() and applied it to BOTH /commands/stream and /terminal — a write-collaborator with terminal access revoked could previously run arbitrary shell via the streaming socket. - #2 latestUserText handles parts-array message content (was sending an array as the agent-memory query, breaking retrieval). - #3 web-search outbound moved to node:http(s) with a connect-time validating lookup (rejects any resolved internal IP at socket-connect) — closes the DNS-rebind TOCTOU global fetch left open; keeps per-hop redirect re-validation. - #4 APIKeyManager getApiKeysFromCookies wraps JSON.parse in try/catch. - #5 preview-proxy skips inspector injection for non-UTF-8 charsets (toString utf8 corrupted ISO-8859-1/Shift_JIS pages) — streams them byte-exact instead. - #6 Preview 'open in new tab' uses noopener,noreferrer (untrusted preview code). Full workspace typecheck + 351 api + 14 preview-proxy + 46 web tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 10, 2026
Deduped vs 198 prior (waves 14-18), all fixed: - HIGH #0 proxyRuntimeSocket async message listener wrapped in try/catch — an unawaited rejection (malformed frame / send to closed client) is a routine event with no process-level handler = API crash vector. - HIGH #1 runtime preview proxy streams the (user-controlled) response via Readable.fromWeb instead of arrayBuffer-buffering it whole — cross-tenant OOM. - #2 org-suspension enforced on POST /projects/:id/deployments; #3 on AI message posting (suspended orgs were still queuing builds / burning LLM tokens). - #4 proxyRuntimeSocket pendingMessages buffer capped (1000) — unbounded pre-open client input. - #5 ai-gateway quorum: file-only runs (no opinion votes) no longer spuriously REJECTED — decide by accepted file claims + participation. - #6 selectContext maxTokens cap (matches createSummary). - #7 ws-manager touch() swallows P2025 on concurrent row deletion. - #8 self-repair cookie parser tolerates malformed percent-encoding (safeDecode). - #9 action-runner threads the action abortSignal into the self-repair LLM call. Full workspace typecheck + local web build (client+SSR) + 351 api + 42 ai-gateway + 14 ws-manager tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 10, 2026
Deduped vs 208 prior (waves 14-19), all fixed (0 high — mostly hardening of established patterns): - #0/#1/#2/#9 fetch timeouts (AbortSignal.timeout 15s) on gitlab-branches (x2), github-branches (x2), gitlab-projects, github-user (x4) — hung upstream pinned the handler. - #3 org-suspension enforced on deployment redeploy; #4 redeploy in-flight guard + #5 deploy-create quota now run inside an ORG-level withSerializedMutation (org-scoped quota was bypassable by concurrent creates across different projects). - #6 ai-gateway Byzantine agreementScore excludes file votes (consistent with Quorum); #7 memory rate-limiter gains close() that clears the sweep interval. - #8 ws-manager startWorkspace runs k8s cleanup BEFORE the FAILED store.update so a store failure can't leak the Pod/Service. - #10 githubConnection token cookies set Secure+SameSite=strict+expiry (match useGit). Full typecheck + local web build (client+SSR) + 351 api + 42 ai-gateway + 14 ws-manager tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 10, 2026
…(1 high) Deduped vs 219 prior (waves 14-20): - HIGH: GitLab SSRF IPv6-bypass — both api.gitlab-projects + api.gitlab-branches used a string-prefix host blocklist that let bracketed IPv6 / IPv4-mapped-IPv6 ([::1], [::ffff:169.254.169.254], fd00::1) through while forwarding the user's bearer token. New shared isSafeGitForgeUrl/isBlockedHost in utils/url.ts (IPv6-aware via isPrivateIp + CGNAT 100.64/10); isAllowedUrl (web-search) also routed through it. - agent-memory remember() quota TOCTOU: create() now enforces the per-user+scope cap atomically (advisory-lock tx: count+insert), so concurrent writes can't exceed AGENT_MEMORY_SCOPE_LIMIT; remember() maps the coded error to skipped. - ws-manager touch() rolls back the throttle marker on a non-P2025 update failure (stale lastActiveAt risked premature GC). - connector-proxy only buffers responses with a known small Content-Length (≤1MB); large/unknown json/text now stream (was unbounded arrayBuffer = OOM). - netlify.ts store fetches get AbortSignal.timeout(15s). Full typecheck + local web build (client+SSR) + 351 api + 15 connector-proxy + 14 ws-mgr tests pass. FLAGGED for your decision (not a safe mechanical fix): wave-21 #1 — the admin SPA is deployed (replicas=2) but has no ingress route, so it's only reachable via kubectl port-forward. Wiring a public /admin route would expose an in-progress admin UI; whether/where to expose it (domain, auth) is a product+security call. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 11, 2026
…c rollback, MCP SSRF, token leak, project race) - #0 synchronous POST /commands honored no terminal-revocation gate (only the two WS shell paths did) — an editor/member with revoked terminal access could still run arbitrary commands. Extracted isTerminalAccessRevoked() and applied it. - #1 static (and github-pages/cloud-run/docker) rollback was created QUEUED but triggerProviderRollback has no follow-up for them, so it never reached READY and held a ~40min in-flight lock blocking all deploys. New providerRollbackProviders set = only vercel/netlify/cloudflare-pages get QUEUED; others READY immediately. - #2/#7 isBlockedMcpUrl bypassable via IPv4-mapped IPv6 literals (URL parser folds 127.0.0.1 to ::ffff:7f00:1, matching no dotted regex). Fold ::ffff: forms to dotted IPv4 and re-check the private ranges. - #3 github-stats (unauthenticated loader) fell back to the server GITHUB_TOKEN, letting any anonymous caller read the platform account. Dropped the server-env fallback; only the caller's cookie token is used. - #4 #persistRuntimeFilesToProjectStorage re-read the live #projectId at the destructive replaceExisting POST; a project switch mid-flow wrote one project's files into another's storage. Capture projectId+runtime once at entry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 11, 2026
…visory-lock pool deadlock - #0 (HIGH) assertRealPathContained let a DANGLING symlink at the final path component through: realpath() throws ENOENT on a dangling link, the loop treated it as 'not created yet' and approved, then writeFile/patch/restore FOLLOWED the link and wrote OUTSIDE the workspace root (sandbox escape). Now lstat the final component and reject any symlink target on the write paths (create/rename already immune). - #1 (HIGH) withSerializedMutation held pg_advisory_xact_lock on a transaction borrowed from the MAIN pool while fn() needed a second main-pool connection. Under same-key burst >= poolMax, waiters' blocked transactions exhausted the pool and the lock holder's fn() deadlocked. Run the lock transaction on a small DEDICATED pool (createDatabaseClient now takes poolMax) so lock-wait connections never starve fn(); serialization semantics unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 11, 2026
…dings - #0 Stripe 'incomplete'/'paused'/unknown subscription status no longer maps to CANCELED (which the reactivation guard then locked permanently → paid SCA/EU customers stuck on free). incomplete→UNPAID, paused→PAST_DUE, incomplete_expired→CANCELED, unknown→UNPAID (recoverable). - #1 POST /runtime/workspaces/:id/restart now enforces workspaces.active quota (serialized) when bringing a STOPPED workspace back up — stop→restart could exceed the plan limit. - #2 api.github-user (unauthenticated loader) drops the server GITHUB_TOKEN fallback — only the caller's cookie token (same as github-stats). - #3 runtime-socket upstream 'error' listener guards client.send — an unguarded send on the already-closing downstream socket crashed the whole API. - #16/#18 ws-agent /terminal onExit listener is now REMOVED on socket close (backend.onExit returns a disposer) — my wave-29 #3 fix leaked a listener on every reattach to a shared session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 11, 2026
…ert theme (UI wave 1 #1/#32/#55/#52) - #1/#32 Markdown content p/a/inline-code break long unbroken strings (overflow-wrap:anywhere + word-break) on ALL chat surfaces, not just the IDE panel — long URLs/tokens/paths no longer overflow the column / force h-scroll. - #55 ChatAlert error description wraps + scrolls (max-h-40) instead of stretching. - #52 SupabaseAlert dismiss button uses theme tokens (was hardcoded dark-brown, broken on light theme). Web build green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 17, 2026
…) for P5 Per Avi: providers&models/updates/status/debug/logs/MCP-global + all new (credits/usage/payg/plans/quotas/Stripe-health/users+impersonation/flags) → ADMIN. Profile/Settings/Connections/Data/Notifications/own-MCP + NEW Billing&Usage (plan/credit balance/effort usage/invoices = #1 user gap) → USER, surfaced VISIBLY in the Dashboard (not the Bolt modal). Task Manager/Debug/Event Logs → admin-only; chat API-key entry + per-user provider toggles → removed (admin registry). Model selector stays in the IDE composer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 17, 2026
…er gap) Adds the user-facing Credits & usage section to /billing (already in the Dashboard Organization nav): credit balance, purchased pack balance, total available, budget cap, and recent agent checkpoints (effort-based proof-of-work with build-tier + high-power/extended-thinking/turbo flags). Backed by GET /orgs/:id/credits; a credits fetch failure never breaks billing; shows a 'Preview (not charged)' badge in shadow mode. Verified LIVE (registered user, real API on :3001): 0 console errors, responsive 1440 + 390. User account sections already visible in the Dashboard sidebar. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 20, 2026
From the exhaustive Replit Git-pane capture, two confirmed real gaps: - Diff (#1): clicking a changed file loaded the unified diff into a raw <pre> dump. Replace it with GitDiffView — a parsed, line-numbered, add/remove-coloured renderer (old/new gutters, hunk + file-meta styling), matching Replit's diff view. Frontend-only; the diff text already arrives from /git/diff. - Working tree (#2, partial): add "Stage all" / "Clear" bulk controls to the working-tree header (client-side staging selection, like the existing per-file checkboxes). Per-file/Discard-All (real git restore) is a follow-up needing a new /git/discard route. Gates green: monorepo typecheck, eslint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 21, 2026
…pen time From the sweep (double-verified), money-critical but latent (credits shadow/dormant): - HIGH #25: debitCredits recorded the ENTIRE non-pack amount as a wallet-balance debit, driving the balance negative so settle's paygChargeCents (creditCents - fromPacks - fromBalance) was ~always 0 → PAYG AI usage was NEVER reported to Stripe and budget-cap / spend alerts stayed dead. Now caps the balance draw at the real wallet balance; the uncovered remainder overflows to PAYG. New test: 50¢ balance vs 130¢ cost → fromBalance clamped to 50, wallet lands at 0 (not -80), 80¢ becomes the PAYG overage. 18/18 green. - #8: workspace reopen reset lastActiveAt but not lastMeteredAt, so the next stop metered the entire STOPPED window (pod deleted, zero compute) as reserved compute. Now resets the metering marker on reopen so only the new active window bills. Idempotency trio (#1 storage-sweep, #7 PAYG-dedup constraint, #9 GC cross-replica) next — each needs a dedup store-method / migration / compare-and-set. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 21, 2026
…day) The daily object-storage sweep (meterAllObjectStorage) had no dedup — unlike the deployment meter (lastMeteredAt) and PAYG ledger (checkpointId), it recorded a usage event + debited credits unconditionally on every invocation, so a duplicate trigger (pod restart mid-sweep, double-schedule, manual re-run of the internal route) silently double-charged the day's GiB-months. Add store.hasUsageEventSince(org, type, sinceMs) and skip any org already metered for storage.objectGiBMonths since UTC midnight. orgsMetered now reflects actually-metered orgs. Test: two runs same day → second is a no-op, single usage event. 8/8 green. Remaining billing: #7 (CreditLedger @@unique migration) + #9 (GC cross-replica compare-and-set) — flagged for a careful focused pass (migration + concurrency). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 22, 2026
…stead of failing Fan-out #1 critical (the real cold-provision root cause). The manager's /workspaces/start blocks on pod readiness (waitForReadiness 180s + waitForAgentReachable 45s) which far exceeds the api->manager request timeout (RUNTIME_PROXY_TIMEOUT_MS=15s). So essentially every COLD provision threw WORKSPACE_MANAGER_UNAVAILABLE (502) — surfacing as a 'crashed runtime' on the user's very first prompt-to-workspace step — while the manager kept provisioning to RUNNING, leaving the api record stuck STARTING (the reconcile only ran on a *returned* FAILED, never on a *thrown* timeout). Now the start managerRequest is wrapped: a transient provisioning timeout / manager 5xx is NOT treated as failure — the record stays STARTING (it genuinely is) and the route returns a 'starting' session so the IDE adapter polls /status until RUNNING (its existing non-running poll path), instead of a false 502. A deterministic manager error (4xx) DOES reconcile the record to FAILED (releases the active-workspace slot, no quota lock) and rethrows the real error. SAFE: the success path is unchanged (warm starts that return within 15s are untouched), so this cannot break working starts; it only converts the timeout/error path from a false 502 into the poll-to-ready flow. API typecheck clean; critical-paths/workspace api specs 7/7. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 22, 2026
… (CRITICAL) Fan-out panel-wiring #1 (critical): in remote-kubernetes (production) the editor showed EMPTY content for every file. reloadFromRuntime() built the map from listFiles(), whose API route (/files -> mapRuntimeNodes) and agent (/files/tree) return tree only — NO content (content is read lazily per file via readFile). It then set content: node.content ?? '' and HARD-REPLACED the whole map, so every file landed with ''. ProjectWorkspaceProvider hydrates real content from project storage first, but the subsequent loadRuntimeFiles('.') reload overwrote it with empty strings, and there is no lazy readFile on open — so opening any file showed a blank editor. (Existing tests masked it because their listFiles mock injects content, unlike the real mapRuntimeNodes.) Fix: MERGE instead of clobber — when the runtime tree omits content (node.content undefined) and the current map already holds non-empty content for that path (from project storage), keep the existing entry. Tree structure (adds/removes/folders) still applies; local/webcontainer mode (tree includes content) is unchanged. New files.reload-content.spec.ts (3 tests): content preserved on tree-only reload; runtime content used when provided; empty fallback for a brand-new tree file. Existing reload spec + typecheck + lint green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 28, 2026
…kages) Re-scan of all 19 packages/* (billing/quota/security/audit/rbac/auth/observability/editor/ runtime/db/...) via 7 cluster agents: 120 actionable surfaceable capabilities appended. KEY: the Billing/Usage backend is ALREADY BUILT (routes billing.tsx + usage.tsx; endpoints /orgs/:id/credits, /credits/limits, /billing/checkout|portal|invoices, /orgs/:id/usage) — the #1 user gap needs SURFACING, not a new backend. Coverage now certified across app/routes + components + stores + services + packages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jun 30, 2026
…push (Replit parity) Replit-parity SSH/git-in-workspace, all at the same place Replit puts git auth: - Git Settings (⚙) → "SSH keys" section (GitSettingsPanel): generate (ed25519), copy public key, Test access (real `git ls-remote` in the project's isolated workspace pod), delete, and a "bound to origin" badge using the same host-match rule as push/pull key selection. Reuses the Terminal→SSH key store (generate-keypair / delete-ssh / git-ssh intents) so one key works in both places — no new backend. Mobile terminal SSH UI untouched. - Remote bar shows an "SSH" badge when the origin speaks SSH; Pull tooltip notes it clones into an empty tree (verified: fresh-tree SSH pull materializes the repo). - Shared client URL helpers in app/components/git/git-ssh-url.ts (unit-tested). Hardening from adversarial review of the shipped SSH push path: - #1: stop swallowing `git fetch` stderr; distinguish a missing remote branch (first push → create) from an auth/network failure on an existing branch (ls-remote guard → abort with a clear message) instead of orphaning history. - #5: when the pod tree has no .gitignore, run `git add -A` under a transient core.excludesFile so node_modules/.env/build output are never swept into the push — without writing a .gitignore the user didn't author. Verified in real against an OpenSSH origin: push committed only app.txt (not the present node_modules/ or .env); pull/clone into an empty tree materialized the repo. 24 domain unit tests + 86 git-suite tests green; scoped typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jul 5, 2026
…ew is live (#1) Live investigation of the reported session (ws-628cf) proved the BACKEND is healthy: the pod is 1/1 Running, workspace-manager emitted workspace.running, and API requests return 200 — the "Remote runtime request failed: 500" + stuck "PENDING workspace" was a TRANSIENT cold-start state (the pod became ready after the initial provisioning request timed out / returned a transient 500). 500 is already in the retry TRANSIENT_STATUSES, so the request recovers — but the client kept the stale workspaceError, so Problems kept showing "Error runtime … 500" and the status stayed Error/PENDING even though the app was already serving. Fix (client): when any forwarded port is actually serving (ready / has a URL), clear the stale workspaceError (self-corrects — the store re-sets it if the runtime errors again), and treat a live port as "Running" in the status label so it no longer sticks at Error/PENDING. Pairs with the #3 chip fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jul 9, 2026
…+reseed+reinstall (#1) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jul 16, 2026
…y Profiles) · Gallery requalifiée · statut CALCULÉ (audit v4 P0-#1/#2) P0-#1 collecteur — 3 familles (docs/launch-channel/product-route). Les product-routes (gallery/community/pricing) sont RENDUES via navigateur headless UA-réaliste (un fetch brut = coquille ou blocage Cloudflare). Run réel : le collecteur RETROUVE « Community Profiles » (manifest.watchHits.community, sha256 e9b562a2…). manifest schemaVersion 2 avec familles/watchTerms/watchHits. P0-#2 Gallery — requalifiée sur MESURES rendues+hashées, pas déductions : RPL-17 VERIFIED (auteurs, Views 20,650, Used 79 times, ~22 catégories, 82 Results, détail /gallery/work/…, « Submit your App » → Typeform EXTERNE = intake humaine curée PAS self-service), RPL-18 CONFIRMED (Trust & Safety), RPL-19 VERIFIED (Community Profiles). Sources SRC-GALLERY-RENDERED / SRC-GALLERY-DETAIL / SRC-COMMUNITY-RENDERED (URL+hash+archive rendue). Conséquence produit : DEC-GALLERY-NO-SELF-PUBLISH (« Publish to Gallery » self-service = DÉPASSEMENT, pas parité — décision E-CODE avec coût modération ou ne pas faire). Statut CALCULÉ, jamais écrit à la main — generate-approval-status.mjs génère APPROVAL_STATUS.json depuis les registres ; le validateur ré-exécute et ÉCHOUE sur dérive. Nouveaux registres P0_REGISTRY (4 P0 PROVEN — pas CLOSED, pas de reviewer humain), DECISION_REGISTRY (3), UNKNOWN_REGISTRY (4) — owner/priority/ nextAction/targetDate/expiration + schémas JSON. CI ajoute : refs croisées surfaces↔e2e sans orphelin, freshness SLA 30j, aucune surface DONE sans evidenceId, aucun P0 CLOSED sans commit+reviewer+preuve. 3 tests négatifs prouvent que ces contrôles REFUSENT (dérive, CLOSED sans reviewer, id orphelin). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jul 16, 2026
…ython/go on PATH) Agent 83459ff deployed (workspace-agent:sha-83459fff87, helm rev 859). Python workspace reprovisioned with new agent + /nix (zone-a). With the exact PATH sanitizedChildEnv appends, bare uv/python3/go resolve: uv 0.11.21, Python 3.12.13. HONEST: toolchain-on-PATH is automatic (#2 solved, uv=nix-built); the nix MOUNT is still manual-allowlist-gated (#1 auto-mount remaining, default-for-all gated on multi-zone per OPERATIONS_DR SPOF-1). Helm pending-upgrade incident (my orphaned rev 857) resolved via rollback 856 -> re-upgrade 859; prod stayed 200. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jul 23, 2026
… fail-closed + E2E negatives 1. k8s barrier fails on ANY delete failure: freezeWorkspace attempts every revoke but THROWS if any rejected (never claims the barrier / marks the row stopped while a write path may be live). Negative: manager.spec reserve #1. 2. Real GCS backend required: eraseSubjectStorage refuses (unverified, never deletes) when buckets exist but no active backend — a NoopObjectStorage can never certify absence. Negatives: module spec + REAL GCS E2E (inert backend refused, bucket survived). 3. Block ALL object-storage writes during purge: the barrier marks projects purge-frozen; upload-url/ensure-bucket/move return 403 OBJECT_STORAGE_PURGE_ FROZEN, so nothing is recreated after the zero-check. 4. Inventory by REAL authorization: workspaces for EVERY project in ANY org the subject is a member of (shared orgs too, without a ProjectCollaborator row), plus explicit collaborations. 5. Only an authenticated NotFound = absence: pvcExists no longer swallows k8s.get errors — the client returns undefined only on a real NotFound and re-throws network/RBAC errors, so a read error fails closed. Negatives: manager.spec reserve #5 + kind E2E (surviving PVC reported present). 6. E2E negatives: real GCS + kind E2Es each carry a negative; the #1/#5 error negatives are proven deterministically in manager.spec (a k8s error cannot be reliably injected through kubectl/kind). api + workspace-manager typecheck clean; 34 api purge tests + 48 manager tests green; both real E2Es pass with negatives (artifacts hashed in docs/deploy-evidence/2026-07-23-physical-purge-e2e/). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jul 31, 2026
…fact brut) Refus expert v4 (« une commande prête à jouer n'est pas une preuve exécutée ») levé par EXÉCUTION réelle en prod le 2026-07-23, sur code intégré (merge #45 = 6d57a40, api 6d57a40) : - POST /projects/cmrma9wof/nix-lock → 201, lock pinné gen-2 (storePath+sha256 du catalogue signé) - Publish #1 (gen-2 ACTIVE) → READY, URL 200, metadata storeGeneration=gen-2 - helm --set-file nixGenerations=<gen-2 REVOKED> (rev 897) + rollout api - Publish #2 (lock gen-2 révoquée) → FAILED, refus TYPÉ ECODE_LOCK_GENERATION_REVOKED (« ...is REVOKED... — refusing to use it »), URL → 410 SERVER_DEPLOY_NOT_LIVE (aucun repli vers l'active) - Restauration gen-2 ACTIVE (rev 898), VÉRIFIÉE (revokedAt absent), Publish #4 → READY/200 (restauration comportementale confirmée) ; session QA supprimée Les 4 réserves de l'expert désormais levées (pin obligatoire + persisté release/rollback + validation exhaustive catalogue + négatif live EXÉCUTÉ). Artefacts bruts horodatés + hashes : docs/deploy-evidence/2026-07-23-ctr-runtime-nix-v4/. Contrat v5, PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jul 31, 2026
Answers the four reserves that refused PR #47, with REAL before/after E2E proof on actual GCS + Kubernetes (not the memory adapters that were rejected). 1. Write barrier (#1): eraseSubjectStorage now calls WriteBarrierPort.freeze BEFORE any delete; a freeze failure aborts the erasure (nothing deleted, not verified) so nothing is recreated between erase/verify and the tombstone. Prod path: workspace-manager POST /workspaces/:id/freeze (revoke agent token + stop pod). 2. Real disappearance (#2): verification re-checks the LIVE backend — GCS list, and a new GET /workspaces/:id/pvc-exists that does a real — never the workspace row's DELETED status (a partial k8s delete can leave a PVC). 3. By data subject (#3): the inventory is per-subject — the subject's sole-org buckets AND their per-user workspace in EVERY project they touched (sole-org + collaborator, via ProjectCollaborator), not just one main workspaceId. 4. Real proof (#4): two replayable, hashed E2Es under WIF-proof guardrails (dedicated test resources, no persistent keys, ~$0, full teardown): - GCS: GcsObjectStorage → eraseSubjectStorage against a throwaway bucket in the test project ecode-proof-b906ss; 3 objects → bucket+objects gone. - Kubernetes: a real Bound PVC on a throwaway local kind creates and manages local Kubernetes clusters using Docker container 'nodes' Usage: kind [command] Available Commands: build Build one of [node-image] completion Output shell completion code for the specified shell (bash, zsh or fish) create Creates one of [cluster] delete Deletes one of [cluster] export Exports one of [kubeconfig, logs] get Gets one of [clusters, nodes, kubeconfig] help Help about any command load Loads images into nodes version Prints the kind CLI version Flags: -h, --help help for kind -q, --quiet silence all stderr output -v, --verbosity int32 info log verbosity, higher value produces more output --version version for kind Use "kind [command] --help" for more information about a command. cluster → deleted → verified gone via live (kind, not GKE, so $0 — no cost sign-off needed per the guardrail). Artifacts + SHA256 in docs/deploy-evidence/2026-07-23-physical-purge-e2e/. Removes the old in-memory proof the expert rejected. Module + route + real-Postgres suites green (33 tests). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Jul 31, 2026
… fail-closed + E2E negatives 1. k8s barrier fails on ANY delete failure: freezeWorkspace attempts every revoke but THROWS if any rejected (never claims the barrier / marks the row stopped while a write path may be live). Negative: manager.spec reserve #1. 2. Real GCS backend required: eraseSubjectStorage refuses (unverified, never deletes) when buckets exist but no active backend — a NoopObjectStorage can never certify absence. Negatives: module spec + REAL GCS E2E (inert backend refused, bucket survived). 3. Block ALL object-storage writes during purge: the barrier marks projects purge-frozen; upload-url/ensure-bucket/move return 403 OBJECT_STORAGE_PURGE_ FROZEN, so nothing is recreated after the zero-check. 4. Inventory by REAL authorization: workspaces for EVERY project in ANY org the subject is a member of (shared orgs too, without a ProjectCollaborator row), plus explicit collaborations. 5. Only an authenticated NotFound = absence: pvcExists no longer swallows k8s.get errors — the client returns undefined only on a real NotFound and re-throws network/RBAC errors, so a read error fails closed. Negatives: manager.spec reserve #5 + kind E2E (surviving PVC reported present). 6. E2E negatives: real GCS + kind E2Es each carry a negative; the #1/#5 error negatives are proven deterministically in manager.spec (a k8s error cannot be reliably injected through kubectl/kind). api + workspace-manager typecheck clean; 34 api purge tests + 48 manager tests green; both real E2Es pass with negatives (artifacts hashed in docs/deploy-evidence/2026-07-23-physical-purge-e2e/). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 3, 2026
Answers the four reserves that refused PR #47, with REAL before/after E2E proof on actual GCS + Kubernetes (not the memory adapters that were rejected). 1. Write barrier (#1): eraseSubjectStorage now calls WriteBarrierPort.freeze BEFORE any delete; a freeze failure aborts the erasure (nothing deleted, not verified) so nothing is recreated between erase/verify and the tombstone. Prod path: workspace-manager POST /workspaces/:id/freeze (revoke agent token + stop pod). 2. Real disappearance (#2): verification re-checks the LIVE backend — GCS list, and a new GET /workspaces/:id/pvc-exists that does a real — never the workspace row's DELETED status (a partial k8s delete can leave a PVC). 3. By data subject (#3): the inventory is per-subject — the subject's sole-org buckets AND their per-user workspace in EVERY project they touched (sole-org + collaborator, via ProjectCollaborator), not just one main workspaceId. 4. Real proof (#4): two replayable, hashed E2Es under WIF-proof guardrails (dedicated test resources, no persistent keys, ~$0, full teardown): - GCS: GcsObjectStorage → eraseSubjectStorage against a throwaway bucket in the test project ecode-proof-b906ss; 3 objects → bucket+objects gone. - Kubernetes: a real Bound PVC on a throwaway local kind creates and manages local Kubernetes clusters using Docker container 'nodes' Usage: kind [command] Available Commands: build Build one of [node-image] completion Output shell completion code for the specified shell (bash, zsh or fish) create Creates one of [cluster] delete Deletes one of [cluster] export Exports one of [kubeconfig, logs] get Gets one of [clusters, nodes, kubeconfig] help Help about any command load Loads images into nodes version Prints the kind CLI version Flags: -h, --help help for kind -q, --quiet silence all stderr output -v, --verbosity int32 info log verbosity, higher value produces more output --version version for kind Use "kind [command] --help" for more information about a command. cluster → deleted → verified gone via live (kind, not GKE, so $0 — no cost sign-off needed per the guardrail). Artifacts + SHA256 in docs/deploy-evidence/2026-07-23-physical-purge-e2e/. Removes the old in-memory proof the expert rejected. Module + route + real-Postgres suites green (33 tests). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 3, 2026
… fail-closed + E2E negatives 1. k8s barrier fails on ANY delete failure: freezeWorkspace attempts every revoke but THROWS if any rejected (never claims the barrier / marks the row stopped while a write path may be live). Negative: manager.spec reserve #1. 2. Real GCS backend required: eraseSubjectStorage refuses (unverified, never deletes) when buckets exist but no active backend — a NoopObjectStorage can never certify absence. Negatives: module spec + REAL GCS E2E (inert backend refused, bucket survived). 3. Block ALL object-storage writes during purge: the barrier marks projects purge-frozen; upload-url/ensure-bucket/move return 403 OBJECT_STORAGE_PURGE_ FROZEN, so nothing is recreated after the zero-check. 4. Inventory by REAL authorization: workspaces for EVERY project in ANY org the subject is a member of (shared orgs too, without a ProjectCollaborator row), plus explicit collaborations. 5. Only an authenticated NotFound = absence: pvcExists no longer swallows k8s.get errors — the client returns undefined only on a real NotFound and re-throws network/RBAC errors, so a read error fails closed. Negatives: manager.spec reserve #5 + kind E2E (surviving PVC reported present). 6. E2E negatives: real GCS + kind E2Es each carry a negative; the #1/#5 error negatives are proven deterministically in manager.spec (a k8s error cannot be reliably injected through kubectl/kind). api + workspace-manager typecheck clean; 34 api purge tests + 48 manager tests green; both real E2Es pass with negatives (artifacts hashed in docs/deploy-evidence/2026-07-23-physical-purge-e2e/). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 3, 2026
…tate (#1+#2) Root cause of the flaky "prompt → app in Preview" loop: the runtime reseed (importing persisted project files into the pod) is CLIENT-driven only (ProjectWorkspaceProvider). A pod reached OUTSIDE a live IDE session is never seeded — the remote adapter re-provisions a GC'd/reaped pod on the first agent 502 to serve a background terminal/file/preview op, and the new pod comes up with an EMPTY /workspace. The op then runs against nothing: `npm run dev` → ENOENT package.json, `sh: vite: not found`, blank Preview. Files "saved by the agent" are not the files that run. Reconcile it server-side at the source: whenever POST /workspaces (start) or /restart brings a pod to RUNNING, list its root via the agent and, if it is empty of project files, seed it from the authoritative persisted ide-state (same source /export/zip derives via ensureProjectStorageFromIdeState). - Content-driven, not flag-driven: fires for cold start, re-provision-after-GC, and a wiped PVC alike; no-ops on a warm pod that already carries its files (the common reattach path — one cheap /files/tree call). - Best-effort: an unreachable agent or a mid-seed write blip never fails the start; the client reseed and the next runtime op still reconcile. - Emits workspace_runtime_reseed_total so the fix is observable in prod. Extracted the pure emptiness decision into runtime-reseed.ts with 7 unit tests (empty tree, dotfile-only, mixed, malformed/non-array responses). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 3, 2026
+#2) Live proof surfaced a sharper failure than an empty pod: after generation the runtime carried src/, index.html and the configs but NOT package.json, while the persisted ide-state had all 7 files — so `npm run dev` → ENOENT package.json, `sh: vite: not found`, blank Preview. The first cut only reseeded a fully-EMPTY pod, so it skipped this warm-but-diverged pod as "already populated". Broaden the reconciliation from "seed if empty" to "ADD every persisted file absent from the runtime tree": - flattenRuntimeTreeFilePaths walks the agent /files/tree recursively into a set of file paths; runtimeFilesMissingFromPersisted diffs persisted → runtime. - Purely ADDITIVE: an existing runtime file (possibly a newer edit) is never overwritten and a runtime-only file is left untouched — a stale persisted snapshot can only restore a missing file, never clobber live state. - Still fires on start/restart→RUNNING, still best-effort, still no-ops on a fully-synced warm pod (one /files/tree call, zero writes). Metric renamed reason=restored-missing-from-persisted. runtime-reseed.spec.ts rewritten: 9 tests incl. the exact package.json-only divergence, empty pod, fully-synced no-op, additive/no-overwrite, path normalization. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 3, 2026
…#2) Live root-cause of blocker #1 (`sh: vite: not found` while npm install reports success): it is a CONTENT divergence, not a missing file. After generation the runtime's /workspace/package.json was a 200-byte stub — name + scripts only, with NO dependencies/devDependencies — while the persisted ide-state carried the full manifest (react, vite, @vitejs/plugin-react, …). `npm install` therefore saw zero deps ("up to date, audited 1 package"), created no node_modules, and vite was never present → `npm run dev` → sh: vite: not found → blank Preview. The additive-only reconcile skipped it because the file EXISTS. Broaden the reconcile to bring the runtime back in line with persisted: WRITE every persisted file that is either absent OR present with different bytes. - persistedFileContentMatches byte-compares persisted vs the agent /files/read reply, normalising utf8/base64 encodings — so a matching file is skipped (warm no-op) and only genuine divergence is rewritten. - Scope stays exactly the persisted (editor-owned) file set: runtime-only files (build output, npm lockfile, node_modules) are never touched. - Metric reason=reconciled-from-persisted with {missing, diverged} counts. runtime-reseed.spec.ts: 13 tests incl. the exact stripped-package.json vs full persisted divergence, cross-encoding compare, binary diff, warm no-op. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 4, 2026
Answers the four reserves that refused PR #47, with REAL before/after E2E proof on actual GCS + Kubernetes (not the memory adapters that were rejected). 1. Write barrier (#1): eraseSubjectStorage now calls WriteBarrierPort.freeze BEFORE any delete; a freeze failure aborts the erasure (nothing deleted, not verified) so nothing is recreated between erase/verify and the tombstone. Prod path: workspace-manager POST /workspaces/:id/freeze (revoke agent token + stop pod). 2. Real disappearance (#2): verification re-checks the LIVE backend — GCS list, and a new GET /workspaces/:id/pvc-exists that does a real — never the workspace row's DELETED status (a partial k8s delete can leave a PVC). 3. By data subject (#3): the inventory is per-subject — the subject's sole-org buckets AND their per-user workspace in EVERY project they touched (sole-org + collaborator, via ProjectCollaborator), not just one main workspaceId. 4. Real proof (#4): two replayable, hashed E2Es under WIF-proof guardrails (dedicated test resources, no persistent keys, ~$0, full teardown): - GCS: GcsObjectStorage → eraseSubjectStorage against a throwaway bucket in the test project ecode-proof-b906ss; 3 objects → bucket+objects gone. - Kubernetes: a real Bound PVC on a throwaway local kind creates and manages local Kubernetes clusters using Docker container 'nodes' Usage: kind [command] Available Commands: build Build one of [node-image] completion Output shell completion code for the specified shell (bash, zsh or fish) create Creates one of [cluster] delete Deletes one of [cluster] export Exports one of [kubeconfig, logs] get Gets one of [clusters, nodes, kubeconfig] help Help about any command load Loads images into nodes version Prints the kind CLI version Flags: -h, --help help for kind -q, --quiet silence all stderr output -v, --verbosity int32 info log verbosity, higher value produces more output --version version for kind Use "kind [command] --help" for more information about a command. cluster → deleted → verified gone via live (kind, not GKE, so $0 — no cost sign-off needed per the guardrail). Artifacts + SHA256 in docs/deploy-evidence/2026-07-23-physical-purge-e2e/. Removes the old in-memory proof the expert rejected. Module + route + real-Postgres suites green (33 tests). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 4, 2026
… fail-closed + E2E negatives 1. k8s barrier fails on ANY delete failure: freezeWorkspace attempts every revoke but THROWS if any rejected (never claims the barrier / marks the row stopped while a write path may be live). Negative: manager.spec reserve #1. 2. Real GCS backend required: eraseSubjectStorage refuses (unverified, never deletes) when buckets exist but no active backend — a NoopObjectStorage can never certify absence. Negatives: module spec + REAL GCS E2E (inert backend refused, bucket survived). 3. Block ALL object-storage writes during purge: the barrier marks projects purge-frozen; upload-url/ensure-bucket/move return 403 OBJECT_STORAGE_PURGE_ FROZEN, so nothing is recreated after the zero-check. 4. Inventory by REAL authorization: workspaces for EVERY project in ANY org the subject is a member of (shared orgs too, without a ProjectCollaborator row), plus explicit collaborations. 5. Only an authenticated NotFound = absence: pvcExists no longer swallows k8s.get errors — the client returns undefined only on a real NotFound and re-throws network/RBAC errors, so a read error fails closed. Negatives: manager.spec reserve #5 + kind E2E (surviving PVC reported present). 6. E2E negatives: real GCS + kind E2Es each carry a negative; the #1/#5 error negatives are proven deterministically in manager.spec (a k8s error cannot be reliably injected through kubectl/kind). api + workspace-manager typecheck clean; 34 api purge tests + 48 manager tests green; both real E2Es pass with negatives (artifacts hashed in docs/deploy-evidence/2026-07-23-physical-purge-e2e/). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 4, 2026
Answers the four reserves that refused PR #47, with REAL before/after E2E proof on actual GCS + Kubernetes (not the memory adapters that were rejected). 1. Write barrier (#1): eraseSubjectStorage now calls WriteBarrierPort.freeze BEFORE any delete; a freeze failure aborts the erasure (nothing deleted, not verified) so nothing is recreated between erase/verify and the tombstone. Prod path: workspace-manager POST /workspaces/:id/freeze (revoke agent token + stop pod). 2. Real disappearance (#2): verification re-checks the LIVE backend — GCS list, and a new GET /workspaces/:id/pvc-exists that does a real — never the workspace row's DELETED status (a partial k8s delete can leave a PVC). 3. By data subject (#3): the inventory is per-subject — the subject's sole-org buckets AND their per-user workspace in EVERY project they touched (sole-org + collaborator, via ProjectCollaborator), not just one main workspaceId. 4. Real proof (#4): two replayable, hashed E2Es under WIF-proof guardrails (dedicated test resources, no persistent keys, ~$0, full teardown): - GCS: GcsObjectStorage → eraseSubjectStorage against a throwaway bucket in the test project ecode-proof-b906ss; 3 objects → bucket+objects gone. - Kubernetes: a real Bound PVC on a throwaway local kind creates and manages local Kubernetes clusters using Docker container 'nodes' Usage: kind [command] Available Commands: build Build one of [node-image] completion Output shell completion code for the specified shell (bash, zsh or fish) create Creates one of [cluster] delete Deletes one of [cluster] export Exports one of [kubeconfig, logs] get Gets one of [clusters, nodes, kubeconfig] help Help about any command load Loads images into nodes version Prints the kind CLI version Flags: -h, --help help for kind -q, --quiet silence all stderr output -v, --verbosity int32 info log verbosity, higher value produces more output --version version for kind Use "kind [command] --help" for more information about a command. cluster → deleted → verified gone via live (kind, not GKE, so $0 — no cost sign-off needed per the guardrail). Artifacts + SHA256 in docs/deploy-evidence/2026-07-23-physical-purge-e2e/. Removes the old in-memory proof the expert rejected. Module + route + real-Postgres suites green (33 tests). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 4, 2026
… fail-closed + E2E negatives 1. k8s barrier fails on ANY delete failure: freezeWorkspace attempts every revoke but THROWS if any rejected (never claims the barrier / marks the row stopped while a write path may be live). Negative: manager.spec reserve #1. 2. Real GCS backend required: eraseSubjectStorage refuses (unverified, never deletes) when buckets exist but no active backend — a NoopObjectStorage can never certify absence. Negatives: module spec + REAL GCS E2E (inert backend refused, bucket survived). 3. Block ALL object-storage writes during purge: the barrier marks projects purge-frozen; upload-url/ensure-bucket/move return 403 OBJECT_STORAGE_PURGE_ FROZEN, so nothing is recreated after the zero-check. 4. Inventory by REAL authorization: workspaces for EVERY project in ANY org the subject is a member of (shared orgs too, without a ProjectCollaborator row), plus explicit collaborations. 5. Only an authenticated NotFound = absence: pvcExists no longer swallows k8s.get errors — the client returns undefined only on a real NotFound and re-throws network/RBAC errors, so a read error fails closed. Negatives: manager.spec reserve #5 + kind E2E (surviving PVC reported present). 6. E2E negatives: real GCS + kind E2Es each carry a negative; the #1/#5 error negatives are proven deterministically in manager.spec (a k8s error cannot be reliably injected through kubectl/kind). api + workspace-manager typecheck clean; 34 api purge tests + 48 manager tests green; both real E2Es pass with negatives (artifacts hashed in docs/deploy-evidence/2026-07-23-physical-purge-e2e/). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 4, 2026
Answers the four reserves that refused PR #47, with REAL before/after E2E proof on actual GCS + Kubernetes (not the memory adapters that were rejected). 1. Write barrier (#1): eraseSubjectStorage now calls WriteBarrierPort.freeze BEFORE any delete; a freeze failure aborts the erasure (nothing deleted, not verified) so nothing is recreated between erase/verify and the tombstone. Prod path: workspace-manager POST /workspaces/:id/freeze (revoke agent token + stop pod). 2. Real disappearance (#2): verification re-checks the LIVE backend — GCS list, and a new GET /workspaces/:id/pvc-exists that does a real — never the workspace row's DELETED status (a partial k8s delete can leave a PVC). 3. By data subject (#3): the inventory is per-subject — the subject's sole-org buckets AND their per-user workspace in EVERY project they touched (sole-org + collaborator, via ProjectCollaborator), not just one main workspaceId. 4. Real proof (#4): two replayable, hashed E2Es under WIF-proof guardrails (dedicated test resources, no persistent keys, ~$0, full teardown): - GCS: GcsObjectStorage → eraseSubjectStorage against a throwaway bucket in the test project ecode-proof-b906ss; 3 objects → bucket+objects gone. - Kubernetes: a real Bound PVC on a throwaway local kind creates and manages local Kubernetes clusters using Docker container 'nodes' Usage: kind [command] Available Commands: build Build one of [node-image] completion Output shell completion code for the specified shell (bash, zsh or fish) create Creates one of [cluster] delete Deletes one of [cluster] export Exports one of [kubeconfig, logs] get Gets one of [clusters, nodes, kubeconfig] help Help about any command load Loads images into nodes version Prints the kind CLI version Flags: -h, --help help for kind -q, --quiet silence all stderr output -v, --verbosity int32 info log verbosity, higher value produces more output --version version for kind Use "kind [command] --help" for more information about a command. cluster → deleted → verified gone via live (kind, not GKE, so $0 — no cost sign-off needed per the guardrail). Artifacts + SHA256 in docs/deploy-evidence/2026-07-23-physical-purge-e2e/. Removes the old in-memory proof the expert rejected. Module + route + real-Postgres suites green (33 tests). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 4, 2026
… fail-closed + E2E negatives 1. k8s barrier fails on ANY delete failure: freezeWorkspace attempts every revoke but THROWS if any rejected (never claims the barrier / marks the row stopped while a write path may be live). Negative: manager.spec reserve #1. 2. Real GCS backend required: eraseSubjectStorage refuses (unverified, never deletes) when buckets exist but no active backend — a NoopObjectStorage can never certify absence. Negatives: module spec + REAL GCS E2E (inert backend refused, bucket survived). 3. Block ALL object-storage writes during purge: the barrier marks projects purge-frozen; upload-url/ensure-bucket/move return 403 OBJECT_STORAGE_PURGE_ FROZEN, so nothing is recreated after the zero-check. 4. Inventory by REAL authorization: workspaces for EVERY project in ANY org the subject is a member of (shared orgs too, without a ProjectCollaborator row), plus explicit collaborations. 5. Only an authenticated NotFound = absence: pvcExists no longer swallows k8s.get errors — the client returns undefined only on a real NotFound and re-throws network/RBAC errors, so a read error fails closed. Negatives: manager.spec reserve #5 + kind E2E (surviving PVC reported present). 6. E2E negatives: real GCS + kind E2Es each carry a negative; the #1/#5 error negatives are proven deterministically in manager.spec (a k8s error cannot be reliably injected through kubectl/kind). api + workspace-manager typecheck clean; 34 api purge tests + 48 manager tests green; both real E2Es pass with negatives (artifacts hashed in docs/deploy-evidence/2026-07-23-physical-purge-e2e/). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 4, 2026
Answers the four reserves that refused PR #47, with REAL before/after E2E proof on actual GCS + Kubernetes (not the memory adapters that were rejected). 1. Write barrier (#1): eraseSubjectStorage now calls WriteBarrierPort.freeze BEFORE any delete; a freeze failure aborts the erasure (nothing deleted, not verified) so nothing is recreated between erase/verify and the tombstone. Prod path: workspace-manager POST /workspaces/:id/freeze (revoke agent token + stop pod). 2. Real disappearance (#2): verification re-checks the LIVE backend — GCS list, and a new GET /workspaces/:id/pvc-exists that does a real — never the workspace row's DELETED status (a partial k8s delete can leave a PVC). 3. By data subject (#3): the inventory is per-subject — the subject's sole-org buckets AND their per-user workspace in EVERY project they touched (sole-org + collaborator, via ProjectCollaborator), not just one main workspaceId. 4. Real proof (#4): two replayable, hashed E2Es under WIF-proof guardrails (dedicated test resources, no persistent keys, ~$0, full teardown): - GCS: GcsObjectStorage → eraseSubjectStorage against a throwaway bucket in the test project ecode-proof-b906ss; 3 objects → bucket+objects gone. - Kubernetes: a real Bound PVC on a throwaway local kind creates and manages local Kubernetes clusters using Docker container 'nodes' Usage: kind [command] Available Commands: build Build one of [node-image] completion Output shell completion code for the specified shell (bash, zsh or fish) create Creates one of [cluster] delete Deletes one of [cluster] export Exports one of [kubeconfig, logs] get Gets one of [clusters, nodes, kubeconfig] help Help about any command load Loads images into nodes version Prints the kind CLI version Flags: -h, --help help for kind -q, --quiet silence all stderr output -v, --verbosity int32 info log verbosity, higher value produces more output --version version for kind Use "kind [command] --help" for more information about a command. cluster → deleted → verified gone via live (kind, not GKE, so $0 — no cost sign-off needed per the guardrail). Artifacts + SHA256 in docs/deploy-evidence/2026-07-23-physical-purge-e2e/. Removes the old in-memory proof the expert rejected. Module + route + real-Postgres suites green (33 tests). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 4, 2026
… fail-closed + E2E negatives 1. k8s barrier fails on ANY delete failure: freezeWorkspace attempts every revoke but THROWS if any rejected (never claims the barrier / marks the row stopped while a write path may be live). Negative: manager.spec reserve #1. 2. Real GCS backend required: eraseSubjectStorage refuses (unverified, never deletes) when buckets exist but no active backend — a NoopObjectStorage can never certify absence. Negatives: module spec + REAL GCS E2E (inert backend refused, bucket survived). 3. Block ALL object-storage writes during purge: the barrier marks projects purge-frozen; upload-url/ensure-bucket/move return 403 OBJECT_STORAGE_PURGE_ FROZEN, so nothing is recreated after the zero-check. 4. Inventory by REAL authorization: workspaces for EVERY project in ANY org the subject is a member of (shared orgs too, without a ProjectCollaborator row), plus explicit collaborations. 5. Only an authenticated NotFound = absence: pvcExists no longer swallows k8s.get errors — the client returns undefined only on a real NotFound and re-throws network/RBAC errors, so a read error fails closed. Negatives: manager.spec reserve #5 + kind E2E (surviving PVC reported present). 6. E2E negatives: real GCS + kind E2Es each carry a negative; the #1/#5 error negatives are proven deterministically in manager.spec (a k8s error cannot be reliably injected through kubectl/kind). api + workspace-manager typecheck clean; 34 api purge tests + 48 manager tests green; both real E2Es pass with negatives (artifacts hashed in docs/deploy-evidence/2026-07-23-physical-purge-e2e/). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 5, 2026
Answers the four reserves that refused PR #47, with REAL before/after E2E proof on actual GCS + Kubernetes (not the memory adapters that were rejected). 1. Write barrier (#1): eraseSubjectStorage now calls WriteBarrierPort.freeze BEFORE any delete; a freeze failure aborts the erasure (nothing deleted, not verified) so nothing is recreated between erase/verify and the tombstone. Prod path: workspace-manager POST /workspaces/:id/freeze (revoke agent token + stop pod). 2. Real disappearance (#2): verification re-checks the LIVE backend — GCS list, and a new GET /workspaces/:id/pvc-exists that does a real — never the workspace row's DELETED status (a partial k8s delete can leave a PVC). 3. By data subject (#3): the inventory is per-subject — the subject's sole-org buckets AND their per-user workspace in EVERY project they touched (sole-org + collaborator, via ProjectCollaborator), not just one main workspaceId. 4. Real proof (#4): two replayable, hashed E2Es under WIF-proof guardrails (dedicated test resources, no persistent keys, ~$0, full teardown): - GCS: GcsObjectStorage → eraseSubjectStorage against a throwaway bucket in the test project ecode-proof-b906ss; 3 objects → bucket+objects gone. - Kubernetes: a real Bound PVC on a throwaway local kind creates and manages local Kubernetes clusters using Docker container 'nodes' Usage: kind [command] Available Commands: build Build one of [node-image] completion Output shell completion code for the specified shell (bash, zsh or fish) create Creates one of [cluster] delete Deletes one of [cluster] export Exports one of [kubeconfig, logs] get Gets one of [clusters, nodes, kubeconfig] help Help about any command load Loads images into nodes version Prints the kind CLI version Flags: -h, --help help for kind -q, --quiet silence all stderr output -v, --verbosity int32 info log verbosity, higher value produces more output --version version for kind Use "kind [command] --help" for more information about a command. cluster → deleted → verified gone via live (kind, not GKE, so $0 — no cost sign-off needed per the guardrail). Artifacts + SHA256 in docs/deploy-evidence/2026-07-23-physical-purge-e2e/. Removes the old in-memory proof the expert rejected. Module + route + real-Postgres suites green (33 tests). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 5, 2026
… fail-closed + E2E negatives 1. k8s barrier fails on ANY delete failure: freezeWorkspace attempts every revoke but THROWS if any rejected (never claims the barrier / marks the row stopped while a write path may be live). Negative: manager.spec reserve #1. 2. Real GCS backend required: eraseSubjectStorage refuses (unverified, never deletes) when buckets exist but no active backend — a NoopObjectStorage can never certify absence. Negatives: module spec + REAL GCS E2E (inert backend refused, bucket survived). 3. Block ALL object-storage writes during purge: the barrier marks projects purge-frozen; upload-url/ensure-bucket/move return 403 OBJECT_STORAGE_PURGE_ FROZEN, so nothing is recreated after the zero-check. 4. Inventory by REAL authorization: workspaces for EVERY project in ANY org the subject is a member of (shared orgs too, without a ProjectCollaborator row), plus explicit collaborations. 5. Only an authenticated NotFound = absence: pvcExists no longer swallows k8s.get errors — the client returns undefined only on a real NotFound and re-throws network/RBAC errors, so a read error fails closed. Negatives: manager.spec reserve #5 + kind E2E (surviving PVC reported present). 6. E2E negatives: real GCS + kind E2Es each carry a negative; the #1/#5 error negatives are proven deterministically in manager.spec (a k8s error cannot be reliably injected through kubectl/kind). api + workspace-manager typecheck clean; 34 api purge tests + 48 manager tests green; both real E2Es pass with negatives (artifacts hashed in docs/deploy-evidence/2026-07-23-physical-purge-e2e/). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 5, 2026
Answers the four reserves that refused PR #47, with REAL before/after E2E proof on actual GCS + Kubernetes (not the memory adapters that were rejected). 1. Write barrier (#1): eraseSubjectStorage now calls WriteBarrierPort.freeze BEFORE any delete; a freeze failure aborts the erasure (nothing deleted, not verified) so nothing is recreated between erase/verify and the tombstone. Prod path: workspace-manager POST /workspaces/:id/freeze (revoke agent token + stop pod). 2. Real disappearance (#2): verification re-checks the LIVE backend — GCS list, and a new GET /workspaces/:id/pvc-exists that does a real — never the workspace row's DELETED status (a partial k8s delete can leave a PVC). 3. By data subject (#3): the inventory is per-subject — the subject's sole-org buckets AND their per-user workspace in EVERY project they touched (sole-org + collaborator, via ProjectCollaborator), not just one main workspaceId. 4. Real proof (#4): two replayable, hashed E2Es under WIF-proof guardrails (dedicated test resources, no persistent keys, ~$0, full teardown): - GCS: GcsObjectStorage → eraseSubjectStorage against a throwaway bucket in the test project ecode-proof-b906ss; 3 objects → bucket+objects gone. - Kubernetes: a real Bound PVC on a throwaway local kind creates and manages local Kubernetes clusters using Docker container 'nodes' Usage: kind [command] Available Commands: build Build one of [node-image] completion Output shell completion code for the specified shell (bash, zsh or fish) create Creates one of [cluster] delete Deletes one of [cluster] export Exports one of [kubeconfig, logs] get Gets one of [clusters, nodes, kubeconfig] help Help about any command load Loads images into nodes version Prints the kind CLI version Flags: -h, --help help for kind -q, --quiet silence all stderr output -v, --verbosity int32 info log verbosity, higher value produces more output --version version for kind Use "kind [command] --help" for more information about a command. cluster → deleted → verified gone via live (kind, not GKE, so $0 — no cost sign-off needed per the guardrail). Artifacts + SHA256 in docs/deploy-evidence/2026-07-23-physical-purge-e2e/. Removes the old in-memory proof the expert rejected. Module + route + real-Postgres suites green (33 tests). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 5, 2026
… fail-closed + E2E negatives 1. k8s barrier fails on ANY delete failure: freezeWorkspace attempts every revoke but THROWS if any rejected (never claims the barrier / marks the row stopped while a write path may be live). Negative: manager.spec reserve #1. 2. Real GCS backend required: eraseSubjectStorage refuses (unverified, never deletes) when buckets exist but no active backend — a NoopObjectStorage can never certify absence. Negatives: module spec + REAL GCS E2E (inert backend refused, bucket survived). 3. Block ALL object-storage writes during purge: the barrier marks projects purge-frozen; upload-url/ensure-bucket/move return 403 OBJECT_STORAGE_PURGE_ FROZEN, so nothing is recreated after the zero-check. 4. Inventory by REAL authorization: workspaces for EVERY project in ANY org the subject is a member of (shared orgs too, without a ProjectCollaborator row), plus explicit collaborations. 5. Only an authenticated NotFound = absence: pvcExists no longer swallows k8s.get errors — the client returns undefined only on a real NotFound and re-throws network/RBAC errors, so a read error fails closed. Negatives: manager.spec reserve #5 + kind E2E (surviving PVC reported present). 6. E2E negatives: real GCS + kind E2Es each carry a negative; the #1/#5 error negatives are proven deterministically in manager.spec (a k8s error cannot be reliably injected through kubectl/kind). api + workspace-manager typecheck clean; 34 api purge tests + 48 manager tests green; both real E2Es pass with negatives (artifacts hashed in docs/deploy-evidence/2026-07-23-physical-purge-e2e/). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 5, 2026
Answers the four reserves that refused PR #47, with REAL before/after E2E proof on actual GCS + Kubernetes (not the memory adapters that were rejected). 1. Write barrier (#1): eraseSubjectStorage now calls WriteBarrierPort.freeze BEFORE any delete; a freeze failure aborts the erasure (nothing deleted, not verified) so nothing is recreated between erase/verify and the tombstone. Prod path: workspace-manager POST /workspaces/:id/freeze (revoke agent token + stop pod). 2. Real disappearance (#2): verification re-checks the LIVE backend — GCS list, and a new GET /workspaces/:id/pvc-exists that does a real — never the workspace row's DELETED status (a partial k8s delete can leave a PVC). 3. By data subject (#3): the inventory is per-subject — the subject's sole-org buckets AND their per-user workspace in EVERY project they touched (sole-org + collaborator, via ProjectCollaborator), not just one main workspaceId. 4. Real proof (#4): two replayable, hashed E2Es under WIF-proof guardrails (dedicated test resources, no persistent keys, ~$0, full teardown): - GCS: GcsObjectStorage → eraseSubjectStorage against a throwaway bucket in the test project ecode-proof-b906ss; 3 objects → bucket+objects gone. - Kubernetes: a real Bound PVC on a throwaway local kind creates and manages local Kubernetes clusters using Docker container 'nodes' Usage: kind [command] Available Commands: build Build one of [node-image] completion Output shell completion code for the specified shell (bash, zsh or fish) create Creates one of [cluster] delete Deletes one of [cluster] export Exports one of [kubeconfig, logs] get Gets one of [clusters, nodes, kubeconfig] help Help about any command load Loads images into nodes version Prints the kind CLI version Flags: -h, --help help for kind -q, --quiet silence all stderr output -v, --verbosity int32 info log verbosity, higher value produces more output --version version for kind Use "kind [command] --help" for more information about a command. cluster → deleted → verified gone via live (kind, not GKE, so $0 — no cost sign-off needed per the guardrail). Artifacts + SHA256 in docs/deploy-evidence/2026-07-23-physical-purge-e2e/. Removes the old in-memory proof the expert rejected. Module + route + real-Postgres suites green (33 tests). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 5, 2026
… fail-closed + E2E negatives 1. k8s barrier fails on ANY delete failure: freezeWorkspace attempts every revoke but THROWS if any rejected (never claims the barrier / marks the row stopped while a write path may be live). Negative: manager.spec reserve #1. 2. Real GCS backend required: eraseSubjectStorage refuses (unverified, never deletes) when buckets exist but no active backend — a NoopObjectStorage can never certify absence. Negatives: module spec + REAL GCS E2E (inert backend refused, bucket survived). 3. Block ALL object-storage writes during purge: the barrier marks projects purge-frozen; upload-url/ensure-bucket/move return 403 OBJECT_STORAGE_PURGE_ FROZEN, so nothing is recreated after the zero-check. 4. Inventory by REAL authorization: workspaces for EVERY project in ANY org the subject is a member of (shared orgs too, without a ProjectCollaborator row), plus explicit collaborations. 5. Only an authenticated NotFound = absence: pvcExists no longer swallows k8s.get errors — the client returns undefined only on a real NotFound and re-throws network/RBAC errors, so a read error fails closed. Negatives: manager.spec reserve #5 + kind E2E (surviving PVC reported present). 6. E2E negatives: real GCS + kind E2Es each carry a negative; the #1/#5 error negatives are proven deterministically in manager.spec (a k8s error cannot be reliably injected through kubectl/kind). api + workspace-manager typecheck clean; 34 api purge tests + 48 manager tests green; both real E2Es pass with negatives (artifacts hashed in docs/deploy-evidence/2026-07-23-physical-purge-e2e/). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 5, 2026
Answers the four reserves that refused PR #47, with REAL before/after E2E proof on actual GCS + Kubernetes (not the memory adapters that were rejected). 1. Write barrier (#1): eraseSubjectStorage now calls WriteBarrierPort.freeze BEFORE any delete; a freeze failure aborts the erasure (nothing deleted, not verified) so nothing is recreated between erase/verify and the tombstone. Prod path: workspace-manager POST /workspaces/:id/freeze (revoke agent token + stop pod). 2. Real disappearance (#2): verification re-checks the LIVE backend — GCS list, and a new GET /workspaces/:id/pvc-exists that does a real — never the workspace row's DELETED status (a partial k8s delete can leave a PVC). 3. By data subject (#3): the inventory is per-subject — the subject's sole-org buckets AND their per-user workspace in EVERY project they touched (sole-org + collaborator, via ProjectCollaborator), not just one main workspaceId. 4. Real proof (#4): two replayable, hashed E2Es under WIF-proof guardrails (dedicated test resources, no persistent keys, ~$0, full teardown): - GCS: GcsObjectStorage → eraseSubjectStorage against a throwaway bucket in the test project ecode-proof-b906ss; 3 objects → bucket+objects gone. - Kubernetes: a real Bound PVC on a throwaway local kind creates and manages local Kubernetes clusters using Docker container 'nodes' Usage: kind [command] Available Commands: build Build one of [node-image] completion Output shell completion code for the specified shell (bash, zsh or fish) create Creates one of [cluster] delete Deletes one of [cluster] export Exports one of [kubeconfig, logs] get Gets one of [clusters, nodes, kubeconfig] help Help about any command load Loads images into nodes version Prints the kind CLI version Flags: -h, --help help for kind -q, --quiet silence all stderr output -v, --verbosity int32 info log verbosity, higher value produces more output --version version for kind Use "kind [command] --help" for more information about a command. cluster → deleted → verified gone via live (kind, not GKE, so $0 — no cost sign-off needed per the guardrail). Artifacts + SHA256 in docs/deploy-evidence/2026-07-23-physical-purge-e2e/. Removes the old in-memory proof the expert rejected. Module + route + real-Postgres suites green (33 tests). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
openaxcloud
pushed a commit
that referenced
this pull request
Aug 5, 2026
… fail-closed + E2E negatives 1. k8s barrier fails on ANY delete failure: freezeWorkspace attempts every revoke but THROWS if any rejected (never claims the barrier / marks the row stopped while a write path may be live). Negative: manager.spec reserve #1. 2. Real GCS backend required: eraseSubjectStorage refuses (unverified, never deletes) when buckets exist but no active backend — a NoopObjectStorage can never certify absence. Negatives: module spec + REAL GCS E2E (inert backend refused, bucket survived). 3. Block ALL object-storage writes during purge: the barrier marks projects purge-frozen; upload-url/ensure-bucket/move return 403 OBJECT_STORAGE_PURGE_ FROZEN, so nothing is recreated after the zero-check. 4. Inventory by REAL authorization: workspaces for EVERY project in ANY org the subject is a member of (shared orgs too, without a ProjectCollaborator row), plus explicit collaborations. 5. Only an authenticated NotFound = absence: pvcExists no longer swallows k8s.get errors — the client returns undefined only on a real NotFound and re-throws network/RBAC errors, so a read error fails closed. Negatives: manager.spec reserve #5 + kind E2E (surviving PVC reported present). 6. E2E negatives: real GCS + kind E2Es each carry a negative; the #1/#5 error negatives are proven deterministically in manager.spec (a k8s error cannot be reliably injected through kubectl/kind). api + workspace-manager typecheck clean; 34 api purge tests + 48 manager tests green; both real E2Es pass with negatives (artifacts hashed in docs/deploy-evidence/2026-07-23-physical-purge-e2e/). PROVEN_REVIEW_PENDING. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fallback to /user/emails API when GitHub returns null email