Inbox Triage — Specification (updated, 2026-10-10)
Status: APPROVED (chief of staff, 2026-10-10) on the UI/UX-confirmed text (sha256 7aabc4cd40cac9c54ea62437271614530350a53bf402012c594b8786e03795b5, 65,466 bytes — the confirmed hash governs the approval record), with the chief-directed F3 finalization amendment folded on the approved text (§3.5/§5.4) and the F4 lifecycle parenthetical (§5.4). Joint product of the planner (spec text; story grooming) and the scrum master (board/lane mechanics, §7; filing). Drafted on activation after the codified llm-wiki skill's delivery, per the owner's sequencing. Inputs of record: the commission (drafts/inbox-triage-commission-terms.md, sha256 9d4b7468…) and the owner inputs file (drafts/inbox-triage-spec-inputs .md, sha256 18f58f93…), which together govern wherever any earlier framing differs. Owner decisions carried: Inbox Triage is a dashboard function; the task surface is Microsoft Planner (Trello and Microsoft To Do are both retired and appear nowhere in this spec's design); the app is the to-do list.
1. Purpose and re-home
Inbox Triage is Justin's single working surface for incoming demands on his attention. Its unifying purpose (owner, 2026-10-10): a complete view of his commitments in one place, prioritized — every commitment from every surface in a single prioritized view (§5.4 defines "commitment" and the priority model). The intake surfaces — his mailboxes (Outlook and Gmail), his Microsoft Teams messages, his Microsoft Planner tasks, and his calendars (Outlook Calendar and Google Calendar) — are assessed by the Jev model against one question (is this a do-today item?), and the output is worked in a to-do component built into the tool itself, which is the home of his personal to-dos.
The program's home is jknash/dashboard: a dedicated llm-kanban board root and an llm-wiki estate stand up in that repo for it (§7). The application code continues in jknash/inbox-triage — its own repo, deployed to Cloudflare from there. (Folding the app code into the dashboard repo was not in the owner's terms and would churn the staged deployment path for no benefit; the dashboard repo carries the program's board, wiki, and the workbench tile in its site.)
2. Dispositions
The 2026-10-03 staging (~/workspace/jev-inbox-cloud/)
CARRIES as the build base. It is a superset of the Mac
app — one codebase, two modes (HOSTED=1): Worker →
Durable Object → container (gunicorn/Flask), Cloudflare
Access in front with Worker-side Access-JWT verification,
the MSAL authorization-code sign-in redesign, all state in
an R2 store (inbox-triage-state) with the token cache
Fernet-encrypted, and results emitted in the workbench
contract shape. Staged and locally verified, never
deployed. Everything in this spec that the staging already
does is built by deploying and extending it, not by
rebuilding it.
The existing Mac app (~/workspace/jev-inbox/) RETIRES as the daily driver at cutover. Its local mode remains the rollback story during cutover (the staging tree runs both modes by design); once the hosted form is verified in daily use, the Mac app is retired and the hosted service is the only Inbox Triage. The device-code sign-in path retires with it.
WB-003 (Workbench board, "Inbox Triage results publisher," 3 pts, ready, unclaimed, 2026-S01) is SUPERSEDED by this program's stories, executed by the scrum master with the Workbench S01 record updated in the same move (§7). The workbench relationship, against the in-tool to-do form: the workbench /inbox/ section carries a launch tile + summary counts only (do-today total, per-surface counts), reading a summary the tool emits in WB-003's contract shape (run metadata and counts, flagged items — never bodies). WB-003's contract shape thus survives as the tool's emitted summary consumed by the tile; no triage UI is duplicated in the workbench, and the full results terminate in the tool. (Recommendation carried from the inputs file, for the chief's ruling with this spec.)
3. The surfaces
Account mapping (design assumption, stated per inputs #2 and #4): the Outlook mailbox and Outlook Calendar ride his PERSONAL Microsoft account; Teams + Planner ride his WORK account; Gmail and Google Calendar ride his GOOGLE account. Three identity worlds; no token, app registration, or consent is ever shared between them.
3.1 Mailbox (personal leg — as built)
Microsoft Graph, delegated Mail.Read, personal
account, via the staging's authorization-code flow
(ms_auth_web.py; token cache encrypted in the state
store; silent refresh; status never touches the network —
the staging's lessons carry verbatim). The fetch and
per-item state text are as built (sender, subject, body
excerpt).
3.2 Teams (work leg — new)
His Microsoft Teams messages: the chats and channel conversations he is party to, read via Graph under the work account's own Entra app registration and consent — never the personal app's. Scope posture (least privilege; consent architecture ruled by the security consult, §8.2 F-A): Chat.Read delegated for his chats, Tasks.Read delegated for Planner (no RSC form exists for Planner), and for channel messages Teams resource-specific consent (RSC) is the architecture of record — a team enters scope when its RSC grant exists, revocable per team, its promise per-team and never tenant-wide. The tenant-wide delegated ChannelMessage.Read.All exists only as a recorded fallback: for a specific team only, where RSC is unavailable for it, each grant recorded on IT-05's record (the team, the reason RSC was unavailable, the date), replaced by RSC when RSC becomes available; the work app carries no standing tenant-wide channel grant outside a recorded fallback. Named for §10: RSC consent is a team-owner act, and whether the delegated remainder requires tenant admin consent he cannot give is owner question Q1 — now asked against this architecture; the surfaces narrow by that ruling, not by discovery at build. The intake unit is a conversation thread (chat or channel thread), with its recent messages as the assessment's state text — a single isolated message is rarely classifiable, and the spec does not pretend otherwise.
3.3 Planner (work leg — new)
Everything in Planner syncs into the app (owner's
words). The sync reads the Planner tasks assigned to him
(Graph /me/planner/tasks, delegated Tasks.Read) on
each triage run and on app open, mirroring them into the
to-do store keyed by the Planner task id — Microsoft
Planner is the source of record for synced work tasks.
Due date, priority, and percentComplete mirror with the
task. Sync is idempotent: re-syncing never duplicates a
task (source-identity keyed), and a task completed in
Planner arrives completed. Planner has no separate home
in this design and needs none: the Tasks tab is its review
surface — its rows mirror the Planner facts (due,
priority, % complete) beside the assessment (§5.2) — and
the to-do board is where its cards work (§5.2). Both, per
the direction of record. Write-back is a separate,
named decision (§5.3) — it is not part of the sync.
3.4 Gmail (Google leg — new)
A second mail surface, symmetric with §3.1 in everything
except identity: the Gmail API under his Google account's
OAuth consent (gmail.readonly), the same fetch shape and
per-item state text, the same question set (§4.2). Which
mailbox an item came from is part of its identity — the
two mail surfaces never merge their items, however the
frontend groups their tabs (§5).
3.5 Calendars (both systems — new)
Outlook Calendar (Graph Calendars.Read, riding the
personal leg's app with that scope added) and Google
Calendar (the Google leg, calendar.readonly) are
assessable surfaces: the events on and around today are
items, with a state text of title, description excerpt
(capped at 500 characters, §8.2 F-B), start/end,
attendees, location, and organizer — title and the
metadata fields ride in full; only the description is
excerpted. A calendar
event's do-today question is its own (§4.2): not "attend
this" but "does this event carry an ACTION that belongs
to today" — prep, travel, materials, a decision or a
message before it happens, or a commitment of his that
falls due at the event. What a do-today calendar item
becomes is two distinct things, never conflated: where
the event implies a discrete act, the ACT becomes a
candidate card in Proposed (§5.2) — carrying the event's
time as its due time where the commitment falls due at
the event — linked back to the event. The event itself is
never copied into the to-do store and never becomes a
card: it anchors the day in the working view (§5.4),
pinned to its time. A calendar-born card follows its
event (finalization amendment on the approved text,
chief-directed 2026-10-10): the accepted card carries a
live event chip ("Event: Fri 2:00 PM ↗"); when the event
moves, the card's due time and anchor follow; when an
event that bore a candidate card is cancelled, its
un-acted candidate card retires — shown as
retired, never silently deleted; a card the owner has
acted on (moved, pinned, edited) is his and does not
auto-retire.
3.6 What a surface item is
Every surface produces items in one shape for
assessment (§4): a surface tag (mail | gmail |
teams | planner | calendar), a source identity
(message id / thread id / task id / event id, with its
calendar system where applicable), a title or subject,
the counterparties, a received-or-due timestamp, and a
state text composed for the model. State-text composition
is capped per surface by the consult's F-B ruling (§8.2):
a mail/Gmail body to 3,000 characters (as built); a Teams
thread's state text to 3,000 characters in total (most
recent kept, oldest dropped first); a Planner task's notes
excerpt to 1,000 characters; a calendar description
excerpt to 500 characters. Attachment contents never
enter a state text or an assessment payload, in any form.
Items are the unit of the per-surface tabs, of
the results contract, and of promotion into the to-do
component.
4. Jev assessment
4.1 The engine, as built
The Jev model typesafe/jev-1.13 via the OpenRouter
Decisions endpoint, through the app's existing client and
its settings-driven question machinery (question types
noul / choice / score; one call evaluates all
questions against one item's state text; answers carry
probabilities and confidences). The provider/model remain
runtime preferences exactly as staged; the API key is a
Worker secret (§6). This spec changes the question sets
and the contract, not the engine.
4.2 The question sets (per surface)
Every surface is assessed against the commission's one question — do_today — plus the classification its routing needs:
- mail:
do_today(noul; does this email require the recipient's own action today? — yes means a concrete action, by him, that belongs to today; no means informational, delegable, or later) andwork_class(choice:work|personal— which world the item belongs to; routes the to-do class in §5). - teams:
do_today(noul; same question against the thread's state text — thread/channel or chat name, participants, and recent messages, the whole state text capped at 3,000 characters, §8.2 F-B — not per message). Work class by leg — Teams is the work account; no per-item class question is asked. - planner:
do_today(noul; against the task's state text — title, notes excerpt (capped at 1,000 characters, §8.2 F-B), due date, priority, percentComplete — title and the fact fields ride in full). A Planner task due today or overdue will ordinarily assess yes on its own facts; the model's judgment covers the rest (a far-due task whose content demands starting today). - gmail: the mail question set, unchanged (
do_todaywork_class), against the Gmail item's state text.
- calendar:
do_today(noul; does this event carry a concrete action that belongs to TODAY — preparation, travel, materials, a decision or message before the event, or a commitment of the owner's falling due at it?). The basis is the §3.5 state text (title and metadata in full; description excerpt capped at 500 characters, §8.2 F-B). The question judges the event's implied ACT, not its occurrence: an event that simply happens today is scheduled, not assessed — it anchors the day view by its time whether or not any act is extracted (§3.5). Class follows the leg (both calendar systems as named are personal-world); no class question is asked.
Thresholds are settings (default 0.5), as built. Low confidence (< 0.5) and classification errors flag the item, as built.
Payload discipline (§8.2 F-B, standing): the payload sent for assessment is state text + questions and nothing else — no account identifiers, tenant ids, or message ids beyond what the state text itself carries — and attachment contents are never fetched for assessment, in any form.
4.3 The results contract
Assessment produces, per item, a record in one shape:
surface, source identity, title, counterparties,
timestamp, do_today (probability + yes/no),
work_class where applicable, confidence, and a flagged
marker with its reason. The contract terminates in the
tool: the full records drive the per-surface tabs and the
to-do component (§5). Separately, the tool emits the
summary in WB-003's contract shape — run metadata,
per-surface counts, category counts, flagged items (title
and counterparties only, never bodies, capped as
built) — which the workbench tile consumes (§2). What the
staging wrote as results/latest.json becomes the
summary's transport, unchanged in shape.
5. The to-do component — the product's center
The app is the to-do list (owner's words). The to-do component is not a report of the assessment; it is where his day is worked.
5.1 Homes of record (split by class)
- Personal to-dos are native. Created in the app, held in the app's own database store (§6.2), backed by nothing external. The app's store is their home of record.
- Synced work tasks' home of record is Microsoft Planner (§3.3); the app mirrors them in.
- Work-classified mail/Teams items become app-native to-dos in the app's store on promotion (§5.2); their sources hold no task object and are never written to.
5.2 The working view
The frontend carries SIX tabs, in a fixed order: To-Do · Mail · Gmail · Teams · Tasks · Calendar (UI/UX direction of record, D1, 2026-10-10). To-Do is first and is the app's opening view on every fresh launch — the product is the to-do list (§1), and the surface tabs are intake and review, so the app opens on the work, not on a mailbox. Within a session, returning to the app restores the last tab; a new day opens on To-Do. There are no tab groups, flyouts, or second nav tier: grouping is expressed by adjacency and caption, never containers — Mail and Gmail sit adjacent (the same task, two accounts); Teams and Tasks sit adjacent (the work leg); Calendar last (the most reference-like). On the phone the bar is a bottom tab bar (icon + label per tab, thumb zone); on desktop the same six as a top tab row under the header; no tab scrolls off either bar at any supported width — if a seventh surface ever arrives, the answer is merging by kind (the Calendar pattern), not a "More" menu. Tab names are the short forms above, verbatim; the account truth lives in each tab's header line: Mail → "Outlook · Personal account"; Gmail → "Gmail · Google account"; Teams → "Teams · Work account"; Tasks → "Microsoft Planner · Work account"; Calendar → "Outlook + Google · both systems"; To-Do → "Your board · everything in one place". Badges are numbers, not dots: each surface tab carries a count badge of its do-today items (post-assessment), and To-Do carries the Proposed count; a badge clears when he has visited the tab since the run that produced it; a leg that is down (§6.3) shows a small warning tick at the badge position instead of a count. Tab granularity is a stated decision (the chief's input-#4 relay, governing granularity WITHIN kinds, not the roster): the two mail accounts are TWO tabs, not one merged mail tab — each account is a distinct surface under his standing rule, with its own assessment queue and its own sign-in state, and merging them would blur both. The two calendars are ONE tab — calendar items are events on a single timeline, not two queues, and the day is the unit: both systems' events interleave chronologically, each tagged with its system.
- Every surface tab opens with the same run header
(direction D4): "Last run
<time>· N assessed · M do-today · [Run now]", carrying the leg's account caption (§5.2 above) and, where it applies, the auto-proposal threshold in words ("Items Jev scores ≥80% do-today appear in Proposed automatically") — so a candidate in Proposed is never a mystery. Items are ordered do-today YES first (probability descending), then flagged / needs-review, then everything else reverse-chronological: flagged items outrank the confident noes because their disposition is his to supply. Rows carry counterparty, title/subject (two lines), and time, with the state-text excerpt on open. Mail and Gmail tabs are the same layout pixel-for-pixel — same row anatomy, chips, and promote act — differing only in header caption and source colour: he learns the gesture once. Teams rows show the thread (chat/channel name + latest message excerpt), the thread being the intake unit (§3.2). Tasks rows mirror the Planner facts (due, priority, % complete) beside the assessment. The Calendar tab is one chronological stream, day-grouped (Today / Tomorrow / weekday-date headers), each event tagged with a system chip — "Outlook" / "Google" as text chips in two distinct colours, never colour-only; events that bear action (§3.5) are full-weight rows with an "Action:" line, and pure-attendance events render quieter (dimmer, no action line), so the tab reads as a prep list, not a diary. - The To-Do tab is the working view across both homes
of record — and its form is a board (owner input
#3): buckets with task cards he can drag between
buckets — the board pattern he knows, recreated inside
his own app. (The Trello service stays forgotten; only
the experience is recreated.) Form (direction D2):
desktop columns side by side (~280px) with horizontal
scroll; on the phone, one column at a time, full
width, swipe between columns, the column header showing
name + card count + position dots. Buckets are DATA, not
code — with two named exceptions (direction F5):
Proposed and Done are SYSTEM buckets — fixed names,
not renameable, not deletable, their names load-bearing
in §5.4's lifecycle and §5.3's completion semantics. The
board seeds Proposed, Today, Upcoming, Done; Today
and Upcoming are ordinary buckets that happen to be
seeded, user-added buckets live between Upcoming and
Done, and other buckets can be renamed, added, and
reordered (per-column header menu: rename, move, delete
— delete only when empty, with "move cards to…" if
not — plus a "+ Add bucket" ghost column). A card is a to-do item from either
home of record: (i) native personal to-dos, (ii)
Planner-synced work tasks — every incomplete synced
task is a card (direction F2: no do-today filter;
§1 promises a complete view and §5.4's definition
includes every assigned task), placed per the placement
rule below — (iii) mail/Teams items promoted into it.
Card anatomy is two tiers (direction D2). The FACE
— the glance tier — carries the title (max 2 lines,
then ellipsis), a source chip (icon + text: Native /
Mail / Gmail / Teams / Planner / Calendar), a due
chip in relative words ("Today", "Tomorrow", "Fri",
red "Overdue 2d"), a do-today star when the item is
assessed or ruled do-today, and a thin class rail
on the card's left edge — blue for work, green for
personal — because the unified board mixes both worlds
and source alone doesn't say which world a card belongs
to (direction F6). Planner cards additionally show a
small sync glyph when a completion is "not yet
reflected in Planner" — §5.3's divergence marker belongs
on the card, not buried in a detail view. The OPEN
CARD — the working tier (tap → bottom sheet on phone,
dialog on desktop) — carries the full title, the source
excerpt / state text, the assessment block
(do-today probability as a percentage, confidence,
"flagged:
<reason>" when flagged), a provenance line ("from Planner ·<Planner bucket label>", "from Mail ·<sender>", "from Teams ·<chat or channel>"), the due-date editor, class, a bucket picker, the act row, and deep links ("Open in Planner / open the thread") where the source offers one. Done hygiene: Done cards render dimmed with a completion date; the column shows the last 7 days and collapses older behind "Show N more"; Done never counts toward badges or the Commitments header count. Empty states teach: each column's empty state names what belongs there and why it is empty ("Nothing proposed — the last run (7:42 AM) found no new do-today items"), not a bare "No cards". - Promotion is the owner's act (one tap on an assessed item) plus one standing rule: a mail/Teams item assessed do-today above the high-confidence threshold lands in the Proposed bucket as a candidate card, never silently added to Today — the board is his, and the tool proposes. The promotion affordance is a "+ To-do" button on every surface row (direction D4): one tap promotes to Proposed with a toast + Undo, and the item stays in place in its surface list, its button becoming a state — "In Proposed ✓" (or "In Today ✓" once accepted) — never a second button. An item that arrived by the standing high-confidence rule shows that same state without him tapping anything: the two arrival paths are indistinguishable on the item. In Proposed, candidate cards render visually distinct (dashed border, a "Jev proposes · 92%" chip) as a queue to rule on, with two inline acts (direction D2): Accept (lands in Today by default; the card menu's "Accept to ▸" offers another bucket) and Dismiss. No bulk accept — trust is per item.
- The acts are buttons and menus; drag is an enhancement, never the only path (direction F1 — the spec inverts the drag-first model): Complete is a check button on the card itself (one tap, no open, no drag; in Done the same button re-opens). Move runs through the card's "⋯" menu → "Move to ▸" (listing all buckets) and the open card's bucket picker. Defer sits in the ⋯ menu and the open card — "Defer to tomorrow" / "Defer to Upcoming" / pick a date (deferring a Today card to tomorrow moves it to Upcoming with tomorrow's date; the due chip updates). Dismiss sits in the ⋯ menu and the open card, with a one-step undo (toast, ~6 seconds) instead of a confirm dialog. Keyboard (desktop): cards are focusable, Enter opens, the ⋯ menu is fully keyboard-operable, and "Move to" is drag's keyboard equivalent — no act exists only behind a pointer gesture. Drag itself (pointer-based; long-press to lift on touch) remains for desktop and for phone users who prefer it. Moving a card into Done completes it; out of Done re-opens it — acting on its home of record (native items in the app store; Planner items per §5.3's write-through). Every other bucket move is app-side organization only: the app's buckets are the app's, and no bucket placement syncs to Planner (whose own plan buckets are untouched by this design). Manual creation is a "+ New to-do" affordance pinned at the board's top (and inside the Today column header) — title, optional due date, class personal by default with a one-tap toggle.
- Persistence: card membership, card order within a bucket, and bucket state are persisted in the to-do store (D1, §6.2) — the board he leaves is the board he returns to, on any device.
- Where a synced Planner card lands (stated expressly): placement at sync-in is decided by the task's STATE, not by its Planner bucket: a task completed in Planner (percentComplete 100) lands in Done; every other synced task lands in Proposed, the intake bucket, from which he drags it into his own flow. The task's Planner bucket name rides on the card as a label — context, nothing more: it never creates a board bucket and never chooses one. A Planner task with no bucket lands identically; the rule never depended on the bucket. Reason, stated once: the board's buckets are HIS workflow states and Planner's buckets are his work org's taxonomy — mapping the two onto each other corrupts both, and a bucket-identity mapping would be a one-way fiction regardless, since §5.3 refuses bucket write-back and the first drag would silently diverge the boards.
- What dragging a synced card means (stated expressly): a drag changes ONLY app-side state — bucket and order in D1. The sole Planner write a drag can ever cause is §5.3's completion write-through when the drag crosses into or out of Done. A drag between non-Done buckets PATCHes nothing in Planner; the card's Planner bucket label remains Planner's truth, unchanged.
- The do-today signal on the board (stated expressly): a flag and a view, never a placement — the marker on the card, and the Commitments view (§5.4) where assessed items rank. The tool never drops a card into Today on its own judgment; Today is his commitment to make, by drag or by act.
- State vocabulary (UI/UX direction of record; consistent across surface rows, board cards, and the Commitments view): on surface rows — a green "Do today" chip (the probability lives in the open sheet, not on the row: the row answers whether, the sheet answers how sure); an amber "Jev unsure" chip when confidence < 0.5; a red-outline "Assessment failed" chip for errors — the two non-yes states are DISTINCT chips because his next act differs (judge it himself vs retry). Flagged items group under a "Needs your call" subheader with the reason in plain words and two acts (promote manually — "+ To-do" works regardless of assessment — or dismiss from the tab); errored items carry a Retry act and are counted aloud in the run header ("1 couldn't be assessed") — an errored item never folds into the "no" pile, or a broken morning looks like a quiet one. On board cards the do-today signal is the star (card anatomy above); absence of a chip or star is the not-flagged state. Low-confidence items are NOT auto-proposed (the standing high-confidence rule) and stay visually distinct from confident noes for exactly that reason.
5.3 Write-back (RECOMMENDATION — APPROVED)
Ruled: APPROVED as recommended (chief's ruling, 2026-10-10) — action-time write-through of the completion state only; IT-07 files on this basis, its Tasks.ReadWrite rise consented as its own recorded act (§8.2 F-A).
Drafted against Planner's Graph semantics, per input #2; the owner's words fix sync IN and are silent on write-back, so this is the spec's stated recommendation, not an assumption.
RECOMMENDED: action-time write-through of the
completion state only. Completing (or re-opening) a
Planner-synced item in the app PATCHes its Planner task's
percentComplete at the moment of the act, guarded by
If-Match on the task's current etag. Nothing else is
ever written from the app — no title, due-date, bucket, or
assignment edits (content editing from the app is out of
scope for this spec). A failed write, including a 412
etag conflict, leaves the item in its worked state in the
app, marked not yet reflected in Planner — divergence
is shown, never silent, and the next sync-in surfaces the
conflict for resolution rather than overwriting either
side. Consequences carried openly: write-back raises the
work app's Planner scope from Tasks.Read to
Tasks.ReadWrite; if the chief rules the alternative,
the scope stays read-only.
The named alternative: strict one-way sync — completion never leaves the app. It is simpler and keeps the work leg read-only, at a standing cost: every item completed in the app remains open in Planner, in the one surface his work world sees, permanently diverging from the app's truth.
5.4 Commitments — the unified, prioritized view
Definition. A commitment is anything that obliges his action: a task assigned to or created by him (Planner-synced, native), an assessed item whose content asks for or promises his act (a mail or Teams item whose do-today assessment is yes), or a calendar event that bears his obligation — to bring, decide, present, or prepare something (per §3.5, surfaced as its action). Information is not a commitment. Attendance alone is not a commitment. A commitment always carries an act and, explicitly or by assessment, a time it belongs to.
The view. The To-Do tab carries board and
Commitments as a segmented control — "Board |
Commitments" — at the top of the one tab (UI/UX
direction): the board is the default, and the choice is
remembered per device. Above the control, a shared
header line states the set both presentations show —
"<N> commitments · <M> overdue · updated <time>" —
identical whichever is showing; that sameness is what
teaches "same set, two views". It is one header, one count,
one dataset behind both — the control switches the
presentation, never the data. Under the control, one
helper line states the priority model's core promise:
"Your pins and order always win inside a band." A
filter chip row (All · Work · Personal, each with a
live count) sits under the header — view-only (rule 4),
persisting per presentation session, never re-ranking;
when a filter hides everything in a band, the band
header hides too. Naming rule: "lens"
is design vocabulary for this fact and never appears in
the product; the controls are labeled "Board" and
"Commitments," and commitments are called commitments
in every label, header, and empty state. The
Commitments presentation is one prioritized list
spanning every surface's commitments — native to-dos, Planner
tasks, promoted mail/Teams items, and calendar-born
action cards — each row showing a completion checkbox,
its act, its source, its
time, and why it ranks where it does (its band and
reason, in plain chips — max two per row, band reason
first, assessment second: "overdue", "due today", "assessed
do-today 0.9", "event tomorrow — prep"). The bands
render as labeled sections (Overdue / Today / This
Week / Later as section labels, band headers carrying
their counts) — not a flat list, not
collapsible priority headers. Each row's menu carries
"Show on board" — it switches the presentation and
flashes the card's outline, so the two presentations feel
like one tool. The board and the
Commitments view are two presentations of the same set:
the board organizes the work; the Commitments view orders
everything. Working an item in either presentation acts on the
same record (and its home of record, per §5.1/§5.3).
The priority model (transparent by design — bands and reasons shown, never a hidden score):
- Band by horizon first: OVERDUE (past its time, incomplete) → TODAY (due today; an event today bearing action; assessed do-today) → THIS WEEK → LATER.
- Inside a band, his explicit order wins (drag order on the board, or a pin in the list) — his judgment outranks the model's arithmetic wherever he has expressed it.
- Then the assessment: do-today probability orders assessed items; due time orders the rest.
- Work/personal is a filter, never a rank — neither world outranks the other by default; he filters the view by class when he wants one world at a time.
Bands, thresholds, and the calendar look-ahead window are settings. A commitment's lifecycle: proposed (assessed candidate, in Proposed) → active (on the board / in the view) → done (completed) or dismissed (removed from both views) — only completions enter Done; a dismissal is a rejection of a proposal, not a completion (direction F4). A calendar-born candidate whose event is cancelled retires instead (shown as retired, never silently deleted), unless the owner has acted on it (§3.5).
The working view of the day (board ↔ calendar, stated expressly): the Commitments view's TODAY band is the one working view the program exists to give him — the day's calendar events pinned at their times (shown as time anchors, visually distinct from action rows), interleaved with today's commitments in the model's order. Due-dated cards read against the day's events in one place: what the day holds, when it holds it, and what he owes inside it. Events in the band are anchors, not commitments — they carry no act unless §3.5 extracted one, and that act appears as its own row, linked.
6. Architecture
6.1 The staged design carries
Browser → Cloudflare Access (hostname app; his emails)
→ Worker (verifies the Access JWT itself, then proxies)
→ Durable Object (one named container instance) →
container (gunicorn/Flask, the staged codebase) →
Microsoft Graph (both legs), the Jev endpoint, and the
state stores. Custom domain inbox.knashboard.com,
workers_dev off. Platform facts and the cost shape are
the staging report's (≈ the $5 Workers Paid plan; expected
container overage $0 at his usage). The container's
filesystem is ephemeral; nothing of value is written to it.
6.2 The store decision (input #2: the spec chooses, with reasons)
The to-do store is D1. The to-do list is relational, per-item-mutated data — items queried by class, state, due date, and surface; completion written transactionally with its write-back marker; the dashboard estate's proven D1 pattern (the workbench's own databases) is the fleet's standing shape for exactly this. The R2 state store carries as staged for what it already fits: settings, run state, results/summary transport, and the encrypted token caches — document-shaped state with whole-object reads and writes. Each store does the work its shape fits; neither is stretched into the other's.
Integration shape: the to-do API is served by the Worker against its D1 binding (native, no additional credential anywhere); the container posts assessment results to it and reads the working set from it over the Worker's internal route. No database credential exists inside the container.
6.3 Three sign-ins, three token stores
The personal leg is the staging's auth-code flow (§3.1). The work leg is a second, separate MSAL confidential-client flow against the work tenant's own app registration, with its own redirect, its own client secret, and its own encrypted token cache in the state store, under its own raw Fernet key. Per-leg keys are mandatory (§8.2 F-C): each leg's key is a raw 32-byte Fernet key from a CSPRNG, generated once, held in Secure Vault and entered as a Worker secret. The staged helper's passphrase-derivation path is forbidden and is removed: the app validates the key's form at startup and refuses to start on anything that is not a well-formed raw Fernet key, reporting the key's FORM only — never its value or any prefix of it. Rotation and revocation ride as story criteria (IT-02/IT-04; §8.2 F-C): rotation re-encrypts the leg's cache under a fresh raw key and destroys the old key; suspected exposure remediates by revocation at the identity provider, cache deletion, and re-consent under a fresh key — never re-encryption alone. The plain account-name sidecar beside each cache holds the account name and sign-in state only (§8.2 F-C). The Google leg is a third leg of its own form: its credentials — the OAuth client secret and the refresh token from his one-time bootstrap consent (§10, S6) — are Worker secrets, entered through Secure Vault and never held in the app's state store at all; at runtime the Worker exchanges the refresh token for access tokens per request and persists nothing Google-issued. The legs share code and nothing else: no cache, secret, or consent crosses between the personal, work, and Google worlds. Sign-in state per leg is independent in the UI (any leg can be signed out or expired without disturbing the others). Presentation of that independence (UI/UX direction of record, D5): independence is invisible until it matters — a connections indicator in the app header (person/plug icon) with a single aggregate dot: green = all three legs healthy, amber = one leg needs attention, red = a leg is down with surfaces stale. The panel behind it lists the three legs by world, each with its account identity (the email — so "which Microsoft?" is never ambiguous), its status (Connected / Needs sign-in / Expired), its last successful sync time, and the surfaces it powers ("Work account — Teams, Tasks") — he learns the impact of a dead leg, not just its existence. Degradation rules (firm): a dead leg degrades only its own tabs; those tabs keep showing their last data under an inline banner ("Work account sign-in expired — showing the last sync, Tue 9:14 AM. [Reconnect]") — never an empty tab, never a modal, never a redirect away from what he was doing. No modal re-auth prompts, ever: the only proactive surfaces are the header dot, the per-tab banner, and one dismissible notice chip on the To-Do view per leg per day ("Gmail needs reconnecting — Gmail tab is stale"); if he dismisses it, the dot and banner remain and nothing nags twice. Reconnect is an in-place act: the banner/panel button starts that leg's flow and returns him to the same tab and scroll position. Signing out of one leg is a panel act with a plain consequence line ("Teams and Tasks will stop syncing"). First run is the exception: until all three legs have connected at least once, the To-Do view carries a setup checklist card (three legs, Connect buttons, one line each on what each powers); once complete it never returns — a dead leg later is the banner pattern, not the checklist.
7. Board and wiki estate (scrum master sections)
7.1 The board — a dedicated llm-kanban root in jknash/dashboard
Inbox Triage work is tracked on its own llm-kanban board, stood up inside jknash/dashboard in the separate-root construction the Knowledge Layer lane proved on the agent-orchestration board:
- Root:
kanban/it/inside jknash/dashboard. The Workbench board root (kanban/, prefix WB-) is untouched by the construction; the two boards share a repo and a tool, nothing else. - Prefix:
IT-, declared as theid_prefixof the board's ownschema.yamlinstance. The schema is otherwise the Workbench schema carried whole (same story fields, same lifecycle, same claim/lease/generation rules); no schema invention for this lane. - Agents: its own
agents.yaml, carried from the Workbench board's — the same registered fleet. The lane adds no agents and mints no identities. - Renders: its own
board.mdandstatus.mdunderkanban/it/, generated by the repo's existingkanban/kanban.pydriven with--dir kanban/it— the same tool, the same invocation shape the KL lane runs. Agents never hand-edit generated views. - Contents: its own
stories/,sprints/,retros/, andbacklog.mdunder the root. Sprint files are this board's own (IT-S01, …), scope-bound like every fleet sprint. - Contract: the repo's llm-kanban contract family (claim by commit, first push wins, one active claim per agent, leases with generation fencing, blocked as an overlay, the PM exception) — no new contract is minted for this board.
7.2 The llm-wiki estate in jknash/dashboard
The program's compounding knowledge lives in an llm-wiki estate in the same repo, built exactly to the codified llm-wiki skill (fleet form, codified 2026-10-10; source of record: jknash/hermes-shared- skills, branch hermes-jkdev001, skills/research/llm-wiki/SKILL.md v2.1.0):
- Root:
wiki/at the repository root of jknash/dashboard — the skill's fleet rule (awiki/tree inside the project repo; roots are per-repo, never a global default). ItsSCHEMA.mddeclares the domain: Inbox Triage — the surfaces, the assessment practice, and what the program has learned about them. - Shape, per the skill: three layers under the root —
raw/(immutable source material: articles, papers, transcripts, assets; frontmatter with source_url, ingested date, and a body sha256 for drift detection), the wiki pages (entities/,concepts/,comparisons/,queries/), andSCHEMA.md(domain, conventions, tag taxonomy, page thresholds) — plusindex.md(the sectioned content catalog) andlog.md(append-only action log, rotated per the skill). - Operations are the skill's: ingest, query, lint, run under its disciplines (orient on SCHEMA + index + recent log before any operation; raw sources never modified; every page cross-referenced and added to the index; every action logged).
- Boundaries, stated once: the board (§7.1) is work state — what is claimed, built, and accepted. The wiki is knowledge state — what the program knows. The application's own state (run state, settings, results, and the to-do store of §5) is product state, held by the app per §6. None of the three substitutes for another; the spec's story set (§9) stands the estate up as its own story so the boundary is built, not just described.
7.3 Capacity truth
- Inbox Triage draws on the same four-worker bench as every lane —
maverick-muse-worker-001,maverick-codex-worker_sol-001,maverick-claude-worker_opus-001,maverick-muse-worker_spark-001— dispatched by the scrum master under the standing pipeline (planner proposes, chief approves, scrum commits and dispatches, dual §3 review, merges under the standing delegation). - Priority rule, in the KL lane's form: AO sprint claims keep their order; KL-lane and IT-board claims fill the bench's remaining capacity. An IT claim never displaces an AO or KL claim, and a stalled IT lane never blocks an AO or KL close.
- Nothing dispatches to a worker from this board until this spec is chief-approved (commission term).
7.4 Filing consequences of the settled inputs
- The third surface is Microsoft Planner on the work-account leg (§3): Planner syncs into the app. The mailbox leg is the personal account; Teams and Planner are work-account legs under Microsoft Graph. The Planner and Teams stories carry the work-account Entra app/consent steps as named owner dependencies on their faces when filed — the human steps named in the story from the start.
- The app is the to-do list (§5): the story set carries the app's own to-do store as a build item — a component of the tool, not an integration with any external dashboard or task product. (No Trello surface, API, or credential step exists anywhere in this program; the earlier Trello form of this input was superseded by the owner before any build.)
7.5 Filing sequence on this spec's approval
- The inbox-triage docsite project record is updated with the re-home and the new scope (part of this delivery, per the commission's process terms).
- The board root is stood up in jknash/dashboard in one pass — schema instance, agents.yaml, empty renders — and validates with 0 errors before any story is filed (the KL stand-up's order).
- The wiki estate is stood up at
wiki/(structure, SCHEMA with the Inbox Triage domain, initial index and log) per the codified skill. - The groomed story set (§9) is filed in board form from the approved spec — hash-bound drafts, filed unaltered.
- WB-003's disposition is executed on the Workbench board in the same pass: closed by the supersede mechanics naming the absorbing IT- story, on §2's approved wording, with the Workbench 2026-S01 record updated in the same move — nothing left dangling.
- The first sprint (IT-S01) is proposed to the chief in the ordinary form — planner proposal, chief's approval, scrum master commits the sprint file — and dispatch begins only then.
8. Security posture
Posture as designed (the security reviewer's consult precedes finalization; its dispositions are recorded in §8.2 when rendered):
- One door, verified twice: Access in front of the hostname, and the Worker verifies the Access JWT (audience + signature) itself before proxying — as staged. No public path bypasses either check.
- Secrets are platform secrets only: both MS client secrets, the Google OAuth client secret and refresh token (§6.3 — the Google leg's whole credential set lives here, in no store of the app's), the Jev API key, the app secret key, and the token-cache encryption keys exist as Worker secrets / environment, never in the repo, the app's settings, or a request body. The staged settings-drawer discipline (the API ignores secret fields) carries.
- Token caches encrypted at rest, per leg, in the state store, each under its own raw Fernet key (per-leg keys mandatory; the key as a Worker secret, the ciphertext in R2 — never the same store). The staged helper's passphrase-derivation path is forbidden and removed: startup validates the key's form and refuses anything that is not a well-formed raw Fernet key (§8.2 F-C, which carries the rotation/revocation posture).
- Least privilege per leg: personal — Mail.Read +
Calendars.Read. Work — Chat.Read (delegated), Tasks.Read
(delegated; rising to Tasks.ReadWrite only if §5.3's
write-back is approved, and then consented as its own
recorded act, never bundled silently), channel
messages under Teams resource-specific consent, per
team, with delegated ChannelMessage.Read.All only as
a recorded per-team fallback (§8.2 F-A). Application
(app-only) tenant-wide read of chats or channel
messages: never, in any leg. Google —
gmail.readonly+calendar.readonly. No write scope exists anywhere else in the design. - Data minimization outward: the emitted summary carries metadata and counts only, never bodies (§4.3); the to-do store holds his personal data behind the same Access gate as the rest of the tool, single-user by design.
- Body content transits to the Jev endpoint for assessment within the consult's ruled caps (§8.2 F-B): mail/Gmail as built (From + Subject + body to 3,000 characters, or the snippet); Teams threads capped at 3,000 characters in total; Planner notes excerpts at 1,000; calendar description excerpts at 500; attachments never, in any form; the payload is state text + questions only. The OpenRouter account's retention posture is set to the most restrictive the platform offers and its state recorded as evidence at IT-02's cutover.
- The Cloudflare account is itself a custody plane: Worker secrets, R2, D1, and the Access application all sit behind it. MFA enabled on the owner's Cloudflare account is a named owner fact (§10, S3), and the Access application's admission stays limited to the owner's emails, as staged.
8.2 Consult record
Security consult rendered by
maverick-muse-security_reviewer-001 on the consult packet
(sha256 09113bcdf2750294cb85907ec8b42179ab745aa7962640b8fcb20eabadb2925f)
against this spec at sha256
33f8a36aae53562273847708d3c1da6ae1978ed12f6454b7bf3bbb371b67151b. Three findings, three rulings; no disposition
discharges another. Recorded verbatim in effect; the
amended text returns to the security desk for its check
before finalization (the required return loop).
F-A — Work-tenant consent architecture: RULED — resource-specific consent for the channel leg; delegated for chats and Planner; the tenant-wide channel grant exists only as a recorded fallback.
- Channel messages: Teams RSC is the architecture of record. Per-team consent scopes the read to the teams that grant it, is revocable per team without killing the leg, and bounds the blast radius to granted teams — against the tenant-wide delegated grant, whose practical reach is every team the owner belongs to, standing behind one token cache. IT-05 files channels as RSC-gated: a team enters scope when its RSC grant exists; the story's promise is per-team, never tenant-wide.
- Chats: delegated Chat.Read stands — the subject is the owner's own conversations, delegated-as-him is the proportionate form, and no RSC form improves it enough to justify a second consent machinery on the same leg.
- Planner: delegated Tasks.Read stands (Tasks.ReadWrite only on §5.3's approval, ruled with the spec) — Planner has no RSC form; there is nothing to prefer instead.
- Fallback, narrowed: delegated ChannelMessage.Read.All may be taken for a specific team ONLY where RSC is unavailable for it, and each such grant is recorded on IT-05's record (the team, the reason RSC was unavailable, the date), with RSC replacing it when it becomes available. The work app's registration must NOT carry a standing tenant-wide channel grant outside a recorded fallback — an unused broad grant held "just in case" is the worst of both architectures: full blast radius, no use.
- Conditions: (1) the Tasks.ReadWrite elevation, if §5.3 is approved, is consented as its own recorded act, not bundled silently into the initial grant set; (2) §3.2's scope-posture paragraph and §8.1's work-leg line are amended to this architecture (done in this draft); (3) Q1 is untouched and is now asked against THIS architecture — RSC consent is a team-owner act, and whether the delegated remainder needs admin consent he cannot give is exactly Q1's fact to establish; if Q1's answer narrows what he can consent to, the surfaces narrow by the ruling, not by discovery at build.
- Forbidden floor (F-A): application (app-only) tenant-wide read of chats or channel messages (Chat.Read.All / ChannelMessage.Read.All as application permissions) — never, in any leg; and no second work-tenant registration carrying broader scopes as a spare.
F-B — Jev payload content basis: RULED per surface — the as-built mail basis stands for the mail legs; the new surfaces take tighter caps; three standing conditions ride on all of it.
- Mail and Gmail: the as-built basis STANDS — From + Subject + body truncated to 3,000 characters (or the snippet where no body exists). This is the owner's own running design on his own mail, and the do_today judgment legitimately turns on content that sits below a subject line. §4.2's mail composition is unchanged.
- Teams: the basis is the THREAD, capped as a whole — team/channel (or chat) name + participants + the thread's recent messages truncated to a TOTAL of 3,000 characters across the state text, most recent kept, oldest dropped first — NOT 3,000 per message. The content here is a work tenant's: colleagues' and potentially clients' words, not the owner's alone; the classification needs the conversation's recent shape, not its archive.
- Planner: task state text with the notes excerpt capped at 1,000 characters (title, due date, priority, percentComplete ride in full — they are the classification's substance).
- Calendar: description excerpt capped at 500 characters (title, start/end, attendees, location, organizer in full). The calendar question judges whether an ACTION exists; that shows in the title and the description's opening.
- Standing conditions (all surfaces): (1) attachment contents never enter a payload, in any form — no attachment body, text extraction, or file content fetched for assessment, ever; (2) the payload is state text + questions and nothing else — no account identifiers, tenant ids, or message ids beyond what the state text itself carries; (3) the OpenRouter account's retention posture is set to the most restrictive the platform offers (zero-retention routing / data-collection off, in whatever form the platform exposes), and its state is RECORDED at IT-02's cutover as evidence; (4) the summary/results egress is unchanged (metadata and counts, never bodies — §4.3 stands as written).
- Standing data-posture fact for the owner: under this ruling, message content — including, on the personal leg, legal and divorce-case correspondence in his mailbox — transits a third-party endpoint (OpenRouter, under his own key) for assessment, on every surface within the caps above. That is the owner's architecture as built and commissioned; this record stands in front of him at finalization, not buried in a flow diagram. If he wants the legal-correspondence class excluded by sender or folder, that is an owner direction the spec can carry — the consult does not silently assume it either way.
F-C — Token-cache key custody: RULED — acceptable as designed in shape, with the passphrase path forbidden and the rotation/revocation posture named.
- (1) The passphrase-derivation fallback is FORBIDDEN. base64(SHA-256(passphrase)) is a single-round, unsalted, zero-work-factor derivation: if the R2 ciphertext is ever exposed, a human-chosen passphrase falls to offline brute force quickly, and what it protects is worse than a password — MSAL caches hold refresh tokens, i.e. durable silent access to the owner's mailboxes as him. The key is a RAW Fernet key (32 bytes from a CSPRNG, generated once, held in Secure Vault, entered as a Worker secret). The code path accepting a passphrase is REMOVED or hard-refused: the app validates the key's form at startup and refuses to start on anything that is not a well-formed raw Fernet key, and startup reporting states the key's FORM only (accepted/refused), never its value or any prefix of it. This binds IT-02's criteria and §6.3's text (amended in this draft).
- (2) Per-leg keys are MANDATORY, as spec'd. Personal and work legs hold distinct raw keys; a shared key collapses the separation §6.3 exists to keep, and one leg's key exposure must expose only that leg's cache. The Google leg holds no cache; its refresh token as a Worker secret stands under platform-secret custody.
- (3) Rotation and revocation, carried as story criteria (IT-02/IT-04): rotation = generate a fresh raw key, re-encrypt the leg's cache under it, replace the Worker secret, DESTROY the old key (its vault copy deleted; no key history retained) — an operable runbook step, not an intention. On SUSPECTED EXPOSURE of a leg (key, or ciphertext together with any key path): the remediation is REVOCATION AT THE IDENTITY PROVIDER — the leg's sessions/refresh tokens are revoked in Entra, the cache object is DELETED from R2, and the leg re-consents from scratch under a fresh key. Re-encryption alone never remediates a copied refresh token, and the criteria must not pretend otherwise. On leg retirement: cache deleted, key destroyed, consent revoked in the tenant — all three, recorded.
- (4) The plain account-name sidecar is ACCEPTABLE. It carries an account identifier — metadata the status surface legitimately needs without decrypting — and no credential material. Condition: the sidecar holds the account name and sign-in state ONLY; any expansion of its contents (client ids, token metadata, timestamps of token events beyond status need) re-opens this item at the security desk.
- Ciphertext in R2 with the key as a Worker/container secret — the two never in the same store — stands as the design's sound core; §8.1's encryption bullet is completed by (1) and (2) above.
§8.1 — the standing posture, reviewed as it stands: STANDS, with the amendments the rulings force (§8.1's work-leg scope line per F-A; its body-content bullet completed per F-B; its token-cache bullet completed per F-C), plus one addition recorded by the consult: the Cloudflare account is itself a custody plane (Worker secrets, R2, D1, and the Access application all sit behind it) — MFA enabled on the owner's Cloudflare account is a named owner fact for §10's steps (§10 S3, amended in this draft), and the Access application's admission stays limited to the owner's emails, as staged.
9. The story set (groomed sketch; filed on approval per §7)
Filed under the IT- board (§7) at this spec's approval; sizes are the planner's grooming, in the fleet's Fibonacci form, each story one outcome. Final criteria text is written at filing against the approved spec.
- IT-01 — Board + estate stand-up (3). The
kanban/it/root, schema instance, agents.yaml, renders; the llm-wiki estate rooted in jknash/dashboard per the codified skill. (§7's mechanics; the scrum desk's own story.) - IT-02 — Hosted cutover, mail leg live (5). The staged build deployed: Access + Worker + container at inbox.knashboard.com, the personal auth-code flow live, mail triage running hosted as it runs on the Mac today. Carries the §8.2 F-C key-form criteria (raw Fernet key only; the passphrase path removed; startup validates the key's form and reports form only) and the F-C rotation/revocation posture, and records the OpenRouter retention posture (§8.2 F-B) as cutover evidence. Depends: IT-01. Owner steps §10 (S1, S3, S4) precede.
- IT-03 — The to-do component (8). The D1 to-do store
- the Worker to-do API + the To-Do tab in its BOARD form (buckets as data, draggable cards, the seeded default buckets): native personal to-dos (create, complete, re-open, defer, dismiss, drag), the working view, promotion of assessed items (owner's act + the high-confidence proposal rule into Proposed). Depends: IT-02. PRICED HONESTLY (the chief's input-#3 relay): the 8 is store + API (3) and a board UI at Trello grade (5) — drag affordance, order persistence, and bucket management are a larger build than a list view, and the size sits at the board-rule ceiling BECAUSE the board is real. At filing, if the groomed criteria outgrow 8, the story SPLITS (board interaction as its own story) rather than absorbing scope.
- IT-04 — Work leg sign-in (5). The second MSAL flow: work app registration wired, per-leg encrypted cache under its own raw Fernet key (the §8.2 F-C rotation/revocation criteria ride here), independent per-leg sign-in state. Depends: IT-02. Owner steps §10 (S2) precede.
- IT-05 — Teams surface (5). Thread intake (chats under delegated Chat.Read; channels RSC-gated per team — a team enters scope when its RSC grant exists, with delegated ChannelMessage.Read.All only as a recorded per-team fallback, §8.2 F-A), the Teams question set (§4.2), the Teams tab. Depends: IT-04.
- IT-06 — Planner surface (5). Sync-in (idempotent, source-keyed), the Planner question set, the Tasks tab as the surface's review queue (its rows mirroring the Planner facts beside the assessment), synced tasks arriving as cards on the board (§5.2). Depends: IT-04.
- IT-07 — Planner write-back (3). §5.3 as ruled (APPROVED 2026-10-10): completion-state write-through (PATCH percentComplete, If-Match), the not-yet-reflected marker, conflict surfacing; its Tasks.ReadWrite rise consented as its own recorded act (§8.2 F-A). Depends: IT-06.
- IT-08 — Workbench tile + WB-003 supersede (2). The launch tile + summary counts in the workbench /inbox/ section, reading the emitted summary; the supersede executed per §7 step 5. Depends: IT-03 (the summary's final shape).
- IT-10 — Google leg + Gmail surface (5). The Google OAuth flow (third sign-in; credentials held as Worker secrets per §6.3 — no app-side token store), Gmail intake + the mail question set + the Gmail tab. Depends: IT-02. Owner step §10 (S6) precedes.
- IT-11 — Calendar surfaces (5). Outlook Calendar (the personal leg's added Calendars.Read scope) + Google Calendar (via the Google leg): event intake, the calendar question set (§4.2), the Calendar tab, and do-today events proposing their action cards into Proposed (§3.5). Depends: IT-02, IT-10.
- IT-12 — The Commitments view + priority model (5). §5.4 as specified: the unified commitment set across surfaces, the banded priority model with shown reasons, explicit-order-wins, class as filter. Depends: IT-03; composes the other surfaces' outputs as they land (IT-05, IT-06, IT-10, IT-11 each extend its set).
- IT-09 — Retirement + records (2). The Mac app retired as daily driver after hosted verification in daily use; the docsite project record brought current with the re-home and the as-built state; the wiki estate seeded with the cutover record. Depends: IT-02, IT-08.
Sequence truth: IT-02 is the gate for everything hosted; the two work surfaces (IT-05, IT-06) are independent of each other after IT-04 and fill bench capacity in the KL lane's form (§7).
10. Owner questions and owner steps
Questions (for the chief to carry up)
- Q1 — Work-tenant consent. Asked against the consult's ruled architecture (§8.2 F-A): channel messages ride Teams RSC — consent is a team-owner act — and the delegated remainder (Chat.Read, Tasks.Read, and Tasks.ReadWrite if §5.3 is approved, consented as its own recorded act) may require tenant admin consent. Can he make these grants himself, or does his employer's tenant require consents he cannot give? The answer shapes IT-05 (which teams enter scope; chats-only vs chats + granted channels) and is the spec's one open external dependency. If a consent is required and unavailable, the affected surfaces narrow by the ruling, not by discovery at build — named here so the ruling, not the build, discovers it.
- Q2 — §5.3 write-back. RULED with this spec (chief, 2026-10-10): APPROVED as recommended — action-time write-through of the completion state only; IT-07 files on that basis; the Tasks.ReadWrite rise is consented as its own recorded act (§8.2 F-A). Not a separate owner ask.
Owner steps (exact; each gates the story named)
- S1 (gates IT-02) — Entra, personal app (the same app
registration the Mac app uses): Authentication → Add a
platform → Web → Redirect URI
https://inbox.knashboard.com/auth/callback; then Certificates & secrets → New client secret, copying the Value at creation (shown once) into Secure Vault. The existing Mobile/desktop platform stays (the Mac app keeps working until IT-09). (From the staging report, carried verbatim in effect.) - S2 (gates IT-04) — Entra, work tenant: register (or
designate) the work app; add the Web platform Redirect
URI
https://inbox.knashboard.com/auth/callback-work; create its client secret into Secure Vault; grant the §8 scopes (subject to Q1's consent answer; channel access is NOT granted here — it rides per-team RSC under IT-05, §8.2 F-A). - S3 (gates IT-02) — Cloudflare: the knashboard.com zone active in his account; a Workers Paid plan in place; an Access application for inbox.knashboard.com admitting his emails (the workbench pattern); and MFA enabled on the Cloudflare account — the account is a custody plane (§8.1).
- S4 (gates IT-02) — Secrets entry: the Worker secrets entered at cutover from Secure Vault — both MS client secrets, the OpenRouter (Jev) API key, the app secret key, the token-cache encryption keys (one raw Fernet key per MS leg, generated once — 32 bytes from a CSPRNG; the passphrase form is never used, §8.2 F-C). Secrets move vault → platform, never through chat or the repo (the staging discipline).
- S6 (gates IT-10) — Google Cloud: create (or
designate) a Google Cloud project and an OAuth 2.0
client (web application; redirect URI
https://inbox.knashboard.com/auth/google/callback), configure the consent screen for thegmail.readonly+calendar.readonlyscopes, and place the client secret in Secure Vault; then complete the one-time bootstrap consent and place the resulting refresh token as a Worker secret via Secure Vault (never through chat; §6.3 states the leg's form). NAMED CONSIDERATION for the build and the security consult: Google refresh tokens for an OAuth client left in "testing" consent posture expire quickly (7 days) — the consent-screen posture (publishing the app for his own use) is decided at build so the leg does not silently lapse weekly. - S5 (gates IT-02) — Deploy path: the first deploy
runs
wrangler deployfrom a Docker machine (the staging report's platform fact). Fleet-executed from this VM if its Docker + a deploy-scoped Cloudflare API token are provided at cutover; the token is an owner step if the existing workbench-automation token's scope does not cover container deploys.
Draft by maverick-muse-planner-001 (planner) with §7 by maverick-muse-scrum_master-001 · 2026-10-10. Route: scrum §7 confirmation → UI/UX direction (§5's frontend) → security consult (§8.2) → finalization → the chief's approval → docsite publication + the project record update (§7 step 1) and the story set's filing.