THE PLAN

JoyOS Lab v1.2

A marketplace where members learn, experiment, and build, then sell what they create.

Recently shipped to staging-next · synced 2026-09-23 (delta through 66bb8334, RUN revamp-525005-1790203299)

  • Staging reads its own database again, and a failed read says so (delta through 74d4bccb): a request reaching the staging server by its local address was paired with the production database host, so the feature-switch list came back empty even though staging holds 34 switches. The staging database now always resolves to its own server, and when a database read is refused the endpoint answers “temporarily unavailable” instead of pretending there is nothing there (74d4bccb).
  • Pages wait for the feature switches before deciding what to show (delta through f66f63b0): until the server has answered which features are on, a page now shows a small loading spinner (4613d373) instead of briefly flashing “Coming Soon” or the wrong content. The app remembers when that answer has arrived, and a failed answer counts as “everything off” (6ccbe189). The onboarding path now waits for the answer before redirecting anyone (56e236c6). Covered pages include Wins, Wall, Tasks, Speakers, Profile, Pairs, Member Intros, Library, Joy Circles, EPIC dashboard, Announcements, Explorer communities and the app frame (a2d58cfe, bf7e1104, 26af137d, 32fe0c6b and 11 more), with new tests for the frame, the communities page and the onboarding redirect (c738debc, f66f63b0). These are automatic in-progress snapshots on the staging machine: not yet reviewed or pushed.
  • The database can be published again now that every table is private (delta through e85b3cee): once every table went private, the database engine refused the old per-row visibility rules, so the module could not be published anywhere. The 67 per-row rules were removed (d8edf298, e85b3cee). What a visitor may see is now decided only by the app’s endpoints, not by rules inside the database.
  • Sign-in looks up the member through the Users model (delta through ac155570): the sign-in check no longer writes its own database query; it asks the Users model for the member and their tier, so the "only a data model may ask the database" rule now passes for sign-in too. Tests now pass the database connection in the same way production does (ac155570).
  • Only a data model may ask the database for rows (delta through 92a4ae20): a tightening of the rule that keeps pages and endpoints from reaching the database directly. The guard now also covers the middleware and contract layers, and fails the build if anything above the model layer calls the base model’s query functions itself (92a4ae20).
  • Feature flags and the studio gate now come from the server, not from the browser's guess (delta through 908bf890): which features are switched on is now answered by an endpoint (9fc1e777) that can reach the database directly (8b84093a), with the database connection owned by the model layer rather than scattered around (6842c8b0), and the flags are fetched the first time something asks for them rather than the moment the page loads (908bf890, 71cf1210). Separately, the Creator Studio gate now trusts the membership tier the server resolved instead of one the browser supplied (856734c4), and the guard that keeps pages off the database was fixed so it actually tests the rule it claims to (215f8b7b). Smaller passes touched money formatting, a creator's own offers list, and the offer views (cb10da7c, e113d6cb, 3cfc17f9, 6c4f8f82, 6132441b).
  • Every database table is now private, and strangers read through a real API instead (delta through 48a87a26): the biggest security change in this delta. All the platform's tables were closed off at the database level (93b9de18), so nothing reaches the data except through the app's own endpoints — and a signed-out visitor now reads public offers through one of those endpoints rather than by talking to the database. Along the way the client stopped subscribing to twenty-five tables nothing was reading (b70defde), two real leaks were closed — a member's contact details were arriving in every browser on the waitlist surface (4e2c9a8b) and buyer identity was leaking while the server trusted an email the browser claimed (01f8e51a) — and a creator can now only reuse images from offers they actually own (f7ca4201).
  • An offer is now a real thing the code names, not a pile of table rows (delta through 0663d85d): structural, but it is what makes everything above possible. An offer now has a model (e7a76dce), a defined answer to what a creator must give us to name or save one (93b4d31c), two separate views — the offer as its creator sees it and as a stranger may (70efc0df) — and named actions for writing, reading, changing and taking one down (288bd378), plus listing the offers a creator made (b944923d). Who may create and own an offer is stated once, in one place (2b528f5b), and ownership is now proven by looking the offer up rather than believing the request (0663d85d, 2807d9e2).
  • Pages read their data from endpoints now, not from the database (delta through 14abc309): the separation members will eventually feel as a faster, more reliable app. The Learning Lab browse page (2f18461d), the member-facing experiments catalog (eb4b5d58), the marketplace home card's offer count (2c68e14f), a creator's Stripe Connect standing (8814e31f), the orders list (f6d45d15) and release notes (d2234d37, 14abc309) all moved onto endpoints. A guard now blocks any component that tries to reach the database directly (4b655e3a).
  • Tier prices come from the database, and an unpriced tier cannot be sold (delta through d53ee104): a real commerce safeguard. The price a member is charged is read from the stored tier row rather than from a number written into the code (06fe4002), and a guard makes it a build failure for a price to come from anywhere else (d53ee104). The Creator Studio gate and the founding-member badge became enforced rules in the same pass (23361bd4).
  • The endpoints the site actually deploys are generated, not hand-written (delta through c4b64c67): removes a whole class of "it worked locally" failure. The deployable functions directory is now produced from the declared endpoints (c4b64c67), the generated wrappers ship as JavaScript so the hosting platform can run them (f882d6b7), each request gets its own database connection instead of sharing one (d2542f17), and who is calling is taken from the request itself rather than trusted from the browser (36504075). A new test makes a real HTTP request to a deployed route to prove it (3bf0e9c0).
  • Every page now lives in one predictable place (delta through ac791e35): a large, member-invisible tidy-up. The Creator Studio pages — offers, orders, communities, bundles, onboarding, gratitude wall, the studio home — each folded into their own route file (e000e985, e5605eb4, 8446b727, 5567bad2, 3fcdb5ee, 53dc0e04, 45779375), as did the Learning Lab pages (f9015a54, e33c7c11, 2e7dbb13) and the whole admin section including its experiments, tour and storage editors (ac791e35, f2767996, 12c2a6ae, 2bef1a26). The duplicate offer-create route was dropped and one offer builder now serves both the new and edit routes (3ca6cf71, 421c0c85). An unreachable marketplace admin listings page was deleted outright (52ac1d4b).
  • The architecture guards became blocking, and the test suite was cut back to what is real (delta through 4a19a8a9): the guards now have teeth in the build (4fa51a2c), every guard blocks and together they cover the whole old architecture (5e21e360), tests all moved out of the source tree into tests/ (82e2570e, and a run of follow-up moves), and only the new architecture suite runs (4a19a8a9). The short-lived releases endpoint was removed again once its readers moved (48a87a26).
  • Routes now declare their own rules in one place (delta through e2c8dba8): structural work, no member-visible change. Until now a page's access rules were scattered — some in hand-written route metadata, some in separate guard checks. A route can now declare in a single line who must be signed in to reach it and which permission check applies (c7c054b0), with the sign-in requirement and the Creator Studio permission check rewritten to sit on that shared footing (ff4aa5e1, 98dfe276). One guard now runs a page's requirements and then its permission check in order (f7780bb1), installed after the onboarding steps so a member still finishes onboarding first (d6fc3e58). The creator payouts page is the first converted to the new form (a01bece7), and a new test makes it impossible for a page to declare its rules both ways at once (7bf13cce, e2c8dba8). Work-in-progress checkpoints on the payouts page, the Studio placeholder test and the migration notes also landed (a419fd5d, 12d2cf88, 2e250ad3, ff3ef0b9).
  • Views migration continues: Creator Studio pages settle into the new layer (delta through eb8aeb8f): structural work in progress, no member-visible change. The Creator Studio payouts page moved into its Views home and the "coming soon" placeholder it shared with the other unbuilt Studio pages was reclassified as a section component rather than a page of its own (eb8aeb8f). A new guard test that each view holds its own page, and the running notes tracking which pages have moved so far, were both checkpointed while the migration is mid-flight (b26189ac, 3ad7e502, b1f196a5).
  • Page files moved into the new Views layer, and the shared request pipeline filled out (delta through 37dab5a5): structural work, no member-visible change. All 32 route files that previously lived in src/pages now live in src/Views, the layer folder the architecture standard says routes belong in, with the build config updated to look there (37dab5a5). Every route the member uses — the landing and login pages, the learning lab and its discover/library/accelerator pages, the explorer and communities pages, the whole creator studio including offers, orders, payouts, onboarding and settings, and the terms and privacy pages — moved as a pure rename, so nothing about what any of them does changed. The request pipeline added alongside the layers also got its supporting pieces: the middleware, request and resource contracts it passes through were fleshed out and the pipeline itself reworked to match (86e8e4cc).
  • The hexagonal architecture layers are now real folders with guards that enforce them (delta through 2045347b): structural work, no member-visible change yet. The seven layer folders a future page will be built out of — Actions, Contracts, Database, Functions, Models, Policies, Views — now exist in the codebase, and five automated guards keep them honest (9f29b74a, merged 478a964d): one restricts which layers may import which, one forbids any layer folder that is not on the approved list, two require every Action and Policy to be a single exported function sitting next to its own test, and one requires each domain type in Models to say in one line what it is. A shared request pipeline was added alongside them with its own test suite (b51ff4f7, 2045347b), and the project's own working rules were extended to say that the code in these layers has to explain itself through naming rather than through comments (27816213).
  • Creator Studio test suites consolidated into one place (delta through 0ff00885): test housekeeping only, no product code and nothing a member sees. The My Offers, Offer Builder, Communities, Offer Wizard and remaining single-page Studio tests were moved and merged into the shared tests/ directory 86ece549 92382b66 8b4c2a66, and the end-to-end setup-card note now points at the merged file rather than the old path 0ff00885.
  • Repo-root cleanup: build config and test setup moved into real homes, stray artifacts dropped (delta through 46069279): housekeeping only, no product code and nothing a member sees. Every Vite helper that had accumulated at the repository root moved into config/ — the filesystem-deny, manual-chunks, nested-checkout-deny, test, version and watch-whitelist plugins (5ca2a32a, 0ecc9854, 5d5cda68, 01842d8c, 4f242de8, feca59f9, consolidated by 3d5fedd7). The five test-setup shims — global isolation, the jsdom canvas/fetch stub, the fetch-once reset, and the drag-event and confirm polyfills — moved into tests/setup/ (6d51d51e, 4b432e56, 18b26754, 18d9dd07, 6a7590f8). The marketplace download scope note, which four tests read at runtime, moved out of a dotfile at the root into docs/marketplace-download-scope.md with its readers updated to follow (5a895cf7, 46069279). Leftover verification evidence, scratch directories and files under the retired .worktrees serving path were untracked and removed (cceac817, 847b1325), and fourteen checkpoint and notes documents were archived into .planning/finished with two restored after live tests proved they were still in use (ce5dce9c–5a482dec, 96396f87, 671b1625). Plan summaries for phases 40-01 and 40-02 also landed (057bbae7, 9e1467ba).
  • Phase 40 planned: the architecture guards get proven, gated in CI, and the remaining legacy debt scoped (delta through 3a5e0a3a): planning-only delta, no product code. Three plans written for the next phase of the hexagonal-architecture work landed in the previous sync. 40-01 proves the guards can actually fail — the empty-tree assertions that currently pass vacuously are de-brittled so a real violation is what turns them red (2365c5ae). 40-02 promotes the guards into a blocking CI job with a seeded-violation proof run and a legible failure message, and carries its own threat model and acceptance criteria (7f3d83d0, 84ed95f8). 40-03 scopes the remaining legacy debt and closes both open architecture forks (b9ed8dfc), moved to wave 3 so it stops overlapping 40-02 on the ownership manifest (ba394c36, 29fed1d0). ROADMAP finalized with the three-plan list (3a5e0a3a).
  • Hexagonal layer scaffold landed for real, plus five build-failing architecture guards (delta through 5cdd2ef0): the architecture standard adopted in the previous sync is now actually on the branch. Seven layer folders under src/ — Actions/, Contracts/, Database/, Functions/, Models/, Policies/, Views/ — are scaffolded and held in place by five guard tests in tests/guards/: allowed-layers (only declared layers may exist), hex-layer-import-restriction (no imports across a forbidden boundary), actions-policies-colocated-test and actions-policies-single-export (every action and policy has a co-located test and exactly one export), and models-doc-comment (every model carries a doc comment). eslint.config.js gains the matching import-restriction rules and vite.config.ts wires tests/ into vitest so the guards run. Net effect across the delta: 16 files, ~1,790 insertions (8a81a824 tests first, 084917e0 implementation, d37984a6 final reapply, 5cdd2ef0 merge). The delta also contains sixteen revert/reapply commits of this same change — branch churn while the guards were stabilized, not sixteen separate pieces of work.
  • Hexagonal architecture standard adopted: layered src/ scaffold plus five build-failing guards (delta through 77895014): the codebase gains an explicit architecture standard and the structure to hold it. New top-level layers under src/ — Actions/, Contracts/, Database/, Functions/, Models/, Policies/, Views/ — are scaffolded and pinned by five new guard tests in tests/guards/: allowed-layers.guard.test.ts (only declared layers may exist), hex-layer-import-restriction.guard.test.ts (a layer cannot import across a forbidden boundary), actions-policies-colocated-test.guard.test.ts and actions-policies-single-export.guard.test.ts (every action/policy has a co-located test and exactly one export), and models-doc-comment.guard.test.ts (every model carries a doc comment). eslint.config.js, vite.config.ts and the package manifest updated to match. The standard itself and its running review log live in .planning/ARCHITECTURE-STANDARD.md and ARCHITECTURE-STANDARD-REVIEW-LOG.md, iterated across the 200-commit delta.
  • Pre-push hook — pinned build container (d60ed27c, 7b15fe54, 4368f08f): the pre-push hook now uses a pinned Docker build container instead of relying on ambient tooling. Three files touched: .githooks/pre-push (modified), tests/githooks/pre-push.build-command.test.sh and tests/githooks/pre-push.wiring.guard.test.sh (new, 189 insertions total) — verifying the gate fires correctly on drift.
  • My Offers list — identity-mismatch fix (f9ef3085, dc6ac9d1, 81c8208a, 8e447d98, 55935276): the My Offers list was not showing offers saved via the API because the caller identity did not match the stored row identity. Fixed by aligning identity resolution in useImpersonation.ts (removed 15 lines) and MyOffersView.vue across two iterations, with dedicated test files.
  • FeatureFlags + MarketplacePurchases models (aa75274f, 322fa918, dcb43c9a): new server-side model layers functions/lib/models/FeatureFlags.js (25 lines) and functions/lib/models/MarketplacePurchases.js (29 lines) added, plus dedicated test files for both.
  • CI/infra: staging deploy pipeline unblocked (delta through 84247373, #974, #962, #871): three small fixes landed on top of the guest-flag fix above. A missing migration for the new attention_signals table had been failing every Staging Publish & Deploy run since it merged — declared and closed (84247373, #974). The creator-studio router regression guard was updated to match the 2026-08-28 file-based routing migration instead of asserting stale literal path/component text (50fc3adb, #962). REQ-052 recorded: staging merge conflicts are resolved, not blocked, plus a fix ensuring API failures caught by a view still auto-report (6122b19c, #871).
  • Guest-facing feature flags were unreadable by guests, so every guest-gated flag read OFF forever (ba2eed1a, #831): the anonymous connect path never subscribed to feature_flags, so a signed-out visitor evaluated every guest-gated flag as closed regardless of its database row. Fix: a narrow anonGuestFlagsQuery() subscribes only the GUEST_READABLE_FLAG_KEYS allowlist on the anonymous connection (new standalone subscription builder in useSpacetime.ts, allowlist in tableWaves.ts), with a new guest-flag-readability.guard.test.ts (185 lines) proving a guest can actually read a guest-gated flag and the exhaustive anon-wave expectations updated to pin the scoped allowlist. Five drifted agent docs reviewed and re-stamped; five in-flight PR paths recorded as known-absent rather than stale claims.
  • Cross-feature attention-breadcrumb indicator system (57d8bd7a, #812): a general registry any feature can raise a signal into — new attention_signals table plus raise_attention_signal/clear_attention_signal reducers — replaces per-feature red-dot patterns. A nav item shows a glowing dot when it, or any nested area beneath it, has an active signal; the dot clears only when the member actually lands on that area (never on click-through or a timer), and the underlying signal clears only when the owning feature resolves the real thing. Area paths derive from the route structure via pathToAreaPath, so no per-feature nav mapping is hardcoded. Gated behind the new attention_breadcrumbs flag at min_tier=admin (closed until granted). First intended consumer is commerce’s unfulfilled physical-goods orders signal (interface documented, not yet wired). Same PR lands the live-chat-spaces owner agent onto staging-next (it was invisible to dispatch) and creates the cohorts owner agent recording Ethan’s model verbatim (a subscription community containing offers, NOT an offer type) and flagging the live “Live Cohort” offer-picker option as an open contradiction. Bindings regenerated.
  • update_listing no longer wipes inventory, URL, file key or featured status on blank input; ai_draft_json gets a real column (20ebb8e3, #929): the reducer treated an omitted or blank field as an instruction to clear it, so a routine edit could silently erase a listing’s inventory_count, url, file_key and featured flag. Pure merge logic extracted into reducer-logic.marketplace.ts and pinned by reducer-logic.update-listing-data-loss.test.ts. Migration 0020 appends ai_draft_json to marketplace_listings additively (with up/down tests); bindings and schema snapshot regenerated; the joyos-labs-stdb owner doc corrected and manifest re-stamped. Offer-builder-affecting: the builder now has a durable home for the AI draft it already produces.
  • Founding-member badge replaces the founder tier at every decision point (1170d27e, #803): a new isFoundingMemberBadge accessor and shared useFoundingMemberBadge composable now drive the Founding Hub gate, the Profile banner, the AppShell feedback-visibility check, the Creator Studio Founding Creator banner, the joy_circles override in useFeatureFlags.ts, and the founding-call reminder email eligibility (which now joins user_badges instead of filtering on tier='founder'). Per arch-map decision founder-tier-to-badge-migration-scope--chosen; a commerce handoff doc specs the badge-conditional 80/20 vs 90/10 revenue split for a later change; founding-hub doctrine corrected and its agent-currency re-stamped for the 11 reviewed pairs only.
  • Creator payouts cannot actually be paid out — REQ-21 disbursement gap recorded, nothing built (d8be84d2, #923): commerce verified creator_payouts can accrue but no code path moves a row to paid_out (three write sites, zero Stripe transfer calls, schema ready and unused); anonymous probe with negative controls shows zero rows on both production and staging, so nobody is currently owed. Eleven new registry entries (REQ-065..075) cover disbursement existence, who initiates, onboarding precondition, refund-window hold, minimum threshold, idempotency, failure visibility, the PayoutsView “coming soon” honesty gap, 1099-K scope, launch posture (flagged as contradicting the standing “payouts work. period.” ruling, owed to decisions.yaml), and a creator_payouts public:true security finding routed to its owners. Tier 3 (money), blocks on Ethan.
  • Per-purchase physical-goods fulfillment checklist (73029d67, #811): new purchase_fulfillment_checklist table (creator-scoped RLS), toggle_fulfillment_checklist_item reducer, automatic seeding on purchases of a listing with kind === 'physical', and a physical_fulfillment_checklist feature flag (closed by default, catalyst ceiling). GET/POST /api/marketplace/fulfillment-checklist exposes pendingCount as the needs-attention signal; the Orders page renders a Needs Fulfillment panel with per-item checkboxes when the flag is on. Also ships a Stripe Connect payout/tax research plan for the co-founder call and a merge fix hoisting the listing/creator lookup above the payout-accrual try block so both paths can see it. Bindings and schema snapshot regenerated.
  • Gratitude attribution now fails CLOSED (18a6ec21, #883, REQ-052): users.gratitude_public_attribution_consent defaulted to true, submit_gratitude read it as !== false (so missing/null/undefined all counted as consent), and register_user wrote an explicit true into every new account. All three fail-open sites fixed: hasGratitudeAttributionConsent() is the single strict === true read path, the column defaults to false, registration writes false, and refusal is proven across missing, null, undefined and explicit false. A structural guard enumerates every consent column against every fail-open read shape and blocks new unchecked consent columns; it surfaced two more ?? true reads (ai_personalization_consent, community_aggregation_consent) carried as explicit reasoned exemptions pending Ethan’s decision. Existing rows deliberately not remediated. 258 tests green, guard sabotage-proven both ways.
  • CI: the daily KV-migration purge job had failed every day since at least 2026-08-10 (2901087b, #972): both purge steps ran with no spacetime login, so maincloud rejected the runner’s stale cached credential with 401 Invalid token. Now mirrors the spacetime login --token "$SPACETIME_TOKEN" pattern deploy.yml and staging.yml already use.
  • Expired sessions now always redirect to login, enforced at router level (70942a70, #946): Ethan: "this app should always redirect to login if we ever get an expired login." Measured live twice on staging: the redirect lived in AppShell.vue, a lazy async chunk, so the splash flipped sessionExpired before AppShell's watcher existed and nobody acted on it. New src/router/sessionExpiryRedirect.ts installs the listener at module scope with immediate: true, preserves the return path via ?redirect=, never loops on /login, hard-navigates if the push rejects, and is the single consumer of the signal. The last two swallowed 401s (AdminView admin-config, WeekBlock space-audit chat) now route through the session authority instead of rendering an empty panel or telling the member to refresh.
  • Socket-only signals no longer produce auth errors (c95a61df, #931): the SpacetimeDB socket must have no say in whether a member is signed in. The no-user-row grace timer, the immediate identity-mismatch check, and the stranded-identity watcher in useSpacetime.ts now log a diagnostic warning only; a missing users row over the socket is a data-loading condition, never an auth conclusion. Five existing test files flipped to assert authError stays null on these paths. The cookie/API auth layer the mandate expects is not built in this change.
  • API response notifications: grouped toast layer plus flag decision (e738b7a4, #935): adds vue-sonner and useApiResponseToasts, a transient 2.5s success toast for mutating methods only (silent on GET) with repeated failures collapsing into one id-keyed toast carrying an occurrence count. isApiResponseNotificationsEnabled is the pure decision function for the api_response_notifications flag: admins always on, members off unless explicitly opted in, and the single point any server-side check must call so enforcement never trusts a client boolean.
  • Modal backdrop dismiss keys off mousedown, not click (cfc17ac3, #930): a text-selection drag that started inside a modal field and released over the backdrop fired a click targeted at the backdrop and closed the modal mid-typing (Ethan, live on staging: "click up handlers need to be changed to click down"). All 38 @click.self occurrences across 28 views switched to @mousedown.self, with a build-failing guard (modal-mousedown-dismiss.guard.test.ts) that refuses any new @click.self without a written allowlist reason, proven by sabotage both ways.
  • Explorer storefront gate is the API's answer, not an unreadable client flag (80ec7fae, #888): the endpoint served 13 real cards anonymously while /explorer showed "coming soon" to every signed-out visitor, because the view read explorer_marketplace_public over a SpacetimeDB connection that anonymous browsers never open on that route, and all 15 unit tests mocked the flag true. The gate is now the endpoint's own feature_disabled refusal, still enforced server-side; the view loses its last database read per the frontend/backend separation rule. The flag mock now defaults to false, a source tripwire blocks any return of isFlagEnabled(, and a new server_enforced gateStyle is registered for flags with no client read.
  • Storefront catalog now served by the public-items API, not table reads (d3124e91, #892): implements Ethan's 2026-08-23 rule ("private, no exceptions" / "the storefront needs to use an api call to load public items"). list-listings.js migrated onto the shared public-marketplace-catalog.js policy so /learning-lab and /explorer cannot disagree about what is public; the old "never 5xx, return [] on error" shape replaced with three distinguishable states; new usePublicCatalog client seam reads the catalog over HTTP instead of the marketplace tables and never blocks on the SpacetimeDB connection; card allowlist widened by exactly one field (slug). This is the "ship the API first" prerequisite for locking the three marketplace tables private.
  • Two new build-failing guards on data exposure and deploy targets (4ae000ca #879, 003ce579 #837): guest-reachable-table-reads.guard.test.ts fails the build if any guest-reachable Explorer/Communities route acquires a direct table read or PUBLIC_TABLES grows (364 lines); staging-db-target-parity.test.ts fails the build when CI declarations disagree about the staging vs production database target, with build-only steps in pr-tests.yml/merge-on-green.yml exempted (386 lines).
  • CI honesty fixes (328743be #891, 8628aac0 #921): the requirements-registry id-preservation guard was wired to no workflow and its vitest control compared the registry against itself, so a destructive merge passed CI — now a never-skipped pr-tests.yml step runs it against the PR base ref before build, with a discrimination control proving it fails on deletion (proven: REQ-052..059 flagged). The PR-CI watchdog no longer counts a completed startup_failure run (zero test signal, root-caused to the 2026-08-26 GitHub platform outage) as CI coverage.
  • Commerce payout split pinned by test (c165bc97, #922): new reducer-logic test asserts the actual creator payout amounts per tier — 80/20 standard, 90/10 founding (102 lines).
  • Welcome CTA label corrected (75600cdf, #911): the /welcome final-step button said "Start with Gratitude" but its real destination (per REQ-062, the /start gate having been archived 2026-08-25) is Creator Studio or the member's remembered lane home. Renamed to "Enter the Lab". The gratitude wall still has no composer — left as a separate open decision for Ethan.
  • Decision record: the 2026-07-07 global gratitude gate is now formally superseded (10aa4e8f, #909): locked decision Q8 ("all pages require gratitude first. global rule.") recorded in decision-log.yaml for the first time, then marked superseded by Ethan's 2026-08-25 instruction to archive the gate (evidence: 2026-08-07 Kristin call, 2026-08-25 decision D4). Two questions it raised are logged open, not settled: gratitude-requirement opt-in level (community vs cohort) and the creator-access-gate interface.
  • Doctrine: feature flags and the serving-worktree auto-stash landmine (e533da3d #896, cebbd88a #881): CLAUDE.md gains a verified account of how feature flags actually work (a flag is a database row; five surfaces must agree; two stale recipe steps corrected; three open items named, including feature_flags being public against the every-table-private rule). Separately records that the staging serving worktree silently stashes hand-edits via the tunnel watchdog's dev-up --force path, adds hard rule 6.12 to the agent doctrine, and pins it in doctrine-check.sh.
  • New Pages-Functions API layer: base Model, User and MarketplaceListings models, validation contract, error-mapping middleware (delta through fbcd12e6): the first slice of the frontend/database separation contract on the server side. Adds functions/lib/models/Model.js (CRUD over SpacetimeDB with auto-escaping and write-failure surfacing), User.js over the users table, MarketplaceListings.js with an ownership gate, a declarative validation contract (functions/lib/validation/contract.js), a shared request-context helper, an API exception hierarchy (errors.js) and a withApi middleware mapping those exceptions to HTTP statuses. save-offer-draft is the first endpoint rewritten onto the layer (159 lines changed, 112 removed). Tests: unit tests for every piece, a no-SQL-in-endpoints source-shape guard, and a live curl e2e of the deployed save endpoint — 1,623 lines across 17 files (2e78e8e7 tests, 552f750b impl, fbcd12e6 integrate; task 50ac1145).
  • Landing mockups: presumptuous copy removed, fabricated testimonials labeled illustrative (34d78133, #800): all three landing mockup directions (Feed, Exchange, Wall) no longer diagnose the reader ("most people have never felt it"); every story/testimonial card and showcase credit now carries a visible ILLUSTRATIVE badge plus a banner stating these are examples, not real members. Copy-only, dev-only mockup routes.
  • Serving-worktree protection hook (c2501d91, #788): a git reference-transaction hook now blocks ref mutations (checkout/reset/branch moves) inside the shared staging-next serving worktree, with a test suite proving the block.
  • E2E evidence gate for production promotion (8d97bd0f, #786): adds the receipt-format spec, a deny-by-default gate script and sabotage tests so a promotion cannot proceed without real e2e evidence. Bundled in the same PR: a btree index on feedback_message.feedback_id (migration 0013) fixing the admin subscription wave that had been silently dark since the 2026-08-01 RLS fix, regenerated bindings, and doc cleanup retiring per-PR-preview language.
  • My Offers list now shows offers saved under any of the creator's identities (delta through 0b8ce7ca): a creator whose account has more than one SpacetimeDB identity (e.g. after a Google token refresh) could save an offer via the API and not see it in Creator Studio → My Offers, because the list filtered on the current identity only. Fix: new GET /api/marketplace/my-identities endpoint (auth-gated, returns every identity on the caller's users rows), a useMyIdentities composable fetched once per session, and MyOffersView.vue now matches the union of known identities. Tests: endpoint tests (success / missing token / garbage token), save-offer-draft test asserting the email-derived creator identity on the saved row, multi-identity component tests, a guard test pinning set-membership filtering, and a cross-identity Playwright e2e with screenshots — 690 lines across 8 files (cbf84393 tests, f1431d5a impl, 0b8ce7ca integrate).
  • Offer Builder: auth-session refactor for readability (delta through c4baf08a): follow-on cleanup for task bb726cf9 making the session-handling code self-explanatory — APP_TOKEN_KEY exported as a named constant, a hasStoredSession() helper added, and multi-paragraph comments cut down to single constraint lines (c4baf08a). No behaviour change.
  • Offer-save bug run: root cause identified, fix verified, e2e regression added (delta through d6874075): root cause of the Creator Studio offer-save bug identified and the fix verified in tests (a7cb236a, d6874075), plus a new e2e regression spec test-offer-save.spec.js (22db0766).
  • Creator Studio offer route renamed to /creator-studio/offers/new: the standalone offer-builder entry point moved from /creator-studio/new to /creator-studio/offers/new across the router, typed-router, generated pages, and every view that linked to the old path — 17 autocommit commits (4332bbdd, 59dfab2f, a1742308, 7fa21917, 902806f4, 44a8b7a1, 73c02fde, a49b82c9, d49f1b3f, 2fee254e, e8015096, 00376705, 49eaca95, 57ae5ce4, 8285ac83, 976d1478, 7d1d5ee0). Plus two noise commits skipped: b5e52787 (stray PostScript artifacts os/sys) and a675924f (local verify-results JSON output, never pushed to origin). The write-back commit from the blocked prior attempt also included: 1e3ac848 (d1c7e637 already on live page).
  • E2E test suite — offer-builder + studio-create-listing roundtrip iterations: offer-builder-write-e2e.test.ts refactored with 94 insertions net-positive (94 in, 255 out — large structural rewrite, not deletion; cbb179cd); studio-create-listing-roundtrip.spec.ts iterated twice to fix the write-flow assertion path (540e81a6, 77c478db); and playwright-staging-auth.mjs updated to reflect current auth flow (d1c7e637).
  • Dev-auth tooling — debug-reducer + stdb-client + dev-magic-link iteration: a new debug-reducer.mjs + debug-reducer.test.mjs pair added to functions/api/ (5967a3cf); stdb-client.js edited twice for auth-path consistency (f4b5f2df, dadadce0); and dev-magic-link.js went through a refactor cycle (first pass: 39 deletions, 10 insertions; second pass: 39 insertions, 10 deletions — net structural improvement; adb32bd6, b954f7a3).
  • Offer Builder E2E + backend — write-flow + save-draft cycle tested: the offer-builder-write-e2e.test.ts Playwright spec iterated through 9 cycles to cover the full write-to-catalog path (691a5b59, cedc6b31, 32c7de82, c50641ba, f3f364a6, c9f294a0, 808650b2, ddcdecd0, 631e4d9d); the save-offer-draft.js endpoint edited twice to fix the write path (d32ba3fe, af1e621a); and the debug-reducer.test.mjs test added to the functions directory (b2a000dd).
  • Offer-builder-fix plan — Gate 2 architecture doc drafted: a new docs/plans/offer-builder-fix/02-architecture.md committed as the Gate 2 draft, laying out the API-first architecture for the create-offer path — code already written and E2E-tested per the plan; the remaining work is deploy + live E2E verification (5a33998b). Plan lives in docs/ only.
  • Planning doc sync: .planning/index.html updated (1c7ee668).
  • revamp-sync delta through fc7145b3 — 75 commits not previously summarized: the local staging-next branch had run ahead of the most recent write-back; this run captured the missing 75 product commits into 5 themed clusters below (ff05518c–4284738c).
  • STDB auth model — JWT signing retired, SPACETIME_TOKEN is the only auth path: functions/lib/stdb-client.js simplified to authenticate via the SPACETIME_TOKEN env var alone (5ee0b113); the client-side JWT-signing path is fully removed from the auth flow, with a .gitignore update for the dropped creds file (c0654304, 80aec2bc, c4f33f30); src/lib/apiError.ts got a JSON.stringify regression that was reverted (cae01a03, ba22fb20); and the connection-architecture + new-endpoint-workflow docs landed in .claude/ to make the new auth shape the single source of truth (619bb30e).
  • Marketplace API — list-my-offers + offer lifecycle tests, List-my-offers cleanup: list-my-offers.js auto-committed after auth migration (9137d133); the stale list-my-offers.test.ts was deleted in favor of the JS+source-grep split (15901252); save-offer-draft.test.ts and list-my-offers.test.ts regenerated (725810c5); and archive-offer.test.ts/restore-offer.test.ts added to cover the full CRUD lifecycle (ebd76715).
  • Listing model — schema-complete coverage extended (slices 7-12 in plan): src/models/Listing.ts iterated to expand schema coverage; the schema-complete test suite now covers 17/32 columns (slug), 16/32 (request_id), 15/32 (inventory_count), 14/32 (currency) as plan status updates rolled in; new tests added and the Listing model adjusted to match (103a6d88, 106dfe0d, 14120054, 5cf10b20, 60ff3bb8, 45d33ba8, 938b3b1d, c0b5100b, a60e9b88, 748bf4cb, f2f0d9d1, e8d54394, bf6d4ba4, db89807d). BaseModel.test.ts is now in the schema-complete sweep as well.
  • Dev-stack + plans-doc + workflow hygiene: scripts/dev.sh auto-committed three times during the EACCES / WRANGLER_HOME / HOME-override hardening sequence (c5e0d6bf, 58f93709, 3c0bfd90); docs/plans/offer-builder-api-first/00-status.md got three tracking edits as slices 7-12 closed (22dccdbd, 70bc4d96, 539ae959, b34d658d, c65d348e); the Slice 7 cont. status rollups mark slug, request_id, inventory_count, currency DONE; .claude/SKILL.md updated (9fbeed03); the new src/router/creator-studio-file-based-moves.test.ts mirrors a route-table move (151f746c); and a src/pages/zz-sabotage-probe.vue/new.vue pair recorded intent (030f4134, b7722788, 3376a305).
  • Noise floor — owner-tool / create-mode / permission probes + SUMMARY.md rolls: 29 SUMMARY.md autocommit rolls (ecbc409d through 4284738c); a PROBE-perm-owner-20260830.txt intent file plus a create(tmp-create-mode-string-boolean-probe.txt) created via the edit-test tool (840224c0, f8043455); their follow-up cleanup commits removed both probe files (42b30907, 240d2744); a stray zz-sabotage-probe.vue / create-mode probe pair created then removed (b7722788); and root CLAUDE.md rolled (ff05518c, e4ab837c). These are scaffolding artefacts, not product work.
  • Offer-Builder Working Correctly plan — continued: 01-product.md trimmed with minor edits (4 insertions, 12 deletions) and a new 02-architecture.md committed (69 lines) as the next draft gate; 00-status.md also updated (16 insertions) — all part of the ongoing Gate 1 (Product) iteration within the offer-builder-fix plan (7a504eb6, 4797b90e, 5af14250). Plan lives in docs/ only — no app code touched.
  • Review Console 0 / 15 phases seen
    Full plan (all phases)