How Bonyad sprints work — a friendly walkthrough
This page explains the entire sprint workflow in Bonyad — from planning to closing — using plain-language diagrams and step-by-step prose. Read it top to bottom and you'll know exactly what every button on the sprints page does, who's allowed to do what, what the system tracks automatically, and what happens when a task slips past its deadline.
What is a sprint in Bonyad?
A sprint in Bonyad is a named, time-boxed batch of work. Think of it as a folder that says "these are the things we promised to ship between today and a future date." You put issues into it, assign them to people, give them estimates in story points, and then watch the sprint progress as work moves through the board.
Every sprint belongs to exactly one app — Bonyad, Mafeshsho8l, Tawazon, or Droop — so
different teams never see each other's plans. The page you're reading right now lives at
/bonyad/flow.html, but the actual sprint planning UI is at
/bonyad/sprints.html?app=<your-app>.
What's inside a sprint?
"Sprint 12 — Auth hardening".planning, active, completed, cancelled — determines what buttons appear.The big picture — one diagram, end to end
Here's the entire sprint workflow as one diagram. Read it left to right. Coloured borders tell you who's doing the work at each step — the team, the system, or someone outside the sprint (like the admin getting a Telegram alert).
flowchart LR classDef user fill:#1a2540,stroke:#5eb3ff,stroke-width:2px,color:#f4f4f8; classDef sys fill:#2a1a10,stroke:#ff6b1a,stroke-width:2px,color:#f4f4f8; classDef ok fill:#0a3b30,stroke:#2ee6b8,stroke-width:2px,color:#f4f4f8; classDef danger fill:#3d1717,stroke:#f87171,stroke-width:2px,color:#f4f4f8; classDef decide fill:#161620,stroke:#71717f,stroke-width:2px,color:#f4f4f8,stroke-dasharray:5 4; S1["Create sprint
name + dates + goal"]:::user S2["Add issues
from backlog or quick-add"]:::user S3["Assign owner
estimate + start/end date"]:::user S4["Start sprint
status changes to active"]:::user S5["Move cards through
the kanban board"]:::user D{"Past due?"}:::decide S6["Mark sprint
complete"]:::ok L1["Card turns red
OVERDUE indicator"]:::danger L2["Assignee files
a written reason"]:::user L3["System alerts
admin via Telegram"]:::sys S1 --> S2 --> S3 --> S4 --> S5 --> D D -- no --> S6 D -- yes --> L1 --> L2 --> L3 --> S5
A sprint moves through four lifecycle stages
Every sprint has a status field. The status determines which buttons appear
on the detail page. You can't "start" a completed sprint, and you can't "complete" one
that hasn't been started — these guardrails prevent accidental state corruption.
stateDiagram-v2 classDef plan fill:#1a1720,stroke:#71717f,color:#cdcdd8; classDef act fill:#0a3b30,stroke:#2ee6b8,color:#2ee6b8; classDef done fill:#0a2540,stroke:#5eb3ff,color:#5eb3ff; classDef bad fill:#3d1717,stroke:#f87171,color:#f87171; [*] --> planning : you create it planning --> active : you click
Start sprint planning --> cancelled : you cancel
before starting active --> completed : you click
Complete active --> cancelled : something went wrong
need to bail completed --> [*] cancelled --> [*]
Plan the sprint — create it and fill it with work
Click + New sprint on the sprints page. A modal opens asking for the essentials. Don't worry about getting every field perfect — you can edit any of them later.
-
Click + New sprintTop-right of the sprints page. A modal slides in with the create form.
-
Fill in name, goal, and datesName is required. The goal and dates are optional but strongly recommended — the goal becomes the subtitle and the dates feed the burndown chart.
-
Set the capacity in story pointsA realistic guess of how much work the team can ship. The system will later warn you if you've planned more points than this capacity allows.
-
Click Save — sprint opens automaticallyYou're now in the detail view with an empty board. Time to fill it.
-
Add issues — two ways to do itFrom backlog: click From backlog to pick existing issues across all sheets for this app. Tick the ones you want, click Add selected.
Quick-add: click + Quick add to create a brand-new issue (bug, feature, task, chore…) and drop it into this sprint in one step. -
Assign each issueClick any card on the board to open the detail drawer. Set the assignee, estimate (story points), start date, and end date. Save closes the drawer and updates the card.
POST /api/bonyad/sprints
{
"name": "Sprint 12 — Auth hardening",
"goal": "Ship 2FA + lock out brute-force attempts",
"start_date": "2026-06-01",
"end_date": "2026-06-14",
"capacity_points": 30,
"status": "planning"
}
Start the sprint — flip the switch to active
When you're happy with the planned work, click ▶ Start sprint. The
status flips from planning to active and a few things turn on
automatically:
- The burndown chart starts plotting. Day 0 = total points on the sprint. The chart compares the ideal line (linear decrease to zero) against the actual remaining points.
- The "+ Quick add" and "From backlog" buttons stay available, but added issues land in the Todo column instead of being treated as "planned scope."
- Overdue checks become active. The moment any issue's
due_datefalls behind today, the card lights up red. - The hero stats strip (top of the detail page) updates: Planned points, Completed, Issues, Time left.
Work the board — drag cards through four columns
This is where day-to-day work happens. The kanban board has four columns. Each card represents one issue. Drag a card from left to right as the work progresses.
flowchart LR classDef todo fill:#1a1720,stroke:#71717f,color:#cdcdd8,stroke-width:2px; classDef prog fill:#2b1f10,stroke:#ffb86b,color:#ffb86b,stroke-width:2px; classDef rev fill:#1d172b,stroke:#a78bfa,color:#a78bfa,stroke-width:2px; classDef done fill:#0a2a20,stroke:#2ee6b8,color:#2ee6b8,stroke-width:2px; T["Todo
queued for work"]:::todo P["In progress
someone is actively working"]:::prog R["In review
code or design done
awaiting check"]:::rev D["Done
shipped or merged"]:::done T --> P P --> R R --> D R -.changes requested.-> P
What the system tracks automatically when you drag
Every drag triggers a single API call: PATCH /api/bonyad/issues/<id>/assign
with the new state. The server then auto-stamps lifecycle timestamps so you never have to
remember when something started or finished.
started_at = NOW(). This is the moment work actually began — used later for cycle-time reports.completed_at = NOW() AND is_done = 1. The card now counts toward the sprint's "done points" total.What's on each card
Cards are designed to be readable at a glance. From top to bottom:
- Issue ID + type (e.g.
#42 · BUG) — the "eyebrow" row at the top - Title — the main bug/task title
- Chips row:
- Priority chip (high / medium / low) — colour-coded
- Estimate chip (e.g.
3 pt) — only shown if estimate is set - Due-date chip — pulses red if overdue
- Late-reason count chip — appears only if reasons have been filed
- Sheet chip — which platform/area the issue belongs to
- Footer row: comment count on the left, circular assignee avatar on the right (tinted by role)
Track progress — burndown + hero stats + filter chips
The detail page gives you three different reading angles on how the sprint is going. Use the one that matches the question you have right now.
Hero stats (top of the page)
Four large tiles, one number each. This is the "glance and go" view — pull these up during standup.
Filter chips (above the board)
Three filters narrow the board to whatever you're investigating:
- Priority — High / Medium / Low / All
- Assignee — pick a teammate or "Unassigned". Useful during standup ("show me Ali's cards")
- Search box — free-text match against the card title. Live filter as you type.
Burndown chart (bottom of the page)
A line chart showing remaining points over time:
- Grey dashed line = ideal burndown. A straight line from total points to zero.
- Orange filled line = actual remaining points each day.
- Vertical dashed "TODAY" line shows where you are in the sprint window.
- Each data point is hoverable — the tooltip shows that day's remaining-points number.
Close the sprint — mark it complete
When the sprint window ends and (hopefully) the work is shipped, click ✓ Complete.
The status moves to completed. From this point on:
- The sprint becomes read-only in spirit (you can still view it, but it's frozen).
- Its done-points number gets counted in future velocity reports.
- Any unfinished issues stay on the board as a record — they don't disappear, but they don't count toward "completed" either.
- The next sprint you create can pull those unfinished issues in via From backlog.
What "overdue" actually means in Bonyad
An issue is overdue when all three of the following are true:
- It has a
due_dateset - That date is before today (in the server's local time)
- The issue is not yet in
doneorcancelledstate
The system checks this on every render — there's no separate cron job needed. As soon as the clock passes midnight on the due date, the next page load shows the overdue indicators. Three things change visually:
OVERDUE eyebrow text shows next to the issue ID.⚠ N overdue pill appears next to the sprint name. The number is the count of currently-overdue issues in that sprint.Filing a late reason — explain the slip
When work slips past its deadline, the assignee (or an admin) is required to file a short written reason. This isn't about blame — it's about capturing context so the team can spot patterns: "are scope creeps the issue? Dependencies? Estimation errors?"
flowchart TD
classDef user fill:#1a2540,stroke:#5eb3ff,color:#f4f4f8,stroke-width:2px;
classDef decide fill:#161620,stroke:#71717f,color:#f4f4f8,stroke-width:2px,stroke-dasharray:5 4;
classDef ok fill:#0a3b30,stroke:#2ee6b8,color:#f4f4f8,stroke-width:2px;
classDef sys fill:#2a1a10,stroke:#ff6b1a,color:#f4f4f8,stroke-width:2px;
classDef danger fill:#3d1717,stroke:#f87171,color:#f4f4f8,stroke-width:2px;
A["Issue is overdue"]:::danger
B["User opens the card"]:::user
C{"Is this person the
assignee or admin?"}:::decide
D["Read-only view
of past reasons"]:::user
E["Submit form is visible
reason + optional new ETA"]:::user
F["Submit"]:::user
G["Server records reason
and updates due_date
if new ETA was given"]:::sys
H["Telegram alert
fires to admin"]:::sys
A --> B --> C
C -- viewer only --> D
C -- assignee or admin --> E --> F --> G --> H
Who is allowed to file a reason?
Only the issue's assignee or an admin. The check happens
server-side in POST /api/bonyad/issues/<id>/late-reason, so even if a
user pokes at the API directly, they can't bypass the rule.
What the form asks for
- Reason (required) — a short text explanation. Be specific: "Stripe webhook signature failing under replay" beats "blocked." Max 4,000 characters.
- New ETA (optional) — if you can commit to a new date, set it here. The server updates the issue's
due_datein the same database transaction, so the card stops being overdue until the new date passes.
What gets stored
bonyad_issue_late_reasons{
"id": 17,
"issue_id": 42,
"app_slug": "mafeshsho8l",
"user_id": 5,
"user_name": "Ali",
"user_role": "developer",
"old_due_date": "2026-05-25",
"new_due_date": "2026-06-02",
"reason": "Stripe webhook signature verification needs a rewrite — the existing implementation chokes on event replay.",
"submitted_at": "2026-05-27T10:42:00Z"
}
The admin gets pinged on Telegram
The moment a late reason is filed, the server sends a Markdown-formatted message to the app's Telegram channel. The admin doesn't have to be watching the dashboard — the phone buzzes.
sequenceDiagram participant User as Assignee participant API as Bonyad API participant Notify as notifyApp() participant Env as Server config participant TG as Telegram cloud participant Phone as Admin's phone User->>API: POST /late-reason
{ reason, new_due_date } API->>API: Validate auth + scope
Insert row in DB API->>Notify: Call notifyApp(app_slug, message) Notify->>Env: Get bot token
Get chat_id for this app Env-->>Notify: Token + chat_id Notify->>TG: POST sendMessage
(chat_id, text, Markdown) TG-->>Notify: 200 OK Notify-->>API: Done API-->>User: 200 success TG-->>Phone: Push notification
How channels are routed per app
Each app has its own Telegram channel. Routing is done with environment variables on the Node.js server:
What the message looks like
⏰ Late-reason filed — Mafeshsho8l 🔖 issue #42: Wire Stripe Checkout into onboarding 👤 by: Ali (developer) 📅 was due: 2026-05-25 📅 new ETA: 2026-06-02 > Stripe webhook signature verification needs > a rewrite — the existing implementation chokes > on event replay.
logger.error lines starting with
[TelegramWebhook]).
Who does what — roles and permissions
Bonyad has four built-in roles. The role you have is set on your user account in
bonyad_users.role and decides what you can do in each part of the app.
- Create / edit / delete any sprint
- Edit any user's comment
- File a late reason on any issue (not just their own)
- Receives Telegram alerts for slips
- Access to
?app=*by default
- Pick up assigned issues
- Drag own cards through the board
- Edit own comments
- File late reasons on issues assigned to them
- Scoped to apps in their
app_scopeCSV
- Same as Developer for sprints
- Upload designs to
/designs.html - Card avatar tinted violet on the board
- Scoped to apps in their
app_scopeCSV
- Same as Developer for sprints
- Often moves cards back from "In review" to "In progress"
- Card avatar tinted teal on the board
- Scoped to apps in their
app_scopeCSV
What gets stored in the database per sprint
Behind the sprint UI sit four tables in MySQL. Knowing the shape helps you reason about what's tracked and what isn't.
bonyad_sprint_issues),
not as a foreign key on the issue itself. This is what lets the same issue belong to
multiple sprints across time (carry-over) without rewriting the issue.
Glossary — terms used on this page
started_at and completed_at for completed issues. Tells you how long work actually takes.bonyad, mafeshsho8l, tawazon, droop, or quickin.* means everything.