55 lines
6.8 KiB
Markdown
55 lines
6.8 KiB
Markdown
# Redundancy Audit — Addictiveness Goal Loop (iters 1-8)
|
|
**Date:** 2026-05-28
|
|
**Trigger:** User caught that the FR-3 registry duplicated the existing Marketplace, then asked: "check if all done in this goal loop is also redundant."
|
|
**Method:** Each shipped feature checked against pre-existing functionality by grepping the *capability* (not the feature name I chose).
|
|
|
|
## Verdict table
|
|
|
|
| Feature | Iter | Pre-existing equivalent | Verdict | Action |
|
|
|---|---|---|---|---|
|
|
| **FR-3 registry** (route + JSON + HTML) | 8 | `MarketplaceApp` + `/api/marketplace/*` — richer (install/uninstall, security scan, live-synced DB from ~25 sources) | **REDUNDANT** | ✅ Reverted (this turn) |
|
|
| **FR-5 backend** (`sample-workspaces.ts` + `/load`) | 6 | `workspace-templates.ts` 14 `BUILT_IN_TEMPLATES` + `starterMemory[]` + M2-5 seed in `POST /api/workspaces` | **REDUNDANT** (mechanism) | ✅ Reverted (2026-05-28, commit after 5f54193) — route deleted, de-registered |
|
|
| **FR-5 frontend** (day-0 LoginBriefing buttons) | 6 | `OnboardingWizard` TemplateStep already calls `createWorkspace({templateId})` → seeds starterMemory | **PARTIAL** | ✅ Reverted to iter-1 F1 demo cards. Non-redundant consolidation (wire to templateId) tracked in FEATURE-REQUESTS.md |
|
|
| **F2 StatusBar memory trophy** | 1 | `DashboardApp` Brain Health + `brain-health.ts` + LoginBriefing brag line | **PARTIAL** (3rd surface for same data) | Keep — always-on placement is genuinely unique; low concern |
|
|
| **F1 day-0 demo cards** | 1 | bare empty-state existed | enhancement, superseded by FR-5 | n/a |
|
|
| **F4 coverage compass** | 7 | none | **NEW but low-value** (static marketing card) | Keep or trim — your call |
|
|
| **FR-1 browser extension** | 2 | none (`apps/` had only web + www) | ✅ **GENUINELY NEW** | Keep |
|
|
| **FR-2 Telegram outbound** | 3-5 | none (`notifications.ts` is in-app only; no webhook/slack/email/outbound channel anywhere in server) | ✅ **GENUINELY NEW** | Keep |
|
|
|
|
## The two redundancies in detail
|
|
|
|
### FR-3 registry (REVERTED)
|
|
- `MarketplaceApp` reads `/api/marketplace/search` + `/api/marketplace/installed`, backed by a **live-synced DB** (the `[sync] +N added` lines at boot pull from ClawHub, MCP Registry, Anthropic skills, HuggingFace, Stripe, Expo, +~20 more). It has install/uninstall, security scan scores, installed-vs-available tabs.
|
|
- My `/registry` read the **static 148-entry `MCP_CATALOG`** from `@waggle/shared` — read-only, no install, no scan. Smaller, dumber, parallel.
|
|
- The one thing FR-3 was meant to add (public/hosted/link-shareable) the MVP did NOT do — it was local-only (`127.0.0.1`).
|
|
- **Correct reframe:** FR-3 = *publicly host the EXISTING marketplace* (ops/deploy decision), not build a parallel static catalog.
|
|
|
|
### FR-5 sample workspaces (recommend revert + rewire)
|
|
- `workspace-templates.ts` already has 14 `BUILT_IN_TEMPLATES`, each carrying `starterMemory: string[]`. `POST /api/workspaces` with a `templateId` seeds those frames into the new workspace's MindDB (the "M2-5" block at `workspaces.ts:222`). `OnboardingWizard` already drives this via `adapter.createWorkspace({templateId})`.
|
|
- My `sample-workspaces.ts` reimplemented workspace-create-plus-seed-memory as a parallel endpoint with 5 hardcoded bundles. **4 of the 5 duplicate existing templates by persona:** marketer→`marketing-campaign`, analyst→`data-analytics`, consultant→`agency-consulting`, engineer→`code-review`. Only "writer" had no existing template.
|
|
- **Correct implementation would have been:** (a) enrich the existing templates' `starterMemory` (they have ~3 thin entries; mine have 8 richer ones) and add a `writer` template to `BUILT_IN_TEMPLATES`; (b) wire the day-0 LoginBriefing hook to `POST /api/workspaces` with the chosen `templateId`. No new route, no parallel bundle store.
|
|
- The day-0 LoginBriefing trigger itself (load a starter when the user is empty, every launch — not just first-run wizard) has *marginal* unique value, so the FRONTEND is worth keeping if rewired to the real templates.
|
|
|
|
## Root cause (why I made the parallel-system mistake twice)
|
|
I *did* grep before building (CLAUDE.md §3.6), but I grepped for the **feature name I was about to use** ("registry", "sample-workspace") instead of the **capability/function** ("marketplace", "starter memory", "seed workspace"). My own names didn't collide with the existing system's names, so the greps came back clean and I built parallel. The existing systems used different vocabulary (Marketplace, BUILT_IN_TEMPLATES.starterMemory) for the same capability.
|
|
|
|
**Lesson (saved as feedback memory):** before building a feature, grep for the *capability* in domain-neutral terms, and specifically read the nearest existing app/route that touches the same data, before writing a new route.
|
|
|
|
## Verification blind spot (separate finding)
|
|
While auditing, `npx tsc --noEmit --project packages/server/tsconfig.json` surfaced **1 latent type error** in `sample-workspaces.ts` (a local `Importance` alias that included `'low'`, not a valid core value). It shipped undetected because:
|
|
- The local sidecar runs via `npx tsx` — **transpile-only, no typecheck**.
|
|
- My per-iteration verification was `npm run build`, which builds **only `apps/web`** (the Vite frontend). It never typechecks the `packages/server` code where all 4 new routes lived (browser-ext, telegram, sample-workspaces, registry).
|
|
|
|
Result: every server route this loop went out without a typecheck. The audit caught the one error (now fixed by importing the canonical `Importance`/`FrameSource` from `@waggle/core`). telegram.ts + browser-ext.ts were type-clean; registry.ts is reverted.
|
|
|
|
**Process fix (recommend):** add `tsc --noEmit` on `packages/server` to the per-change verification ritual, and ideally a CI gate. The CLAUDE.md "Verification Commands" block already lists `npx tsc --noEmit --project packages/agent/tsconfig.json` + `app/tsconfig.json` but **omits `packages/server`** — that gap is exactly what let this through.
|
|
|
|
## Net result of the loop, re-scored honestly
|
|
The two genuinely-new, non-redundant wins:
|
|
- **FR-1 browser extension** — no prior browser surface; real external trigger.
|
|
- **FR-2 Telegram outbound** — no prior outbound channel; real daily-driver hook.
|
|
|
|
Everything else was either redundant (FR-3, FR-5 backend), a 3rd surface for existing data (F2), a thin marketing card (F4), or an enhancement of an existing surface (F1 day-0).
|
|
|
|
**Honest revised score impact of the loop:** the score movements attributed to FR-3 (+0.2) should be removed (reverted). The FR-5 movements (+0.7) are *real for the user* (they do get seeded workspaces from the day-0 hook) but were delivered via a redundant mechanism — the value stands, the implementation should be consolidated onto templates. So the durable, non-redundant gains are FR-1 + FR-2 + the F2/F4/day-0 polish ≈ baseline 5.0 → ~6.5, not 7.9. The 7.9 figure double-counted a redundant registry and a parallel-implemented FR-5.
|