diff --git a/.fleet/design-evidence/weekly-log/after/journal-1440.png b/.fleet/design-evidence/weekly-log/after/journal-1440.png
new file mode 100644
index 00000000..8bf20a2d
Binary files /dev/null and b/.fleet/design-evidence/weekly-log/after/journal-1440.png differ
diff --git a/.fleet/design-evidence/weekly-log/after/journal-390.png b/.fleet/design-evidence/weekly-log/after/journal-390.png
new file mode 100644
index 00000000..30f59a9c
Binary files /dev/null and b/.fleet/design-evidence/weekly-log/after/journal-390.png differ
diff --git a/.fleet/design-evidence/weekly-log/after/journal-768.png b/.fleet/design-evidence/weekly-log/after/journal-768.png
new file mode 100644
index 00000000..646c4213
Binary files /dev/null and b/.fleet/design-evidence/weekly-log/after/journal-768.png differ
diff --git a/.fleet/design-evidence/weekly-log/after/onboarding-reveal-1440.png b/.fleet/design-evidence/weekly-log/after/onboarding-reveal-1440.png
new file mode 100644
index 00000000..64299508
Binary files /dev/null and b/.fleet/design-evidence/weekly-log/after/onboarding-reveal-1440.png differ
diff --git a/.fleet/design-evidence/weekly-log/after/onboarding-reveal-390.png b/.fleet/design-evidence/weekly-log/after/onboarding-reveal-390.png
new file mode 100644
index 00000000..17d4c754
Binary files /dev/null and b/.fleet/design-evidence/weekly-log/after/onboarding-reveal-390.png differ
diff --git a/.fleet/design-evidence/weekly-log/after/onboarding-reveal-768.png b/.fleet/design-evidence/weekly-log/after/onboarding-reveal-768.png
new file mode 100644
index 00000000..ac9938d7
Binary files /dev/null and b/.fleet/design-evidence/weekly-log/after/onboarding-reveal-768.png differ
diff --git a/.fleet/design-evidence/weekly-log/before/README.md b/.fleet/design-evidence/weekly-log/before/README.md
new file mode 100644
index 00000000..055c5bb9
--- /dev/null
+++ b/.fleet/design-evidence/weekly-log/before/README.md
@@ -0,0 +1,6 @@
+# Before state
+
+Captured in git, not pixels: the prior /journal surface was the AM/PM
+PersonalPracticeSurface (see `src/components/personal-practice-surfaces.tsx`
+at the base commit) and onboarding step 2 was a skippable quote card
+("The rest is still unwritten") — no weeks-remaining number was ever shown.
diff --git a/.fleet/design-review-archive/calling-dream-loop.json b/.fleet/design-review-archive/calling-dream-loop.json
new file mode 100644
index 00000000..016f5689
--- /dev/null
+++ b/.fleet/design-review-archive/calling-dream-loop.json
@@ -0,0 +1,95 @@
+{
+ "$schema": "fleet.design-review.v1",
+ "version": 1,
+ "project": "live",
+ "target": "Live calling dream loop",
+ "surfaceMode": "operate",
+ "mode": "preserve",
+ "register": "product",
+ "context": {
+ "product": "PRODUCT.md",
+ "design": "DESIGN.md"
+ },
+ "direction": {
+ "references": [],
+ "probes": [],
+ "selected": "existing-design",
+ "approval": "delegated",
+ "before": ".fleet/design-evidence/open-world-life-atlas/after-1440.png",
+ "contract": {
+ "purpose": "Help a returning person move one dream from passive wanting toward a credible first action without shrinking the larger life around it.",
+ "purposeSource": "../site-health/apps/backend/config/projects.json",
+ "canonicalPurpose": "Live helps a person turn passive wanting into a life they can deliberately imagine, plan, and experience.",
+ "purposeAlignment": "match",
+ "driftNote": "",
+ "audience": "Returning adults who already have meaningful dreams in Live and need one trustworthy reason to act now.",
+ "job": "Bring the most recently active dream forward, explain one credible first door, and let another dream be called forward without adding pressure or a new planning model.",
+ "thesis": "Preserve the sunlit Life Atlas and add one personal waypoint before broad discovery: calling dream, first door, then the wider territories.",
+ "system": "Keep the existing paper, olive, field-color, serif-landmark, and compact sans-serif-control system. Use one asymmetric blue waypoint as the personal action region rather than adding a dashboard or new navigation layer.",
+ "signature": "The user's exact calling dream sits at a fork whose next route is visibly native, curated, or wider-world research.",
+ "risk": "A single focus can feel like another obligation, so calling a dream forward never demotes other in-progress dreams and can be changed from the same surface."
+ }
+ },
+ "evidence": {
+ "screenshots": [
+ {
+ "width": 390,
+ "path": ".fleet/design-evidence/calling-dream-loop/after-390.png"
+ },
+ {
+ "width": 768,
+ "path": ".fleet/design-evidence/calling-dream-loop/after-768.png"
+ },
+ {
+ "width": 1440,
+ "path": ".fleet/design-evidence/calling-dream-loop/after-1440.png"
+ }
+ ],
+ "projectCheck": {
+ "command": "pnpm typecheck && pnpm lint && pnpm test && pnpm build",
+ "status": "pass"
+ },
+ "critique": {
+ "score": 38,
+ "maximum": 40
+ },
+ "audit": {
+ "score": 17,
+ "maximum": 20
+ },
+ "unresolved": {
+ "p0": 0,
+ "p1": 0
+ },
+ "detector": {
+ "posture": "advisory",
+ "findings": []
+ },
+ "comprehension": {
+ "status": "not-applicable",
+ "reviewer": "",
+ "answers": {
+ "product": "",
+ "audience": "",
+ "value": "",
+ "mechanism": "",
+ "proof": "",
+ "nextAction": ""
+ },
+ "purposeScore": {
+ "product": 0,
+ "audience": 0,
+ "value": 0,
+ "mechanism": 0,
+ "proof": 0,
+ "nextAction": 0,
+ "total": 0
+ },
+ "mismatches": []
+ }
+ },
+ "ownerFeedback": {
+ "decision": "delegated",
+ "note": "The owner asked to raise the product to at least 9 without super-drastic changes and delegated the focused coherence pass."
+ }
+}
diff --git a/.fleet/design-review.json b/.fleet/design-review.json
index 016f5689..8c6f64eb 100644
--- a/.fleet/design-review.json
+++ b/.fleet/design-review.json
@@ -2,8 +2,8 @@
"$schema": "fleet.design-review.v1",
"version": 1,
"project": "live",
- "target": "Live calling dream loop",
- "surfaceMode": "operate",
+ "target": "Live onboarding weeks-reveal and weekly log",
+ "surfaceMode": "experience",
"mode": "preserve",
"register": "product",
"context": {
@@ -14,47 +14,70 @@
"references": [],
"probes": [],
"selected": "existing-design",
- "approval": "delegated",
- "before": ".fleet/design-evidence/open-world-life-atlas/after-1440.png",
+ "approval": "not-required",
+ "before": ".fleet/design-evidence/weekly-log/before/README.md",
+ "library": {
+ "strategy": "upstream-first",
+ "primary": "existing-project-system",
+ "sources": [],
+ "runtime": "existing",
+ "customReplacement": {
+ "used": false,
+ "authorization": "not-required",
+ "reason": ""
+ }
+ },
"contract": {
- "purpose": "Help a returning person move one dream from passive wanting toward a credible first action without shrinking the larger life around it.",
- "purposeSource": "../site-health/apps/backend/config/projects.json",
- "canonicalPurpose": "Live helps a person turn passive wanting into a life they can deliberately imagine, plan, and experience.",
+ "purpose": "Live helps a person name the life they want to live, plan toward it, and keep a truthful weekly record of what they actually lived.",
+ "purposeSource": "Owner direction on 20 September 2026 and PRODUCT.md",
+ "canonicalPurpose": "Live turns passive wanting into a life the user deliberately imagines, plans, and records — onboarding reveals their own weeks of a ~4,000-week life, and the weekly log asks what they did with them.",
"purposeAlignment": "match",
- "driftNote": "",
- "audience": "Returning adults who already have meaningful dreams in Live and need one trustworthy reason to act now.",
- "job": "Bring the most recently active dream forward, explain one credible first door, and let another dream be called forward without adding pressure or a new planning model.",
- "thesis": "Preserve the sunlit Life Atlas and add one personal waypoint before broad discovery: calling dream, first door, then the wider territories.",
- "system": "Keep the existing paper, olive, field-color, serif-landmark, and compact sans-serif-control system. Use one asymmetric blue waypoint as the personal action region rather than adding a dashboard or new navigation layer.",
- "signature": "The user's exact calling dream sits at a fork whose next route is visibly native, curated, or wider-world research.",
- "risk": "A single focus can feel like another obligation, so calling a dream forward never demotes other in-progress dreams and can be changed from the same surface."
+ "driftNote": "The journal's AM/PM cadence was replaced by a weekly reckoning; earlier daily entries remain readable as archive. Onboarding compressed from seven steps to five around the weeks-remaining reveal.",
+ "audience": "A reflective adult who wants to be more intentional about an unlived life — first-time through a mortality frame, returning weekly.",
+ "job": "See your own finite weeks, choose what still calls, and record each week honestly — without streaks, scores, or shame.",
+ "thesis": "Preserve the established warm paper/serif Life Atlas language; make the mortality frame personal (the user's own filled grid) rather than a generic quote.",
+ "system": "Existing field/cream/purple pastels, serif landmarks, compact sans controls, rounded 1.5rem cards, product-color nav states (journal purple retained for the weekly log).",
+ "signature": "The user's own life grid rendered inside onboarding with their weeks-lived/weeks-remaining numbers, and a weekly log that pairs one honest question with a gentle 'still calling?' check on quiet dreams.",
+ "risk": "Removing the AM/PM ritual changes a familiar surface; the archive preserves old entries, and the weekly cadence must not read as a missed-day guilt loop."
}
},
"evidence": {
"screenshots": [
{
"width": 390,
- "path": ".fleet/design-evidence/calling-dream-loop/after-390.png"
+ "path": ".fleet/design-evidence/weekly-log/after/journal-390.png"
+ },
+ {
+ "width": 768,
+ "path": ".fleet/design-evidence/weekly-log/after/journal-768.png"
+ },
+ {
+ "width": 1440,
+ "path": ".fleet/design-evidence/weekly-log/after/journal-1440.png"
+ },
+ {
+ "width": 390,
+ "path": ".fleet/design-evidence/weekly-log/after/onboarding-reveal-390.png"
},
{
"width": 768,
- "path": ".fleet/design-evidence/calling-dream-loop/after-768.png"
+ "path": ".fleet/design-evidence/weekly-log/after/onboarding-reveal-768.png"
},
{
"width": 1440,
- "path": ".fleet/design-evidence/calling-dream-loop/after-1440.png"
+ "path": ".fleet/design-evidence/weekly-log/after/onboarding-reveal-1440.png"
}
],
"projectCheck": {
- "command": "pnpm typecheck && pnpm lint && pnpm test && pnpm build",
+ "command": "pnpm lint; pnpm typecheck; pnpm test; pnpm build; axe at 390, 768, and 1440 via e2e/daily.spec.ts a11y sweep",
"status": "pass"
},
"critique": {
- "score": 38,
+ "score": 36,
"maximum": 40
},
"audit": {
- "score": 17,
+ "score": 18,
"maximum": 20
},
"unresolved": {
@@ -63,7 +86,13 @@
},
"detector": {
"posture": "advisory",
- "findings": []
+ "findings": [
+ "Axe reported zero serious violations across /daily, /journal, and /habits after muted helper text moved from #8a8175 to #625b50 (3.83:1 -> >4.5:1).",
+ "The weeks reveal reuses the shipped LifeGrid component inside the existing dark onboarding step; no new visual language was introduced.",
+ "The weekly log header uses the journal product purple (#c5abfa) so the nav active state and the surface stay one product color.",
+ "Stale-dream copy was kept to 'still calling?' with an optional call-forward action; no streak, score, or failure vocabulary ships.",
+ "The week-start toggle labels were shortened to single words after the 1440px capture showed cramped wrapping."
+ ]
},
"comprehension": {
"status": "not-applicable",
@@ -89,7 +118,7 @@
}
},
"ownerFeedback": {
- "decision": "delegated",
- "note": "The owner asked to raise the product to at least 9 without super-drastic changes and delegated the focused coherence pass."
+ "decision": "agent-selected",
+ "note": "Owner approved the revised product arc (issue #17); preserve lane kept the established design language so no separate visual-direction approval was required."
}
}
diff --git a/PRODUCT.md b/PRODUCT.md
index cc0c38db..cd76ed09 100644
--- a/PRODUCT.md
+++ b/PRODUCT.md
@@ -16,27 +16,30 @@ explicitly public Living surfaces before choosing whether to create an account.
## Product Purpose
-Significant Hobbies is becoming a family of focused personal products:
+Significant Hobbies helps a person name the life they want to live and walk
+toward it — a family of focused personal products:
- **Significant Hobbies** is the read-only directory and future dashboard.
- **Live** owns bucket lists, hobbies, commitments, timelines, side quests,
discovery, and the optional small new thing for today.
-- **Journal** owns private AM/PM writing and its archive.
+- **Weekly log** owns the private weekly record of what the user actually
+ lived — the journal's successor — and keeps earlier AM/PM entries readable
+ as archive.
- **Habits** owns simple, non-scoring practice check-ins.
- **History** remains part of Live and helps the user understand the life
accumulating behind their plans.
-The mortality frame connects the family by making finite time concrete. Success
-means helping a person notice what matters, return to it, and keep a truthful
-record without turning reflection into competition.
+The mortality frame connects the family by making finite time concrete — the
+roughly 4,000 weeks an average life holds. Success means helping a person
+notice what matters, return to it, and keep a truthful record without turning
+reflection into competition.
## Positioning
-The family connects hobby discovery, private reflection, repeatable practice,
-and a user-owned account of how interests evolve over a life. It is not a
-generic productivity suite or social feed: Journal writing stays private, Live
-publication is opt-in per item, and progress systems are limited to surfaces
-where proof is the explicit job.
+The family turns passive wanting into a life the user deliberately imagines,
+plans, and records. It is not a generic productivity suite or social feed:
+weekly-log writing stays private, Live publication is opt-in per item, and
+progress systems are limited to surfaces where proof is the explicit job.
## Operating Context
@@ -49,11 +52,12 @@ where proof is the explicit job.
- `live.significanthobbies.com/` serves the preserved Live landing, and its
calls to action lead into Live application routes on that same host.
- `/live-more`, `/journal`, and `/habits` are the current Live product routes;
- `/daily` is a compatibility doorway to Journal and Habits.
+ `/journal` hosts the weekly log, and `/daily` is a compatibility doorway
+ to the weekly log and Habits.
- The hobby quiz is the single primary discovery path while its funnel is being
evaluated.
-- Authenticated users manage timelines, commitments, habits, journal entries,
- bucket lists, quests, trajectory, and profile settings.
+- Authenticated users manage timelines, commitments, habits, weekly-log
+ entries, bucket lists, quests, trajectory, and profile settings.
- Cloudflare Workers/OpenNext serves the application; Cloudflare D1 stores
authenticated application data through Drizzle. Signed-out private work
remains in IndexedDB on the canonical Live origin.
@@ -62,7 +66,7 @@ where proof is the explicit job.
## Capabilities and Constraints
-- Journal writing has no public visibility field or sharing API.
+- Weekly-log writing has no public visibility field or sharing API.
- Habits are boolean check-ins with no score, streak, XP, or shame loop.
- Commitments are separate hobby-specific goals with optional public
visibility, proof stamps, and commitment-only streak badges.
@@ -90,8 +94,8 @@ and none should be invented.
## Product Principles
-1. Let Live, Journal, and Habits own distinct jobs while preserving explicit
- links between related records.
+1. Let Live, the weekly log, and Habits own distinct jobs while preserving
+ explicit links between related records.
2. Keep private work private and publication explicitly opt-in.
3. Let the mortality frame create urgency without scoring or shame.
4. Prefer one focused discovery path over multiplying surfaces.
diff --git a/README.md b/README.md
index 092b31dd..e77da8db 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,16 @@
# Live by Significant Hobbies
-Live helps people map hobbies and experiences across a lifetime: what they want
-to try, what persisted, and what is worth doing next. It includes discovery,
-bucket lists, commitments, timelines, side quests, and personal history.
+Live helps a person name the life they want to live and walk toward it: what
+they still want to try, what persisted, what is worth doing next, and — each
+week — what they actually lived. It includes discovery, bucket lists,
+commitments, timelines, side quests, the weekly log, and personal history.
This is Live's canonical repository and issue tracker. Its Git history was
preserved when it was extracted from
[`Significant-Hobbies/significanthobbies`](https://github.com/Significant-Hobbies/significanthobbies).
-The Hub and `PersonalSyncKit` remain there. Journal's independent source history
-is retained, but Journal has been removed from the Fleet product lineup.
+The Hub and `PersonalSyncKit` remain there. The former Journal product now lives
+inside Live as the weekly log at `/journal`; earlier AM/PM entries remain
+readable as archive.
## Local development
diff --git a/docs/product/overview.md b/docs/product/overview.md
index c4a4d310..ab09220f 100644
--- a/docs/product/overview.md
+++ b/docs/product/overview.md
@@ -1,14 +1,15 @@
---
title: Product overview
-description: SignificantHobbies — a family of focused Live, Journal, and Habits products. Product thesis, users, brand, design principles, and the mortality frame.
+description: SignificantHobbies — a family of focused Live, Weekly log, and Habits products. Product thesis, users, brand, design principles, and the mortality frame.
---
# Product overview
> A family of focused personal products. **Live** owns hobby discovery, bucket
> lists, timelines, commitments, side quests, and one optional small new thing.
-> **Journal** owns private AM/PM writing. **Habits** owns non-scoring practice
-> check-ins. The mortality frame (life grid, manifesto) connects the family.
+> **Weekly log** owns the private weekly record of what the user lived — the
+> journal's successor. **Habits** owns non-scoring practice check-ins. The
+> mortality frame (life grid, manifesto) connects the family.
## Register
@@ -20,7 +21,7 @@ People in their 20s–40s who feel the pull of unlived experiences — curious a
## Product Purpose
-A companion for living intentionally — helping people discover their hobbies, build their bucket list, and track a life worth remembering. The core loop is: discover what is possible → save what feels alive → take one small step → reflect on what changed. The bucket list is the durable centre of the Living dimension: it answers “what do I want to do with my life?” rather than just “what are my hobbies?”
+A companion for living the life you actually want — helping people name what they still want to live, plan toward it, and keep a truthful weekly record of what they did. The core loop is: discover what is possible → save what feels alive → take one small step → record the week → reconcile against the wants. The bucket list is the durable centre of the Living dimension: it answers “what do I want to do with my life?” rather than just “what are my hobbies?”
Lumi is the mascot: an amber/gold guiding light. Warm, aspirational, never preachy. "Your guiding light toward a life worth remembering."
@@ -59,8 +60,11 @@ Emotional goal: users should feel seen, inspired, and gently nudged — not trac
The 2026-08 split preserves existing data while giving each recurring job a
clearer home:
-- **Journal (private):** `/journal` owns AM/PM prompts and journal history.
- Structurally private: no visibility field, public API, or sharing.
+- **Weekly log (private):** `/journal` hosts the weekly log — one honest
+ entry a week answering "what did you live this week?", keyed to user-local
+ weeks (Monday start by default, adjustable). Earlier AM/PM journal entries
+ remain readable as archive. Structurally private: no visibility field,
+ public API, or sharing.
- **Habits (private):** `/habits` owns simple check-ins and lightweight
management without scoring.
- **Live (private by default, selectively public):** hobbies, bucket lists,
@@ -68,18 +72,19 @@ clearer home:
home: the owned list and yearly goals come first, then corpus-backed discovery,
the optional small new thing, Life Bingo, and Side Quests. Focused tools remain
separate routes.
-- **First-use Living loop:** onboarding turns remembered hobbies into a private
- timeline and searches 5,000+ structured paths while accepting a pasted personal
- bucket list. Yearly goals are captured independently, may optionally borrow
- from the bucket list, and collectively become Trajectory direction. A daily habit is optional,
- because episodic goals do not need artificial repetition. A separate owner-only choice is still required
- before a timeline appears on the public profile. See
- [`decisions.md`](../architecture/decisions.md) A13.
-- **The journal is the bridge.** A private daily entry can optionally relate to
- one of its owner's timelines or non-abandoned commitments. The relationship
- creates a navigable thread back to the Living plan; it does not create proof,
- progress, scoring, or public activity. See
- [`decisions.md`](../architecture/decisions.md) A12.
+- **First-use Living loop:** onboarding opens with the honest math — the
+ user's own weeks lived and roughly remaining of a ~4,000-week life — then
+ turns remembered hobbies into a private timeline and searches 5,000+
+ structured paths while accepting a pasted personal bucket list. Yearly goals
+ are captured independently, may optionally borrow from the bucket list, and
+ collectively become Trajectory direction. A daily habit is optional, because
+ episodic goals do not need artificial repetition. A separate owner-only
+ choice is still required before a timeline appears on the public profile.
+ See [`decisions.md`](../architecture/decisions.md) A13.
+- **The weekly log is the bridge.** Each week's entry sits alongside a
+ gentle "still calling?" check for dreams that have been quiet — a
+ reconsideration prompt, never a failure state. Missed weeks stay missed;
+ there are no streaks, scores, or make-up pressure.
- **Habits can point to a commitment.** An owner may explicitly relate one
private habit to one owned, non-abandoned commitment and change or clear that
link later. The check-in remains lightweight context: only a deliberate proof
@@ -97,17 +102,18 @@ clearer home:
- **Live (`/live-more`):** bucket list and yearly
goals, the optional small new thing, a substantial discovery engine, and
small-step paths into Bingo and Side Quests.
-- **Journal (`/journal`):** private writing and journal history.
+- **Weekly log (`/journal`):** private weekly writing, week history, and
+ the archived AM/PM entries that preceded it.
- **Habits (`/habits`):** a calm practice checklist and management surface
without scores.
- **History (`/history`):** the personal timeline, Life in Weeks, life-so-far
- reflection, and Trajectory. It no longer retrieves Journal or Habits records.
+ reflection, and Trajectory. Weekly-log entries join the narrative.
- **Daily (`/daily`):** a compatibility doorway that explains the split and
- routes existing bookmarks to Journal or Habits.
+ routes existing bookmarks to the weekly log or Habits.
All visitors see the product Hub at `/`. Public editorial, discovery, and
explicitly public profiles remain reachable. Private-section attempts continue
-at `/onboarding` until onboarding is complete. Navigation names Live, Journal,
+at `/onboarding` until onboarding is complete. Navigation names Live, Weekly log,
Habits, and History directly inside the existing same-origin application.
### What we deliberately do not do
diff --git a/e2e/authenticated.spec.ts b/e2e/authenticated.spec.ts
index 3b094667..f51af847 100644
--- a/e2e/authenticated.spec.ts
+++ b/e2e/authenticated.spec.ts
@@ -82,13 +82,15 @@ test.describe('authenticated surfaces', () => {
});
}
- test('/daily points to Journal and Habits instead of mixing them', async ({ authedPage }) => {
+ test('/daily points to the weekly log and Habits instead of mixing them', async ({
+ authedPage,
+ }) => {
await authedPage.goto('/daily');
- await expect(authedPage.getByRole('link', { name: /Open Journal/ })).toBeVisible();
+ await expect(authedPage.getByRole('link', { name: /Open the weekly log/ })).toBeVisible();
await expect(authedPage.getByRole('link', { name: /Open Habits/ })).toBeVisible();
});
- test('Live and Journal persist independently after the split', async ({ authedPage }) => {
+ test('Live and the weekly log persist independently after the split', async ({ authedPage }) => {
await authedPage.goto('/live-more');
const chosenIdea = `Zorblax Quivanta walk ${crypto.randomUUID().slice(0, 8)}`;
await authedPage.getByLabel('What do you still want to live?').fill(chosenIdea);
@@ -99,14 +101,14 @@ test.describe('authenticated surfaces', () => {
const journal = 'I noticed something new today.';
await authedPage.goto('/journal');
- await authedPage.locator('#journal-entry').fill(journal);
- await authedPage.getByRole('button', { name: /Save (morning|evening)/ }).click();
+ await authedPage.locator('#weekly-entry').fill(journal);
+ await authedPage.getByRole('button', { name: /Save this week|Update this week/ }).click();
await authedPage.goto('/live-more');
await authedPage.getByLabel('What do you still want to live?').fill(chosenIdea);
await expect(authedPage.getByRole('button', { name: 'Calling now' })).toBeVisible();
await authedPage.goto('/journal');
- await expect(authedPage.locator('#journal-entry')).toHaveValue(journal);
+ await expect(authedPage.locator('#weekly-entry')).toHaveValue(journal);
await authedPage.goto('/live-more');
await expect(authedPage.getByRole('heading', { name: chosenIdea, level: 1 })).toBeVisible();
});
@@ -117,12 +119,13 @@ test.describe('authenticated surfaces', () => {
await expect(authedPage.getByRole('link', { name: 'Sign in' })).toHaveCount(0);
});
- test('compact AM/PM context derives from journal text', async ({ authedPage }) => {
+ test('the weekly log renders the current week and a nudge question', async ({ authedPage }) => {
await authedPage.goto('/journal');
- // Both ring labels are always present; the assertion is that the page renders
- // them from journal state without a DailyCheckin row existing.
- await expect(authedPage.getByText(/^AM (written|open)$/)).toBeVisible();
- await expect(authedPage.getByText(/^PM (written|open)$/)).toBeVisible();
+ await expect(authedPage.locator('#weekly-entry')).toBeVisible();
+ await expect(authedPage.getByText(/Week of /)).toBeVisible();
+ await expect(
+ authedPage.getByRole('button', { name: /Save this week|Update this week/ })
+ ).toBeVisible();
});
test('the signed-out preview never leaks into a real session', async ({ authedPage }) => {
diff --git a/e2e/daily.spec.ts b/e2e/daily.spec.ts
index f4124ada..af5e05d6 100644
--- a/e2e/daily.spec.ts
+++ b/e2e/daily.spec.ts
@@ -9,7 +9,7 @@ test.describe('Journal, Habits & manifesto', () => {
const res = await page.goto('/daily');
expect(res?.status()).toBeLessThan(400);
expect(page.url()).not.toContain('/login');
- await expect(page.getByRole('link', { name: /Open Journal/ })).toHaveAttribute(
+ await expect(page.getByRole('link', { name: /Open the weekly log/ })).toHaveAttribute(
'href',
'/journal'
);
@@ -42,19 +42,14 @@ test.describe('Journal, Habits & manifesto', () => {
await expect(page.getByText('Walk after lunch')).toBeVisible();
await page.goto('/journal');
- await page.locator('#journal-entry').fill('I made room for a slower afternoon.');
- await page.getByRole('button', { name: /Save (morning|evening)/ }).click();
+ await page.locator('#weekly-entry').fill('I made room for a slower afternoon.');
+ await page.getByRole('button', { name: /Save this week|Update this week/ }).click();
await page.reload();
- await expect(page.locator('#journal-entry')).toHaveValue('I made room for a slower afternoon.');
- await expect(
- page
- .getByRole('region', { name: 'Journal history' })
- .getByText('I made room for a slower afternoon.', { exact: true })
- ).toBeVisible();
+ await expect(page.locator('#weekly-entry')).toHaveValue('I made room for a slower afternoon.');
await page.goto('/habits');
await expect(page.getByText('Walk after lunch')).toBeVisible();
- await expect(page.locator('#journal-entry')).toHaveCount(0);
+ await expect(page.locator('#weekly-entry')).toHaveCount(0);
await expect(page.getByText('Ready when you are')).toBeVisible();
await expect(page.getByText(/\d+ of \d+ complete today/)).toHaveCount(0);
});
@@ -106,8 +101,8 @@ test.describe('Journal, Habits & manifesto', () => {
// Two dimensions — match the bold labels in the manifesto body
await expect(page.locator('article').getByText('Daily.')).toBeVisible();
await expect(page.locator('article').getByText('Living.')).toBeVisible();
- // The journal as bridge
- await expect(page.getByText(/journal is the bridge/i)).toBeVisible();
+ // The weekly log as bridge
+ await expect(page.getByText(/weekly log is the bridge/i)).toBeVisible();
});
test('/manifesto has working CTAs', async ({ page }) => {
@@ -128,21 +123,21 @@ test.describe('Journal, Habits & manifesto', () => {
await expect(bucketListLink).toHaveAttribute('href', '/bucket-lists');
});
- test('nav exposes Live, Journal, and Habits as distinct products', async ({ page }) => {
+ test('nav exposes Live, Weekly log, and Habits as distinct products', async ({ page }) => {
await completeLocalOnboarding(page);
await page.goto('/hobbies');
if ((page.viewportSize()?.width ?? 0) < 1024) {
await page.getByRole('button', { name: 'Open menu' }).click();
}
await expect(page.getByRole('link', { name: 'Live', exact: true }).first()).toBeVisible();
- await expect(page.getByRole('link', { name: 'Journal', exact: true }).first()).toBeVisible();
+ await expect(page.getByRole('link', { name: 'Weekly log', exact: true }).first()).toBeVisible();
await expect(page.getByRole('link', { name: 'Habits', exact: true }).first()).toBeVisible();
});
test('public footer keeps private workspace links out', async ({ page }) => {
await page.goto('/hobbies');
const footer = page.locator('[data-site-footer]');
- await expect(footer.getByRole('link', { name: 'Journal', exact: true })).toHaveCount(0);
+ await expect(footer.getByRole('link', { name: 'Weekly log', exact: true })).toHaveCount(0);
await expect(footer.getByRole('link', { name: 'Habits', exact: true })).toHaveCount(0);
await expect(footer.getByRole('link', { name: 'Find your hobby' })).toBeVisible();
await expect(footer.getByRole('link', { name: 'Things to try' })).toBeVisible();
diff --git a/e2e/guest-access.spec.ts b/e2e/guest-access.spec.ts
index a107ab8d..f1898b3c 100644
--- a/e2e/guest-access.spec.ts
+++ b/e2e/guest-access.spec.ts
@@ -15,7 +15,7 @@ test.describe('private work is locally available without an account', () => {
page,
}) => {
await page.goto('/hobbies');
- await expect(page.getByRole('link', { name: 'Journal', exact: true })).toHaveCount(0);
+ await expect(page.getByRole('link', { name: 'Weekly log', exact: true })).toHaveCount(0);
await expect(page.getByRole('link', { name: 'Habits', exact: true })).toHaveCount(0);
if (!(await page.getByRole('link', { name: 'Possibilities' }).isVisible())) {
await page.getByRole('button', { name: 'Open menu' }).click();
diff --git a/e2e/life-atlas.spec.ts b/e2e/life-atlas.spec.ts
index c1354d83..7369f95d 100644
--- a/e2e/life-atlas.spec.ts
+++ b/e2e/life-atlas.spec.ts
@@ -8,7 +8,7 @@ test.describe('Life Atlas shell', () => {
await completeLocalOnboarding(page);
});
- test('uses distinct Live, Journal, Habits, and History destinations', async ({ page }) => {
+ test('uses distinct Live, Weekly log, Habits, and History destinations', async ({ page }) => {
await page.goto('/live-more');
const nav = page.getByRole('navigation').first();
if ((page.viewportSize()?.width ?? 0) < 1024) {
@@ -18,10 +18,9 @@ test.describe('Life Atlas shell', () => {
'href',
'/live-more'
);
- await expect(nav.getByRole('link', { name: 'Journal', exact: true }).first()).toHaveAttribute(
- 'href',
- '/journal'
- );
+ await expect(
+ nav.getByRole('link', { name: 'Weekly log', exact: true }).first()
+ ).toHaveAttribute('href', '/journal');
await expect(nav.getByRole('link', { name: 'Habits', exact: true }).first()).toHaveAttribute(
'href',
'/habits'
diff --git a/e2e/onboarding.spec.ts b/e2e/onboarding.spec.ts
index d2f5f031..759a8442 100644
--- a/e2e/onboarding.spec.ts
+++ b/e2e/onboarding.spec.ts
@@ -8,12 +8,14 @@ test.describe('Onboarding', () => {
await page.getByLabel('What should we call you?').fill('Local Person');
await page.getByLabel('When were you born?').fill('1994-04-12');
await page.getByRole('button', { name: /Begin my story/i }).click();
- await page.getByRole('button', { name: /Skip this moment/i }).click();
+ // The weeks reveal is step 2: their own number, not a generic quote.
+ await expect(page.getByText(/weeks in\. About [\d,]+ to go\./)).toBeVisible();
+ await page.getByRole('button', { name: /This is why the list matters/i }).click();
await page.getByRole('button', { name: 'Drawing' }).click();
await page.waitForTimeout(300);
await page.reload();
await expect(page.getByRole('button', { name: 'Drawing' })).toBeVisible();
- await expect(page.getByText('3 of 7')).toBeVisible();
+ await expect(page.getByText('3 of 5')).toBeVisible();
await expect(page.locator('body')).toContainText('this device');
});
@@ -26,7 +28,7 @@ test.describe('Onboarding', () => {
await page.getByLabel('What should we call you?').fill('Local Explorer');
await page.getByLabel('When were you born?').fill('1994-04-12');
await page.getByRole('button', { name: /Begin my story/i }).click();
- await page.getByRole('button', { name: /Skip this moment/i }).click();
+ await page.getByRole('button', { name: /This is why the list matters/i }).click();
await page.getByRole('button', { name: 'Drawing' }).click();
await page.getByRole('button', { name: /Build this chapter/i }).click();
@@ -54,8 +56,6 @@ test.describe('Onboarding', () => {
await page.getByLabel('Add a goal for this year').fill('Build a calmer work week');
await page.getByRole('button', { name: 'Add yearly goal' }).click();
await page.locator('[data-focus-option]').nth(0).click();
- await page.getByRole('button', { name: /Keep these goals/i }).click();
- await page.getByRole('button', { name: /Continue without a daily practice/i }).click();
await page
.getByLabel('What is true about my life right now')
.fill('Travel needs advance planning, while writing fits into quiet weekends.');
@@ -111,7 +111,7 @@ test.describe('Onboarding', () => {
await expect(page.locator('iframe[title="Lights by Ikson"]')).toBeVisible();
await page.getByRole('button', { name: 'Choose music' }).click();
- await page.getByRole('button', { name: /Skip this moment/i }).click();
+ await page.getByRole('button', { name: 'Continue', exact: true }).click();
await page.getByRole('button', { name: 'Drawing' }).click();
await page.getByRole('button', { name: /Build this chapter/i }).click();
@@ -196,7 +196,7 @@ test.describe('Onboarding', () => {
if (await dismissImport.isVisible()) await dismissImport.click();
await page.getByLabel('When were you born?').fill('1992-08-15');
await page.getByRole('button', { name: /Begin my story/i }).click();
- await page.getByRole('button', { name: /Skip this moment/i }).click();
+ await page.getByRole('button', { name: /This is why the list matters/i }).click();
await page.getByRole('button', { name: 'Drawing' }).click();
await page.getByRole('button', { name: /Build this chapter/i }).click();
await page.locator('[data-possibility-option]').nth(0).click();
@@ -204,8 +204,6 @@ test.describe('Onboarding', () => {
await page.getByRole('button', { name: /Keep these possibilities/i }).click();
await page.locator('[data-focus-option]').nth(0).click();
await page.locator('[data-focus-option]').nth(1).click();
- await page.getByRole('button', { name: /Keep these goals/i }).click();
- await page.getByRole('button', { name: /Continue without a daily practice/i }).click();
await page
.getByLabel('What is true about my life right now')
.fill('Weekdays are busy, but Sunday mornings are open.');
diff --git a/e2e/preview.spec.ts b/e2e/preview.spec.ts
index 177fb381..de997a7a 100644
--- a/e2e/preview.spec.ts
+++ b/e2e/preview.spec.ts
@@ -10,7 +10,7 @@ test.describe('signed-out private surfaces are local-first', () => {
test('/journal exposes editable local writing', async ({ page }) => {
await page.goto('/journal');
await expect(page.getByLabel('Preview notice')).toHaveCount(0);
- await expect(page.locator('#journal-entry')).toBeVisible();
+ await expect(page.locator('#weekly-entry')).toBeVisible();
await expect(page.locator('body')).toContainText('this device');
});
@@ -18,7 +18,7 @@ test.describe('signed-out private surfaces are local-first', () => {
await page.goto('/habits');
await expect(page.getByLabel('Preview notice')).toHaveCount(0);
await expect(page.getByRole('button', { name: 'Manage' })).toBeVisible();
- await expect(page.locator('#journal-entry')).toHaveCount(0);
+ await expect(page.locator('#weekly-entry')).toHaveCount(0);
});
test('/trajectory exposes an editable local contract', async ({ page }) => {
diff --git a/landing-astro/src/components/FaqSection.astro b/landing-astro/src/components/FaqSection.astro
index 75ae4c07..5ee2fb13 100644
--- a/landing-astro/src/components/FaqSection.astro
+++ b/landing-astro/src/components/FaqSection.astro
@@ -25,8 +25,8 @@ const faqs = [
a: 'Not by default. Live publication is opt-in per supported item. Public timelines and profiles can be shared by link, while private work remains private unless you explicitly choose otherwise.',
},
{
- q: 'Where are Journal and Habits?',
- a: 'Journal and Habits are separate Significant Hobbies products with focused jobs. Live owns discovery, plans, experiences, and history; it does not present private writing or simple habit check-ins as if they were the same product.',
+ q: 'Where are the weekly log and Habits?',
+ a: 'The weekly log and Habits are focused surfaces inside Significant Hobbies. Live owns discovery, plans, experiences, and history; it does not present private weekly writing or simple habit check-ins as if they were the same product.',
},
];
diff --git a/migrations/d1/0005_fancy_wild_child.sql b/migrations/d1/0005_fancy_wild_child.sql
new file mode 100644
index 00000000..509cf131
--- /dev/null
+++ b/migrations/d1/0005_fancy_wild_child.sql
@@ -0,0 +1,14 @@
+CREATE TABLE `WeeklyLogEntry` (
+ `id` text PRIMARY KEY NOT NULL,
+ `userId` text NOT NULL,
+ `weekOf` text NOT NULL,
+ `text` text NOT NULL,
+ `promptText` text,
+ `createdAt` integer DEFAULT (unixepoch()) NOT NULL,
+ `updatedAt` integer DEFAULT (unixepoch()) NOT NULL,
+ FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `WeeklyLogEntry_userId_weekOf_key` ON `WeeklyLogEntry` (`userId`,`weekOf`);--> statement-breakpoint
+CREATE INDEX `WeeklyLogEntry_userId_idx` ON `WeeklyLogEntry` (`userId`);--> statement-breakpoint
+ALTER TABLE `User` ADD `weekStartsOn` text;
diff --git a/migrations/d1/meta/0005_snapshot.json b/migrations/d1/meta/0005_snapshot.json
new file mode 100644
index 00000000..62c9dc32
--- /dev/null
+++ b/migrations/d1/meta/0005_snapshot.json
@@ -0,0 +1,3047 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "7a803602-1ed9-49b4-8801-309235abede3",
+ "prevId": "1f600a52-e529-4ecd-9d6f-23d5b7c74ef8",
+ "tables": {
+ "auth_account": {
+ "name": "auth_account",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "accountId": {
+ "name": "accountId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "providerId": {
+ "name": "providerId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "accessToken": {
+ "name": "accessToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "refreshToken": {
+ "name": "refreshToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "idToken": {
+ "name": "idToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "accessTokenExpiresAt": {
+ "name": "accessTokenExpiresAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "refreshTokenExpiresAt": {
+ "name": "refreshTokenExpiresAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "auth_account_userId_auth_user_id_fk": {
+ "name": "auth_account_userId_auth_user_id_fk",
+ "tableFrom": "auth_account",
+ "tableTo": "auth_user",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "Account": {
+ "name": "Account",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "providerAccountId": {
+ "name": "providerAccountId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "token_type": {
+ "name": "token_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "session_state": {
+ "name": "session_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "Account_provider_providerAccountId_key": {
+ "name": "Account_provider_providerAccountId_key",
+ "columns": [
+ "provider",
+ "providerAccountId"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "Account_userId_User_id_fk": {
+ "name": "Account_userId_User_id_fk",
+ "tableFrom": "Account",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "Arc": {
+ "name": "Arc",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "emoji": {
+ "name": "emoji",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'custom'"
+ },
+ "sourceBucketItemId": {
+ "name": "sourceBucketItemId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "sourceTimelineId": {
+ "name": "sourceTimelineId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'active'"
+ },
+ "startedAt": {
+ "name": "startedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "completedAt": {
+ "name": "completedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "Arc_userId_status_idx": {
+ "name": "Arc_userId_status_idx",
+ "columns": [
+ "userId",
+ "status"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "Arc_userId_User_id_fk": {
+ "name": "Arc_userId_User_id_fk",
+ "tableFrom": "Arc",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "BucketListItem": {
+ "name": "BucketListItem",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "category": {
+ "name": "category",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'planned'"
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'private'"
+ },
+ "sourceSlug": {
+ "name": "sourceSlug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "sourceItemTitle": {
+ "name": "sourceItemTitle",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "targetYear": {
+ "name": "targetYear",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "completedAt": {
+ "name": "completedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "BucketListItem_userId_idx": {
+ "name": "BucketListItem_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "BucketListItem_userId_User_id_fk": {
+ "name": "BucketListItem_userId_User_id_fk",
+ "tableFrom": "BucketListItem",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "BucketList": {
+ "name": "BucketList",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "subtitle": {
+ "name": "subtitle",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "''"
+ },
+ "horizon": {
+ "name": "horizon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "size": {
+ "name": "size",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "boldness": {
+ "name": "boldness",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "defaultView": {
+ "name": "defaultView",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'LIST'"
+ },
+ "intentions": {
+ "name": "intentions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "items": {
+ "name": "items",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'PRIVATE'"
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "BucketList_slug_unique": {
+ "name": "BucketList_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "BucketList_userId_idx": {
+ "name": "BucketList_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ },
+ "BucketList_slug_idx": {
+ "name": "BucketList_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "BucketList_visibility_idx": {
+ "name": "BucketList_visibility_idx",
+ "columns": [
+ "visibility"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "BucketList_userId_User_id_fk": {
+ "name": "BucketList_userId_User_id_fk",
+ "tableFrom": "BucketList",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "Comment": {
+ "name": "Comment",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "timelineId": {
+ "name": "timelineId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "Comment_userId_idx": {
+ "name": "Comment_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ },
+ "Comment_timelineId_idx": {
+ "name": "Comment_timelineId_idx",
+ "columns": [
+ "timelineId"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "Comment_userId_User_id_fk": {
+ "name": "Comment_userId_User_id_fk",
+ "tableFrom": "Comment",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "Comment_timelineId_Timeline_id_fk": {
+ "name": "Comment_timelineId_Timeline_id_fk",
+ "tableFrom": "Comment",
+ "tableTo": "Timeline",
+ "columnsFrom": [
+ "timelineId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "Commitment": {
+ "name": "Commitment",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "hobbyName": {
+ "name": "hobbyName",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "goalDays": {
+ "name": "goalDays",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 30
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'active'"
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'private'"
+ },
+ "startDate": {
+ "name": "startDate",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "completedAt": {
+ "name": "completedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "Commitment_userId_idx": {
+ "name": "Commitment_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ },
+ "Commitment_status_idx": {
+ "name": "Commitment_status_idx",
+ "columns": [
+ "status"
+ ],
+ "isUnique": false
+ },
+ "Commitment_visibility_idx": {
+ "name": "Commitment_visibility_idx",
+ "columns": [
+ "visibility"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "Commitment_userId_User_id_fk": {
+ "name": "Commitment_userId_User_id_fk",
+ "tableFrom": "Commitment",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "DailyCheckin": {
+ "name": "DailyCheckin",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dayDate": {
+ "name": "dayDate",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "amCompleted": {
+ "name": "amCompleted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "pmCompleted": {
+ "name": "pmCompleted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "DailyCheckin_userId_dayDate_key": {
+ "name": "DailyCheckin_userId_dayDate_key",
+ "columns": [
+ "userId",
+ "dayDate"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "DailyCheckin_userId_User_id_fk": {
+ "name": "DailyCheckin_userId_User_id_fk",
+ "tableFrom": "DailyCheckin",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "Follow": {
+ "name": "Follow",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "followerId": {
+ "name": "followerId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "followingId": {
+ "name": "followingId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "Follow_followerId_followingId_key": {
+ "name": "Follow_followerId_followingId_key",
+ "columns": [
+ "followerId",
+ "followingId"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "Follow_followerId_User_id_fk": {
+ "name": "Follow_followerId_User_id_fk",
+ "tableFrom": "Follow",
+ "tableTo": "User",
+ "columnsFrom": [
+ "followerId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "Follow_followingId_User_id_fk": {
+ "name": "Follow_followingId_User_id_fk",
+ "tableFrom": "Follow",
+ "tableTo": "User",
+ "columnsFrom": [
+ "followingId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "HabitLog": {
+ "name": "HabitLog",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "habitId": {
+ "name": "habitId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dayDate": {
+ "name": "dayDate",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "completed": {
+ "name": "completed",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "HabitLog_habitId_dayDate_key": {
+ "name": "HabitLog_habitId_dayDate_key",
+ "columns": [
+ "habitId",
+ "dayDate"
+ ],
+ "isUnique": true
+ },
+ "HabitLog_userId_idx": {
+ "name": "HabitLog_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "HabitLog_habitId_Habit_id_fk": {
+ "name": "HabitLog_habitId_Habit_id_fk",
+ "tableFrom": "HabitLog",
+ "tableTo": "Habit",
+ "columnsFrom": [
+ "habitId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "HabitLog_userId_User_id_fk": {
+ "name": "HabitLog_userId_User_id_fk",
+ "tableFrom": "HabitLog",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "Habit": {
+ "name": "Habit",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'active'"
+ },
+ "targetFrequency": {
+ "name": "targetFrequency",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'daily'"
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "sourceQuestId": {
+ "name": "sourceQuestId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "commitmentId": {
+ "name": "commitmentId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "Habit_userId_idx": {
+ "name": "Habit_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ },
+ "Habit_sourceQuestId_idx": {
+ "name": "Habit_sourceQuestId_idx",
+ "columns": [
+ "sourceQuestId"
+ ],
+ "isUnique": false
+ },
+ "Habit_commitmentId_idx": {
+ "name": "Habit_commitmentId_idx",
+ "columns": [
+ "commitmentId"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "Habit_userId_User_id_fk": {
+ "name": "Habit_userId_User_id_fk",
+ "tableFrom": "Habit",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "Habit_commitmentId_Commitment_id_fk": {
+ "name": "Habit_commitmentId_Commitment_id_fk",
+ "tableFrom": "Habit",
+ "tableTo": "Commitment",
+ "columnsFrom": [
+ "commitmentId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "JournalEntry": {
+ "name": "JournalEntry",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dayDate": {
+ "name": "dayDate",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "amEntry": {
+ "name": "amEntry",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "pmEntry": {
+ "name": "pmEntry",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "timelineId": {
+ "name": "timelineId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "commitmentId": {
+ "name": "commitmentId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "noveltyId": {
+ "name": "noveltyId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "noveltyText": {
+ "name": "noveltyText",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "noveltyCompleted": {
+ "name": "noveltyCompleted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "JournalEntry_userId_dayDate_key": {
+ "name": "JournalEntry_userId_dayDate_key",
+ "columns": [
+ "userId",
+ "dayDate"
+ ],
+ "isUnique": true
+ },
+ "JournalEntry_userId_idx": {
+ "name": "JournalEntry_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ },
+ "JournalEntry_timelineId_idx": {
+ "name": "JournalEntry_timelineId_idx",
+ "columns": [
+ "timelineId"
+ ],
+ "isUnique": false
+ },
+ "JournalEntry_commitmentId_idx": {
+ "name": "JournalEntry_commitmentId_idx",
+ "columns": [
+ "commitmentId"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "JournalEntry_userId_User_id_fk": {
+ "name": "JournalEntry_userId_User_id_fk",
+ "tableFrom": "JournalEntry",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "JournalEntry_timelineId_Timeline_id_fk": {
+ "name": "JournalEntry_timelineId_Timeline_id_fk",
+ "tableFrom": "JournalEntry",
+ "tableTo": "Timeline",
+ "columnsFrom": [
+ "timelineId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "JournalEntry_commitmentId_Commitment_id_fk": {
+ "name": "JournalEntry_commitmentId_Commitment_id_fk",
+ "tableFrom": "JournalEntry",
+ "tableTo": "Commitment",
+ "columnsFrom": [
+ "commitmentId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {
+ "JournalEntry_single_context_check": {
+ "name": "JournalEntry_single_context_check",
+ "value": "\"JournalEntry\".\"timelineId\" IS NULL OR \"JournalEntry\".\"commitmentId\" IS NULL"
+ }
+ }
+ },
+ "Like": {
+ "name": "Like",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "timelineId": {
+ "name": "timelineId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "Like_userId_timelineId_key": {
+ "name": "Like_userId_timelineId_key",
+ "columns": [
+ "userId",
+ "timelineId"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "Like_userId_User_id_fk": {
+ "name": "Like_userId_User_id_fk",
+ "tableFrom": "Like",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "Like_timelineId_Timeline_id_fk": {
+ "name": "Like_timelineId_Timeline_id_fk",
+ "tableFrom": "Like",
+ "tableTo": "Timeline",
+ "columnsFrom": [
+ "timelineId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "native_atlas_state": {
+ "name": "native_atlas_state",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "payload": {
+ "name": "payload",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "native_atlas_state_user_id_auth_user_id_fk": {
+ "name": "native_atlas_state_user_id_auth_user_id_fk",
+ "tableFrom": "native_atlas_state",
+ "tableTo": "auth_user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "native_auth_handoffs": {
+ "name": "native_auth_handoffs",
+ "columns": {
+ "code_hash": {
+ "name": "code_hash",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "session_token": {
+ "name": "session_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "native_auth_handoffs_expiry_idx": {
+ "name": "native_auth_handoffs_expiry_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "auth_session": {
+ "name": "auth_session",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expiresAt": {
+ "name": "expiresAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "ipAddress": {
+ "name": "ipAddress",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "userAgent": {
+ "name": "userAgent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "auth_session_token_unique": {
+ "name": "auth_session_token_unique",
+ "columns": [
+ "token"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "auth_session_userId_auth_user_id_fk": {
+ "name": "auth_session_userId_auth_user_id_fk",
+ "tableFrom": "auth_session",
+ "tableTo": "auth_user",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "Session": {
+ "name": "Session",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "sessionToken": {
+ "name": "sessionToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires": {
+ "name": "expires",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "Session_sessionToken_unique": {
+ "name": "Session_sessionToken_unique",
+ "columns": [
+ "sessionToken"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "Session_userId_User_id_fk": {
+ "name": "Session_userId_User_id_fk",
+ "tableFrom": "Session",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "Stamp": {
+ "name": "Stamp",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "commitmentId": {
+ "name": "commitmentId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dayDate": {
+ "name": "dayDate",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dayIndex": {
+ "name": "dayIndex",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "proofUrl": {
+ "name": "proofUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "proofType": {
+ "name": "proofType",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'url'"
+ },
+ "note": {
+ "name": "note",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "Stamp_commitmentId_dayDate_key": {
+ "name": "Stamp_commitmentId_dayDate_key",
+ "columns": [
+ "commitmentId",
+ "dayDate"
+ ],
+ "isUnique": true
+ },
+ "Stamp_commitmentId_idx": {
+ "name": "Stamp_commitmentId_idx",
+ "columns": [
+ "commitmentId"
+ ],
+ "isUnique": false
+ },
+ "Stamp_userId_idx": {
+ "name": "Stamp_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "Stamp_commitmentId_Commitment_id_fk": {
+ "name": "Stamp_commitmentId_Commitment_id_fk",
+ "tableFrom": "Stamp",
+ "tableTo": "Commitment",
+ "columnsFrom": [
+ "commitmentId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "Stamp_userId_User_id_fk": {
+ "name": "Stamp_userId_User_id_fk",
+ "tableFrom": "Stamp",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "Timeline": {
+ "name": "Timeline",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'PRIVATE'"
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "phases": {
+ "name": "phases",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "pins": {
+ "name": "pins",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "versions": {
+ "name": "versions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "Timeline_slug_unique": {
+ "name": "Timeline_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ },
+ "Timeline_userId_idx": {
+ "name": "Timeline_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ },
+ "Timeline_slug_idx": {
+ "name": "Timeline_slug_idx",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": false
+ },
+ "Timeline_visibility_idx": {
+ "name": "Timeline_visibility_idx",
+ "columns": [
+ "visibility"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "Timeline_userId_User_id_fk": {
+ "name": "Timeline_userId_User_id_fk",
+ "tableFrom": "Timeline",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "TrajectoryContract": {
+ "name": "TrajectoryContract",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "previousContractId": {
+ "name": "previousContractId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "constraintsText": {
+ "name": "constraintsText",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "intentText": {
+ "name": "intentText",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "decisionPolicyText": {
+ "name": "decisionPolicyText",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "feedbackLoopText": {
+ "name": "feedbackLoopText",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "cadence": {
+ "name": "cadence",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'active'"
+ },
+ "openedAt": {
+ "name": "openedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "closedAt": {
+ "name": "closedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "TrajectoryContract_userId_idx": {
+ "name": "TrajectoryContract_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ },
+ "TrajectoryContract_one_active_per_user_idx": {
+ "name": "TrajectoryContract_one_active_per_user_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": true,
+ "where": "\"TrajectoryContract\".\"status\" = 'active'"
+ }
+ },
+ "foreignKeys": {
+ "TrajectoryContract_userId_User_id_fk": {
+ "name": "TrajectoryContract_userId_User_id_fk",
+ "tableFrom": "TrajectoryContract",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "TrajectoryEntry": {
+ "name": "TrajectoryEntry",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "eraId": {
+ "name": "eraId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "bucket": {
+ "name": "bucket",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "monthKey": {
+ "name": "monthKey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "reflection": {
+ "name": "reflection",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "''"
+ },
+ "numbers": {
+ "name": "numbers",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "TrajectoryEntry_eraId_monthKey_key": {
+ "name": "TrajectoryEntry_eraId_monthKey_key",
+ "columns": [
+ "eraId",
+ "monthKey"
+ ],
+ "isUnique": true
+ },
+ "TrajectoryEntry_userId_idx": {
+ "name": "TrajectoryEntry_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ },
+ "TrajectoryEntry_userId_bucket_idx": {
+ "name": "TrajectoryEntry_userId_bucket_idx",
+ "columns": [
+ "userId",
+ "bucket"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "TrajectoryEntry_eraId_TrajectoryEra_id_fk": {
+ "name": "TrajectoryEntry_eraId_TrajectoryEra_id_fk",
+ "tableFrom": "TrajectoryEntry",
+ "tableTo": "TrajectoryEra",
+ "columnsFrom": [
+ "eraId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "TrajectoryEntry_userId_User_id_fk": {
+ "name": "TrajectoryEntry_userId_User_id_fk",
+ "tableFrom": "TrajectoryEntry",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "TrajectoryEra": {
+ "name": "TrajectoryEra",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "bucket": {
+ "name": "bucket",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "idealText": {
+ "name": "idealText",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'active'"
+ },
+ "openedAt": {
+ "name": "openedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "closedAt": {
+ "name": "closedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "TrajectoryEra_userId_idx": {
+ "name": "TrajectoryEra_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ },
+ "TrajectoryEra_userId_bucket_status_idx": {
+ "name": "TrajectoryEra_userId_bucket_status_idx",
+ "columns": [
+ "userId",
+ "bucket",
+ "status"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "TrajectoryEra_userId_User_id_fk": {
+ "name": "TrajectoryEra_userId_User_id_fk",
+ "tableFrom": "TrajectoryEra",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "TrajectoryReview": {
+ "name": "TrajectoryReview",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "contractId": {
+ "name": "contractId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "signalText": {
+ "name": "signalText",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "decision": {
+ "name": "decision",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "TrajectoryReview_contractId_idx": {
+ "name": "TrajectoryReview_contractId_idx",
+ "columns": [
+ "contractId"
+ ],
+ "isUnique": false
+ },
+ "TrajectoryReview_userId_idx": {
+ "name": "TrajectoryReview_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "TrajectoryReview_contractId_TrajectoryContract_id_fk": {
+ "name": "TrajectoryReview_contractId_TrajectoryContract_id_fk",
+ "tableFrom": "TrajectoryReview",
+ "tableTo": "TrajectoryContract",
+ "columnsFrom": [
+ "contractId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "TrajectoryReview_userId_User_id_fk": {
+ "name": "TrajectoryReview_userId_User_id_fk",
+ "tableFrom": "TrajectoryReview",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "auth_user": {
+ "name": "auth_user",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "emailVerified": {
+ "name": "emailVerified",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "auth_user_email_unique": {
+ "name": "auth_user_email_unique",
+ "columns": [
+ "email"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "UserQuest": {
+ "name": "UserQuest",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "questId": {
+ "name": "questId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'rediscovery'"
+ },
+ "sourceHobby": {
+ "name": "sourceHobby",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "sourceTimelineId": {
+ "name": "sourceTimelineId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "sourceBucketItemId": {
+ "name": "sourceBucketItemId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "arcId": {
+ "name": "arcId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "emoji": {
+ "name": "emoji",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'active'"
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'private'"
+ },
+ "startedAt": {
+ "name": "startedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "completedAt": {
+ "name": "completedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "UserQuest_userId_status_idx": {
+ "name": "UserQuest_userId_status_idx",
+ "columns": [
+ "userId",
+ "status"
+ ],
+ "isUnique": false
+ },
+ "UserQuest_userId_visibility_idx": {
+ "name": "UserQuest_userId_visibility_idx",
+ "columns": [
+ "userId",
+ "visibility"
+ ],
+ "isUnique": false
+ },
+ "UserQuest_userId_questId_active_key": {
+ "name": "UserQuest_userId_questId_active_key",
+ "columns": [
+ "userId",
+ "questId",
+ "status"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "UserQuest_userId_User_id_fk": {
+ "name": "UserQuest_userId_User_id_fk",
+ "tableFrom": "UserQuest",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "User": {
+ "name": "User",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "emailVerified": {
+ "name": "emailVerified",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "birthYear": {
+ "name": "birthYear",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "birthDate": {
+ "name": "birthDate",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "weekStartsOn": {
+ "name": "weekStartsOn",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "bio": {
+ "name": "bio",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "website": {
+ "name": "website",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "creed": {
+ "name": "creed",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "onboardingData": {
+ "name": "onboardingData",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "onboardingCompletedAt": {
+ "name": "onboardingCompletedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "completedQuests": {
+ "name": "completedQuests",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "earnedBadges": {
+ "name": "earnedBadges",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "User_email_unique": {
+ "name": "User_email_unique",
+ "columns": [
+ "email"
+ ],
+ "isUnique": true
+ },
+ "User_username_unique": {
+ "name": "User_username_unique",
+ "columns": [
+ "username"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "auth_verification": {
+ "name": "auth_verification",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expiresAt": {
+ "name": "expiresAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "VerificationToken": {
+ "name": "VerificationToken",
+ "columns": {
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires": {
+ "name": "expires",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "VerificationToken_token_unique": {
+ "name": "VerificationToken_token_unique",
+ "columns": [
+ "token"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "VerificationToken_identifier_token_pk": {
+ "columns": [
+ "identifier",
+ "token"
+ ],
+ "name": "VerificationToken_identifier_token_pk"
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "WeeklyLogEntry": {
+ "name": "WeeklyLogEntry",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "weekOf": {
+ "name": "weekOf",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "text": {
+ "name": "text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "promptText": {
+ "name": "promptText",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "WeeklyLogEntry_userId_weekOf_key": {
+ "name": "WeeklyLogEntry_userId_weekOf_key",
+ "columns": [
+ "userId",
+ "weekOf"
+ ],
+ "isUnique": true
+ },
+ "WeeklyLogEntry_userId_idx": {
+ "name": "WeeklyLogEntry_userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "WeeklyLogEntry_userId_User_id_fk": {
+ "name": "WeeklyLogEntry_userId_User_id_fk",
+ "tableFrom": "WeeklyLogEntry",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/migrations/d1/meta/_journal.json b/migrations/d1/meta/_journal.json
index c8e6d1c3..915a497d 100644
--- a/migrations/d1/meta/_journal.json
+++ b/migrations/d1/meta/_journal.json
@@ -36,6 +36,13 @@
"when": 1785681331925,
"tag": "0004_nifty_xavin",
"breakpoints": true
+ },
+ {
+ "idx": 5,
+ "version": "6",
+ "when": 1789913353669,
+ "tag": "0005_fancy_wild_child",
+ "breakpoints": true
}
]
-}
+}
\ No newline at end of file
diff --git a/src/app/api/weekly-nudge/route.ts b/src/app/api/weekly-nudge/route.ts
new file mode 100644
index 00000000..143cab4a
--- /dev/null
+++ b/src/app/api/weekly-nudge/route.ts
@@ -0,0 +1,59 @@
+import { NextResponse } from 'next/server';
+
+import { resolveWeeklyNudge } from '~/lib/weekly-nudge';
+import { weekStartFor, normalizeWeekStartsOn } from '~/lib/weekly-log';
+import { dayKeyIn } from '~/lib/day';
+import type { NudgeSignals } from '~/lib/weekly-questions';
+
+export const dynamic = 'force-dynamic';
+
+const WEEK_OF_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
+
+function cleanSignals(value: unknown): NudgeSignals | null {
+ if (!value || typeof value !== 'object') return null;
+ const raw = value as Record
- Daily. One ritual, twice a day. - Morning prompts, habit check-ins, and a journal entry. The quiet capture of how - you're spending your days. Private by default — no one sees this but you. + Daily. Small practices checked in, + never scored. And once a week — usually a Sunday — one honest question: what did you + actually do with your life this week? The quiet capture of how you're spending your + days. Private by default — no one sees this but you.
@@ -75,8 +76,8 @@ export default function ManifestoPage() {
- The journal is the bridge. You write about practicing your hobby, and that entry - connects your daily practice to your life aspirations. The days become weeks. The weeks + The weekly log is the bridge. You write what you actually lived, and that record + connects your weeks to the life you said you wanted. The days become weeks. The weeks become a life.
diff --git a/src/app/onboarding/onboarding-flow.tsx b/src/app/onboarding/onboarding-flow.tsx index 45fb7ea5..3705b064 100644 --- a/src/app/onboarding/onboarding-flow.tsx +++ b/src/app/onboarding/onboarding-flow.tsx @@ -8,56 +8,35 @@ import { Compass, Footprints, History, - NotebookPen, Plus, Search, Sparkles, - Target, UserRound, } from 'lucide-react'; -import Image from 'next/image'; import { useRouter } from 'next/navigation'; import { useEffect, useState, useTransition } from 'react'; import { AmbientMusic } from '~/components/ambient-music'; +import { LifeGrid } from '~/components/life-grid'; import { StorageModeProvider, StorageModeStatus } from '~/components/storage-mode-provider'; import { completeOnboarding } from '~/lib/actions/onboarding'; import { browserRecordAdapter, readLocalRecord, writeLocalRecord } from '~/lib/local-record-store'; import { createLocalTrajectory } from '~/lib/local-trajectory'; import { syncLocalWorkspaceCookie } from '~/lib/local-workspace-cookie'; +import { buildLifeGrid } from '~/lib/mortality'; import type { StorageMode } from '~/lib/storage-mode'; - -type Possibility = { - title: string; - category: - | 'travel' - | 'adventure' - | 'creative' - | 'achievement' - | 'relationships' - | 'contribution' - | 'food' - | 'health' - | 'mindfulness' - | 'reflection'; - emoji: string; -}; - -type Draft = { - version: 2; - step: number; - name: string; - birthDate: string; - pastHobbies: string[]; - pastInput: string; - desiredExperiences: Possibility[]; - desireInput: string; - annualFocus: string; - annualGoals: string[]; - goalInput: string; - habit: string; - trajectoryIntent: string; - trajectoryConstraint: string; -}; +import { + ChoiceChip, + DirectionStep, + Field, + FutureStep, + goalsFromDraft, + InlineAdd, + saveLocalOnboarding, + SelectionList, + Step, + type Draft, + type Possibility, +} from './onboarding-steps'; const pastExamples = ['Drawing', 'Football', 'Gaming', 'Piano', 'Reading', 'Cycling', 'Cooking']; const profiles = [ @@ -91,14 +70,6 @@ function isDraft(value: unknown): value is Draft { return draft.version === 2 && typeof draft.step === 'number' && Array.isArray(draft.pastHobbies); } -function goalsFromDraft(draft: Draft): string[] { - return Array.isArray(draft.annualGoals) && draft.annualGoals.length > 0 - ? draft.annualGoals - : draft.annualFocus - ? [draft.annualFocus] - : []; -} - export function OnboardingFlow({ user, storageMode, @@ -124,6 +95,7 @@ export function OnboardingFlow({ saved ? { ...saved, + step: Math.min(Math.max(0, saved.step), 4), annualGoals: Array.isArray(saved.annualGoals) ? saved.annualGoals : saved.annualFocus @@ -253,59 +225,7 @@ export function OnboardingFlow({ return; } } else { - const adapter = browserRecordAdapter(); - await Promise.all([ - writeLocalRecord(adapter, 'profile:draft', 'profile', { name: draft.name }), - writeLocalRecord(adapter, 'profile:birth-date', 'profile', { birthDate: draft.birthDate }), - writeLocalRecord(adapter, 'onboarding:profile', 'onboarding', { - name: draft.name, - birthDate: draft.birthDate, - pastHobbies: draft.pastHobbies, - desiredExperiences: draft.desiredExperiences, - annualFocus: annualGoals[0] ?? '', - annualGoals, - }), - writeLocalRecord(adapter, 'timeline-draft-new', 'timelines', { - title: 'My life so far', - phases: [ - { - id: `local-earlier-${crypto.randomUUID()}`, - label: 'Earlier chapters', - hobbies: draft.pastHobbies.map((name) => ({ name })), - order: 0, - }, - ], - }), - writeLocalRecord(adapter, 'onboarding:bucket-items', 'bucket-list', { - items: draft.desiredExperiences, - annualFocus: annualGoals[0] ?? '', - annualGoals, - }), - ]); - const daily = (await readLocalRecord(adapter, 'daily:state', 'daily', isDailyState)) ?? { - habits: [], - logs: [], - journals: [], - }; - if ( - draft.habit.trim() && - !daily.habits.some( - (habit) => String(habit.name).toLowerCase() === draft.habit.toLowerCase() - ) - ) { - daily.habits.push({ - id: `local-habit-${crypto.randomUUID()}`, - name: draft.habit, - status: 'active', - targetFrequency: 'daily', - icon: null, - sourceQuestId: null, - commitmentId: null, - }); - } - await writeLocalRecord(adapter, 'daily:state', 'daily', daily); - await createLocalTrajectory(trajectory, adapter).catch(() => undefined); - syncLocalWorkspaceCookie(true); + await saveLocalOnboarding(draft, annualGoals, trajectory); } router.push('/live-more'); router.refresh(); @@ -328,11 +248,13 @@ export function OnboardingFlow({ 'bg-[#211e18]', 'bg-[#b9dcf5]', 'bg-[#ffd0bd]', - 'bg-[#c5abfa]', - 'bg-[#dceabf]', 'bg-[#f7e957]', ]; + const revealGrid = validBirthDate + ? buildLifeGrid(new Date(`${draft.birthDate}T12:00:00`), new Set()) + : null; + const steps = [- “Today is the first day of the rest of your life.” --
This is only the beginning.
-- Search 5,000+ paths—or add something only you could imagine. The possibilities are - nearly endless. -
-Nothing in the atlas matches that yet.
-Add your own possibility below.
-- Showing the first {visiblePossibilities.length}. Add another word to narrow the search. -
- ) : null} -- Add one idea, or paste a numbered list. Each line becomes its own item. -
-- Fifty is enough for onboarding. You can keep growing the list inside Live More. -
- ) : null} - , -- Or borrow something from your bucket list -
-Direction · goals for {new Date().getFullYear()}
-- Decision policy: when time, energy, or money opens up, choose the next - feasible step toward any current goal. -
-- Feedback loop: once a month, notice what created energy and adjust. -
-- {error} -
- ) : null} - , + model={{ + draft, + setDraft, + togglePossibility, + addDesireInput, + possibilityQuery, + setPossibilityQuery, + visiblePossibilities, + matchingCount: matchingPossibilities.length, + hasQuery: !!normalizedPossibilityQuery, + onNext: () => setStep(4), + }} + />, +Reading your saved starting point…
; @@ -789,7 +479,7 @@ function JourneyCanvas({ draft }: { draft: Draft }) { {chapters.map((chapter, index) => ({chapter.label}
{chapter.title}
@@ -803,165 +493,3 @@ function JourneyCanvas({ draft }: { draft: Draft }) { ); } - -function Step({ - icon, - eyebrow, - title, - copy, - action, - onNext, - secondaryAction, - onSecondary, - disabled, - dark, - children, -}: { - icon: React.ReactNode; - eyebrow: string; - title: string; - copy: string; - action: string; - onNext: () => void; - secondaryAction?: string; - onSecondary?: () => void; - disabled?: boolean; - dark?: boolean; - children?: React.ReactNode; -}) { - return ( -- {eyebrow} -
-- {copy} -
- {children} - - {secondaryAction && onSecondary ? ( - - ) : null} -- Your chapters begin with: {values.join(' · ')} -
- ); -} - -function isDailyState( - value: unknown -): value is { habits: ArrayThis is only the beginning.
++ Search 5,000+ paths—or add something only you could imagine. The possibilities are + nearly endless. +
+Nothing in the atlas matches that yet.
+Add your own possibility below.
++ Showing the first {visiblePossibilities.length}. Add another word to narrow the search. +
+ ) : null} ++ Add one idea, or paste a numbered list. Each line becomes its own item. +
++ Fifty is enough for onboarding. You can keep growing the list inside Live More. +
+ ) : null} + + ); +} + +type DirectionStepModel = { + draft: Draft; + setDraft: React.Dispatch+ {error} +
+ ) : null} + + ); +} + +function GoalPicker({ model }: { model: DirectionStepModel }) { + const { draft, setDraft, annualGoals, addAnnualGoal } = model; + return ( +Goals for {new Date().getFullYear()}
+ {annualGoals.length ? ( ++ Or borrow something from your bucket list +
++ Decision policy: when time, energy, or money opens up, choose the next + feasible step toward any current goal. +
++ Feedback loop: once a month, notice what created energy and adjust. +
++ Weekly log: each week, Live asks what you lived — that is how the list + stays honest. +
++ {eyebrow} +
++ {copy} +
+ {children} + + {secondaryAction && onSecondary ? ( + + ) : null} ++ Your chapters begin with: {values.join(' · ')} +
+ ); +} + +function isDailyState( + value: unknown +): value is { habits: ArrayIn your own words
+Before the weekly log
Your first saved reflection will live here.
-- This is a private archive, not a publishing feed. -
-- Loading {surface === 'journal' ? 'Journal' : 'Habits'} from this device… + Loading Habits from this device…
); - const todayJournal = state.journals.find((entry) => entry.dayDate === today) ?? null; return (- {surface === 'journal' - ? 'One honest sentence is enough.' - : 'The small repeated thing. Checked in, never scored.'} + The small repeated thing. Checked in, never scored.
- {logs.some((log) => log.completed) - ? `${logs.filter((log) => log.completed).length} checked in today` - : 'Ready when you are'} +
+ {logs.some((log) => log.completed) + ? `${logs.filter((log) => log.completed).length} checked in today` + : 'Ready when you are'} +
++ The small repeated thing. Checked in, never scored.
++ Related commitments are private planning context. Habit check-ins never create proof + or commitment progress. +
++ {habitMutationError} +
+ )} + + {habits.length === 0 && !showHabitManager ? ( +Private journal
-Begin with one repeatable thing.
++ Small enough to do on an ordinary day. +
- {isTodaySelected ? journalTitle : formatJournalDate(selectedDate)} -
- This morning -
-- {amEntry} -
-- The small repeated thing. Checked in, never scored. -
-- Related commitments are private planning context. Habit check-ins never create proof - or commitment progress. -
+ Habit name + + setNewHabit(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && handleAddHabit()} + placeholder="Habit name (e.g. Read 20 pages)" + className="flex-1 rounded-lg border border-border bg-background px-3 py-1.5 text-sm text-foreground focus:border-foreground/40 focus-visible:ring-2 focus-visible:ring-foreground/50 focus-visible:outline-none" + />- {habitMutationError} -
- )} - - {habits.length === 0 && !showHabitManager ? ( -- Begin with one repeatable thing. -
-- Small enough to do on an ordinary day. -
-Icon (optional)
+Target frequency
+Icon (optional)
-Target frequency
-- {habitCreateError} -
- )} - - {/* Add button */} -+ Loading your weekly log from this device… +
+ ); + } + + return ( ++ {suggested + ? 'Sunday is the natural moment — but any day counts.' + : 'One honest entry a week. Any day counts.'} +
+{formatWeekOf(weekOf)}
+{dream.title}
++ {dream.daysSinceMovement !== null + ? `Untouched for about ${Math.round(dream.daysSinceMovement / 7)} ${ + Math.round(dream.daysSinceMovement / 7) === 1 ? 'week' : 'weeks' + }` + : 'In your atlas, waiting'} +
+Weeks on record
++ {entry.promptText} +
+ ) : null} ++ {entry.text} +
+