Skip to content

fix(tier-truth): sell only reachable Signal features — Blind Spots + Knowledge Gaps - #473

Merged
runyourempire merged 8 commits into
mainfrom
worktree-ad030-promise-retirement-v2
Aug 16, 2026
Merged

fix(tier-truth): sell only reachable Signal features — Blind Spots + Knowledge Gaps#473
runyourempire merged 8 commits into
mainfrom
worktree-ad030-promise-retirement-v2

Conversation

@runyourempire

Copy link
Copy Markdown
Collaborator

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.ai/code/session_01MMarGhXjbKyNJzsm3JG1jw

runyourempire and others added 8 commits August 12, 2026 03:46
…stack

The last behavioral scoring path left after the v19 demotion (AD-029),
found by the post-merge verification sweep: build_negative_stack injected
0.30 Bayesian suppression priors from auto-detected anti_topics rows whose
confidence is pure dismissal count (rejection_count/10) — five dismissals
could auto-ban a topic to x0.30 composite authority via
negative_stack_prior. Inert on the live corpus (anti_topics has 0 rows,
priors byte-identical, hence NO PIPELINE_VERSION bump — precedent and
probe documented in scoring/mod.rs), but structurally live.

The negative stack now builds from competing-tech inference over the
actual dependency graph only. Explicit topic suppression remains
user-authored exclusions, which hard-filter upstream. blind_spots'
duplicate anti-topic feed removed with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016puYGGmj3oHxmcKBbJ5vXq
The promise described a mechanism AD-029 removed, was never measurable
("all risk, no demonstrated lift" — 7 explicit feedback signals against a
200k-item corpus), and post-v19 left ~100 instances of self-contradicting
copy, several asserting dead mechanisms inside Score Autopsy — live
INV-023 violations. Pre-launch (public, unadvertised) is the only cheap
moment; after launch this is a retraction.

New canonical (propagated from CLAUDE.md through every surface):
"4DA reads the internet for developers — privately, locally. Your
codebase decides what''s relevant." "Yesterday''s noise becomes tomorrow''s
signal" SURVIVES, re-attributed to corpus re-judging (verdict epochs +
re-examination) — it is true and implemented. The 92%/98% benchmark stays
as body copy with methodology. "Compound knowledge" survives only in the
framework''s dev-process sense.

- Governance first: AD-030 (with re-claim criteria: a /calibrate-measured
  lift, published) + amendments to AD-017/025/026/028 — all four decisions
  survive, only rationales rewrite (AD-028''s lifetime honesty now rests on
  the cost structure: zero marginal cost + offline 2099 key)
- P0: in-app strings that asserted dead mechanisms rewritten or deleted
  ("will be penalized in relevance scoring", "Boosted because you''ve shown
  interest", "scoring corrections applied", the "Learned" badge, learned
  factor rows + affinity chips — the anti factor also had inverted
  magnitude/multiplier semantics and rendered a false penalty on every item)
- "Compound Intelligence" tab renamed "Personal, Not Aggregate" — its
  content (per-user, inspectable, local-only) was always a privacy story
- 13 locales: 45 stale keys purged per locale, regenerated from corrected
  English via i18n:fill; generator scripts purged first so nothing
  re-injects (apply-translations.cjs carried 12 hardcoded copies)
- Ship-locked surfaces: installer shortDescription/longDescription,
  homepage JSON-LD (incl. the Offer), npm MCP README (lands on next
  publish)
- Site + docs: 22 files rewritten under the house rule (present-tense
  verifiable statements; improvement attributed to engine updates and the
  user''s codebase, never engagement); signal.njk''s 16 compounding claims
  reframed on the analysis layer; the refund-adjacent trial claim deleted;
  framework §7 kept as a dev-process thesis with scoring-accuracy claims
  stripped across all three mirrored copies
- New public post: site/src/writing/retiring-a-claim-we-could-not-measure
  — the incident record as an engineering story, ending on the re-claim bar
- Enforcement: scripts/check-retired-claims.cjs (+9 tests) wired into
  test:scripts and validate; historical-record files allowlisted;
  retired-ok escape hatch for quoting the claim as history

Verified: guard clean repo-wide; translation parity 0 errors across 13
languages; tsc clean; frontend suite green; Rust 4,419 green; fmt clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016puYGGmj3oHxmcKBbJ5vXq
…uilt onto current main)

Merges origin/worktree-promise-retirement-ad030 (PR #419) with conflicts
resolved in favour of current main's newer facts:
- locale ui.json files: main's structure kept; #419's 312 reworded values
  and 130 deleted affinity-surface keys ported per-leaf (its 752 key
  additions dropped — those surfaces were deleted from en by #421)
- README/index.njk/signal.njk/SETUP-GUIDE/LICENSE-ACTIVATION: main's newer
  copy (93% figure, Precision Ledger, BYOK rationale) kept, retired
  phrases replaced with the canonical line
- Developer DNA kept OUT of paid-feature lists (AD-025/026 tier truth)
- apply-translations.cjs / missing-keys.json / BadgeRow.tsx stay deleted
  (deleted on main since #419's base)
- CLAUDE.md + package.json deliberately untouched: claimed by live peer
  lanes (#466, agent-a875) — gate wiring + tagline follow in-branch once
  those claims release

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMarGhXjbKyNJzsm3JG1jw
- 92% -> 93% / 98% -> 98.9% / 215 -> 245-item corpus (the current
  measured benchmark, #460) in COMPARISON.md and the retirement post
- RELEASE-NOTES-v1.0.0 tier table + closing line matched to AD-025/026
  tier truth (DNA, Signal Chains, Score Autopsy are free; the paid set
  is Blind Spots, Knowledge Gaps, Standing Queries, Semantic Shifts,
  cross-project intelligence)
- retirement post added to /writing index (retired-ok teaser quote) and
  sitemap
- two Rust doc comments un-mangled; engagement_telemetry header no
  longer claims facets influence scoring (INV-023)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMarGhXjbKyNJzsm3JG1jw
The last 2 retired-claim violations in the repo. The tagline mandate now
teaches the canonical description and points at the enforcement gate
instead of restating the retired phrases. Deferred from #469 because this
file was claimed by the #466 lane; that lane is merged and its worktree
reaped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMarGhXjbKyNJzsm3JG1jw
…Knowledge Gaps

Verified against the live code: of the features 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 (backend exists, 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 + the
attention dashboard (backend command deleted as a ghost in #421; the
settings.attention locale keys are orphans), Decision Health, Trust
Ledger analytics, and the Precision Ledger (unbuilt in both layers).

Also corrects tier misattribution the same surfaces carried: Score
Autopsy, Developer DNA and Signal chain analysis are FREE (AD-025/026)
and now sit in the Free column/card, not the Signal one. The Terms'
Signal-tier list (site/src/terms.njk + docs/legal/TERMS-OF-SERVICE.md)
was internally inconsistent — §3.2 sold channels/DNA as paid while §4.5
said they stay free; both now state the true paid set. Legal wording
otherwise untouched — flag 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.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMarGhXjbKyNJzsm3JG1jw
@runyourempire
runyourempire enabled auto-merge (squash) August 16, 2026 14:19
@runyourempire
runyourempire merged commit 690ced1 into main Aug 16, 2026
10 checks passed
@runyourempire
runyourempire deleted the worktree-ad030-promise-retirement-v2 branch August 16, 2026 14:27
runyourempire added a commit that referenced this pull request Aug 16, 2026
…AD-029/AD-030 arc) (#474)

Implements the **v20a-1 dead-code removal** for the AD-029/AD-030 arc:
deletes every learned-affinity surface that is STRUCTURALLY DEAD
(provably unreachable or provably no-op), and nothing that is live.
−1,620 lines, +107.

## Proof classes (what made each removal behavior-neutral)

- **Pinned inputs** — pipeline_v2 pins `affinity_mult=1.0` /
`anti_penalty=0.0`, so these could never produce output: the
learned-axis strength branch in `compute_signal_strength_bonus` (both
conditions structurally unreachable), the `_anti_mult` dead binding, the
`LearnedPreference` explanation factor (`learned_weight = max(0.0,
1.0−1.0)`, never fires — variant removed from `FactorKind` in Rust + the
FE mirror), and `probes_engine`'s `ax_learned` condition (probe report
now lists only the four axes that can actually fire).
- **Empty-map reads** — the AD-029 quarantined loader returns an empty
`topic_affinities` map, so these always saw nothing:
`compute_affinity_multiplier` + `compute_anti_penalty` (whole
`scoring/affinity.rs` deleted), the `channel_render` multiplier +
always-"Not yet learned" `affinity_summary` prompt line, the
explanation-confidence affinity contribution, the two dead
`semantic/embeddings.rs` loops, and the frontend
`learnedAffinities`/`antiTopics`/`lastLearnedTopic` store state with its
500 ms refresh round-trip feeding nothing. `ctx.feedback_boosts` was a
hardcoded empty map → the `feedback_boost` computation always yielded
0.0; the then-unread field was deleted with its writers.
- **Phantom schema** — two PRE-EXISTING bugs, now deleted rather than
shipped broken: `weekly_digest::collect_topics` queried a nonexistent
`ace_topic_affinities` table (real table: `topic_affinities`) —
`top_topics` empty since the day it was written;
`developer_dna::get_blind_spots` filtered on a phantom `weight` column
(real: `affinity_score`) — prepare failed, `blind_spots` always `[]`.
(The real Blind Spots feature in `blind_spots.rs` is untouched.)
- **Unreachable / zero callers** — gate's learned axis never confirms
(`learned_confirmed = false`); dead commands `ace_get_single_affinity` +
`get_learning_stats` (handler + registration + CommandMap types +
victauri allowlist entry + ghost-backlog entry); the persona-posterior
READ side (`get_dominant_persona`, `get_persona_topic_boosts`, drift
detection + `posterior_snapshots`); `stability_detector`'s three unused
query fns (+ their orphaned private helper); the monitoring FROZEN
threshold auto-tune "would have done" log block; simulation
affinity-injection arms (persona_data*, enrichment, feedback_sim,
enriched_lifecycle, ace_validation).

## Deliberately KEPT

- Capture/writers: `ace/behavior/tracking.rs`, decay, engagement
telemetry, interactions, persona-posterior WRITE side
(`update_posterior`, `seed_from_taste_test`).
- The AD-029 quarantined loader + its guard test
(`ace_context_quarantines_topic_affinities`) — the quarantine itself.
- Learned Preferences panel, `preference_commands.rs`,
stability-detector core, EngagementPulse, `get_engagement_summary`,
`tech_radar` overlay_affinities, `standing_queries_suggestions`,
`get_top_engaged_topics`, `data_export`, mcp-4da-server.
- **`affinity_mult` / `anti_penalty` / `feedback_boost` FIELDS in
`ScoreBreakdown`, still pinned 1.0/0.0/0.0** — stored `score_breakdown`
JSON in user databases carries them; the serialized struct is unchanged.
- `ace_get_topic_affinities` / `ace_get_anti_topics` commands (live
readers of real rows): their only FE caller was the dead store refresh,
so they are now backlogged in `ghost-command-backlog.json` with reviewed
reasons rather than deleted.
- anomaly.rs / sovereign profile / contradiction & skill-gap boosts —
v20a-2 (INV-023) territory, untouched.

## DSL

Removed only the two verified orphans: `semantic_boost.affinity_clamp`
and `engagement_weights.affinity_w` (generated constants with zero src
references; DSL validation is per-param, no required-key checks, and
`scoring_dsl.rs` tests use their own inline SAMPLE_DSL — no test churn).
The remaining affinity/feedback constants (AFFINITY_THRESHOLD,
FEEDBACK_SCALE, FEEDBACK_CAP_RANGE, SIGNAL_NORMALIZATION_AFFINITY_RANGE,
SIGNAL_NORMALIZATION_FEEDBACK_STRENGTH, AFFINITY_MULT_RANGE,
AFFINITY_EFFECT, ANTI_PENALTY_MAX) stay in the DSL: they generate into
`scoring_config` under its existing module-wide `#![allow(dead_code)]`,
and removing them would cascade beyond the two verified orphans.

## NO PIPELINE_VERSION bump

Every removed path was provably emitting nothing (pinned 1.0/0.0 inputs
or empty-map reads), so stored scores cannot differ and a corpus
re-stamp would be a no-op. Simulation/version-comparison suites confirm:
all asserted precision numbers unchanged.

## Skipped (fleet coordination)

`src-tauri/src/commands.rs` (inventory item 3: score-autopsy
`learned_affinities` build+emit, the pinned "Learned Affinity" /
"Anti-Topic Penalty" components, and `score_tuning_snapshot`'s
mislabeled `feedback_interaction_count` field) — the file is claimed by
worktree-agent-a01f9610ade32c536, so per protocol its changes were
skipped. Verified compile-safe: commands.rs references only the kept
pinned breakdown fields and the kept quarantined map, none of the
deleted symbols. The FE side of the autopsy (render block, interface
field, `autopsy.learnedPreferences` en-only locale key) IS removed — the
backend's extra JSON field is simply ignored.

## Observation (not fixed here)

`context_engine.rs` carries a duplicate `topic_affinities` CREATE TABLE
schema alongside the canonical one in `ace/db.rs` — a schema-drift
hazard of exactly the class that produced the two phantom-schema bugs
deleted above.

## Verification

- `cargo fmt` clean; `cargo test --lib` **4372 passed / 0 failed / 8
ignored**; `cargo clippy --lib -- -D warnings` clean
- `pnpm install --frozen-lockfile`; `pnpm run i18n:types` (no diff);
`pnpm run typecheck` clean; `pnpm run test` **1242 passed / 0 failed**
(111 files)
- `npx eslint` clean on all touched TS files
- `node scripts/check-file-sizes.cjs` — no errors (48 pre-existing
warnings)
- `node scripts/check-retired-claims.cjs` — OK
- `node scripts/ghost-commands.cjs` — 0 new ghosts, 0 unregistered, 0
stale backlog
- `node scripts/validate-commands.cjs` — 383/383/383 consistent
- No new `#[allow(dead_code)]`
- Rebased onto origin/main (690ced1) before push; no file overlap with
#473

🤖 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
Bring the branch onto current main (690ced1) so CI validates the real merge
result rather than a base seven commits old. No conflicts; #465, #466, #467,
#469, #472 and #473 landed in the meantime.
runyourempire added a commit that referenced this pull request Aug 16, 2026
Bring the branch onto current main (690ced1) so CI validates the real merge
result rather than a base seven commits old. No conflicts; #465, #466, #467,
#469, #472 and #473 landed in the meantime.
runyourempire added a commit that referenced this pull request Aug 19, 2026
… key-in-logs, +6 (#495)

## What this is

Wave 2 of the licensing-pipeline hardening audit (Wave 1 = #494,
merged+deployed). A deeper adversarial sweep — **three parallel hunters
(site / app / frontend) + direct analysis** — found several more issues
across the whole pipeline. Every confirmed finding here is fixed and
test-covered; two items requiring an app release or a product decision
are documented for the operator (not code-fixable this pass).

## Fixed & verified

### Server (Cloudflare Pages)
- **H1 (HIGH) — session-lookup returned ANY customer's key with no
payment check.** `GET ?session_id=` trusted a session id minted at
*creation* (pre-payment) and resolved the customer by *buyer-typed
email*, so starting a checkout with a victim's email and abandoning it
(no payment) let an attacker read that victim's offline-verifiable key.
Now: gate on `sessionProvesPurchase` (status complete / paid), bind to
`session.customer` (never an email list), and time-box the session id to
24h (`sessionWithinWindow`). Pure predicates unit-tested.
- **M4 (MED) — no `charge.dispute.closed` handler.** A *won* dispute
left a paying monthly subscriber terminal forever (the isTerminal guard
then blocks every renewal). `handleDisputeClosed` restores `active` on
`status === 'won'`, only from the exact `chargeback` state.
- **M3 (MED) — `notify.js` unauthenticated + unmetered** → unbounded
Stripe customer creation. LICENSE_KV per-IP limiter (20/hr), fail-open,
separate key namespace.
- **CSP + no-referrer (L6/L7)** — strict CSP (`default-src 'none'`,
`connect-src 'self'`) on the two key-bearing pages so an injected script
can't exfiltrate the key, and the session-id credential never leaks in a
Referer.
- **checkout.js** writes `signal_tier` (was legacy `streets_tier`) —
hygiene.

### App (Rust)
- **Key-in-production-logs (MED)** — the full
`fourda://activate?key=<SECRET>` was logged at info/warn across 9 sites
and written to the `security_events` DB, so the bearer key landed in
`data_dir/logs/*.log` in cleartext (support bundles, cloud-synced
folders). New `utils::redact_deep_link` (byte-safe vs hostile UTF-8)
masks the `key=` value everywhere.
- **Expiry never enforced + settings.json tamper (MED)** —
`has_license_key_available` returned true for any non-empty key, so an
*expired* `4DA-` key kept granting Signal forever (a cancelled monthly
subscriber never downgraded) and pasting `tier:"signal"`+garbage
unlocked Signal. New `key_is_usable` verifies signature **and** expiry.
Makes Terms §4.5 true for subscriptions for the first time.
- **Recovery auto-activated an unverified key (F3)** — removed (the live
server never returns a key here; it was dead + dangerous if `4da.ai`
were repointed).
- **Forgeable Keygen cache (F2)** — the doc comments claimed it was
cryptographic; it isn't (bare SHA-256). Corrected the claims + added a
named adversarial test pinning the real honesty-box behaviour.
- **Trial 45-vs-14 lie** — `start_trial` now reports the real value from
`get_trial_status`.
- **Terms §5.3** reworded from the impossible "the licence is
deactivated" to the enforceable "the licence terminates; continued use
is not permitted."

## Verified
- **Rust**: 25 license tests + 22 url tests pass (6 new), `cargo fmt` +
`clippy --lib` clean, no size breaches.
- **Site**: 64 lib tests pass (session-predicate + revoked-matrix
added).

## Documented for the operator (not code-fixable this pass)
- **Deep-link activation consent (MED)** — a website can silently swap a
paying user's licence for the attacker's own valid key (DoS/griefing).
The fix is a UI consent flow, and the project's verification rule
requires UI/entitlement changes to be tested against the running app;
deferred as a focused, live-verified follow-up with the design ready
(email-match refusal + confirmation modal). This is the top open
security item.
- **Lease-client arc** — real lifetime-refund revocation + killing the
monthly re-paste. Needs an app release + a NETWORK.md/ADR decision.
- **Signal-feed "isPro" gate** — per tier-truth #473 that feed is
free-floor, so this is a vestigial sales-tease, NOT a data leak (all
actually-sold features enforce server-side). Product call, not a patch.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01RuQRKzs6RawLs5zxxNPBYp

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant