Audit remediation: remove ~53k lines of dead code, fix legal/tier accuracy, close enforcement gaps - #421
Merged
Merged
Conversation
Curated feed registry (125 feeds, 124 verified serving live items): - 19 feeds were returning 404/HTML and silently contributing nothing on every fetch cycle. Recovered the correct URLs via RSS autodiscovery rather than dropping the sources (tokio, react, vite, supabase, stripe, datadog, cockroach, turso, shadcn, remix, oxide, socket.dev, pydantic, discord, railway, cursor, w3c, python-insider, burntsushi, linear...). - SQLite moved to its GitHub releases atom feed, which actually matches the entry's release_notes content_type (changes.rss is gone). - cursor-blog -> cursor-changelog: no blog feed exists; entry retyped honestly rather than pointing a "blog" entry at a changelog. - Dropped only where no feed exists anywhere: Anthropic, Uber Engineering, LangChain. rachelbythebay KEPT (unreachable from this network, but not proven dead). i18n: removed 9,490 dead keys + 558 orphans across 13 locales (-10,199 lines). Dead set built by full-corpus scan; the 643 ambiguous keys (dynamic prefixes / computed access) were deliberately left in place. validate-translations: 0 errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
…ware
The first purge treated i18next plural storage forms (`key_one` /
`key_other`) as independent keys. They are referenced by their BASE key —
`t('key', { count })` — so they looked unreferenced and 24 live strings
were removed (blindspots.signal.count, calibrationView.sprint.*,
preemption.tier.*, trialExpiry.title, ...).
Re-ran against the pre-purge baseline with suffix-aware resolution: a
stored key counts as used if the corpus references it OR any of its
plural/context/ordinal ancestors. Net 9,230 dead + 562 orphan keys
removed instead of 9,490 — the difference is exactly the plural sets.
Verified: of 1,800 literal t() call sites, 0 are broken by this purge.
(185 were already unresolvable before any of this work — a pre-existing
defect worth its own fix.) validate-translations: 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
…king internals The site moved to Cloudflare Pages on 2026-07-21 but kept shipping its Vercel past: - rotate-key.sh rotated the LIVE Stripe key via `vercel env` against a DEAD project, so a rotation would silently fail to change the key the live deployment uses. Rewritten for `wrangler pages secret put`. - privacy.njk named Vercel as hosting provider/data processor in six places, including the GDPR international-transfer and security clauses. - Deleted site/api/ (3 superseded handlers; the Cloudflare ports in site/functions/api/ are strictly newer) and vercel.json. Stale build output was reaching production: Eleventy never cleaned _site and cf:deploy uploaded it wholesale, which is why 4da.ai served old server-side source and the internal Stripe E2E harness. build now cleans first and cf:deploy builds before deploying. Legal accuracy: licence validation is cached 90 days (VALIDATION_CACHE_HOURS = 2160), not 24 hours. Subscription-lapse clause corrected — Score Autopsy, signal chains and channels are free-tier and are not lost on lapse. Also fixes a defect found in passing: the robots.txt passthrough pointed at a nonexistent root file and .txt is not an Eleventy template format, so the site emitted NO robots.txt at all. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
…drift mcp-4da-server (the published package): output-manager.ts (280 lines, zero importers), llm.ts (633 lines, imported only by its own test), and the whole scheduler/ subsystem (225 lines, initScheduler never called since creation). Build passes; tests 135 -> 122, exactly the 13 deleted LLM tests. Version drift closed: .claude-plugin/plugin.json (4.6.7) and gemini-extension.json (4.6.4) -> 5.0.0. Deleted relay/.github/workflows/ci.yml — a nested workflow that can never run, since Actions only reads the repo-root .github/workflows. Relay now has openly-zero CI rather than false assurance. Deleted packaging/aur/ (diverged duplicate carrying retired 4da-home-bin branding). Regenerated the five frozen AI-tool configs from the maintained CLAUDE.md — they had contradicted it since February on size limits, design tokens, principles and architecture paths. AGENTS.md: dead .Codex/ paths corrected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
Legal (material): PRIVACY-POLICY named Vercel as processor in six places three weeks after the Cloudflare migration and claimed a 24-hour licence cache against a 2160-hour constant. TERMS-OF-SERVICE was missing the $299 Lifetime plan, used USD where the operative terms use AUD, and described a Keygen machine fingerprint the code does not send. LICENSE-ACTIVATION documented a device-limit and deactivation flow that does not exist anywhere in the licence path, plus a key format matching neither real format. Tier truth: Developer DNA, natural language search and AI briefings are free per AD-025/AD-026 (the gate list carries those ADRs as comments), and Score Autopsy, signal chains and channels have no gate at all — four docs sold them as Signal. Corrected across README, SETUP-GUIDE, LICENSE-ACTIVATION and TERMS. README also gained the missing Lifetime plan. SETUP-GUIDE documented a nine-tab app that has four. README referenced a removed "Evidence tab". Auditor-facing guides pointed at eight renamed source files — the entire purpose of those docs is telling an auditor which file to open. ARCHITECTURE docs claimed "82+ Tauri commands" against 404. Deleted docs/latest.json (placeholder-signed updater relic; the real feed is the GitHub release asset). settings.example.json: dropped the 7 config blocks whose Rust fields no longer exist. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
…aths The codebase carried its own cleanup contract — "REMOVE BY <date>" markers — that nothing ever enforced, so ~40 had silently expired. Added scripts/check-remove-by.cjs (allowlist seeded empty); it now reports 0 expired across 922 files. Deleted 23 orphan scripts with no caller in package.json, CI, hooks or docs: the superseded i18n one-shot family (~290KB, replaced by i18n-sync), GAME-era shader/geometry tools for a dropped project, and CADE-era reporting whose runner was never wired. Verified no dangling reference to any. Deleted sync-ai-rules.sh: it generated the tool configs FROM .ai/RULES.md and overwrote CLAUDE.md, so running it would have clobbered the maintained source of truth with stale content. De-duplicated PII hashes into a shared module (the rule itself instructed updating two copies). Dead CI paths removed: validate.yml and hermetic.yml triggered on a `develop` branch that does not exist, and both validate.yml and pre-push filtered a root locales/ directory that does not exist. .nvmrc said 20 while every workflow pins 22. knip: tailwindcss ignored (it is imported by App.css via @tailwindcss/vite — a false positive). Stale size-gate allowlist entries pointing at files that are now directories removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
The V1 scoring pipeline has been unreachable since 2026-03-03 — scoring/mod.rs pinned `const USE_V2: bool = true` with no cfg/env override — yet it was still being co-maintained (pipeline.rs was edited in the AD-029 PR on 2026-08-11 to keep "both pipelines" aligned). Removed ~3,100 lines: pipeline.rs (663-line score_item), its 1,446-line test file, semantic/taste.rs, the V1 halves of pipeline_signals/explanation/gate/affinity/calibration, and the simulation version-registry that existed only to dispatch V1-vs-V2. ScoringInput/ScoringOptions moved to scoring/types.rs and re-exported at the same path, so no out-of-module importer changed. Dead modules deleted: job_queue.rs (564 lines, zero refs) and reachability.rs (195 lines, "REMOVE BY 2026-07-31" never wired). 40 expired removal markers across 21 files resolved; check-remove-by now reports 0 expired. decisions.rs check_alignment was justified as "MCP API surface", but the MCP server implements that tool entirely in TypeScript against SQLite — it was a second, drift-prone implementation. Duplication: hoisted the verbatim-duplicated platform-column filter into platform_filter.rs (security-relevant filtering maintained twice); added sources::classify_http_status applied at 24 sites across 19 adapters; extracted one tweet_to_item and gave twitter.rs real SourceError variants instead of string-matching its own error text. Removed pragma probes guarding columns that migrate() guarantees exist. bluesky's 7 configured queries all fetched the same hardcoded URL; the GitHub fallback parsed an HTML page as RSS and could never yield items; the audio extractor advertised 6 formats it cannot transcribe; dependency-snapshot expiry existed but was never called, so the table grew forever — now wired into run_maintenance. 7 Settings fields + 6 config structs for features that do not exist were serialized into every user's settings.json; safe to remove because Settings is #[serde(default)] with no deny_unknown_fields. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
`module.unknown`, `module.fileNotFound` and `translation.sourceIsEnglish` lost their last consumers when `get_playbook_content` and `translate_playbook_module` were deleted as ghost commands. Verified orphaned: zero references across src/, src-tauri/src/ and public/. 39 entries removed (3 keys x 13 locales). validate-translations: 0 errors; typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
After the playbook/personalization commands were removed as ghosts, the only survivor of `content_personalization/` was `context::compute_has_llm` plus `prune_personalization_cache` — a janitor command that, with `temporal.rs` gone, swept two tables nothing writes any more. `defer.ts` recorded it costing 576-902ms at every startup for that no-op. Removed on BOTH sides of the IPC boundary in one change (the only safe way to remove a command with live frontend callers): the Rust handler, the CommandMap entry, the App.tsx call site, the defer.ts scheduling note, and the victauri REGISTERED_COMMANDS entry. `compute_has_llm` — the single source of truth for LLM availability, with its own dedicated gate — moved to `llm_gate.rs` rather than being left as the sole occupant of a package whose reason for existing was deleted. Its 7 callers follow the new path; `check-llm-gate-honesty.cjs` has its define-site exclusion repointed (a path follow, not a relaxation) and still reports "all availability checks route through compute_has_llm". Verified: cargo build 0 warnings; cargo test --lib 4,251 passed / 0 failed; typecheck clean; vitest 111 files / 1,249 passed; ghost-commands 0 / IPC health 100%; validate-commands consistent; llm-gate-honesty clean. Note: the now-unused `content_personalization_cache` and `content_read_state` tables are left in place — dropping them needs a schema migration and a live migrate-path check, which belongs in its own change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
.git-msg-streets.txt was swept into the previous commit by a git add -A. Removed, and .git-msg-*.txt ignored so the scratch files this workflow uses cannot be committed again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
The paid-tier page was substantially inaccurate. Of its nine feature cards, only three described something both real AND gated: - FREE, sold as paid: Signal Chains, Developer DNA, Score Autopsy — none of them appear in SIGNAL_FEATURES, and Developer DNA carries an explicit "un-gated (AD-026)" comment in the gate list itself. - DELETED: Attention Report and Project Health. Both commands were removed as ghosts because nothing could invoke them, so they were already undeliverable before that cleanup. - PHANTOM: "Decision Signals" mapped to get_decision_health_report, which is not defined or registered anywhere in the crate. It sat in SIGNAL_FEATURES gating a command that does not exist. Removed from the gate list too — dead config in the revenue path. Rewrote the cards, the pricing list, the FAQ, the three-phase narrative and the page metadata against the authoritative SIGNAL_FEATURES list, so the page now describes only Blind Spots, Knowledge Gaps, Standing Queries, Semantic Shifts, cross-project intelligence and the Precision Ledger. The FAQ now names Developer DNA, signal chains and Score Autopsy as free, which is what the code enforces. Also replaces the SPA catch-all with a real 404. `/* /index.html 200` was carried over from Vercel and made every unknown path a soft-404 — the homepage returned with HTTP 200. That is a duplicate-content problem for search engines, and it meant the deleted /api/*.js handlers answered 200 after their files were removed. Every real route on this site is a real file (Pages resolves clean URLs itself), so the catch-all only ever matched genuine 404s. `_redirects` documented this exact tradeoff and its fix; this applies it. Verified with `wrangler pages dev`: /, /privacy, /terms, /docs/, /streets still 200; /nonexistent-page and /api/streets/activate.js now 404; the live Function at /api/streets/activate still executes (400 on a bodyless GET, as expected). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
These t() calls pass no default, so the raw key was rendering to the user. 14 of them are the entire ScoreAutopsy panel — a free-tier feature — which was showing 'autopsy.title', 'autopsy.components' and so on instead of labels. The other three are aria-labels in settings, i.e. screen readers were announcing raw keys. All 15 pre-date this session's locale purge (verified against the pre-purge baseline), so they are a pre-existing defect, not fallout from it. Measured precisely: of the t() call sites whose key does not resolve, 176 supply a literal fallback string and render correctly; only these 15 unique keys were genuinely broken. My earlier '185 broken keys' figure counted both groups and was wrong. English only — fallbackLng is 'en', so other locales resolve until translated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
sharp's only consumers were the orphan icon scripts deleted earlier in this branch; ws had none at all. The pnpm override pinning sharp >=0.35 stays -- it protects transitive pulls, which is a separate concern from the direct devDependency. knip: ignore site/, a separate workspace with its own package.json. Scanning it from the root manifest reported @noble/ed25519 and stripe as 'unlisted dependencies' on every run when both are correctly declared in site/package.json. Remaining knip output is 41 unused exported types, ~30 of which are the team/enterprise surface being kept deliberately for post-launch. The rest are redundant type re-exports (the underlying types are used); type-only, erased at build, left alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
…ssing assess_blind_spots_with_ai, get_cached_blind_spot_assessment and add_package_watch each call require_signal_feature(), but their names were never added to SIGNAL_FEATURES. Since is_signal_feature_available() is `!SIGNAL_FEATURES.contains(&feature)`, a name absent from the list is AVAILABLE — so all three gates always passed and the features were free in practice. The intent is not ambiguous: the call sites exist, BlindSpotsPaywall.tsx and BlindSpotsView.paywall.test.tsx implement and assert the gated experience, BlindSpotsView carries a `paywalled` state that the auto-assess path respects, and get_blind_spots itself is already gated. Three signals said paid; only the list disagreed. USER-VISIBLE: free-tier users lose AI blind-spot assessment and package watching, and get the paywall that was already built for them. One-line revert if that is not wanted. Also records why ipc_guard's SSRF validator is deliberately unwired rather than leaving it to look like orphaned hardening: its policy blocks ALL private IPs with a hardcoded exemption for Ollama's default port, so wiring it to the fetch path would break self-hosted RSS feeds and non-Ollama local LLM endpoints — both legitimate here. The note explains that a correct version must distinguish user-authored URLs from content-derived ones. cargo test --lib: 4,251 passed, 0 failed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
Removing the two unused devDependencies without regenerating the lockfile broke Hermetic Fresh-Clone: `pnpm install --frozen-lockfile` fails with ERR_PNPM_OUTDATED_LOCKFILE because the lockfile still carried specifiers package.json no longer has. The local pre-commit and pre-push gates do not run a frozen install, which is exactly why this reached CI. The hermetic fresh-clone job is what caught it — working as intended. Lockfile diff is 314 deletions and zero additions: no version churn on any other package. Verified locally with the same command CI runs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
Appended by `vercel link` while disconnecting the retired 4da-home Vercel project. Kept deliberately: site/.gitignore previously only covered .env*.local, so a plain .env or .env.production could have been committed. The .env.local the CLI generated (it carries a VERCEL_OIDC_TOKEN) and the .vercel/ link dir were both deleted immediately; neither was ever tracked. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC
…anup-0812 # Conflicts: # src-tauri/src/community_intelligence.rs # src-tauri/src/content_personalization/template_processor.rs
runyourempire
added a commit
that referenced
this pull request
Aug 14, 2026
…cation storm, lobotomised reranker, lying metrics (#423) Four independent failures found by reading the live app's debug console, then confirmed against the running process, the live SQLite DB, `data/usage.json`, and live HTTP calls to the failing endpoints. Every one of them was **invisible in the logs** — three reported success, one reported the wrong cause. ## What was broken **1. Lobste.rs — 100% dead, zero rows ever ingested.** `submitter_user` changed upstream from `{"username": "..."}` to a bare string `"fzakaria"`. The binding required the object form. `#[serde(default)]` did not help: the field is *present with the wrong type*, and `default` only covers *absent* fields. Because the batch decoded as `Vec<LobstersStory>`, one drifted record zeroed every fetch on both endpoints. The unit test kept passing the whole time — it asserted the stale shape. The DB confirms it: 19 source types present, `lobsters` is not one of them. **2. Stack Overflow — a self-inflicted 12.9h IP ban it could not escape.** Stack Exchange signals throttling as **HTTP 400 with the reason in the body**, not 429. The old code classified on status alone and returned *before reading the body*, so: the ban was logged as "Bad Request"; `quota_remaining` could never be re-read once throttled, so it never backed off, so the next cycle renewed the ban; and the `backoff` field returned on *success* had no binding at all. Reproduced independently outside the app: ``` HTTP 400 {"error_id":502,"error_name":"throttle_violation", "error_message":"too many requests from this IP, more requests available in 46472 seconds"} ``` **3. Chain notifications — 2,880 critical toasts/day.** `maybe_notify_escalating_chains` sat bare in the scheduler loop. Its comment said "hourly"; the loop ticks every **60 seconds**. Every sibling job is interval-gated with a `mark_job_complete`; this one had no gate, no interval constant, no persisted state, and no `gate_policy` check — so it also ignored the battery/power throttle. Observed live at 01:57:29, 01:58:30, 01:59:28, 02:00:28, 02:01:28, 02:02:28, 02:03:29, 02:04:28Z, `notified=2` every time. Critical toasts display for 8s, so one was on screen ~27% of all waking hours. **4. LLM rerank — dead 22.4 of every 24 hours, reported as success.** Five early-return paths all yielded a bare `None`, three with no log line at all; the caller printed `"LLM rerank phase complete" elapsed_ms=0` regardless. Live `data/usage.json` at the time: ``` tokens_today 102677 limit 100000 EXCEEDED cost_today_cents 51 limit 50 EXCEEDED ``` The budget resets 00:00Z and was exhausted by 01:36Z — the analysis loop runs every ~10.5min at ~11.4k tokens/rerank, so ~9 passes burned the day's allowance in 96 minutes. **5. Metrics that lie.** `coverage_pct=9.5 / not_scored=9171` claimed 9,171 items "were never scored". Checked against the live DB: all 10,172 items were scored *and* stamped at the current `PIPELINE_VERSION`. Real coverage was 100%. The line would send a reader hunting a recall crisis that does not exist. ## What changed | Area | Fix | |---|---| | Lobsters | untagged enum accepts both shapes; per-record decode so one bad story costs one story, not the source; records-arrived-but-none-decoded is an error, not a silent empty feed; byte-faithful live-payload fixture pinned | | Stack Overflow | read the body before classifying; parse the deadline; process-global circuit breaker persisted atomically to `data/.stackoverflow_throttle` so all three pipeline drivers share it; clamp 24h, only ever extend; honour `backoff`; abort remaining tags (the ban is per-IP, not per-tag) | | Chain notifications | `CHAIN_NOTIFY_INTERVAL=3600` + persisted `chain_notify` scheduler job + `JobPriority::Normal` gate; per-chain ledger re-arming on genuine phase change or after 24h | | Rerank | `RerankOutcome::{Reranked, Skipped}` with 9 tagged skip reasons carrying real numbers; all 3 call sites log the truth; budget paced proportionally across the UTC day | | Metrics | selection ratio relabelled as per-pass throughput, not coverage; summary logs scored/survivors/removed_by_dedup | Note on the rebase onto #421: that PR centralised status handling into `sources::classify_http_status`. Lobsters adopts it. **Stack Overflow deliberately does not**, and carries an in-line comment saying why — it is the one upstream where the status code does not carry the meaning, and centralising the call there reintroduces this exact bug. ## Deliberately not done **Caching judgments so unchanged items aren't re-judged.** That is the real ~10x token saving, but it rewires the calibration-sample and provenance write path — the same subsystem quarantined three days ago for a poisoned curve (AD-029). It deserves its own change with its own verification, not a rider on this one. ## Verification - `cargo clippy -- -D warnings` clean on **both** CI matrix legs (default and `--features experimental`) - `cargo fmt --check` clean - **full lib suite: 4,290 passed / 0 failed / 10 ignored** (post-rebase) - **23 new tests**, including negative cases: a genuine `bad_parameter` 400 must *not* arm the breaker; a shorter throttle must not shorten an active pause; pacing must have blocked the exact observed burn - CI green: Fresh clone ubuntu + windows, Rust default + experimental, Hermetic Success, Validate Success **Not yet live-verified in-app.** Per `.claude/rules/victauri-verification.md` these changes still need a rebuild + restart and a Victauri pass before anyone calls them confirmed in production. The GUI process exited during this session, so that verification is outstanding. `cargo clippy --all-features` fails on `team_sync_crypto.rs` / `webhooks/mod.rs` — pre-existing, untouched here, owned by the `feature-rot` lane. CI does not lint `--all-features`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 14, 2026
runyourempire
added a commit
that referenced
this pull request
Aug 14, 2026
… email (#427) ## What does this PR do? Closes an unauthenticated licence-key disclosure in the live Signal licence endpoint, without breaking licence recovery for real customers. > **Relationship to #421:** #421 deleted the *stale Vercel duplicate* of this handler (`site/api/streets/activate.js`) as part of its dead-code sweep. It did not touch the live Cloudflare Pages handler, which is where the vulnerability actually lives — `git log` shows `site/functions/api/streets/activate.js` last changed in #357. This PR is rebased on top of #421 and #423 and fixes the live one. ### The vulnerability `site/functions/api/streets/activate.js` serves two GET paths. The `session_id` path is correct: a Stripe checkout session id is high-entropy, unguessable, and re-verified against Stripe, so holding one is proof of purchase. The `email` path was not. It took the address straight off the query string, looked the customer up, and returned that customer's full licence key in the response body — no authentication, no proof the caller owned the address, nothing: ```js // site/functions/api/streets/activate.js:373-406 (before) } else { customerEmail = email; } // caller-supplied, never verified const customers = await stripe.customers.list({ email: customerEmail.toLowerCase(), limit: 1 }); return json({ license_key: license, tier, issued_at, expires_at, status }, 200, headers); ``` Impact, in order of severity: 1. **Anyone who knows or guesses a customer's email gets a working licence.** The keys are Ed25519-signed and verified **offline** against the public key embedded in the desktop app, so a stolen key keeps working indefinitely — there is no revocation channel that could take it back. 2. **Customer-list oracle.** `200` vs `404` answered "is this person a paying 4DA subscriber?" for any address the caller cared to try. 3. **Nothing stood in front of it.** CORS is not an access control — a plain HTTP client sends no `Origin` header at all. There is no `_middleware.js`, no Turnstile, and `site/wrangler.toml` declares zero KV/D1 bindings, so no rate limiter existed or could have existed without new infrastructure. ### The fix The `email` path now **never puts the key on the wire to an unverified caller**. It mails the key to the address on file and returns the same `202` either way. - **`site/functions/api/streets/activate.js:356-470`** — the GET handler splits into two named functions with explicitly different trust properties. `handleSessionLookup` is byte-for-byte the old verified behaviour (untouched on purpose). `handleEmailRecovery` validates the address shape, checks that outbound mail is provisioned, then **responds before doing the Stripe lookup** — the lookup and send are scheduled on `waitUntil`. That makes the response constant in *both* body and latency, so the oracle is closed on every path rather than just the obvious one. - **`site/lib/recovery-email.js`** (new) — Resend delivery via raw `fetch`, the same provider and pattern `paddle-webhook/api/paddle.ts:363` already uses; no new dependency. It only ever mails an address that is **already a Stripe customer holding a licence**, so the endpoint cannot be turned into an open relay against arbitrary third parties. Expired licences get a "renew at 4da.ai/signal" notice instead of a key. It never throws and never returns the key to the caller. - **Honest degradation.** Delivery needs `RESEND_API_KEY` and `RESEND_FROM_EMAIL` in the Cloudflare Pages environment (see operator actions below). If they are unset, the endpoint returns `503` with "contact support@4da.ai from your purchase email" — uniformly, before any Stripe call. It does **not** fall back to returning the key; that fallback is the vulnerability. - **`json()` now sets `Cache-Control: no-store`** so no browser, proxy or CDN retains a key-bearing response from the `session_id` path either. ### Consumers updated to the new contract Legitimate recovery had three consumers and all three were carried across rather than broken: - **`src-tauri/src/settings_commands_license.rs:326-443`** — `recover_license_by_email` now handles `202` (`reason: "emailed"`), `400` and `503`. It no longer auto-activates, because the server no longer hands it a key; the doc comment explains why the 200 arm is now unreachable. - **`src/components/settings/LicenseSection.tsx`**, **`src/components/LicenseRecoveryBanner.tsx`** — `emailed` renders as an informational (gold) state, not a red error. It is the success case. - **`src/locales/*/ui.json`** — 3 new keys across all 13 locales, plus updated recovery copy that says the key is emailed and is never shown in-app. - **`site/src/signal/success.njk`** — the public form's button is now "Email me my key", with a note explaining why, and it handles `202`/`503`. ### Privacy disclosure This change introduces **Resend** as a processor of a customer's email address and licence key, so it is added to the third-party tables in `site/src/privacy.njk` and `docs/legal/PRIVACY-POLICY.md`. #421 had already corrected the Vercel→Cloudflare drift in both, so that part is not re-done here. ## Type of change - [x] Bug fix (security) ## Checklist - [x] `pnpm run lint` — 0 errors - [x] `tsc --noEmit` — clean - [x] `pnpm run test` — 1362 tests / 125 files pass - [x] `cargo fmt --check` and `cargo clippy --lib` — clean - [x] `node scripts/validate-translations.cjs` — 0 errors - [x] `node scripts/check-file-sizes.cjs` — passes (see the note below about #423) - [x] `npx @11ty/eleventy` — site builds - [x] No secrets or API keys committed ## Testing **Live-verified** against `wrangler pages dev` with throwaway credentials (a fake Stripe key is sufficient — the whole point is that the response does not depend on the lookup): | Request | Result | |---|---| | `?email=` a plausible address | `202`, body `{"delivery":"email","message":"If that address has a 4DA licence…"}` | | `?email=` a different address | `202`, **byte-identical body** | | `?email=` malformed | `400`, rejected on shape alone | | no parameters | `400` | | any of the above | `Cache-Control: no-store`, no `license_key` field anywhere | `site/test-e2e-stripe.mjs` previously *asserted the vulnerable behaviour* (`data.license_key === licenseKey`). Those assertions are now regression tests for the fix, covering the two properties that matter: - the email path's response contains **no** licence key and no `license_key` field, in the live, cancelled and expired cases; - a known customer address and a random non-existent address produce a **byte-identical body and identical status**, so there is no customer oracle. That script needs live Stripe test keys, so it is for the operator to run against a preview deployment — it is not wired into CI. ## Operator actions required before this is fully live 1. **Set `RESEND_API_KEY` and `RESEND_FROM_EMAIL`** (e.g. `4DA <licenses@4da.ai>`, on a Resend-verified domain) in Cloudflare Pages → `4da-site` → Settings → Environment variables. Until they are set, email recovery honestly returns "contact support" instead of working. 2. **Deploy.** Cloudflare is direct-upload, not git-auto-deploy — merging does not ship this. It needs `wrangler pages deploy`. 3. **Confirm the dormant Vercel project `4da-home` is not still serving the old copy.** #421 removed `site/api/**` from the repo, but if that project still has a live deployment on a `*.vercel.app` URL with `STRIPE_SECRET_KEY` and `LICENSE_PRIVATE_KEY_HEX` populated, the old vulnerable endpoint is still reachable there regardless of what the repo says. 4. **Consider rotating `LICENSE_PRIVATE_KEY_HEX`** if there is reason to think keys were harvested while the endpoint was open. This invalidates every issued key and forces re-issue, so it is a judgement call, not automatic. ## Note for the #423 author — file-size gate was left red `src-tauri/src/analysis_rerank.rs` went from 739 → **1032 lines** in #423, past the 1000-line hard error in `scripts/check-file-sizes.cjs`, with no exception entry added. That left `main` red on the gate and blocked *every* local commit via the pre-commit hook. I added an explicitly **TEMPORARY** entry so this PR could be committed at all, with a comment naming #423 as owing the split and instructing that the line be deleted afterwards. It is not a blessing to keep growing the file — please split the reranker and remove the entry. ## Residual risk, deliberately not fixed here - **No rate limiting.** The email path is still unauthenticated and unmetered, so it can be driven in a loop to repeatedly mail an existing customer their own key. It cannot mail anyone who is *not* already a customer, which bounds this to inbox nuisance rather than open-relay abuse. A real limiter needs per-IP counters and this Pages project declares no KV or D1 binding to hold them. The zero-code mitigation is a **Cloudflare WAF rate-limiting rule on `/api/streets/activate`**, configurable from the dashboard. This is noted in a comment at the top of the email path. - **The address still travels in a query string**, so it lands in Cloudflare access logs and browser history. Moving recovery to `POST` would fix that but would break already-shipped desktop builds that call `GET`, so the compatible shape was kept. - **Stripe is still absent from both privacy processor tables**, even though it has processed Signal subscriptions for some time. That is a pre-existing gap, not one this change introduces, so it is flagged rather than silently rewritten — legal copy should be your call. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01AUeKTKwNmdow8yUk3q8RB2 Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
runyourempire
added a commit
that referenced
this pull request
Aug 14, 2026
…ncovered paths (#429) ## What this fixes Four CI gates reported success without doing the work they claim. Each was verified against live run data before anything was changed. ### 1. The integration tests had never run in CI — on any workflow Every `cargo test` in `.github/workflows/` was `--lib`-scoped (`validate.yml:303/308`, `hermetic.yml:167/176`). No `--tests`, no `--all-targets`. So **all 154 real integration tests in `src-tauri/tests/` had never executed in CI**, including all 12 migration tests and the repo's only forward-migration coverage — against a `TARGET_VERSION = 103` migration chain that has no checksums and no downgrade path. **I measured before changing anything.** Full `cargo test --tests` run on this branch's base (`a6ece843`), isolated data dir, exit code 0: | Target | Result | |---|---| | lib unittests | **4,290 passed**, 0 failed, 10 ignored | | `4da` (cli bin) | 13 passed, 0 failed | | `fourda` / `fourda-engine` bins | 0 tests each | | `migration_tests` | **12 passed**, 0 failed | | `pipeline_integration` | **13 passed**, 0 failed | | `source_resilience` | **5 passed**, 0 failed | | `stack_simulation` | **124 passed**, 0 failed | | `victauri_dogfood` | 157 passed, 3 ignored (self-skips without `VICTAURI_E2E=1`) | | **Total** | **4,614 passed across 9 binaries, 0 failed** | **Nothing was broken and nothing had to be excluded.** They are also hermetic by construction, which I verified rather than assumed: `pipeline_integration` uses an in-memory DB (`test_utils::test_db()` → `:memory:`) and `migration_tests` uses `tempfile::tempdir()`. Both workflows now run `--tests` (lib + bins + integration) under the same throwaway-data-dir isolation `hermetic.yml` already used. Two follow-on fixes were required to avoid landing a red gate: - The count floor took `tail -1` of the `test result:` lines. With `--tests` there are **9** test binaries, so it would have read the *last* binary's total (157) and tripped the 2000 floor on every run. It now **sums** all binaries. - A new assertion fails if fewer than 5 test binaries report — so if this is ever re-scoped to `--lib`, it fails loudly instead of silently dropping the integration suite again. >⚠️ **Non-obvious trap for reviewers:** the isolation directory name must keep containing the substring `data`. `src/state.rs::test_get_db_path_points_to_data_dir` asserts the resolved DB path contains `"data"`. Pointing `FOURDA_DATA_DIR` at e.g. `/tmp/4da-hermetic` makes that lib test fail; `…/4da-hermetic-data` passes. I hit this during measurement. Both call sites are commented. ### 2. The hermetic fresh-clone canary never ran outside PRs `fresh-clone` needs the PR-only `changes` job. GitHub skips any job whose `needs` was skipped **unless its `if:` contains a status function** — and `hermetic.yml:94` had none. So push-to-main, the nightly cron and manual dispatch all skipped the clone and reported success: | Trigger | Duration | Result | |---|---|---| | push → main (08-14 03:52) | **9s** | "success" | | push → main (08-13 16:42) | **7s** | "success" | | schedule (08-13 08:21) | **7s** | "success" | | pull_request (real work) | ~19min | success | The file's own comment at `:56-60` claimed these paths "ALWAYS run the full canary". **The nightly cron had never built a single fresh clone.** Fixed with `!cancelled()` — not `always()`, so a cancelled run doesn't spawn a 45-minute cold build. The **identical defect** silently disabled `workflow_dispatch` for Frontend, MCP Server and the entire Rust matrix in `validate.yml`: the `github.event_name == 'workflow_dispatch'` clause on those three jobs had never once fired, while `Validate Success` (`if: always()`) still went green. Same fix. ### 3. A path-filter hole took the whole fleet down today This stopped being hypothetical while this PR was being written: - **#423 was a Rust-only PR.** Its `Frontend` job was skipped by the path filter. - `scripts/check-file-sizes.cjs --ci` ran **only inside the Frontend job** — but it scans `SCAN_DIRS = ['src', 'src-tauri/src']`, i.e. it guards Rust files too. - So #423 merged a **1032-line `src-tauri/src/analysis_rerank.rs`** past the 1000-line hard error threshold, with the gate never executing. - `check-file-sizes.cjs` then exited 1 on `main`, and `.husky/pre-commit:38-41` treats that as blocking — **every developer in the fleet was unable to commit.** A gate that guards Rust files must not be reachable only through a filter that excludes Rust. This PR adds a **`repo-guards` job with no path filter and no `needs:`** — it runs on every PR and dispatch, and carries `check-file-sizes`, `check-no-window-spawns`, `check-release-channel` and the guard self-tests. They are removed from `Frontend` (they were never frontend-specific). Hosted, ~40s, no `pnpm install` needed: all three guards use only node builtins. It is also in `validate-success`'s `needs`, so it actually gates the merge — `repo-guards` is now the only leg guaranteed to have run. **Additionally, "no filter matched" now means RUN, not PASS.** `site/`, `paddle-webhook/`, `mcp-memory-server/`, `editors/vscode/` and `.husky/` matched no filter, while `Validate Success` is the only required check and auto-merge is enabled repo-wide — so a Dependabot bump into the payment webhook, or a PR weakening `.husky/` itself, could merge with nothing run. Added `.github/**` and `.husky/**` explicitly, plus an `uncovered` fail-safe filter that catches anything unrecognised **including directories added in future**. > The `uncovered` filter uses `predicate-quantifier: 'every'`, which is **required** — the default `'some'` ORs the patterns, and a list of negations OR'd together matches every file. Verified against the action's source at the pinned SHA (`src/filter.ts:110-113` → `patterns.every(...)`; `MatchOptions = {dot: true}`, so `.husky/**` matches). ### 4. The guards' own self-tests ran nowhere `pnpm run test:scripts` (53 tests across 6 files) executed in **no hook and no workflow** — nothing verified the guards still detect what they claim. `pnpm run validate` isn't run by CI either (the Frontend job runs its steps individually), so wiring it into `package.json` alone would not have gated it. It is now a step in `repo-guards`. ## Deliberately NOT done - **No branch-protection or ruleset change.** Making `Hermetic Success` required is the correct end state — currently `Validate Success` is the *only* required check in the active `main-protection` ruleset (verified via the API; classic branch protection is disabled). But with ~30 open PRs and Hermetic historically failing on #421, flipping it now would block the fleet. **Recommended as an explicit follow-up** once this lands and Hermetic is observed green on push-to-main for a few days — which, note, is the first time that signal will ever have existed. - **`package.json` untouched.** `test:scripts` was going to be added to the `validate` chain, but a peer worktree agent holds a claim on that file and #418 also edits that exact line. Wiring it into `repo-guards` achieves the real goal (it now runs in CI) without touching the claimed file. - **`analysis_rerank.rs` / `check-file-sizes.cjs` untouched** — a separate agent owns the immediate unblock. This PR fixes the structural cause only. - **No per-package jobs for `site/`, `paddle-webhook/`, `mcp-memory-server/`, `editors/vscode/`.** The `uncovered` fail-safe means they now trigger the generic gate instead of passing silently, but that gate does not *build* them. Dedicated jobs are the right follow-up and belong in their own PR. - **Rust job timeout raised 30 → 45 min.** Not cosmetic: this job now compiles the integration test targets, and Swatinem's cache is only saved from `main`, so until this lands there every PR run pays a cold link for 5 extra binaries. 30 was too tight for that first window, and a timeout on a required gate is a red gate. ## Conflicts with open PRs Checked `gh pr diff --name-only` on every PR touching these files: | PR | Overlap | Notes | |---|---|---| | #387, #350 | none | Dependabot `actions/checkout` SHA pins only — different lines | | #388 | none | `taiki-e/install-action` SHA pin only | | #424 | none | Adds 3 matrix legs at `validate.yml:236-255`; my edits are at 301+ and inside `steps:`. I deliberately did **not** add a matrix key — an integration-floor key would have had to be added to its new legs. Verified `cargo test --tests --features experimental` compiles clean, so its `test-floor: 0` compile-gate legs are unaffected. | | #418 | **1 line** | Both edit `validate-success`'s `needs:`. #418 adds `pr-metadata`, this adds `repo-guards`. Resolution is a union of the two lists — whoever merges second takes both. Flagged rather than pre-empted. | `uncovered` was also deliberately placed *before* the main filter step, to stay clear of the end-of-job boundary #418 inserts a job into. ## Live CI evidence from this PR's own run The first run of this branch already proves the fix, on both platforms: | Check | Result | |---|---| | Fresh clone (ubuntu-22.04) | **pass**, 12m22s | | Fresh clone (windows-latest) | **pass**, 18m21s | | Hermetic Success | **pass** | | Rust (default) | **pass**, 12m41s | | Rust (experimental) | **pass**, 12m02s | The hermetic job log shows **all 9 test binaries executing on both legs** — `migration_tests` 12, `pipeline_integration` 13, `source_resilience` 5, `stack_simulation` 124, `victauri_dogfood` 157, plus lib (4,290 windows / 4,284 ubuntu — a 6-test platform delta, far above the 2000 floor) and the 3 bin targets. **Zero failures.** That is the first time any of those integration tests has run in CI. Rust finished in ~12min against the old 30min cap, so the 45min bump is headroom for the first cold-cache window rather than a response to an observed timeout. ## Verification - Both workflows parse as YAML; all `if:` expressions and filter blocks inspected post-rebase. - `cargo test --tests` measured green in full **before** any workflow edit, and **re-run green after** rebasing onto current `main` (table above) — #421 removed ~54k lines and #423 changed pipeline code between those two runs. - `dorny/paths-filter` negation + `every` semantics confirmed from source at the pinned SHA, not assumed. - `check-no-window-spawns`, `check-release-channel`, `test:scripts` all verified exit 0 locally; `check-file-sizes` correctly exits 1 (the live outage above). - Rebased onto latest `main`; only the two workflow files differ. The `analysis_rerank.rs` unblock (#430) has landed, so `repo-guards` passes; this branch is rebased on top of it. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
runyourempire
added a commit
that referenced
this pull request
Aug 14, 2026
…he ghost gate (#434) check-remove-by.cjs shipped in #421 wired to nothing; running it caught osv::Affected::versions falling due 2026-08-15. Removed after verifying inert (no .versions read in osv/, matching uses affected_ranges with an assume-affected fallback, serde ignores unknown fields) plus its five constructors. Also backlogged the 13 commands #425's seed missed, which had been failing the ghost gate for every Rust-touching commit; entries record that they are not a #421 regression (their callers were components nothing mounted). Flags that Standing Queries and Cross-Project Intelligence are marketed on /signal with no UI despite complete, tested backends. No always-on invariants workflow: #429 already shipped Repo guards.
runyourempire
added a commit
that referenced
this pull request
Aug 15, 2026
…eal warnings (#438) ## Why `EXCEPTIONS` in `scripts/check-file-sizes.cjs` is consulted **before** any size comparison: ```js if (EXCEPTIONS[normalized]) continue; ``` So an entry suppresses the **warn** tier as well as the **error** tier. An entry for a file that is no longer over the *error* threshold is not a harmless leftover — it silently hides a legitimate warning, and it disarms the hard limit on a file that may keep growing. Seven entries were in that state. All line counts below were measured with the gate's **own** `countLines()`, not `wc`. ## Removed | Entry | Lines | Warn | Error | Why it's stale | |---|---:|---:|---:|---| | `src-tauri/src/analysis_rerank.rs` | 866 | 700 | 1000 | Entry was self-described `TEMPORARY`, owed a split back to #423. **#430 (`02c105d9`) did the split (1032 → 866) but only touched the two `.rs` files — it never removed the exception it was owed.** | | `src/components/preemption/PreemptionCard.tsx` | 388 | 350 | 500 | Justification claimed *"9 lines over"*. It is 38 over **warn** and 112 **under** error — the stated reason was false. | | `src/components/enterprise/SsoConfigPanel.tsx` | 355 | 350 | 500 | Justification claimed *"5 lines over"*. 5 over **warn**, 145 under error. | | `src-tauri/src/scoring/pipeline_tests.rs` | *deleted* | 700 | 1000 | File was **deleted in #421**. Same class as the five dead entries #421 already swept — it missed this one. | | `src-tauri/src/settings/types.rs` | 908 | 700 | 1000 | Under error; was hiding a warning. | | `src/store/slice-types.ts` | 446 | 300 | 500 | Under error; was hiding a warning. | | `src-tauri/src/sources/adapter_resilience_tests.rs` | 1802 | *n/a* | 2000 | Test file, so warn-exempt — removing it changes no output today, but it left the 2000-line hard limit silently unenforceable on a file that is actively grown. | ## Kept Every other entry is genuinely over its **error** threshold and is doing its job — those were left alone. One deliberate keep that looks like a miss: **`src/types/i18n-resources.d.ts` does not resolve on disk**, but it is gitignored and generated by `pnpm run i18n:types`, which `validate:all` runs *before* this gate. That is already documented inline; the entry stays. `src-tauri/src/briefing_pipeline_tests.rs` is a different file from the deleted `scoring/pipeline_tests.rs` and was never in the map. ## Verification ``` before: 42 file(s) approaching size limits (warnings only). exit 0 after: 47 file(s) approaching size limits (warnings only). exit 0 ``` The 5 new warnings are exactly the suppressed files now reporting honestly (`settings/types.rs` 908, `analysis_rerank.rs` 866, `slice-types.ts` 446, `PreemptionCard.tsx` 388, `SsoConfigPanel.tsx` 355). **No file crosses an ERROR threshold**, so the pre-commit gate cannot block the fleet — the failure mode that took every developer offline on 2026-08-14 (#423 → #430). Every consumer of this script (`.husky/pre-commit`, the `repo-guards` CI job, `build-guardian.cjs`, `compound-quality-check.cjs`, `sentinel-scan.cjs`) keys on exit code or `ERROR` lines only, so warnings are safe everywhere. Also verified: after this change every remaining entry passes the "file exists **and** is over its error threshold" test, except the documented generated-file case above. ## Scope `scripts/check-file-sizes.cjs` only — 12 deletions, no source file touched, nothing split. 4 of the 7 (`pipeline_tests.rs`, `settings/types.rs`, `slice-types.ts`, `adapter_resilience_tests.rs`) were found by auditing all 38 entries rather than being named up front. Each is an independent line and can be dropped in review without affecting the others. ## Hook note This worktree has no `node_modules`, so `.husky/_` does not exist and `core.hooksPath` resolves to nothing — git silently ran **no** hooks. `--no-verify` was **not** used. The gates were run manually instead, all green: `check-file-sizes` · `check-doc-location` · `check-llm-gate-honesty` · `check-vanity-metrics` · `check-release-channel` · `i18n-guard` · `validate-boundary-calls` · `compound-quality-check` · push-range `scan-secrets --diff-added` · `private-asset-guard` Not runnable here (no `node_modules`): `tsc`, ESLint, and the frontend suite. This change is a Node tooling script with no TypeScript or frontend surface; CI's required gates cover them. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01AUeKTKwNmdow8yUk3q8RB2 Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
runyourempire
added a commit
that referenced
this pull request
Aug 16, 2026
…nt main, replaces #419) (#469) ## AD-030: retire the "gets sharper every day" promise — rebuilt onto current main Replaces **#419**, which was CONFLICTING, 31 commits behind, with CI dead since 08-11 (self-hosted runner timeouts). Rather than rebase a 46k-line diff dominated by whole-file locale churn, this PR **merges #419's branch onto current `main`** and resolves every conflict in favour of main's newer facts. Net diff: **62 files, +848/−677** (vs #419's +23,646/−22,838). Operator decision on record (2026-08-14): the promise is completely retired, on every surface. Decision record: `.ai/DECISIONS.md` **AD-030** (in this PR). ### What this ships - **New canonical line** on every copy surface: *"4DA reads the internet for developers — privately, locally. Your codebase decides what's relevant."* — README, site (title/meta/OG/JSON-LD/hero/FAQ), docs, scan page, streets module, writing footers, installer metadata (`tauri.conf.json`), mcp README, RELEASE-NOTES. - **"Yesterday's noise becomes tomorrow's signal" KEPT** — re-attributed to corpus re-judging (verdict epochs), never to engagement. - **13 locales, surgically**: #419's translations ported **per-leaf** onto main's current files — 312 rewordings + 130 deletions of dead affinity-surface keys. Its 752 key *additions* were dropped: they belong to surfaces #421 already deleted from `en` (momentum, wisdom, comparison, toolkit…), and porting them would have resurrected orphans. `validate-translations`: 0 errors, warnings identical to main. - **v19.1 hotfix**: auto-detected `anti_topics` removed from both `build_negative_stack` call sites — the last structurally-live behavioral scoring path. No `PIPELINE_VERSION` bump; justification (live probe: `anti_topics` = 0 rows → byte-identical priors) recorded at the constant. - **Enforcement gate**: `scripts/check-retired-claims.cjs` + 9 tests. Violations on main measured before: **57 across 24 files**; after this PR: **2, both in CLAUDE.md** (see residuals). - **Public handling**: `site/src/writing/retiring-a-claim-we-could-not-measure.njk` — the engineering record, now linked from /writing and the sitemap. Site builds clean (11ty, 127 files). ### Conflict-resolution calls worth reviewing - Kept main's newer copy where it was already promise-free and more current: the 93%/98.9%/245-item benchmark (#460), Precision Ledger references, BYOK never-paywalled rationale. - **Fixed 3 fact regressions #419 would have reintroduced**: stale 92%/98%/215 benchmark figures (2 files) and a RELEASE-NOTES tier table listing Developer DNA / Signal Chains / Score Autopsy as paid (they are free per AD-025/026). - `apply-translations.cjs`, `missing-keys.json`, `BadgeRow.tsx` stay deleted (deleted on main since #419's base). ### Residuals (tracked in `.claude/plans/PENDING-DECISION.md`) 1. **`CLAUDE.md` tagline + `package.json` gate wiring** — both files are claimed by live peer lanes (#466; agent-a875). A follow-up commit on this branch lands the moment the claims release. Until then the gate exists but is not yet wired into `validate`, so CI on this PR does not self-fail on CLAUDE.md. 2. **npm README** — `@4da/mcp-server` live README still serves the old table; needs a republish (different terminal, tag `mcp-v5.0.0`). 3. **Live 4da.ai** — CF Pages is direct-upload; needs `wrangler pages deploy --branch main` after merge. 4. **Close #419** after this merges. ### Verification - Frontend: **1249/1249** pass, `tsc --noEmit` clean, ESLint clean, i18n types regenerate in sync. - Rust: `cargo check --lib` clean; targeted tests (negative_stack / context / blind_spots) green. - Gate self-test: 9/9; full-tree scan = only the 2 deferred CLAUDE.md hits. - Site: eleventy build clean, post renders. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01MMarGhXjbKyNJzsm3JG1jw --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
runyourempire
added a commit
that referenced
this pull request
Aug 16, 2026
…Knowledge Gaps (#473) Closes the standing 08-12 audit flag (SIGNAL FEATURES SOLD WITH NO UI) — and it was worse than flagged. Verified against the live code: of everything sold on /signal, README, LICENSE-ACTIVATION, SETUP-GUIDE, RELEASE-NOTES and the Terms, only **Blind Spots (with AI assessment)** and **Knowledge Gaps** have a reachable UI. **Removed from every sales surface:** Standing Queries (no creation UI — only a Brief listener that can never fire), Semantic Shifts / cross-project intelligence / Project Health comparison (commands registered, zero frontend callers), Attention Report + attention dashboard (backend deleted as ghosts in #421; `settings.attention.*` locale keys are orphans), Decision Health, Trust Ledger analytics, Precision Ledger (unbuilt in both layers). **Tier misattribution also fixed:** Score Autopsy, Developer DNA, Signal chain analysis are FREE (AD-025/026) — moved to the Free card/column. The Terms were internally inconsistent (§3.2 sold channels/DNA as paid; §4.5 said they stay free) — both now state the true paid set. **Legal wording otherwise untouched — flag `site/src/terms.njk` + `docs/legal/TERMS-OF-SERVICE.md` for counsel review.** Backend + gating for the unreachable features are KEPT; each returns to the sales surfaces when its UI ships. Site builds clean; retired-claims gate green. Needs a site redeploy after merge. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01MMarGhXjbKyNJzsm3JG1jw --------- Co-authored-by: Claude Fable 5 <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.
Remediation of the full-codebase audit (2026-08-12). 390 files changed, +3,349 / −54,041.
Every deletion was grep-verified repo-wide before removal, and every surface was re-verified after. Where a claim from the audit turned out to be wrong, it is corrected below rather than quietly dropped.
Verification
cargo test --libcargo build --libcargo fmt --checkpnpm typecheckpnpm testpnpm buildvalidate-translationscheck-remove-bycheck-release-channelWhat changed
Dead code. The V1 scoring pipeline had been unreachable since 2026-03-03 (
const USE_V2: bool = true, no override) yet was still co-maintained —pipeline.rswas edited in the AD-029 PR the day before this audit "to keep both pipelines aligned". ~3,100 lines removed. Plusjob_queue.rs(564) andreachability.rs(195), both entirely dead; 18 ghost Tauri commands and their newly-orphaned backing modules (~5,800 lines, incl.project_health.rsand 8content_personalizationsubmodules); 1,138 dead lines from the published@4da/mcp-server; 14 dead React components; 23 orphan scripts; 9,230 dead i18n keys across 13 locales.Correctness fixes found along the way.
release_notestype). 124 of 125 now verified serving live items.dependency_snapshotsgrew forever. Wired intorun_maintenance— via a connection-taking helper, because the obvious call would have deadlocked on a mutexrun_maintenancealready holds..txtisn't an Eleventy template format.site/rotate-key.shrotated the live Stripe key against the dead Vercel project; a rotation would have silently failed to change the key the live deployment uses._siteoutput was reaching production (Eleventy never cleaned it,cf:deployuploaded wholesale), which is why 4da.ai served old server-side source and the internal Stripe E2E harness.Legal / doc accuracy. The privacy policy named Vercel as data processor in six places — including the GDPR international-transfer clause — three weeks after the Cloudflare migration. Licence validation is cached 90 days, not 24 hours. Terms were missing the $299 Lifetime plan, used USD where the operative terms use AUD, and described a Keygen machine fingerprint the code does not send.
LICENSE-ACTIVATIONdocumented a device-limit and deactivation flow that does not exist. Tier lists corrected: Developer DNA, NL search and AI briefings are free per AD-025/AD-026, and Score Autopsy, signal chains and channels have no gate at all.Enforcement. Added
check-remove-by.cjs— the repo carried its own "REMOVE BY" cleanup contract that nothing ever checked, so ~40 markers had silently expired. Also replaced the pre-commit dead-code gate's shell implementation, which forked agrepper line of every staged annotated file (20+ minutes once a 5k-line file was staged; the hook's own comment recorded the symptom). Same work now runs in 1.17s, negative-tested to confirm it still blocks.Corrections to the audit's own claims
scoring/context.rs→assemble_profile→assemble_playbook_progressreadsdocs/streets/at runtime. It is effectively inert (the Playbook UI is gone, soplaybook_progresscan never be populated and it always computes zero), but removing the bundle is a scoring-input change and was left alone.sovereign_developer_profile,content_personalization::context,tech_radar,playbook_commandsandsunsall have live non-command callers. Only the command entry points were removable; the load-bearing helpers stayed.check-release-channelencodesEXPECTED_DESKTOP_VERSION_LINE = '1.0'with "do not publish 1.1.0 yet". The gate reflects a deliberate decision and wins.key_one/key_other) as independent keys; they are referenced by their base key viat('key', {count}), so 24 live strings were deleted. Caught by checking every literalt()call site against the pre-purge baseline, restored, and redone plural-aware — 0 of 1,800 call sites broken. That check also surfaced 185 keys already unresolvable before this work, a pre-existing defect worth its own fix.Not done — needs a decision or its own PR
apply_source_quality_gatehas exactly one call site repo-wide).sources/fallback.rsis therefore effectively dead in normal operation.ace/db.rsruns a second 469-linemigrate()with no version tracking and error-swallowing ALTERs; whichever init runs first wins. Already caused the documentedkv_storeaffinity drift.ipc_guard.rs— tested SSRF/path-traversal validators with zero production callers. Wire them in or delete them.main— its path filters skip the matrix, so the workflow's green ticks onmainare skipped jobs, not passes. Combined with no Visual Studio on the runner image, a transientprebuild-installfailure (seen on this PR:socket hang up) falls through tonode-gypand hard-fails with nothing to fall back on. Wants a retry around the frozen install.Done since this description was first written
site/src/signal.njkrewritten — 9 cards to 6 (Blind Spots, Knowledge Gaps, Cross-Project Intelligence, Semantic Shifts, Standing Queries, Precision Ledger). Score Autopsy and signal chains are now stated as free in the FAQ rather than sold as Signal-tier.wrangler pages deploy --branch main; the legal corrections are live on 4da.ai and verified against the domain, not the CLI's success line.origin/main(fix(utf8): eliminate 23 string byte-slicing panic vectors on ingested text #422, UTF-8 panic-vector fix). Two conflicts, both resolved toward this branch's deletions after verifying the code was unreachable:template_processor.rs(module deleted; no callers) andcommunity_intelligence.rs(peer's fix sat insideget_community_status, a ghost command removed here). Re-verified after merging:cargo test --lib4,267 passed,pnpm test1,249 passed.🤖 Generated with Claude Code
https://claude.ai/code/session_01WJ6BP3GX5HYrnjW1nGtrvC