โ† Agora
Agora ยท v0.13.0

One board.
The whole team
on it.

The team-shaped app on the Kosmos engine โ€” one shared board per team, eight store-shaped pipelines, work with a name on it, and every member's AI seeing exactly what that member sees.

Agora v0.13.0 ยท agora.appolis.app ยท source of truth: APP_BREAKDOWN.md
The problem

A team board is not a solo board with more people on it

Kosmos is one brain per account, hard-isolated. Agora is one JSON doc that a whole team โ€” plus every member's AI connector โ€” reads and writes at the same moment. Everything below is a bug that simply cannot happen when you are the only person holding the list.

โœ…โ†’โฌœ
Two people tick the same list at the same moment. One tick survives. Nobody sees an error.
๐ŸŽญ
A person's AI reads and writes what their own screen hides โ€” because the website enforced roles and the connector did not.
๐Ÿ—‘
Work vanishes off a teammate's plate and they are never told โ€” the app that deleted it only ever thought about the deleter.

Each one was reproduced as a failing test first. Several of those tests still carry a control leg running the OLD code, asserted to keep failing โ€” so a green run can never be a test that quietly stopped reproducing the bug.

The board ยท eight ways a store team works

Eight sections. Each with its own pipeline โ€” and its own way of stalling.

Ecommerce-first, shaped by what an online-store team actually does. Anything sitting in a section's stall stage for 7+ days wears a badge on its tile.

SectionPipelineStalls at
๐ŸŽฏ InitiativesIdea โ†’ Scoped โ†’ In Motion โ†’ Review โ†’ Shipped โ†’ LiveReview ยท Shipped
๐Ÿ›๏ธ Products & DropsIdea โ†’ Sourcing โ†’ Listing โ†’ Launch โ†’ SellingSourcing ยท Listing
๐Ÿ“ฃ CampaignsBrief โ†’ Planned โ†’ Producing โ†’ Review โ†’ Live โ†’ ResultsReview
๐ŸŽฌ ContentIdea โ†’ Brief โ†’ Producing โ†’ Review โ†’ ApprovedReview
๐Ÿช Store OpsIntake โ†’ Next Up โ†’ Doing โ†’ Review โ†’ DoneReview
๐ŸŒ Website ChangesRequest โ†’ Scoped โ†’ Building โ†’ Review โ†’ LiveReview
๐ŸŽช EventsIdea โ†’ Planned โ†’ Prep โ†’ Live โ†’ WrappedPrep
๐Ÿค Partners & AffiliatesProspect โ†’ Talking โ†’ Terms โ†’ Onboarding โ†’ ActiveTerms
every new project is born with 4 working lists, shaped by its section ๐ŸŽฏ Initiatives โ†’ ๐Ÿ“‹ Action items ยท ๐Ÿšง Blockers ยท ๐Ÿ“ Decisions ยท ๐Ÿ”ฎ Ideas & later ๐ŸŽช Events โ†’ ๐Ÿ“‹ Planning checklist ยท ๐ŸŽช Day-of run sheet ยท ๐Ÿ“ฆ Supplies & vendors ยท ๐Ÿ“ Wrap-up notes every stage carries its own playbook prompts
๐ŸŽญ The mirror rule ยท platform doctrine

A person's AI gets exactly that person's permissions โ€” never more.

Tyler's call, 2026-07-24, now the rule for the whole suite. Agora is the reference implementation: the board enforced five capabilities correctly on the website while the connector enforced none of them, so someone's AI could read and write what their own screen hid.

1
One capability model, both doors. boardCaps() is exported from lib/api.js and imported by lib/mcp.js โ€” the web door's own gates are built from it, so a rule change lands on both in the same commit.
๐Ÿ‘
The read half is one choke point, not per-tool gates. Every tool is handed a compartmentalized view: hidden projects โ€” and their notes, to-dos and events โ€” are simply not in the arrays. A hidden thing reads as not found, never forbidden, so there is no existence oracle. Future tools start safe.
๐Ÿ”’
Fail closed. A missing role resolves to contributor, never admin. Writers work the raw doc; the view's arrays are frozen, so a missed repoint throws loudly instead of silently dropping a teammate's work.
viewer
reads
contributor
works only ASSIGNED projects; completions await approval
editor
works anything they can see; NEW adds await a manager
manager ยท admin
anything, anywhere
31-assertion role ร— project-shape matrixthe roster is part of the mirror โ€” one directory feeds both doors/breakdown is compartmentalized too, not hidden
Chain of command ยท Tyler's two calls

Starting work and handing work out are lead jobs.

Who may start work
Owner / manager only
You get added to Agora because you are on a team, and you see the projects you were put on. Creating a project โ€” and archiving or re-filing one, which are creation moves wearing a different hat โ€” is a lead's call, on both doors. Before this it had no role gate at all: a viewer could create projects. The ๏ผ‹ New tiles hide from anyone who can't use them, because a button that 403s is its own bug.
Who may assign
Handing work out: lead. Picking work up: anyone.
Giving a task to another person โ€” or taking it off them โ€” is owner/manager only, across to-dos, list items, the project crew and the connector's assign_work. What stays open to everyone: claiming a free task for yourself and putting your own back down. That is working, not delegating. The ๐Ÿ‘ค picker offers a non-boss exactly those two choices instead of a roster they cannot use.

One helper โ€” canAssign(current, next) inside boardCaps โ€” is the whole rule, and both doors call it. Personal view preferences (like ๐Ÿ™ˆ hiding a section from your own dashboard) stay writable by everyone; house tunables are editor-and-up.

๐Ÿšฆ One write channel ยท v0.10.0

Eight teammates added a to-do at the same moment. One survived.

The board is one JSON document. Every change used to be read-it, edit-it, put-it-back straight against storage โ€” so two overlapping writes each took a copy and each put the whole thing back, and the slower one silently erased the faster one's work. With edge caching, "overlapping" could mean a minute apart. Nobody saw an error.

Before โ€” reproduced as a failing test
1 of 8 survived
Seven people's work gone, seven success toasts shown.
After โ€” same test
8 of 8 survived
And the harness fails itself if the old side ever stops losing writes, so a green can't be a test that skipped the race.
๐Ÿšฆ
One Durable Object per board, named after the key it owns โ€” so every server worldwide talks to the same one object for the same board.
โ†ป 15
Saves are compare-and-swap: a writer holding a superseded copy is rejected and the whole request replays on fresh data โ€” up to 15 tries, then a loud 503. Never a success toast for a write that didn't land.
๐Ÿ”Œ
A conflict is a flag, never a thrown error โ€” the connector wraps its whole reply in try/catch and would have swallowed a throw into a "200 OK", leaving the AI door quietly losing writes while the website looked fixed.
storage stays a write-through mirror, so external inspection and rollback stay correctkill switch: one secret reverts it in ~30s, no deploy
๐Ÿ“Œ Item-level writes ยท v0.11.0

The checkmark that kept disappearing

Ana ticks item 1. Ben ticks item 3, the same moment, on the same list. Ana's tick is gone โ€” and neither of them is told. This is the most ordinary thing two people can do to a shared list, and it was broken.

Why it happened
Ticking a box sent the whole list back โ€” built from a copy the browser had been holding for up to 45 seconds. So Ben's tick didn't just say "item 3 is done"; it also re-asserted "item 1 is not done", from a copy taken before Ana touched it. Fixing the storage race didn't help: the replay faithfully re-applied the same stale list, so the checkmark still vanished โ€” just deterministically instead of racily.
What it does now
A tick sends one operation โ€” "check this item" โ€” not the list. Three verbs (set ยท add ยท remove) cover check, rename, flag and assign, so there is one resolver and one permission funnel. Operations resolve against the fresh document inside the replay, so a retry re-resolves rather than re-erases. Every reply carries the fresh list back, so each tap doubles as a mini-sync.
๐Ÿ†”
An id never falls back. A missing id means the item is gone, not licence to guess โ€” duplicate text is normal on a ๐Ÿ› Bugs list, and guessing would tick the wrong task.
โ†ป
An operation that can't resolve is a neutral "refreshed" nudge with the current list โ€” never a silent no-op (that is the original bug in a new hat) and never a scary error.
โ†ถ
Undo was worse than the bug. It restored a snapshot taken minutes earlier, erasing every teammate change since while the person believed they'd undone one thing. Undo is now an inverse operation: un-check that item, restore that exact item at its index.
๐Ÿ—‘ The Trash ยท v0.12.0

Delete is soft, for 30 days โ€” and it tells you whose work it takes

Agora had no way to delete a project at all until v0.12.0, because on a shared board deleting is not a private act. A project leaves every door the instant it's binned, stays fully restorable for 30 days, then purges for good along with its notes, to-dos, events and uploaded images.

The confirm counts other people's plates
โ€œMove Summer Bundle Drop to the Trash?

It takes 4 lists and 11 to-dos with it.

This takes it off 3 teammates' plates straight away โ€” they will not be notified.

You can restore all of it for 30 days.โ€
Deleting in a solo app only ever affects the deleter. Here the number goes in front of the person before they press the button.
Seven doors, one rule
A project visible on even ONE door after delete is worse than no feature, because then the board and the AI disagree about what exists. So it disappears from the web board, the connector's view, the live /breakdown doc, the deleted-notes list, the cross-app teams strip, the linked-project feed and the calendar feed โ€” that last one matters most, because a calendar subscription refreshes on its own with no in-app affordance to stop it.
boss-only on both doors the retention clock is the server's, never the caller's restore brings notes, to-dos and events back with it the 30-day sweep runs on writes only โ€” a purge inside a page load would turn a board read into a 503 storm 88 assertions
๐Ÿ‘ค Assignment & My Work

Anyone on a project can work it. Assignment makes it explicit.

A ๐Ÿ‘ค toggle on every to-do, every working-list row and every rollup item; a ๐Ÿ‘ฅ Crew row on the project itself. Names are stored with the work, so a plate renders without asking a directory anything.

๐Ÿ‘ค
My Work โ€” every open to-do and list item handed to you, grouped by project, plus the crews you're on. Anyone can flip to a teammate's plate to see who's carrying what โ€” bounded by what the server already lets them see.
๐Ÿ“‡
One directory, every door. The picker and both connector doors read the same roster from the Appolis teams registry โ€” people disabled there drop out of every picker.
โณ
Approvals. A contributor's completion and an editor's new add wait for a manager, with a badge in the nav and a chip on the row โ€” and the AI door says "awaiting approval" rather than claiming the item is done.
๐Ÿ”’
Access per project. Open, or assigned-only โ€” and an assigned-only project, with everything filed under it, simply isn't there for the unassigned.

A connector that offers a different set of PEOPLE than the person's own picker breaks the mirror rule just as surely as a missing gate โ€” it hands work to someone that person could not have chosen. It did, once. Now it can't: there is only one roster function.

๐Ÿ”Œ The connector ยท the AI door

Three doors, one set of rules.

Every account gets a private connector URL, and the suite-wide Appolis Connector opens the same room โ€” those two build identical options from one function, so a permission or roster change cannot land on one entrance and miss the other. The third is a read-only calendar token. It shares no code with them, but since v0.12.1 it answers to the same capability view, so your .ics feed carries exactly the work you can see. Getting that one wrong was invisible for months: a calendar refreshes on its own schedule, with nothing in the app to notice.

๐Ÿ’ฌ โ€œCass finished the PDP copyโ€
โ†’
โ˜‘ check_item
โ†’
๐Ÿ“ฑ the whole team's board updates
28
tools โ€” overview, projects, stages, notes & lists, to-dos, assignment, import, search, decision forms, trash
๐ŸŽญ
every one of them behind the same capability model as the website โ€” read view and write gates both
๐Ÿ“œ
the door hands every AI client the house rules first: scan the board, read the item's full text, prefer additive moves โ€” this board takes concurrent writes from several sessions
45s
the app polls for changes, so an AI's edits appear without anyone refreshing
๐Ÿ“ฅ Bring the work in ยท ๐Ÿ—“ take the dates out

Arrive with a tracker. Leave with your calendar.

๐Ÿ“ฅ
Tracker import. Drop a Basecamp or Trello JSON export, a CSV (project ยท list ยท item ยท due ยท assignee ยท done โ€” Monday lands via its CSV export), or paste lists straight out of a Google Doc. Assignee emails auto-map onto real team members.
๐Ÿ‘€
Nothing writes until you approve the preview. Always a dry run, untickable per project; existing projects and lists are reused and duplicate items skipped, so a second run doesn't double the board.
๐Ÿ—“
A live calendar feed. A read-only token subscribes Google, Apple or Outlook to the board's due dates and milestones โ€” and external calendars can be pulled in the other way.
20
21
Bundle drop live
22
PDP copy due
23
Creator terms
Restock check
24
25
Campaign review
26
๐Ÿ› the Appolis hub button โ€” one popup of exactly the apps your ID opens, same-tab so BACK returns you one login for the whole suite a revoked grant now lands in about ten minutes, not a month
Changelog ยท v0.1.0 โ†’ v0.13.0

Every version, one page.

Three consecutive releases โ€” v0.10.0, v0.11.0, v0.11.1 โ€” are one overhaul: making a shared board tell the truth about who wrote what.

Shipped ยท v0.13.0
One board.
Nobody's work erased.

Live at agora.appolis.app on Cloudflare โ€” one Durable Object per board, storage as a write-through mirror, Appolis ID as the door. Eight sections, five capabilities, 28 connector tools, a 30-day Trash.

357
assertions across 13 zero-dependency harnesses โ€” node test/run.js, green
๐Ÿ”ดโ†’๐ŸŸข
the concurrency and permission harnesses carry control legs running the OLD code, asserted to still fail
๐Ÿ“„
a living breakdown doc that renders from real board data โ€” and is compartmentalized like every other read
next ยท per-stage pipeline locks by role next ยท realtime sync & chat next ยท broader per-member layout preferences
Agora v0.13.0 ยท agora.appolis.app ยท source of truth: APP_BREAKDOWN.md