docs: audit corrections, coverage pages, screenshots, and em-dash removal

Finalizes the docs section after an adversarial audit (code review + per-page
fact-check + coherence critic) and two content requests.

- Accuracy: fixed 10 invented cross-link slugs, the admin-vs-board gating claims
  the verifiers caught (permissions matrix, /settings, invites, onboarding,
  connect-payments, proposal withdraw, Stripe reconciliation, member fields,
  door.operate), and 2 issues in the new pages (project area-at-creation, forum
  thread-delete role).
- Coverage: added 5 how-to pages so every module is documented (recurring Stripe
  dues, tasks & chores, projects, comms & forum, ops knowledge base + secrets
  vault). 34 pages total.
- Screenshots: 12 captioned app screenshots under public/docs-media/, embedded
  as figures. Each tutorial now has 2-3 step screenshots.
- Style: removed all em-dashes/en-dashes/ellipses from the docs (natural ASCII
  punctuation).
- Handoff (docs/HANDOFF-audit-cleanup.md) updated with the current state.

typecheck 0 · lint 0 errors · build ok (all 34 /docs pages prerendered)
This commit is contained in:
Moheeb Zara
2026-08-05 03:15:07 -07:00
parent 16229ae523
commit 8edc9da611
50 changed files with 610 additions and 202 deletions
+13 -7
View File
@@ -8,16 +8,18 @@ You need the right role for each part:
| Task | Required role |
| --- | --- |
| Add or edit a pay-here link | admin or board |
| Add or edit a pay-here link | admin (the Settings screen is admin-only) |
| Import and reconcile payments | admin, board, or treasurer |
This example uses Venmo, but PayPal and Zeffy work exactly the same way.
## Step 1: Add a pay-here link
![The Settings Dues tab: Stripe recurring dues up top, external pay-here links below.](/docs-media/settings-dues.jpg)
1. Go to [/settings](/settings) and open the **Dues** tab.
2. Under **Other ways to pay dues** you will see a card for each platform: PayPal, Zeffy, and Venmo. Find the **Venmo** card.
3. Paste your payment link into the **URL** field. It must be an absolute `https://` URL — anything else is rejected.
3. Paste your payment link into the **URL** field. It must be an absolute `https://` URL, anything else is rejected.
4. Optionally add an instruction, for example `Put your member name in the note`. This is a hint that helps you match the payment later.
5. Leave the toggle set to **shown** so members can see it.
6. Click **Save**.
@@ -26,16 +28,18 @@ The link is now live for your space. You can add all three platforms, and hide a
## Step 2: The member pays externally
There is nothing for you to do here — this is what the member sees.
There is nothing for you to do here, this is what the member sees.
1. The member opens their [/me](/me) page and looks at the **Dues** section.
2. While they still owe dues, the card shows your active links under **Other ways to pay** (or **Pay your dues** if your space has no card billing set up).
3. They click **Pay with Venmo**, which opens your external payment page in a new tab, and they pay you there — adding their name in the note if you asked them to.
3. They click **Pay with Venmo**, which opens your external payment page in a new tab, and they pay you there, adding their name in the note if you asked them to.
The platform does not record this payment automatically. That is the next step.
## Step 3: Record the payment
![The Import and Sync screen: pick Payments, drop a CSV, then map columns, preview, and import.](/docs-media/import.jpg)
When your Venmo activity is available, export it as a CSV and import it.
1. Go to [/import](/import) and switch the **Import type** to **Payments**.
@@ -45,10 +49,12 @@ When your Venmo activity is available, export it as a CSV and import it.
Every imported row lands as an `unlinked` payment. Rows with a bad amount, platform, or date are skipped and counted, so nothing invalid is silently added.
> Recording a single hand-received payment instead? Use the **Log Cash** button on [/payments](/payments) — but note it always records the platform as `cash`.
> Recording a single hand-received payment instead? Use the **Log Cash** button on [/payments](/payments), but note it always records the platform as `cash`.
## Step 4: Reconcile the payment to the member
![The Payments ledger: one summary card per platform above the transactions table.](/docs-media/payments.jpg)
1. Go to [/payments](/payments). The header shows a count like **1 unlinked** whenever payments still need a member.
2. Find your Venmo row. You can narrow the list with the **All Platforms** and **All Status** filters at the top of the transactions table.
3. Click **+ Link member** on that row.
@@ -61,7 +67,7 @@ The row flips to `linked` and shows the member's name with a check mark.
Linking a payment does two things automatically:
- It advances the member's **last paid** date to the payment's date.
- It sets the member's status to `current` — as long as this payment is the most recent one on file for them.
- It sets the member's status to `current`, as long as this payment is the most recent one on file for them.
This is **advance-only**: recording an older or backdated payment never moves a member's dues backward. If the member already has a newer payment recorded, linking an older one leaves their status untouched.
@@ -69,4 +75,4 @@ To verify, open the member on [/members](/members) and check their status, or ha
## What you did
You added an external pay-here link, a member paid through it, you imported the payment as an `unlinked` row, and you reconciled it to the member — advancing their dues to `current`. Repeat Step 3 and Step 4 whenever new external payments come in.
You added an external pay-here link, a member paid through it, you imported the payment as an `unlinked` row, and you reconciled it to the member, advancing their dues to `current`. Repeat Step 3 and Step 4 whenever new external payments come in.
+8 -4
View File
@@ -9,6 +9,8 @@ Set aside about fifteen minutes. You will need the `classes.manage` permission t
## Step 1: Create a class
![The class management screen. A new space starts empty; begin by creating a class, then schedule sessions members can sign up for.](/docs-media/classes-manage.jpg)
A class is the reusable offering ("Woodshop Basics"). Sessions are the individual scheduled occurrences you will add later.
1. Go to [/classes/manage](/classes/manage).
@@ -33,6 +35,8 @@ The session appears under the class with its start time and a **Scheduled** stat
## Step 3: Members sign up
![The member class view: each upcoming session shows its time, location, spots left, and a Sign up button.](/docs-media/classes.jpg)
Members do not need any special permission to sign up, only membership in the space.
1. As a member, go to [/classes](/classes). Upcoming, non-cancelled sessions appear with "X of N spots left" (or "open" when unlimited).
@@ -42,7 +46,7 @@ When registered signups reach capacity, the button reads **Join waitlist** inste
## Step 4: Mark attendance
Now switch back to someone with `classes.instruct`. (A user with only `classes.manage` can open the roster and see who signed up, but cannot check **attended** or run **Complete session** — both require `classes.instruct`.)
Now switch back to someone with `classes.instruct`. (A user with only `classes.manage` can open the roster and see who signed up, but cannot check **attended** or run **Complete session**, both require `classes.instruct`.)
1. On [/classes/manage](/classes/manage), find the session and select **Signups**. (Instructors browsing [/classes](/classes) can use **Attendees** on the session instead.)
2. The roster lists each signup with its status. Check the **attended** box next to each member who showed up.
@@ -54,7 +58,7 @@ Attendance is what determines who receives a certification in the next step, so
1. In the same attendees panel, select **Complete session**.
2. The session status changes to **Completed**.
If the class grants a certification, completion awards it to every member you marked as attended. The result confirms how many certificates were issued. If the instructor lacks `certifications.grant`, the session still completes but you will see "Certificates were NOT issued (requires the certifications.grant permission)" — grant that permission and the members can be certified through [/certifications](/certifications).
If the class grants a certification, completion awards it to every member you marked as attended. The result confirms how many certificates were issued. If the instructor lacks `certifications.grant`, the session still completes but you will see "Certificates were NOT issued (requires the certifications.grant permission)", grant that permission and the members can be certified through [/certifications](/certifications).
Members can see their signups and completed sessions on their own page at [/me](/me), and any certification they earned appears in their certifications.
@@ -62,8 +66,8 @@ Members can see their signups and completed sessions on their own page at [/me](
You now have a class with one completed session, an attendance record, and (optionally) certifications awarded to attendees. From here you can:
- Schedule more sessions of the same class — the offering is reusable.
- Schedule more sessions of the same class, the offering is reusable.
- **Archive** a class you no longer offer instead of deleting it (deleting is only allowed when a class has no sessions).
- **Cancel** a session instead of deleting it to keep the signup history; every signed-up member is emailed when you do.
See the [classes reference](/docs/reference/classes) for every field, status, and permission, and [manage certifications](/docs/how-to/manage-certifications) for setting up the certification types classes can grant.
See the [classes, certifications & equipment reference](/docs/reference/classes-and-equipment) for every field, status, and permission, including how certification types are defined and granted on class completion.
+17 -13
View File
@@ -2,6 +2,8 @@ This tutorial walks you from a blank signup form to a working space where you la
By the end you will have a space of your own, an admin membership inside it, and a mental map of the sidebar.
![The dashboard: stat tiles, open proposals and incidents, quick chores, and who is checked in.](/docs-media/dashboard.jpg)
## Before you start
You need an email address and a password of at least 8 characters. If someone has already set up a space for your group and given you an invite code, skip ahead to [Joining an existing space](#joining-an-existing-space) instead.
@@ -17,7 +19,7 @@ You need an email address and a password of at least 8 characters. If someone ha
Now you are on the **Set up your Space** step.
1. Enter a **Space Name**, such as `HeatSync Labs`. As you type, a **Slug** is filled in for you automatically — it is the lowercase, letters-and-numbers-only handle for your space (for example `heatsynclabs`). You can edit it.
1. Enter a **Space Name**, such as `HeatSync Labs`. As you type, a **Slug** is filled in for you automatically, it is the lowercase, letters-and-numbers-only handle for your space (for example `heatsynclabs`). You can edit it.
2. Optionally enter a **City / Location**.
3. Read the note: you will be the admin of this space, and default channels (general, announcements, ops) are created for you.
4. Click **Create Space →**.
@@ -27,7 +29,7 @@ Depending on how email confirmation is configured, one of two things happens:
- You go straight to `/dashboard`, already signed in.
- You are sent to `/signup/confirm` to verify your email first. Open the link in that email, and you will land on the dashboard afterward.
That is it — your space exists. As the founder you are set up as an **admin** on the **plus** tier with a **current** status, and you skip the member onboarding flow entirely.
That is it, your space exists. As the founder you are set up as an **admin** on the **plus** tier with a **current** status, and you skip the member onboarding flow entirely.
## Step 3: Get your bearings on the dashboard
@@ -40,21 +42,23 @@ The first time you open `/dashboard` in an empty space, you see a **"Let's set u
| Create your first task or chore | `/tasks` |
| Connect payments | `/settings` |
That panel disappears on its own once there is real activity. After that, the dashboard shows a stat row — **Active Members**, **Dues Current**, **Open Tasks**, **Unlinked Payments** — plus quick chores, active projects, who is present now, and recent activity. The **Quick Task** button in the header jumps to `/tasks`.
That panel disappears on its own once there is real activity. After that, the dashboard shows a stat row (**Active Members**, **Dues Current**, **Open Tasks**, **Unlinked Payments**) plus quick chores, active projects, who is present now, and recent activity. The **Quick Task** button in the header jumps to `/tasks`.
## Step 4: Explore the left navigation
![The Members screen is one stop on the left nav: the roster, dues status, and per-member actions.](/docs-media/members.jpg)
The sidebar groups every screen into sections. As an admin you see all of them:
- **Workspace** — Dashboard, Tasks & Chores, Projects, Ops & Facilities, Comms, Forum
- **Governance** — Proposals, Incidents, Policies
- **Access & Resources** — Classes, Equipment, Doors, Forms
- **People** — Members, Attendance, Contacts, Recruitment
- **Finance** — Payments, Financials
- **Account** — My membership
- **Admin** — Customize, Settings, Import / Sync, and the management screens for forms, certifications, classes, equipment, and doors
- **Workspace**, Dashboard, Tasks & Chores, Projects, Ops & Facilities, Comms, Forum
- **Governance**, Proposals, Incidents, Policies
- **Access & Resources**, Classes, Equipment, Doors, Forms
- **People**, Members, Attendance, Contacts, Recruitment
- **Finance**, Payments, Financials
- **Account**, My membership
- **Admin**, Customize, Settings, Import / Sync, and the management screens for forms, certifications, classes, equipment, and doors
The **Admin** section only appears for admins, board members, or members you have granted a specific manage permission. There is also a **Jump to…** button at the top of the sidebar (or press ⌘K) that opens a command palette for fast navigation.
The **Admin** section only appears for admins, board members, or members you have granted a specific manage permission. There is also a **Jump to...** button at the top of the sidebar (or press ⌘K) that opens a command palette for fast navigation.
A good next stop is [inviting your first members](/members), or `/customize` to shape what new members see when they join.
@@ -64,10 +68,10 @@ If you were given an invite code instead of starting a new space:
1. Go to `/signup` (or open the `/signup?invite=CODE` link your admin shared, which prefills the code for you).
2. Fill in your account details and click the **Join a Space** card, then **Continue →**.
3. Enter the **Invite Code** — it looks like `HSL-2025-XXXX`. Ask your space admin for it if you do not have one; they can find the space's default code under Settings.
3. Enter the **Invite Code**, it looks like `HSL-2025-XXXX`. Ask your space admin for it if you do not have one; they can find the space's default code under Settings.
4. Click **Join Space →**.
You join as a **member**. If the space requires approval (the default), your status starts as **unverified** and an admin approves you before you gain full access — until then the dashboard is still yours to look around. If the space does not require approval, you are **current** right away.
You join as a **member**. If the space requires approval (the default), your status starts as **unverified** and an admin approves you before you gain full access, until then the dashboard is still yours to look around. If the space does not require approval, you are **current** right away.
## What you did
+16 -12
View File
@@ -1,4 +1,6 @@
In this tutorial you will bring your first people into your space two ways — a shareable join link and a manual add — shape what a new member sees when they first open the app, and move everyone to `current` status. Work through it as a space admin; by the end you will have a repeatable way to grow your roster.
In this tutorial you will bring your first people into your space two ways (a shareable join link and a manual add), shape what a new member sees when they first open the app, and move everyone to `current` status. Work through it as a space admin; by the end you will have a repeatable way to grow your roster.
![The Members screen, with the invite-codes panel above the member roster.](/docs-media/members.jpg)
## Before you start
@@ -6,8 +8,8 @@ You need the **admin** role (a **board** member can copy and share existing invi
There are two ways in:
- **Invite code / join link** — people sign themselves up, then you approve them.
- **Add member** — you enter someone directly and they are active immediately.
- **Invite code / join link**, people sign themselves up, then you approve them.
- **Add member**, you enter someone directly and they are active immediately.
You will use both.
@@ -16,7 +18,7 @@ You will use both.
1. Open [/members](/members) and find the **Invite codes** card.
2. Click **+ New invite**.
3. Leave the code blank to auto-generate one, or type your own. Optionally set a **Label**, an expiry, and a **Max uses** cap. Leave both blank for a permanent code.
4. Pick what the code **grants** — for new members, choose `member`.
4. Pick what the code **grants**, for new members, choose `member`.
5. Tick **Single use** if the code should auto-disable after one join.
6. Click **Create invite**.
@@ -26,16 +28,16 @@ Your code appears in the list with a `grants member` tag and a live use count.
On the new code's row you have two buttons:
- **Copy code** — copies just the code (for people who will type it during signup).
- **Copy link** — copies the one-click join link, which looks like:
- **Copy code**, copies just the code (for people who will type it during signup).
- **Copy link**, copies the one-click join link, which looks like:
```
https://your-space.example/join/your-space?code=ABC23XYZ
```
Send the link. Whoever opens it lands on a **Join** page for your space, creates an account, and is added as a member with `unverified` status — waiting for your approval.
Send the link. Whoever opens it lands on a **Join** page for your space, creates an account, and is added as a member with `unverified` status, waiting for your approval.
If a code is ever shared too widely, an admin can click **Disable** to revoke it, or **Delete** to remove it outright — both controls are admin-only.
If a code is ever shared too widely, an admin can click **Disable** to revoke it, or **Delete** to remove it outright, both controls are admin-only.
## Step 3: Approve people who joined
@@ -49,7 +51,7 @@ Approving flips the member to `current` and marks them approved, so they count t
## Step 4: Add a member manually
Some people you will enter yourself — a founding member, someone who paid at the door.
Some people you will enter yourself, a founding member, someone who paid at the door.
1. On [/members](/members), click **Add Member**.
2. Fill in **Full Name** and **Email** (both required). Phone and handle are optional.
@@ -57,7 +59,7 @@ Some people you will enter yourself — a founding member, someone who paid at t
4. Optionally set **Joined At** and tick **Card Access**.
5. Click **Add Member**.
A manually added member starts at `current` immediately — no approval step needed.
A manually added member starts at `current` immediately, no approval step needed.
## Step 5: Shape the onboarding flow
@@ -65,7 +67,7 @@ Now decide what a member sees the first time they open the app.
1. Go to [/customize](/customize) and find the **New member onboarding** card.
2. Every new space starts with four built-in steps in order: **Welcome**, **Code of Conduct** (a required acknowledgement), **Complete your profile**, and **Set up your dues**.
3. Edit any step's title inline, and its body where the step has one (body supports Markdown) — the built-in **Complete your profile** step has only an editable title, no body. Change the order with the number box, and untick **Enabled** to hide a step.
3. Edit any step's title inline, and its body where the step has one (body supports Markdown), the built-in **Complete your profile** step has only an editable title, no body. Change the order with the number box, and untick **Enabled** to hide a step.
4. On the **Set up your dues** step, paste your **Payment link** so the member gets a working "Set up payment" button.
5. Add your own with **+ Custom step**, or use **+ Form step** to make members sign a published form or waiver as part of onboarding.
@@ -73,7 +75,9 @@ Built-in steps can be disabled and reordered but not deleted. Only steps you mar
## Step 6: See what the new member sees
The first time an approved member opens the app they are routed to [/onboarding](/onboarding) and walk your enabled steps in order — a progress bar tracks **Step X of Y**. They must tick the code-of-conduct box and any other required step before the final **Finish** button lets them through to the [/dashboard](/dashboard). If no step is required, a **Skip for now** link is available.
![A member's own /me page, where they manage their profile, membership, and activity.](/docs-media/me.jpg)
The first time an approved member opens the app they are routed to [/onboarding](/onboarding) and walk your enabled steps in order, a progress bar tracks **Step X of Y**. They must tick the code-of-conduct box and any other required step before the final **Finish** button lets them through to the [/dashboard](/dashboard). If no step is required, a **Skip for now** link is available.
## You are done