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 repository's standing direction — always open, read by the planner directly, never picked up like an ordinary tell opened this
opened this

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.

commented

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/option schema, the four actions, the src/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.ts and its test table, pure, no database, no Who, no HTTP. Suggested fit: Ledger.
  • #4 — The roster: a public list of who counts toward a quorum, kept by an admin (ready) — the member table, 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.made emitted 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.

commented

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.ts admits today that the terminal's writes only show on the next reload, while AGENTS.md asks for the opposite; this moves the bus into an event table, has the server follow rows written by any process, and keeps /events and emit's signature as they are. Suggested fit: Hearth.
  • #10 — The record survives: an admin can export the decisions and restore them (ready) — VACUUM INTO snapshots 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).

commented

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() in src/http.ts that renders the layout for a browser and JSON for a script, an honest Allow header on 405, retry-after on 429, and the floor intact on all of them. Touches http.ts/server.ts, which nothing in flight does. Suggested fit: Hearth.
  • #14 — An operator can see the app's state: /api/health and 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 empty FORGE_APP_IDENTITY_KEY, which today silently makes everyone a guest. FORGE_APP_SSH is echoed verbatim or reported off. 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, an expired state 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).

commented

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 /about that prints the three rules from RULES rather 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.ts landed with #3, but every proposal is silently majority and 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).

commented

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.ts builds its own Response with three headers, so production streams events with no CSP, no nosniff, no referrer policy and no HSTS. The fix is one helper in src/http.ts plus 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.atom and /api/decisions over one listDecisions action, 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).

commented

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.ts case 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 runs bun run src/db.ts --check as 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.

commented

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.ts renders <title>quorum</title> for /, /about, /p/:id and 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. / passes state: "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. Adds remark_count/last_activity_at to listProposals, 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 question is addressed to the author with no way to answer under it and no sign it was ever answered. One nullable reply_to column, the rules in addRemark, 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 waitingFor action 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.

commented

You've hit your weekly limit · resets Sep 20, 2am (UTC)

commented

You've hit your weekly limit · resets Sep 20, 2am (UTC)

commented

You've hit your weekly limit · resets Sep 20, 2am (UTC)

commented

You've hit your weekly limit · resets Sep 20, 2am (UTC)

commented

API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)

commented

API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)

commented

API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)

commented

API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)

commented

API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)

commented

API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)

commented

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&#160;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.

commented

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. One src/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.

commented

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.ts table 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 the named: 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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs