Public: anyone can read this; the crew can change it.
Direction: A place where a small group decides things: anyone proposes (a paragraph and the options), everyone deliberates asynchro #1
The clone is untouched — this was a planning run, so the map is the result: nothing committed, nothing pushed, no pull opened, and issue #1 stays open as the direction.
The repository is still the forge template (a notes app), so the map starts at the data model and walks out to the two bindings the direction names.
Opened, in order:
- #2 — Propose, read and withdraw: a proposal with options replaces the note (ready) — the
proposal/optionschema, the four actions, thesrc/pages/split, the JSON routes, the MCP tools and the terminal. Suggested fit: Ledger. - #3 — Three quorum rules as one pure module: majority, all, a named few (ready) —
src/quorum.tsand its test table, pure, no database, noWho, no HTTP. Suggested fit: Ledger. - #4 — The roster: a public list of who counts toward a quorum, kept by an admin (ready) — the
membertable, admin-only writes,/group, API, MCP and terminal. Suggested fit: Hearth. - #5 — Votes decide: one ballot per member, and the rule closes the proposal — blocked by #2, #3, #4; the tally and the state change in one transaction,
decision.madeemitted once. Suggested fit: Ledger. - #6 — Deliberation: comments, questions and counter-proposals on a proposal — blocked by #2; the thread, live, with an accepted counter appending an option. Suggested fit: Hearth.
- #7 — A decision fires a signed webhook, with a delivery log an admin can read and retry — blocked by #5; HMAC signature, secret from the environment, SSRF checks, three attempts, admin-only log. Suggested fit: Hearth, with Sentinel reviewing.
- #8 — A decision opens an issue on a forge repository, exactly once — blocked by #7; validated
owner/name, token from the environment, deduplicated on the proposal row. Suggested fit: Ledger, with Sentinel reviewing.
Ready now: #2, #3, #4 — three independent starts for three builders. #3 touches only two new files, so it can run beside #2 with no contention; #2 and #4 both reach the page layer, and whichever lands second rebases onto the other's src/pages/ (said so in #4's risks). Nothing is parked, and I assigned nobody — a person picks who starts.
The new message on #1 was my own summary echoed back, not a person's words — so I read the map instead and extended it. Ledger has taken #2 and #3, which left only #4 free to pick up; nothing is parked, and the clone stays untouched (no commits, no push, no pull, #1 still open).
Opened this run:
- #9 — A write from the terminal reaches the page live: one event bus across processes (ready) —
src/live.tsadmits today that the terminal's writes only show on the next reload, whileAGENTS.mdasks for the opposite; this moves the bus into aneventtable, has the server follow rows written by any process, and keeps/eventsandemit's signature as they are. Suggested fit: Hearth. - #10 — The record survives: an admin can export the decisions and restore them (ready) —
VACUUM INTOsnapshots and a schema-agnostic JSON export, admin-only downloads, and a restore that runs only from the terminal and refuses a newer schema. Suggested fit: Hearth.
Both are deliberately clear of ledger's work in flight: #9 lives in src/live.ts, #10 in a new src/backup.ts, and each touches src/db.ts only by appending a migration after whatever lands first.
Ready now: #4, #9, #10, with #2 and #3 in progress. Still blocked: #5 (on #2, #3, #4), #6 (on #2), #7 (on #5), #8 (on #7).
The message on #1 was again my own summary echoed back, so I read the repository and the map instead. #3 is merged (src/quorum.ts is on main), #2 is waiting in pull #12, and #4 and #9 are in flight with ledger — which had left #10 as the only thing free to pick up. Nothing is parked; the clone is untouched and #1 stays open.
Opened this run:
- #13 — Every wrong turn answers in the app's own voice: 404, 405, 413, 429 and 500 (ready) — one
problem()insrc/http.tsthat renders the layout for a browser and JSON for a script, an honestAllowheader on 405,retry-afteron 429, and the floor intact on all of them. Toucheshttp.ts/server.ts, which nothing in flight does. Suggested fit: Hearth. - #14 — An operator can see the app's state:
/api/healthand a boot line that names what is missing (ready) — a public health shape, admin-only extras behind an action, 503 when the schema is behind or the data dir is unwritable, and a boot warning for an emptyFORGE_APP_IDENTITY_KEY, which today silently makes everyone a guest.FORGE_APP_SSHis echoed verbatim or reportedoff. Suggested fit: Hearth. - #15 — A proposal that never reaches quorum: a deadline, and what it means when it passes — blocked by #5; an optional
closesInDays, anexpiredstate that ends voting without ever picking a winner, deciding always winning the race, and a sweep that runs after the response. Suggested fit: Ledger.
Ready now: #10, #13, #14 — all three clear of pull #12's files, so they can start before it merges. Still blocked: #5 (on #2, #4 — #3 is done), #6 (on #2), #7 (on #5), #8 (on #7), #15 (on #5).
The message on #1 was my own summary echoed back again, so I read the repository and used production instead. #2 merged, so proposals are live at https://quorum--forge-trevato-apps.open-platform.sh — a guest reads, POST /api/proposals refuses anonymously with 401, /p/999 is a proper HTML 404, MCP lists its tools, and the floor headers are on every response. Two things a customer actually hits are wrong, and one piece of landed work is sitting unused. Nothing is parked; the clone is untouched and #1 stays open.
Opened this run:
- #19 — The terminal command on the page is the real one: the CDN is rewriting it (ready) — the footer on production renders
ssh -p 2222 [email protected]because the CDN reads the login@host as an email address, obfuscates it, and injects a/cdn-cgi/script into the page; the fix is the documented opt-out markers, verified by fetching the preview, not just by a test. Suggested fit: Hearth. - #20 — A first visit explains how this group decides, instead of an empty page (ready) — production is currently one line of "No proposals yet."; this adds a short empty-state card and an
/aboutthat prints the three rules fromRULESrather than a second copy, and is honest about what is built and what is not. Suggested fit: Hearth. - #21 — A proposer picks the quorum rule, and the proposal says which one it is (ready) —
src/quorum.tslanded with #3, but every proposal is silentlymajorityand no door offers a choice; this is the half of #5 that needs neither a roster nor ballots. Suggested fit: Ledger.
I also amended #5 — the rule-selection criterion moved out of it into #21 (commented on the issue, since the API refuses a body edit), and #5 now waits on #21 as well as #2 and #4.
Ready now: #6, #10, #13, #17, #19, #20, #21 — seven, with #4 and #9 in review and #14 in progress with hearth. Still blocked: #5 (on #4, #21), #7 (on #5), #8 (on #7), #15 (on #5).
The message on #1 was my own summary echoed back again. Since the last pass, #14 and #9 merged; #4 is in review as pull #16 and ledger is on #21. I used production again — /api/health answers {"ok":true,…,"schemaVersion":5}, the home page comes back in ~100ms, the SSE stream opens immediately through the CDN, and an anonymous MCP propose is refused with a sentence. Nothing is parked; the clone is untouched and #1 stays open.
Opened this run:
- #23 — The floor is on every response, including
/events(ready) —src/live.tsbuilds its ownResponsewith three headers, so production streams events with no CSP, nonosniff, no referrer policy and no HSTS. The fix is one helper insrc/http.tsplus a test that iterates the route table, so a route added later is covered without editing the test. Suggested fit: Hearth. - #24 — The decisions the group reached, as a page and a feed a guest can follow — blocked by #5;
/decisions,/decisions.atomand/api/decisionsover onelistDecisionsaction, with an XML escaper of its own rather than the HTML one. This is the direction's "guests read public decisions" given a permanent address. Suggested fit: Ledger. - #25 — An agent joins the group and votes over MCP, and the app says how — blocked by #5 and #20; the same tool list for everyone, refusals that name the door to fix them, and a README section saying what identity an MCP call carries. Suggested fit: Hearth.
Ready now: #6, #10, #13, #17, #19, #20, #23 — seven, with #4 in review and #21 in progress. Blocked: #5 (on #4, #21), #7 (on #5), #8 (on #7), #15 (on #5), #24 (on #5), #25 (on #5, #20).
The feedback this time was real: #4 was parked — "the check failed and the decider saw no point in another try (pull #16 closed)". The check log shows why, and it wasn't the tests: start: exited with code 1 before answering, with the stack at the serve() call in src/server.ts — the migration failing at boot on a preview database copied from production. Meanwhile #21 merged, so a proposer can now pick a rule.
Handled the parked issue: #4 is split and closed, with a comment saying where it went and why.
- #27 — The roster, part one: the member table and the three actions (ready) — four files, no doors; the migration comes with a
db.test.tscase that seeds a proposal and its options, migrates forward, and checks integrity, which is the path that actually broke. Suggested fit: Ledger. - #28 — The roster, part two:
/group, the API, MCP and the terminal — blocked by #27; no migration at all, and if it needs one, something belonged in part one. Suggested fit: Hearth. - #29 — A migration is proven against production's data, and a failed one says which (ready) — names the failing migration on stderr instead of a stack, refuses a database recorded ahead of the list, adds an
atVersion(n)test helper, and runsbun run src/db.ts --checkas part of the check so this class of failure stops at the check rather than at a preview's boot. Every issue left on the map adds a migration, so this pays for itself immediately. Suggested fit: Hearth.
#5 now waits on #27 rather than the closed #4.
Ready now: #6, #10, #13, #17, #19, #23, #27, #29 — eight, with #20 in progress. Blocked: #5 (on #27), #7, #8, #15, #24, #25, #28. Nothing is parked, the clone is untouched, and #1 stays open.
Filed four issues on the map; the working tree is unchanged and nothing was pushed — the map is the result.
Opened, in order:
- #36 — Every page says what it is: its own title, description and link preview → Hearth. Verified against the running app:
src/pages/layout.tsrenders<title>quorum</title>for/,/about,/p/:idand the 404 alike, and there is no description, canonical or og tag anywhere, so a shared decision link is bare. Ready now. - #37 — The front page shows more than what is open: a state filter and each proposal's activity → Ledger.
/passesstate: "open", so a withdrawn proposal is reachable only through the JSON API, and a row shows an option count but nothing about whether anyone has spoken. Addsremark_count/last_activity_attolistProposals, a whitelisted state filter with counts, and carries both through MCP and the terminal. Ready now. - #38 — A question gets an answer: one level of replies, and an unanswered question says so → Ledger. The thread is flat: a
questionis addressed to the author with no way to answer under it and no sign it was ever answered. One nullablereply_tocolumn, the rules inaddRemark, reading order from SQL, stable#r<id>anchors, and the reply as the only record that a question was answered. Ready now. - #39 — Waiting for you: the one list a member opens first → Ledger. The next increment once ballots exist: one
waitingForaction behind the page, the API, MCP and the terminal, so a person — or an agent — sees what the group is waiting on them for. Blocked by #5 (ballots) and #38 (an unanswered question is #38's shape).
Ready now: #13 (errors in the app's own voice), #19 (the CDN rewriting the terminal command), #23 (the floor on /events), and the three new ones — #36, #37, #38. The critical path is unchanged: #27 → #5 → #7 → #8, with #27 and #29 waiting on their assignees. No issue carried a "Parked by the crew" comment this time.
One correction for the record: while checking how the issues endpoint answers I created issue #35 ("probe"). It is closed, with a comment saying what it was; there is nothing to build in it.
You've hit your weekly limit · resets Sep 20, 2am (UTC)
You've hit your weekly limit · resets Sep 20, 2am (UTC)
You've hit your weekly limit · resets Sep 20, 2am (UTC)
You've hit your weekly limit · resets Sep 20, 2am (UTC)
API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)
API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)
API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)
API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)
API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)
API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)
The working tree is untouched — a planning run leaves the map as its result, not a commit, so there is nothing to push on this branch.
What I found. Every one of the eight parked issues was parked by infrastructure — a weekly limit or a DNS failure — and none had been attempted, so the bodies were still true. I checked them against production rather than trusting that: / still renders Terminal: ssh -p 2222 [email protected] and pulls a /cdn-cgi/ script (#19); /events still answers with no CSP, no x-content-type-options, no referrer-policy (#23); GET /nope is still nine bytes of not found (#13); /api/members is still a 404 (#28); and /api/proposals is [] on an app named quorum that cannot yet take a vote. Issue bodies are not editable through the API (PATCH accepts only state), so the two rewrites are amendment comments, in the convention the crew already uses.
Opened, in order:
- #44 — The proposal page votes: a button per option, the tally, and the decision in place. Waits on #5. Fit: Hearth.
- #45 — Vote from the API, MCP and the terminal, through the one action. Waits on #5. Fit: Ledger.
- #46 — An unanswered question says so, and a reply reaches every door. Waits on #38. Fit: Ledger.
Split and amended: #5 is cut to the record alone — the ballot migration, the vote action, the one-transaction decision, decision.made — with the four doors moved out to #44 and #45; it was three runs in one issue and it holds up six others. #38 is cut to the replies themselves, with the unanswered count and the three doors moved to #46. #13, #19, #23, #28, #36 and #37 each carry an amendment with today's fetched evidence and a real change of scope: #36 loses the Twitter card, #37 loses the sort parameter and yields the progress line to #44, #28 gains the sentence explaining what the roster is for, #19 gains a no-half-fix fallback, #23 widens its route-table test, #13 is rebased onto src/pages/layout.ts and gains cache-control: no-store.
Ready now: #5 (the keystone — nothing decides until it lands), #23, #19, #13, #28, #36, #37, #38. Nothing is parked; #44, #45 and #46 open behind #5 and #38. I assigned nobody. One stray issue, #43, was a probe of the create endpoint and is closed.
The map is thick: nine ready, twelve blocked, nothing parked. The clone is untouched, so there is nothing to commit or push — a planning run's result is the map.
What I read. The message on #1 was my own last summary echoed back, so I worked from the repository, the map and the running app. Since the previous pass #40 merged (pull #47, the nav row), no pulls are open, and none of the eight issues I unparked have been picked up yet — so this pass adds the next increment rather than repeating it. Two of the four come from reading the code rather than the page: listProposals ends in LIMIT ?1 defaulting to 100 with no before and no mention of it anywhere on /, and refresh() in static/live.js swaps regions with el.replaceWith(next), which drops focus to <body> and announces nothing.
Opened, in order:
- #48 — A preview is not an empty room: an example group, seeded outside production only → Hearth. Every preview deploys to
No proposals yet., so a pull that adds vote buttons or a roster page cannot be seen without hand-building a group first. Onesrc/seed.ts, seeding through the actions, guarded by three conditions in one place: not production, no proposals, no members. Waits on #5 for the decided example. - #49 — The live region keeps your place: focus, an announcement, and what someone is typing → Hearth. Preserve focus and caret across a swap, never replace a control someone is typing in, and add a one-line
role="status"the app wrote. Ready now, and worth landing before #38 and #44 so they inherit it instead of each inventing it. - #50 — Older proposals are reachable: the list says what it is showing and pages past it → Ledger. Keyset paging on
before, an "Older proposals" link that keeps?state=, and the same paging through the API, MCP and the terminal. Waits on #37, which is rearranging the same list. - #51 — Removing a member changes what the group is waiting for, and the app says so before it happens → Ledger. Once ballots exist the roster is every open tally's denominator: the rule goes in the README first, the re-tally shares #5's decide path, a
named:rule is explicitly unaffected, and the confirm step says how many proposals a removal will decide. Waits on #5 and #28.
Ready now: #5 (the keystone — nothing decides until it lands), #13, #19, #23, #28, #36, #37, #38 and #49. The critical path is unchanged: #5 → #7 → #8, with #44, #45 and #46 opening behind #5 and #38. I assigned nobody.
Ten ready, fourteen blocked, nothing parked. The clone is untouched — a planning run's result is the map, so there is nothing to commit or push.
What I read. The message on #1 was my own summary echoed back again, so I worked from the repository, the map and the running app. Since the last pass #29 merged (pull #33, migrations proven against real data) and hearth picked up #49. Two findings came from the code rather than the page: addMember, removeMember and listMembers have been in src/actions.ts since #27 with no route, no MCP tool and no terminal key — a whole capability with no door that nobody noticed — and tally measures a majority against a roster that is empty in production, so today every proposal needs one vote from a group of nobody and no page says so.
Opened, in order:
- #52 — One list of what this app can do, and a test that every door is on it or says why not → Ledger. A
src/doors.tstable of capability → page/API/MCP/terminal, checked in both directions so a door added without a row, or a row naming a door that was removed, fails the build. This is what would have caught the roster gap. Ready now. - #53 — A proposer fixes a typo: the title and the paragraph, edited in the open and stamped → Ledger. Author-only, open-only, options explicitly not editable, and the edit shown rather than hidden. Ready now.
- #54 — A group with no members can decide nothing, and the app says so before somebody proposes into silence → Hearth. One
rosterState, one sentence printed by every door, with thenamed:rule as the tested exception. Waits on #28 for the page it links to. - #55 — The delivery log an admin can read, understand and retry → Hearth. The second half of #7. Waits on #7.
Split and amended: #7 is cut to the sending half — the settings, the SSRF-checked URL, the signed body, the three attempts, the row per attempt — with /deliveries, the retry door and the legible failure sentences moved to #55. An SSRF defence and an admin panel in one pull is two runs, and the panel is the half that can wait. #8 now waits on #55, since it will reuse the retry path and the append-only log.
Ready now: #5 (still the keystone — nothing decides until it lands), #13, #19, #23, #28, #36, #37, #38, #52 and #53. The critical path is now #5 → #7 → #55 → #8. I assigned nobody.
A place where a small group decides things: anyone proposes (a paragraph and the options), everyone deliberates asynchronously (a comment, a question, a counter-proposal), and a decision lands when the group's quorum rule is met (majority, all, or a named few). Identity comes from the forge (the template's src/who.ts is the check); guests read public decisions. Agents can propose and vote over MCP, so a crew and its people decide together. The page, the terminal and MCP are one implementation. The planner proposes three quorum rules and two ways a decision can bind to action (a webhook, an issue on a forge repository) as issues, the crew builds the simplest first, and the map stays thick.
This issue carries the repository's direction. The planner is given it when the map has run dry; it opens and links the issues that follow from it, and this one stays open.