Decision Log
Significant business decisions, their rationale, and timestamps. Reference this when asking "why do we do it this way?"
Last updated: Mar 4, 2026
Why Document Decisions?
Storage Valet has gone through 8+ iterations. Without a decision log, we risk revisiting settled questions or forgetting hard-won lessons. This log captures the what, when, and why for key decisions.
For AI assistants: Before proposing changes that contradict documented decisions, reference this log and acknowledge the existing rationale.
Jump to Category
๐ฐ Pricing Decisions
14-Day Complimentary Trial (Replaced Setup Fee)
Decision: Remove the $99 setup fee entirely and replace with a 14-day complimentary trial for all new subscriptions.
Core problem: Customers were being asked to pay a significant upfront amount ($99 + $299) for a service they couldn't immediately experience. Our 48-hour lead time means even in the best case โ inventory already created, first available slot booked โ customers wait at least two days before their first pickup. In practice, that slot may not be available, extending the wait further.
Rationale: The trial solves this friction. Customers can register, explore the portal, create their digital inventory, and schedule their first pickup โ all before billing begins. By the time they're charged, they've already experienced the service. Simpler to explain ("try free for 14 days") than justify an upfront fee for delayed service.
Reversibility: We may revisit the setup fee later once we have established trust and reviews. This decision is for the immediate future, not permanent.
Previous approach: $99 one-time setup fee charged at signup.
Impact: All new customers start with trial. Existing promo codes like SETUPFREE are now redundant but kept for backwards compatibility.
Single-Tier Pricing at $299/month
Decision: One pricing tier only โ $299/month. No "basic" or "premium" tiers.
Rationale: Simplicity. Multi-tier pricing creates decision paralysis and support complexity. $299 is premium enough to filter for serious customers while accessible enough for target market (urban professionals).
Considered alternatives: $199/$299/$399 tiers with different storage limits. Rejected due to operational complexity.
๐ป Technology Decisions
Supabase as Core Backend
Decision: Use Supabase (PostgreSQL + Auth + Edge Functions) as the single backend platform.
Rationale: After 8 failed attempts with Airtable/Zapier/no-code tools, we needed a real database. Supabase provides PostgreSQL, authentication, and serverless functions in one platform. Direct SQL access enables complex queries without middleware.
Previous approaches: Airtable, Notion databases, custom Zapier workflows โ all hit scalability or complexity walls.
Landing Page on Vercel (Not Webflow/Framer)
Decision: Host marketing site (sv-website) on Vercel as static HTML.
Rationale: Faster load times, free hosting, same deployment workflow as portal. Webflow/Framer added complexity without proportional benefit.
Note: AI systems frequently mis-reference this as "Webflow" or "Framer" โ INCORRECT. Always Vercel.
SV-Wiki as Static HTML
Decision: Build internal wiki as static HTML files deployed to Vercel, not a CMS or Notion.
Rationale: AI assistants can read HTML files directly from local filesystem. Git provides versioning. No vendor lock-in. Claude Code can edit files directly.
Trade-off: Manual editing required (no WYSIWYG), but acceptable for technical founder + AI workflow.
sv-website Code Quality Review & Vercel Analytics Fix
Decision: Multi-agent review (Claude Opus 4.6 + OpenAI Codex 5.3) audited all 8 sv-website HTML files. 7 code quality fixes applied, plus a runtime analytics bug discovered and fixed by Codex during verification.
Changes:
- Replaced
favicon.pngreferences withfavicon.svg(signup pages) - Fixed "265 days" typo to "365 days" (partnerships page)
- Converted all
.htmlhrefs to clean URLs (leveraging VercelcleanUrls: true) - Removed dead social media links (placeholder
href="#"in footers) - Added
requestAnimationFramescroll throttling +passive: trueon 5 pages - Added
for/idlabel associations andautocompleteattributes to signup form - Fixed Vercel analytics API:
window.va?.track()(npm-only) replaced withwindow.va?.('event', {name, data})(script-tag API)
Rationale: The analytics bug caused TypeError on every scroll-depth event in production. Root cause: window.va is a function (not an object) when loaded via script tag. window.va?.track evaluates to undefined because the function has no .track property, causing the call to throw.
Platform Operations Toolkit (sv-audit / sv-hygiene / sv-health / sv-check)
Decision: Introduce a tiered platform operations toolkit consisting of four commands that provide session integrity verification, repository hygiene, infrastructure observability, and unified platform checks.
Commands:
sv-auditโ Session integrity and repository hygiene detection. Checks git status across all 6 repos, detects stale PRs (>7 days), orphan remote branches (>30 days), secrets tripwire, root directory policing, and 1Password socket cleanup. Non-destructive.sv-hygieneโ Safe cleanup of remote branches already merged into main. Never deletes unmerged work. Intended for weekly maintenance or after audit flags stale branches.sv-healthโ Platform observability: Supabase migration drift, Vercel deployment failures, GitHub Actions failures, and Stripe webhook processing backlog.sv-checkโ Unified platform verification. Runs audit, auto-triggers hygiene cleanup if audit reports ATTENTION, then runs health checks. Single command for full platform status.
Design principles:
- Audits remain non-destructive (read-only, except 1Password socket cleanup)
- Cleanup actions are explicit and separate (
sv-hygieneis opt-in, not automatic) - Scripts reference canonical git-tracked paths (
~/code/sv-docs/scripts/) rather than symlinks - The system provides both session-start observability (bootstrap snippet flags stale PRs) and session-end observability (audit script)
Rationale: PR #11 in sv-portal went undetected for 35 days because the audit script only checked local git state, never GitHub. This toolkit closes that gap and adds infrastructure health monitoring. The tiered design (detect, clean, verify) prevents accidental destructive actions while enabling comprehensive platform oversight.
Canonical locations: All scripts live in ~/code/sv-docs/scripts/ (git-tracked). Shell shortcuts defined in ~/.zshrc. A convenience symlink at ~/code/sv-final-audit.sh exists for manual use but is not a dependency for any script.
โ๏ธ Operations Decisions
48-Hour Lead Time for Pickups
Decision: Standard lead time for pickup scheduling is 48 hours.
Rationale: Balances customer convenience with operational reality. Zach (as solo operator) needs buffer to coordinate vehicle, route efficiently, and handle day-of issues. Rush service (24hr) may be added later at premium.
Customer Self-Catalogs Items
Decision: Customers photograph and catalog their own items via the portal. We do NOT catalog for them.
Rationale: Scalability. If SV staff had to photograph/catalog every item, it would add significant time per pickup. Customer knows their items best. Enables "search your storage" feature.
Common misconception: AI assistants often describe SV as "we photograph and catalog your items" โ INCORRECT.
๐จ Brand Decisions
Brand v2.0 Color Palette
Decision: Primary colors are Deep Teal (#213C47) and Berry (#A14567). Accent is Stormy Teal (#0E6F6A). Updated Feb 2026: Berry replaced Action Brown (#6B4E3D) as primary CTA. Rose Taupe (#AC928A) and Rosey Tint (#E8DAD6) added. Cool Steel and Mist Blue removed.
Rationale: Premium, trustworthy feel. Berry for CTAs creates warmth and confidence without aggressive red/orange. Teal provides professional anchor.
See: Brand Guide for full specification.
๐ Business Model Decisions
Hudson County as Launch Market
Decision: Launch in Hudson County, NJ and the surrounding area (13 ZIP codes across 7 municipalities) before expanding.
Rationale: Dense urban market with high-rise apartments (limited storage). Affluent professionals willing to pay for convenience. Geographically compact for efficient routing.
ZIP codes: 07020, 07030, 07047, 07086, 07087, 07093, 07302, 07304, 07305, 07306, 07307, 07310, 07311
$2,000 Included Insurance Coverage
Decision: Every subscription includes $2,000 of item protection at no additional cost. (Reduced from $3,000 pre-launch, Jan 2026.)
Rationale: Trust signal. Customers storing valuables need confidence. $2,000 covers most household items without complex appraisal process. Higher coverage tiers may be offered later as add-on.
๐ Security Decisions
1Password CLI as Sole Secrets Source of Truth
Decision: All production secrets are stored in the 1Password "Storage Valet" vault and accessed exclusively via op read / op run. No secrets in ~/.zshrc, environment files, MCP configs, or inline commands.
Rationale: Audit on Feb 6, 2026 discovered that Claude Code's permission caching had persisted plaintext credentials (Stripe secret key, DB password, Calendly PAT, service role key, webhook secret) in ~/.claude/settings.json and settings.local.json. A subsequent audit (Feb 27, 2026) found the Calendly PAT stored in plaintext in ~/.claude.json MCP server config; remediated with an op read-backed launcher script. Root cause: approving commands with inline secrets caused the literal command string to be cached, and MCP env blocks required literal values. Migration to op read and launcher scripts eliminates these accumulation paths entirely.
Previous approach: Secrets exported as plaintext in ~/.zshrc and passed inline in CLI commands.
Defer Supabase Service Role Key Rotation
Decision: Do not rotate the Supabase service role key / JWT signing secret as part of the Feb 2026 credential rotation. Schedule as a separate planned operation with a maintenance window.
Rationale: Rotating the JWT signing secret invalidates all 15 edge functions simultaneously until they pick up the new key. The exposure was local-only (plaintext in a settings file on Zach's machine, now cleaned). The risk of production downtime outweighs the risk of a local-only exposure that has been remediated.
Revisit: If evidence of exfiltration emerges, rotate immediately with coordinated edge function redeployment.
Track A Stabilization: RLS Hardening & Service Area Unification
Decision: Multi-agent code review (Claude Opus 4.6 + OpenAI Codex 5.3) identified and fixed 6 issues across sv-db, sv-edge, and sv-portal. Two P0 security gaps were closed: an RLS regression on actions UPDATE/DELETE (status constraint dropped in migration 0008) and a cross-tenant data leak on booking_events (orphan rows with action_id IS NULL visible to all authenticated users).
Rationale: The RLS regression allowed customers to mutate non-pending actions at the database level. The booking_events leak exposed metadata across tenants. Both were unintentional regressions introduced when adding staff override logic. Additionally, service area ZIP codes were fragmented across 4 sources with 3 different counts; these were unified to a single DB-backed function (is_valid_zip_code) and the portal's SUPPORTED_ZIP_CODES constant.
Scope: 4 DB migrations (20260211000001โ04), stripe-webhook redeployed with DB-backed ZIP validation, portal booking mutations routed through edge functions, profile cache keys unified.
Claude Code Bypass Permissions with Secret-Free Workflow
Decision: Keep Claude Code in bypassPermissions mode (full autonomy, no approval prompts). This is safe because secrets never appear in commands โ they flow through op read โ so there is nothing for the permission cache to capture.
Rationale: Approval prompts did not prevent the original secret exposure; they caused it by caching approved command strings verbatim. With secrets removed from commands, bypass mode is purely a productivity toggle. An automated tripwire in sv-final-audit.sh detects regressions.
Previous approach: Granular permission rules that accumulated to 500+ entries with embedded credentials.
Terms of Service & Privacy Policy Accuracy Review
Decision: Deep accuracy review of customer-facing legal documents (Terms of Service, Privacy Policy) against current business facts and industry benchmarks. Four substantive changes applied.
Changes:
- Declared value threshold: $10,000 โ $2,000. Aligned with included insurance coverage amount โ no reason to require disclosure at 5x the coverage limit.
- Post-termination retrieval: 30 โ 45 days. Matches Clutter (market leader). NJ lien law (N.J.S.A. 2A:44-187) requires ~75-90 days minimum statutory process, so 45 days is well within legal bounds while encouraging timely retrieval.
- Photo deletion: Hard 90-day calendar deadline โ purpose-based deletion ("reasonable period" with exceptions for claims, disputes, regulatory obligations). Avoids premature evidence destruction. Follows FTC enforcement patterns (Everalbum).
- Signup cancel page: "saved your spot" โ "pick up right where you left off." Previous copy was technically false (Stripe saves nothing on cancel). New copy follows Stripe's own sample pattern.
Rationale: Industry research across valet competitors (Clutter, STASH), traditional self-storage (CubeSmart, Public Storage, Extra Space), NJ statutes, and FTC enforcement history. Goal: match market-standard terms while avoiding unnecessary legal exposure.
๐ How to Log a Decision
When making a significant business decision:
- Add a new
.decision-cardto the appropriate category - Include: Decision ID, Date, Title, Decision statement, Rationale, Previous approach (if applicable)
- Mark status as
active,superseded, orunder-review
When to log: Pricing changes, technology choices, operational policies, anything you might need to explain to an investor or new hire.