@hybridlabor-api/aos 4.13.2 → 4.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/AGENTS.md +8 -0
- package/.agents/nodes.json +5 -2
- package/.claude/hooks/conventional-commits.mjs +14 -15
- package/.claude/hooks/env-file-protection.mjs +14 -15
- package/.claude/hooks/go-gate.mjs +152 -10
- package/.claude/hooks/go-token.mjs +55 -0
- package/.claude/hooks/memb-inject.mjs +75 -62
- package/.claude/hooks/trail-autostart.mjs +27 -0
- package/.claude/settings.json +18 -0
- package/.claude/workflows/startcycle-dispatch.mjs +11 -4
- package/.opencode/plugins/bdb-aos.js +98 -121
- package/.opencode/plugins/lib/trail-autostart.js +38 -0
- package/CLAUDE.md +1 -1
- package/README.de.md +6 -6
- package/README.md +6 -6
- package/README.pt.md +6 -6
- package/THIRD_PARTY_NOTICES.md +19 -3
- package/assets/header-v5.png +0 -0
- package/bin/aos-acp.mjs +211 -0
- package/bin/aos-doctor.mjs +1 -1
- package/bin/aos-uninstall.mjs +2 -2
- package/docs/master-session-acp.md +51 -0
- package/installer.js +252 -35
- package/mcps/mcsc/packages/mcp/server.js +6 -7
- package/package.json +4 -3
- package/scripts/validate-skills.mjs +76 -0
- package/skills/basic/bdbmediastorm/SKILL.md +1 -1
- package/skills/basic/godmode-shipping/SKILL.md +3 -0
- package/skills/basic/master-session/SKILL.md +89 -0
- package/skills/basic/startcycle/SKILL.md +1 -1
- package/skills/basic/startcycle-graph/SKILL.md +2 -2
- package/skills/basic/startcycle-graph-user/SKILL.md +1 -1
- package/skills/basic/teamwork-preview/SKILL.md +1 -1
- package/skills/bdbrainstorm/SKILL.md +7 -1
- package/skills/global_config/agentic-harness-patterns/SKILL.md +257 -0
- package/skills/global_config/agentic-harness-patterns/metadata.json +10 -0
- package/skills/global_config/agentic-harness-patterns/references/agent-orchestration-pattern.md +97 -0
- package/skills/global_config/agentic-harness-patterns/references/bootstrap-sequence-pattern.md +106 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering/compress-pattern.md +78 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering/isolate-pattern.md +82 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering/select-pattern.md +86 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering-pattern.md +29 -0
- package/skills/global_config/agentic-harness-patterns/references/hook-lifecycle-pattern.md +111 -0
- package/skills/global_config/agentic-harness-patterns/references/memory-persistence-pattern.md +109 -0
- package/skills/global_config/agentic-harness-patterns/references/permission-gate-pattern.md +111 -0
- package/skills/global_config/agentic-harness-patterns/references/skill-runtime-pattern.md +104 -0
- package/skills/global_config/agentic-harness-patterns/references/task-decomposition-pattern.md +92 -0
- package/skills/global_config/agentic-harness-patterns/references/tool-registry-pattern.md +101 -0
- package/skills/global_config/agenttrail/SKILL.md +8 -0
- package/skills/global_config/agenttrail/bin/agenttrail.mjs +14 -0
- package/skills/global_config/agenttrail/bin/ensure.mjs +118 -0
- package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +1 -1
- package/skills/global_config/bdb-visual-edit/SKILL.md +51 -0
- package/skills/global_config/bdb-visual-edit/references/vite-react-source-attr.md +59 -0
- package/skills/global_config/bdb-visual-edit/scripts/pick-snippet.js +27 -0
- package/skills/global_config/bdb-visual-edit/scripts/sanitize-element.mjs +123 -0
- package/skills/global_config/factory-collect/SKILL.md +74 -0
- package/skills/global_config/factory-human-digest/SKILL.md +92 -0
- package/skills/global_config/factory-lookback/SKILL.md +95 -0
- package/skills/global_config/factory-review-prs/SKILL.md +63 -0
- package/skills/global_config/git-pr-review/SKILL.md +3 -0
- package/skills/global_config/grilling/SKILL.md +2 -0
- package/skills/global_config/mcsc/SKILL.md +1 -1
- package/skills/global_config/plan-arbiter/SKILL.md +125 -0
- package/skills/global_config/plan-canvas/SKILL.md +62 -5
- package/skills/global_config/plan-canvas/scripts/lib/loopback-guard.js +19 -3
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/README.md +285 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/agent-trail.js +129 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/board-client.js +124 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/demo-plan/canvas.mdx +19 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/demo-plan/plan.mdx +18 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/recap-demo/plan.mdx +72 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/README.md +29 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/architecture.json +30 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/builder/00_architecture.html +14950 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/builder/canvas.mdx +511 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/builder/plan.mdx +208 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/recap/plan.mdx +102 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/standard/plan.md +136 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/signup-storyboard/canvas.mdx +124 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/signup-storyboard/plan.mdx +37 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/index.js +188 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/kit.js +123 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/mdx.js +411 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/render.js +1291 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/architecture/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/architecture/plan.mdx +195 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/architecture/standard.md +95 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/bugfix/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/bugfix/plan.mdx +105 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/bugfix/standard.md +76 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/canvas.mdx +81 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/plan.mdx +145 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/standard.md +76 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/migration/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/migration/plan.mdx +172 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/migration/standard.md +100 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap/plan.mdx +67 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap/standard.md +49 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/canvas.mdx +63 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/plan.mdx +49 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/standard.md +39 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-review/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-review/plan.mdx +118 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-review/standard.md +57 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/release/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/release/plan.mdx +173 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/release/standard.md +96 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/research/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/research/plan.mdx +91 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/research/standard.md +54 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/canvas.mdx +53 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/plan.mdx +225 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/standard.md +111 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/theme.css +472 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/trail.js +216 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/markdown.js +1 -1
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/server.js +37 -4
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/ui.js +125 -29
- package/skills/global_config/plan-canvas/scripts/plan-canvas.js +196 -8
- package/skills/global_config/pr-recap/SKILL.md +47 -0
- package/skills/global_config/pr-recap/scripts/pr-recap.mjs +200 -0
- package/skills/global_config/quick-recap/SKILL.md +55 -0
- package/skills/global_config/stay-within-limits/SKILL.md +85 -0
- package/skills/global_config/triage/SKILL.md +3 -0
- package/skills/global_config/visual-edit/README.md +96 -0
- package/skills/global_config/visual-edit/SKILL.md +615 -0
- package/skills/global_config/visual-plan/README.md +93 -0
- package/skills/global_config/visual-plan/SKILL.md +544 -0
- package/skills/global_config/visual-plan/references/canvas.md +139 -0
- package/skills/global_config/visual-plan/references/connection.md +51 -0
- package/skills/global_config/visual-plan/references/document-quality.md +186 -0
- package/skills/global_config/visual-plan/references/exemplar.md +62 -0
- package/skills/global_config/visual-plan/references/local-files.md +99 -0
- package/skills/global_config/visual-plan/references/wireframe.md +319 -0
- package/skills/global_config/visual-recap/README.md +103 -0
- package/skills/global_config/visual-recap/SKILL.md +560 -0
- package/skills/global_config/visual-recap/references/connection.md +51 -0
- package/skills/global_config/visual-recap/references/local-files.md +99 -0
- package/skills/global_config/visual-recap/references/wireframe.md +319 -0
- package/skills/playbooks/pb-ci-fix/SKILL.md +49 -0
- package/skills/playbooks/pb-event-tracker/SKILL.md +45 -0
- package/skills/playbooks/pb-meeting-actions/SKILL.md +42 -0
- package/skills/playbooks/pb-project-new/SKILL.md +48 -0
- package/skills/playbooks/pb-week-plan/SKILL.md +45 -0
- package/assets/header-v4.jpg +0 -0
package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/builder/plan.mdx
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Harbor Notes - Team Notes MVP
|
|
3
|
+
status: draft
|
|
4
|
+
needs-auth: foundations
|
|
5
|
+
needs-notes-api: foundations, auth
|
|
6
|
+
needs-editor: notes-api
|
|
7
|
+
needs-sharing: notes-api, auth
|
|
8
|
+
needs-settings: auth, notes-api
|
|
9
|
+
needs-export: notes-api, sharing
|
|
10
|
+
needs-launch: editor, sharing, settings, export
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Harbor Notes: Team Notes MVP
|
|
14
|
+
|
|
15
|
+
## Goal
|
|
16
|
+
|
|
17
|
+
Harbor Notes is a team notes app. A small team signs up, creates a workspace, writes notes together and shares a read-only link with someone outside the team. The first private beta ships in six weeks. All names and data in this plan are invented.
|
|
18
|
+
|
|
19
|
+
- A new user reaches a working workspace in under two minutes.
|
|
20
|
+
- Two teammates edit the same note without losing text.
|
|
21
|
+
- A shared link works without an account and can be revoked at any time.
|
|
22
|
+
|
|
23
|
+
## Decision
|
|
24
|
+
|
|
25
|
+
<Decision title="How do users verify their email?" question="Which proof do we ask for after the signup form?" options={[
|
|
26
|
+
{ id: "code", label: "Six digit code", detail: "Survives a device switch, which is the common failure for links.", recommended: true },
|
|
27
|
+
{ id: "link", label: "Magic link", detail: "One tap, but breaks when the link opens in a different browser." },
|
|
28
|
+
{ id: "none", label: "No verification in the beta", detail: "Fastest, but invites cannot be trusted and bounces go unnoticed." },
|
|
29
|
+
]}>
|
|
30
|
+
|
|
31
|
+
A code works on the phone and the laptop alike, and costs one extra input box. We can add magic links later without changing the data model.
|
|
32
|
+
|
|
33
|
+
</Decision>
|
|
34
|
+
|
|
35
|
+
## Architecture
|
|
36
|
+
|
|
37
|
+
<Archify src="00_architecture.html" label="Harbor Notes system" height={600} />
|
|
38
|
+
|
|
39
|
+
<Callout tone="note" title="Why a queue and a worker">
|
|
40
|
+
|
|
41
|
+
Exports and invitation emails can take seconds. Running them off the request path keeps the editor responsive and lets us retry a failed job without the user waiting.
|
|
42
|
+
|
|
43
|
+
</Callout>
|
|
44
|
+
|
|
45
|
+
## Acceptance {#acceptance}
|
|
46
|
+
|
|
47
|
+
<Checklist title="Done when" items={[
|
|
48
|
+
{ label: "Signup to an empty dashboard takes six screens or fewer", checked: true },
|
|
49
|
+
{ label: "Back from any signup step keeps the typed values" },
|
|
50
|
+
{ label: "Autosave survives a reload while typing" },
|
|
51
|
+
{ label: "A revoked share link shows the Gone page within one minute" },
|
|
52
|
+
{ label: "A viewer cannot edit, invite or delete" },
|
|
53
|
+
{ label: "Notes list loads in under one second for 2,000 notes" },
|
|
54
|
+
]} />
|
|
55
|
+
|
|
56
|
+
## Build map
|
|
57
|
+
|
|
58
|
+
### Foundations {#foundations}
|
|
59
|
+
|
|
60
|
+
Repository, CI, design tokens and the database schema.
|
|
61
|
+
|
|
62
|
+
<Checklist title="Foundations tasks" items={[
|
|
63
|
+
{ label: "Repository, CI and deploy previews", checked: true },
|
|
64
|
+
{ label: "Design tokens and base components", checked: true },
|
|
65
|
+
{ label: "Schema for users, workspaces, notes and share links" },
|
|
66
|
+
]} />
|
|
67
|
+
|
|
68
|
+
<ImplementationMap files={[
|
|
69
|
+
{ path: "db/schema.sql", change: "added", note: "Users, workspaces, notes, members, share_links" },
|
|
70
|
+
{ path: "ui/tokens.css", change: "added", note: "Colour, spacing and type tokens" },
|
|
71
|
+
{ path: ".github/workflows/ci.yml", change: "added", note: "Lint, test, preview deploy" },
|
|
72
|
+
]} />
|
|
73
|
+
|
|
74
|
+
### Auth and invites {#auth}
|
|
75
|
+
|
|
76
|
+
Sessions, six digit codes and invite tokens.
|
|
77
|
+
|
|
78
|
+
<Checklist title="Auth tasks" items={[
|
|
79
|
+
{ label: "Create account and send the code" },
|
|
80
|
+
{ label: "Verify the code, expire after ten minutes" },
|
|
81
|
+
{ label: "Session cookies with rotation" },
|
|
82
|
+
{ label: "Invite tokens with a role" },
|
|
83
|
+
]} />
|
|
84
|
+
|
|
85
|
+
<ImplementationMap files={[
|
|
86
|
+
{ path: "api/auth/signup.ts", change: "added", note: "Create user and send code" },
|
|
87
|
+
{ path: "api/auth/verify.ts", change: "added", note: "Check code, start session" },
|
|
88
|
+
{ path: "api/auth/session.ts", change: "added", note: "Cookie rotation and logout" },
|
|
89
|
+
{ path: "app/signup/verify.tsx", change: "added", note: "Six code boxes with paste handling" },
|
|
90
|
+
]} />
|
|
91
|
+
|
|
92
|
+
### Notes API {#notes-api}
|
|
93
|
+
|
|
94
|
+
Create, read, update, archive and search notes.
|
|
95
|
+
|
|
96
|
+
<Checklist title="Notes API tasks" items={[
|
|
97
|
+
{ label: "CRUD endpoints with revision check" },
|
|
98
|
+
{ label: "Search by title, body and tag" },
|
|
99
|
+
{ label: "Attachment upload with a 10 MB limit" },
|
|
100
|
+
]} />
|
|
101
|
+
|
|
102
|
+
<ImplementationMap files={[
|
|
103
|
+
{ path: "api/notes/index.ts", change: "added", note: "List, create and search" },
|
|
104
|
+
{ path: "api/notes/item.ts", change: "added", note: "Read, update, archive with revision check" },
|
|
105
|
+
{ path: "api/files/upload.ts", change: "added", note: "Signed upload to object storage" },
|
|
106
|
+
]} />
|
|
107
|
+
|
|
108
|
+
### Editor and notes list {#editor}
|
|
109
|
+
|
|
110
|
+
The editor with autosave and the list with tag filter.
|
|
111
|
+
|
|
112
|
+
<Checklist title="Editor tasks" items={[
|
|
113
|
+
{ label: "Markdown editor with toolbar" },
|
|
114
|
+
{ label: "Autosave every two seconds" },
|
|
115
|
+
{ label: "Title lock while someone edits" },
|
|
116
|
+
{ label: "Notes list with tags and sort" },
|
|
117
|
+
]} />
|
|
118
|
+
|
|
119
|
+
<ImplementationMap files={[
|
|
120
|
+
{ path: "app/notes/list.tsx", change: "added", note: "List, filter and preview pane" },
|
|
121
|
+
{ path: "app/notes/editor.tsx", change: "added", note: "Toolbar, autosave and presence" },
|
|
122
|
+
{ path: "app/notes/use-autosave.ts", change: "added", note: "Debounced save with revision" },
|
|
123
|
+
]} />
|
|
124
|
+
|
|
125
|
+
### Sharing {#sharing}
|
|
126
|
+
|
|
127
|
+
Read-only links with an expiry date, and member roles.
|
|
128
|
+
|
|
129
|
+
<Checklist title="Sharing tasks" items={[
|
|
130
|
+
{ label: "Create and revoke share links" },
|
|
131
|
+
{ label: "Gone page for expired or revoked links" },
|
|
132
|
+
{ label: "Roles: owner, editor, viewer" },
|
|
133
|
+
]} />
|
|
134
|
+
|
|
135
|
+
<ImplementationMap files={[
|
|
136
|
+
{ path: "api/share/index.ts", change: "added", note: "Create, list and revoke links" },
|
|
137
|
+
{ path: "app/s/shared-note.tsx", change: "added", note: "Public read-only page" },
|
|
138
|
+
{ path: "app/notes/share-dialog.tsx", change: "added", note: "Expiry options and people list" },
|
|
139
|
+
]} />
|
|
140
|
+
|
|
141
|
+
### Workspace settings {#settings}
|
|
142
|
+
|
|
143
|
+
General settings, members and the danger zone.
|
|
144
|
+
|
|
145
|
+
<Checklist title="Settings tasks" items={[
|
|
146
|
+
{ label: "General tab with name and defaults" },
|
|
147
|
+
{ label: "Members tab with invites" },
|
|
148
|
+
{ label: "Danger zone with typed confirmation" },
|
|
149
|
+
]} />
|
|
150
|
+
|
|
151
|
+
<ImplementationMap files={[
|
|
152
|
+
{ path: "app/settings/index.tsx", change: "added", note: "Tabs and forms" },
|
|
153
|
+
{ path: "app/settings/members.tsx", change: "added", note: "Member list and invite dialog" },
|
|
154
|
+
]} />
|
|
155
|
+
|
|
156
|
+
### Export {#export}
|
|
157
|
+
|
|
158
|
+
A workspace as a ZIP of Markdown files, built by the worker.
|
|
159
|
+
|
|
160
|
+
<Checklist title="Export tasks" items={[
|
|
161
|
+
{ label: "Queue the export job" },
|
|
162
|
+
{ label: "Worker writes the ZIP to storage" },
|
|
163
|
+
{ label: "Email a download link that expires in a day" },
|
|
164
|
+
]} />
|
|
165
|
+
|
|
166
|
+
<ImplementationMap files={[
|
|
167
|
+
{ path: "worker/jobs/export.ts", change: "added", note: "Collect notes and write the ZIP" },
|
|
168
|
+
{ path: "worker/jobs/email.ts", change: "added", note: "Send the download link" },
|
|
169
|
+
{ path: "api/export.ts", change: "added", note: "Enqueue and report status" },
|
|
170
|
+
]} />
|
|
171
|
+
|
|
172
|
+
### Launch {#launch}
|
|
173
|
+
|
|
174
|
+
<Checklist title="Launch tasks" items={[
|
|
175
|
+
{ label: "Load test with 2,000 notes and 20 editors" },
|
|
176
|
+
{ label: "Accessibility pass: keyboard and contrast" },
|
|
177
|
+
{ label: "Private beta with five invited teams" },
|
|
178
|
+
]} />
|
|
179
|
+
|
|
180
|
+
<ImplementationMap files={[
|
|
181
|
+
{ path: "tests/load/notes.k6.js", change: "added", note: "Load script for the notes list and saves" },
|
|
182
|
+
{ path: "docs/beta-checklist.md", change: "added", note: "Go or no-go list" },
|
|
183
|
+
]} />
|
|
184
|
+
|
|
185
|
+
## Agent trail
|
|
186
|
+
|
|
187
|
+
<AgentTrail live="http://localhost:5331" />
|
|
188
|
+
|
|
189
|
+
## Risks
|
|
190
|
+
|
|
191
|
+
<Table title="Risk register" columns={["Risk", "Likelihood", "Impact", "Mitigation"]} rows={[
|
|
192
|
+
["Concurrent edits overwrite text", "Medium", "High", "Title lock, revision check, conflict banner"],
|
|
193
|
+
["Share links leak in forwarded mail", "Medium", "Medium", "Expiry by default, one click revoke"],
|
|
194
|
+
["Email deliverability is poor", "Medium", "High", "Dedicated sending domain, resend limit"],
|
|
195
|
+
["Export jobs pile up", "Low", "Medium", "Queue depth alert, per workspace rate limit"],
|
|
196
|
+
["Scope grows before the beta", "High", "Medium", "Freeze this list after week two"],
|
|
197
|
+
]} />
|
|
198
|
+
|
|
199
|
+
## Open questions
|
|
200
|
+
|
|
201
|
+
<QuestionForm title="Open Questions" questions={[
|
|
202
|
+
{ title: "Can viewers download attachments from a shared note?", mode: "single", options: [
|
|
203
|
+
{ label: "No, view only", recommended: true }, { label: "Yes, if the owner allows it" } ] },
|
|
204
|
+
{ title: "Default expiry for share links?", mode: "single", options: [
|
|
205
|
+
{ label: "7 days", recommended: true }, { label: "30 days" }, { label: "Never" } ] },
|
|
206
|
+
{ title: "Which beta extras do we want?", mode: "multi", options: [
|
|
207
|
+
{ label: "Audit log of link opens" }, { label: "Dark mode" }, { label: "Keyboard shortcuts sheet" } ] },
|
|
208
|
+
]} />
|
package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/recap/plan.mdx
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Share links with expiry and revoke
|
|
3
|
+
subtitle: Notes can be shared through a read-only link that expires after seven days and can be revoked in one click. Invented example.
|
|
4
|
+
kind: recap
|
|
5
|
+
pr: "#318"
|
|
6
|
+
branch: feat/share-links
|
|
7
|
+
base: main
|
|
8
|
+
commit: 9b3e7d2
|
|
9
|
+
files: 8
|
|
10
|
+
additions: 642
|
|
11
|
+
deletions: 57
|
|
12
|
+
author: sample-author
|
|
13
|
+
date: 2026-02-03
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Summary
|
|
17
|
+
|
|
18
|
+
Until now a note could only be read by workspace members. This change adds read-only share links for people outside the team. A link carries a random token, expires after seven days unless the owner picks another period, and can be revoked at any time. Recipients see a clean read-only page; expired or revoked links show a Gone page instead of an error.
|
|
19
|
+
|
|
20
|
+
- Tokens are 128 bit random values stored hashed; the plain token only appears in the URL.
|
|
21
|
+
- The public page never loads the editor bundle, so it opens fast on slow connections.
|
|
22
|
+
- Owners see who opened a link and how often, without personal data.
|
|
23
|
+
|
|
24
|
+
## Before and after
|
|
25
|
+
|
|
26
|
+
<Compare>
|
|
27
|
+
<Before>
|
|
28
|
+
<Screen surface="web" caption="Share dialog before: members only">
|
|
29
|
+
<FrameScreen>
|
|
30
|
+
<Row><Title text="Share note" /><Btn label="Close" /></Row>
|
|
31
|
+
<Chips items={[{ label: "People", active: true }]} />
|
|
32
|
+
<Row><IconSquare active /><Lines n={2} widths={[60, 36]} /></Row>
|
|
33
|
+
<Row><IconSquare /><Lines n={2} widths={[54, 30]} /></Row>
|
|
34
|
+
<Row><IconSquare /><Lines n={2} widths={[66, 34]} /></Row>
|
|
35
|
+
<Box dashed><Text value="Only workspace members can open this note." tone="muted" /></Box>
|
|
36
|
+
<Divider />
|
|
37
|
+
<Row><Btn label="Invite member" primary /></Row>
|
|
38
|
+
</FrameScreen>
|
|
39
|
+
</Screen>
|
|
40
|
+
</Before>
|
|
41
|
+
<After>
|
|
42
|
+
<Screen surface="web" caption="Share dialog after: link tab with expiry">
|
|
43
|
+
<FrameScreen>
|
|
44
|
+
<Row><Title text="Share note" /><Btn label="Close" /></Row>
|
|
45
|
+
<Chips items={[{ label: "Link", active: true }, { label: "People" }]} />
|
|
46
|
+
<Box><Text value="Anyone with the link can view" /><Lines n={1} widths={[72]} /></Box>
|
|
47
|
+
<SectionLabel label="Link expires" />
|
|
48
|
+
<TaskRow title="In 7 days" done note="default" />
|
|
49
|
+
<TaskRow title="In 30 days" />
|
|
50
|
+
<TaskRow title="Never" />
|
|
51
|
+
<Row><IconSquare active /><Lines n={2} widths={[60, 36]} /></Row>
|
|
52
|
+
<Divider />
|
|
53
|
+
<Row><Btn label="Revoke link" /><Btn label="Copy link" primary /></Row>
|
|
54
|
+
</FrameScreen>
|
|
55
|
+
</Screen>
|
|
56
|
+
</After>
|
|
57
|
+
</Compare>
|
|
58
|
+
|
|
59
|
+
## Files
|
|
60
|
+
|
|
61
|
+
<ImplementationMap title="Changed files" files={[
|
|
62
|
+
{ path: "db/migrations/0012_share_links.sql", change: "added", note: "share_links table with hashed token and expiry" },
|
|
63
|
+
{ path: "api/share/index.ts", change: "added", note: "Create, list and revoke links" },
|
|
64
|
+
{ path: "api/share/resolve.ts", change: "added", note: "Public lookup, returns note or Gone" },
|
|
65
|
+
{ path: "app/s/shared-note.tsx", change: "added", note: "Read-only page without the editor bundle" },
|
|
66
|
+
{ path: "app/notes/share-dialog.tsx", change: "modified", note: "Link tab, expiry options, revoke" },
|
|
67
|
+
{ path: "api/notes/item.ts", change: "modified", note: "Expose share count on the note" },
|
|
68
|
+
{ path: "app/notes/legacy-share-modal.tsx", change: "removed", note: "Replaced by the new dialog" },
|
|
69
|
+
{ path: "tests/share-links.test.ts", change: "added", note: "Expiry, revoke and permission cases" },
|
|
70
|
+
]} />
|
|
71
|
+
|
|
72
|
+
## Behaviour
|
|
73
|
+
|
|
74
|
+
<Table title="Link states" columns={["State", "Recipient sees", "Owner sees", "Next step"]} rows={[
|
|
75
|
+
["Active", "Read-only note", "Open count", "Revoke or wait for expiry"],
|
|
76
|
+
["Expired", "Gone page", "Expired badge", "Create a new link"],
|
|
77
|
+
["Revoked", "Gone page within one minute", "Revoked badge", "Create a new link"],
|
|
78
|
+
["Note archived", "Gone page", "Link paused", "Restore the note to resume"],
|
|
79
|
+
]} />
|
|
80
|
+
|
|
81
|
+
<CodeTabs tabs={[
|
|
82
|
+
{ label: "share/index.ts", language: "ts", code: "export async function createLink(noteId: string, days = 7) {\n const token = randomToken(16);\n await db.insert('share_links', {\n note_id: noteId,\n token_hash: sha256(token),\n expires_at: addDays(now(), days),\n });\n return `${BASE_URL}/s/${token}`;\n}" },
|
|
83
|
+
{ label: "share/resolve.ts", language: "ts", code: "export async function resolve(token: string) {\n const link = await db.findOne('share_links', { token_hash: sha256(token) });\n if (!link || link.revoked_at || link.expires_at < now()) return null;\n return db.get('notes', link.note_id);\n}" },
|
|
84
|
+
{ label: "0012_share_links.sql", language: "sql", code: "CREATE TABLE share_links (\n id BIGSERIAL PRIMARY KEY,\n note_id BIGINT NOT NULL REFERENCES notes(id),\n token_hash TEXT NOT NULL UNIQUE,\n expires_at TIMESTAMPTZ NOT NULL,\n revoked_at TIMESTAMPTZ\n);" },
|
|
85
|
+
]} />
|
|
86
|
+
|
|
87
|
+
## Decision
|
|
88
|
+
|
|
89
|
+
<Decision title="Token storage" question="How do we store share tokens?" options={[
|
|
90
|
+
{ id: "hash", label: "Store only a hash", detail: "A database leak does not expose working links.", recommended: true },
|
|
91
|
+
{ id: "plain", label: "Store the token", detail: "Lets us show the link again, but a leak exposes every link." },
|
|
92
|
+
]}>
|
|
93
|
+
|
|
94
|
+
The dialog shows the full link once. After that only the owner can revoke it and create a new one, which is an acceptable cost for the safer default.
|
|
95
|
+
|
|
96
|
+
</Decision>
|
|
97
|
+
|
|
98
|
+
<Callout tone="note" title="Review focus">
|
|
99
|
+
|
|
100
|
+
Check that revoke is visible to the public resolver within one minute, and that an archived note never leaks through an active link.
|
|
101
|
+
|
|
102
|
+
</Callout>
|
package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/standard/plan.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Harbor Notes: Team Notes MVP
|
|
2
|
+
|
|
3
|
+
> Invented example project. Every name, number and date is made up.
|
|
4
|
+
|
|
5
|
+
**Status:** draft for review **Owner:** product team **Target:** first private beta in six weeks
|
|
6
|
+
|
|
7
|
+
## 1. Goal
|
|
8
|
+
|
|
9
|
+
Harbor Notes is a team notes app. A small team signs up, creates a workspace, writes notes together and shares a read-only link with someone outside the team.
|
|
10
|
+
|
|
11
|
+
**Success looks like**
|
|
12
|
+
|
|
13
|
+
- A new user reaches a working workspace in under two minutes.
|
|
14
|
+
- Two teammates can edit the same note without losing text.
|
|
15
|
+
- A shared link works without an account and can be revoked at any time.
|
|
16
|
+
- Notes list loads in under one second for 2,000 notes.
|
|
17
|
+
|
|
18
|
+
**Out of scope for the beta:** comments, version history, native mobile apps, offline editing.
|
|
19
|
+
|
|
20
|
+
## 2. Decisions
|
|
21
|
+
|
|
22
|
+
| # | Question | Options | Recommendation | Why |
|
|
23
|
+
|---|----------|---------|----------------|-----|
|
|
24
|
+
| D1 | How do users verify their email? | Six digit code / magic link | **Six digit code** | Survives a device switch, the common failure for links |
|
|
25
|
+
| D2 | How are notes stored? | Markdown text / block JSON | **Markdown text** | Simple export, easy diffs, no migration lock-in |
|
|
26
|
+
| D3 | How do edits merge? | Last write wins / CRDT | **Last write wins per note, field locks on title** | Cheapest option that avoids lost text for small teams |
|
|
27
|
+
| D4 | How are files stored? | Database blobs / object storage | **Object storage** | Keeps the database small and backups fast |
|
|
28
|
+
| D5 | How do jobs run? | In request / queue and worker | **Queue and worker** | Exports and emails must not block the editor |
|
|
29
|
+
|
|
30
|
+
## 3. Scope and build order
|
|
31
|
+
|
|
32
|
+
### 3.1 Foundations
|
|
33
|
+
|
|
34
|
+
- [x] Repository, CI and a deploy preview per branch
|
|
35
|
+
- [x] Design tokens and the base component set
|
|
36
|
+
- [ ] Database schema for users, workspaces, notes and share links
|
|
37
|
+
- [ ] Auth service with sessions and invite tokens
|
|
38
|
+
|
|
39
|
+
### 3.2 Notes core
|
|
40
|
+
|
|
41
|
+
- [ ] Notes API: create, read, update, archive, search
|
|
42
|
+
- [ ] Editor with autosave every two seconds
|
|
43
|
+
- [ ] Notes list with tag filter and sort by last edit
|
|
44
|
+
- [ ] Attachment upload with a 10 MB limit
|
|
45
|
+
|
|
46
|
+
### 3.3 Sharing and settings
|
|
47
|
+
|
|
48
|
+
- [ ] Read-only share links with an expiry date
|
|
49
|
+
- [ ] Member invites by email with roles: owner, editor, viewer
|
|
50
|
+
- [ ] Workspace settings: name, members, danger zone
|
|
51
|
+
- [ ] Export a workspace as a ZIP of Markdown files
|
|
52
|
+
|
|
53
|
+
### 3.4 Launch
|
|
54
|
+
|
|
55
|
+
- [ ] Load test with 2,000 notes and 20 concurrent editors
|
|
56
|
+
- [ ] Accessibility pass: keyboard navigation and contrast
|
|
57
|
+
- [ ] Private beta with five invited teams
|
|
58
|
+
|
|
59
|
+
## 4. Request flow
|
|
60
|
+
|
|
61
|
+
```mermaid
|
|
62
|
+
flowchart LR
|
|
63
|
+
U[User] --> W[Web app]
|
|
64
|
+
W --> A[API]
|
|
65
|
+
A --> AU{Session valid?}
|
|
66
|
+
AU -- no --> L[Sign in screen]
|
|
67
|
+
AU -- yes --> DB[(Database)]
|
|
68
|
+
A --> Q[[Job queue]]
|
|
69
|
+
Q --> K[Worker]
|
|
70
|
+
K --> S[(File storage)]
|
|
71
|
+
K --> M[Email service]
|
|
72
|
+
DB --> A
|
|
73
|
+
A --> W
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 5. Sharing a note
|
|
77
|
+
|
|
78
|
+
```mermaid
|
|
79
|
+
sequenceDiagram
|
|
80
|
+
participant O as Owner
|
|
81
|
+
participant W as Web app
|
|
82
|
+
participant A as API
|
|
83
|
+
participant D as Database
|
|
84
|
+
participant R as Recipient
|
|
85
|
+
O->>W: Click Share
|
|
86
|
+
W->>A: POST /notes/42/share (expires in 7 days)
|
|
87
|
+
A->>D: Insert share token
|
|
88
|
+
D-->>A: token abc123
|
|
89
|
+
A-->>W: share URL
|
|
90
|
+
W-->>O: Copy link
|
|
91
|
+
O->>R: Sends the link
|
|
92
|
+
R->>A: GET /s/abc123
|
|
93
|
+
A->>D: Look up token and expiry
|
|
94
|
+
alt token valid
|
|
95
|
+
A-->>R: Read-only note
|
|
96
|
+
else expired or revoked
|
|
97
|
+
A-->>R: 410 Gone page
|
|
98
|
+
end
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## 6. Acceptance criteria
|
|
102
|
+
|
|
103
|
+
- [ ] Signup to empty dashboard takes six screens or fewer
|
|
104
|
+
- [ ] Back from any signup step keeps the typed values
|
|
105
|
+
- [ ] An expired code shows a resend action
|
|
106
|
+
- [ ] Autosave survives a reload during typing
|
|
107
|
+
- [ ] A revoked share link returns the Gone page within one minute
|
|
108
|
+
- [ ] A viewer cannot edit, invite or delete
|
|
109
|
+
|
|
110
|
+
## 7. Risks
|
|
111
|
+
|
|
112
|
+
| Risk | Likelihood | Impact | Mitigation |
|
|
113
|
+
|------|------------|--------|------------|
|
|
114
|
+
| Concurrent edits overwrite text | Medium | High | Field lock on title, revision check on save, conflict banner |
|
|
115
|
+
| Share links leak in forwarded mail | Medium | Medium | Expiry by default, one click revoke, view counter |
|
|
116
|
+
| Email deliverability is poor | Medium | High | Dedicated sending domain, plain text fallback, resend limit |
|
|
117
|
+
| Export jobs pile up | Low | Medium | Queue depth alert, per workspace rate limit |
|
|
118
|
+
| Scope grows before beta | High | Medium | Freeze the list in section 3 after week two |
|
|
119
|
+
|
|
120
|
+
## 8. Open questions
|
|
121
|
+
|
|
122
|
+
1. Should viewers be able to download attachments from a shared note?
|
|
123
|
+
2. Is a 7 day default expiry right, or should links never expire unless set?
|
|
124
|
+
3. Do we need an audit log of share link opens for the beta?
|
|
125
|
+
4. Which region hosts the first workspaces?
|
|
126
|
+
|
|
127
|
+
## 9. Timeline
|
|
128
|
+
|
|
129
|
+
| Week | Milestone |
|
|
130
|
+
|------|-----------|
|
|
131
|
+
| 1 | Schema, auth, signup screens |
|
|
132
|
+
| 2 | Notes API and editor |
|
|
133
|
+
| 3 | Notes list, search, attachments |
|
|
134
|
+
| 4 | Sharing and settings |
|
|
135
|
+
| 5 | Export, load test, accessibility |
|
|
136
|
+
| 6 | Private beta |
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<DesignBoard title="Lumen Notes - signup storyboard" version={1} transitions={[
|
|
2
|
+
{ from: "welcome", to: "create", label: "tap Continue" },
|
|
3
|
+
{ from: "create", to: "verify", label: "submit" },
|
|
4
|
+
{ from: "verify", to: "workspace", label: "code accepted" },
|
|
5
|
+
{ from: "workspace", to: "invite", label: "create workspace" },
|
|
6
|
+
{ from: "invite", to: "dashboard", label: "send or skip" },
|
|
7
|
+
]}>
|
|
8
|
+
<Section id="account" title="Create the account" subtitle="Three steps from first launch to a verified address. Every screen is a low-fidelity sketch, not final UI.">
|
|
9
|
+
<Artboard id="welcome" label="Welcome" surface="mobile" x={80} y={120} width={320} height={580} order={1}>
|
|
10
|
+
<Screen surface="mobile" caption="First launch, signed out.">
|
|
11
|
+
<FrameScreen>
|
|
12
|
+
<StatusBar />
|
|
13
|
+
<Col full>
|
|
14
|
+
<Box dashed>
|
|
15
|
+
<Row><IconSquare active /><Title text="Lumen Notes" /></Row>
|
|
16
|
+
<Lines n={2} widths={[70, 48]} />
|
|
17
|
+
</Box>
|
|
18
|
+
<Text value="Notes that stay in sync on every device." tone="muted" />
|
|
19
|
+
<Lines n={3} widths={[90, 76, 60]} />
|
|
20
|
+
<Btn label="Continue with email" primary />
|
|
21
|
+
<Text value="Already have an account? Sign in" tone="muted" />
|
|
22
|
+
</Col>
|
|
23
|
+
</FrameScreen>
|
|
24
|
+
</Screen>
|
|
25
|
+
</Artboard>
|
|
26
|
+
<Artboard id="create" label="Create account" surface="mobile" x={600} y={120} width={320} height={580} order={2}>
|
|
27
|
+
<Screen surface="mobile">
|
|
28
|
+
<FrameScreen>
|
|
29
|
+
<StatusBar />
|
|
30
|
+
<Col full>
|
|
31
|
+
<Title text="Create account" />
|
|
32
|
+
<Box>
|
|
33
|
+
<Text value="Email" tone="muted" />
|
|
34
|
+
<Lines n={1} widths={[58]} />
|
|
35
|
+
</Box>
|
|
36
|
+
<Box>
|
|
37
|
+
<Text value="Password" tone="muted" />
|
|
38
|
+
<Lines n={1} widths={[36]} />
|
|
39
|
+
</Box>
|
|
40
|
+
<Row><IconSquare active /><Text value="I agree to the terms" /></Row>
|
|
41
|
+
<Divider />
|
|
42
|
+
<Btn label="Create account" primary />
|
|
43
|
+
</Col>
|
|
44
|
+
</FrameScreen>
|
|
45
|
+
</Screen>
|
|
46
|
+
</Artboard>
|
|
47
|
+
<Artboard id="verify" label="Verify email" surface="mobile" x={1120} y={120} width={320} height={580} order={3}>
|
|
48
|
+
<Screen surface="mobile">
|
|
49
|
+
<FrameScreen>
|
|
50
|
+
<StatusBar />
|
|
51
|
+
<Col full>
|
|
52
|
+
<Title text="Check your inbox" />
|
|
53
|
+
<Text value="We sent a six digit code." tone="muted" />
|
|
54
|
+
<Row><IconSquare /><IconSquare /><IconSquare /><IconSquare /><IconSquare /><IconSquare /></Row>
|
|
55
|
+
<Lines n={2} widths={[84, 52]} />
|
|
56
|
+
<Btn label="Open mail app" primary />
|
|
57
|
+
<Text value="Resend code" tone="muted" />
|
|
58
|
+
</Col>
|
|
59
|
+
</FrameScreen>
|
|
60
|
+
</Screen>
|
|
61
|
+
</Artboard>
|
|
62
|
+
</Section>
|
|
63
|
+
<Section id="first-run" title="First run" subtitle="Workspace setup and the empty state a new user lands on.">
|
|
64
|
+
<Artboard id="workspace" label="Name your workspace" surface="mobile" x={1120} y={980} width={320} height={580} order={4}>
|
|
65
|
+
<Screen surface="mobile">
|
|
66
|
+
<FrameScreen>
|
|
67
|
+
<StatusBar />
|
|
68
|
+
<Col full>
|
|
69
|
+
<Title text="Name your workspace" />
|
|
70
|
+
<Box><Lines n={1} widths={[44]} /></Box>
|
|
71
|
+
<SectionLabel label="Use it for" />
|
|
72
|
+
<TaskRow title="Personal" done note="default" />
|
|
73
|
+
<TaskRow title="Team" />
|
|
74
|
+
<TaskRow title="School" />
|
|
75
|
+
<Btn label="Create workspace" primary />
|
|
76
|
+
</Col>
|
|
77
|
+
</FrameScreen>
|
|
78
|
+
</Screen>
|
|
79
|
+
</Artboard>
|
|
80
|
+
<Artboard id="invite" label="Invite teammates" surface="mobile" x={600} y={980} width={320} height={580} order={5}>
|
|
81
|
+
<Screen surface="mobile">
|
|
82
|
+
<FrameScreen>
|
|
83
|
+
<StatusBar />
|
|
84
|
+
<Col full>
|
|
85
|
+
<Title text="Invite teammates" />
|
|
86
|
+
<Row><IconSquare active /><Lines n={1} widths={[70]} /></Row>
|
|
87
|
+
<Row><IconSquare /><Lines n={1} widths={[56]} /></Row>
|
|
88
|
+
<Row><IconSquare /><Lines n={1} widths={[64]} /></Row>
|
|
89
|
+
<Divider />
|
|
90
|
+
<Btn label="Send invites" primary />
|
|
91
|
+
<Text value="Skip for now" tone="muted" />
|
|
92
|
+
</Col>
|
|
93
|
+
</FrameScreen>
|
|
94
|
+
</Screen>
|
|
95
|
+
</Artboard>
|
|
96
|
+
<Artboard id="dashboard" label="Empty dashboard" surface="mobile" x={80} y={980} width={320} height={580} order={6}>
|
|
97
|
+
<Screen surface="mobile" caption="Nothing is created for the user yet.">
|
|
98
|
+
<FrameScreen>
|
|
99
|
+
<StatusBar />
|
|
100
|
+
<Col full>
|
|
101
|
+
<Row><Title text="Today" /><IconSquare active /></Row>
|
|
102
|
+
<Box dashed>
|
|
103
|
+
<Skeleton lines={3} widths={[80, 64, 72]} />
|
|
104
|
+
</Box>
|
|
105
|
+
<Text value="No notes yet" tone="muted" />
|
|
106
|
+
<Btn label="New note" primary />
|
|
107
|
+
</Col>
|
|
108
|
+
</FrameScreen>
|
|
109
|
+
</Screen>
|
|
110
|
+
</Artboard>
|
|
111
|
+
</Section>
|
|
112
|
+
|
|
113
|
+
<Annotation id="note-password" title="Password rules" targetId="create" placement="bottom">
|
|
114
|
+
|
|
115
|
+
Show the rule list only after the first keystroke, so the form starts calm.
|
|
116
|
+
|
|
117
|
+
</Annotation>
|
|
118
|
+
|
|
119
|
+
<Annotation id="note-code" title="Code entry" targetId="verify" placement="right">
|
|
120
|
+
|
|
121
|
+
Paste fills all six boxes at once. The code expires after ten minutes.
|
|
122
|
+
|
|
123
|
+
</Annotation>
|
|
124
|
+
</DesignBoard>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Lumen Notes - Signup Storyboard
|
|
3
|
+
status: draft
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Signup flow
|
|
7
|
+
|
|
8
|
+
## Goal
|
|
9
|
+
|
|
10
|
+
A new visitor reaches an empty, ready-to-use dashboard in six screens or fewer. All names and data in this example are invented.
|
|
11
|
+
|
|
12
|
+
## Decision
|
|
13
|
+
|
|
14
|
+
<Decision title="Verification method" question="How do we verify the email address?" options={[
|
|
15
|
+
{ id: "code", label: "Six digit code", detail: "Works when the mail app opens on another device.", recommended: true },
|
|
16
|
+
{ id: "link", label: "Magic link", detail: "One tap, but breaks when the link opens in a different browser." },
|
|
17
|
+
]}>
|
|
18
|
+
|
|
19
|
+
A code survives a device switch, which is the common failure for links.
|
|
20
|
+
|
|
21
|
+
</Decision>
|
|
22
|
+
|
|
23
|
+
## Acceptance {#acceptance}
|
|
24
|
+
|
|
25
|
+
<Checklist title="Done when" items={[
|
|
26
|
+
{ label: "Every screen fits one viewport without scrolling", checked: true },
|
|
27
|
+
{ label: "Back from any step keeps the typed values" },
|
|
28
|
+
{ label: "Expired codes show a resend action" },
|
|
29
|
+
]} />
|
|
30
|
+
|
|
31
|
+
## Build map {#build-map}
|
|
32
|
+
|
|
33
|
+
<ImplementationMap files={[
|
|
34
|
+
{ path: "app/signup/welcome.tsx", change: "added", note: "Entry screen and sign-in link" },
|
|
35
|
+
{ path: "app/signup/verify.tsx", change: "added", note: "Code boxes with paste handling" },
|
|
36
|
+
{ path: "app/auth/session.ts", change: "modified", note: "Create the workspace after the first verified session" },
|
|
37
|
+
]} />
|