From abfae2890e1a69020557f2a4292c663f49224a60 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 3 Oct 2026 17:13:45 +0000 Subject: [PATCH 1/8] Grants and doors in Ed's words; the setting names table (#223) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The five grant bodies, the doors' ✒️/🛡️ sentences and the noun table the power cards and the founder's clauses read, verbatim from #223. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018T9ZX2oB2xxzsK4Tgxpya7 --- design/copy.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/design/copy.js b/design/copy.js index 272ef3d0..46d49675 100644 --- a/design/copy.js +++ b/design/copy.js @@ -997,8 +997,8 @@ window.COPY = (function () { pw: { phrase: { star: { u: 'amend this at will', a: 'refuse proposals that the membership pass' }, - invite: { u: 'invite people at will', a: 'refuse invitations and applications that the membership pass' }, - remove: { u: 'remove members at will', a: 'refuse removals that the membership pass' }, + invite: { u: 'invite people at will', a: 'block invitations that the membership have decided to make' }, + remove: { u: 'remove people at will', a: 'block removals that the membership have decided to make' }, text: { u: 'amend the text at will', a: 'refuse changes to the text that the membership pass' }, }, // **On the decision card the phrase names its setting** (Q1429, Ed @@ -1025,8 +1025,8 @@ window.COPY = (function () { // (entry 94) and are only ever the veto sentence's. noun: { title: 'the title', slug: 'the link', text: 'the text', - ending: 'the ending', quorum: 'the quorum rule', - authorship: 'the anonymity rule', judgments: 'the vote-reveal rule', + ending: 'the end time', quorum: 'the quorum', + authorship: 'anonymity of proposals', judgments: 'anonymity of votes', chamber: 'the visibility rule', rate: 'the proposal rate', lapse: 'the lapse rule', removal: 'the removal rule', admission: 'the admissions rule', applications: 'the applications rule', @@ -1357,13 +1357,13 @@ window.COPY = (function () { gate: { canpropose: { title: 'Proposals', - why: 'A proposal is a change you write to the document, for the membership to vote on.', + why: 'A proposal is a change to the document that the membership vote on.', waiting: 'Waiting on the start.', done: 'Open — members can propose as soon as they arrive.', }, canjudge: { title: 'Voting', - why: 'A vote is your say on a proposal: you are shown two at a time and choose the one you prefer, or neither.', + why: 'A vote is your say on a proposed change to the document.', waiting: 'Waiting on the rules.', done: 'Open — the rules are settled.', }, @@ -1376,7 +1376,7 @@ window.COPY = (function () { // and tab read *Accept Constitutional Proposals*, the card's own // words, and *Activate Your Membership* went title: 'Constitutional Proposals', - why: 'A 🏛️ is a constitutional proposal: one at a time, returned whole, passing only when all members agree. You are already a member; activating it opens every question, proposal and vote on the rules.', + why: 'A 🏛️ is a constitutional proposal. Members may only make one at a time, and it passes only when all members agree to it.', waiting: 'Waiting on your arrival.', // *Accept 🏛️* like every grant (Q1541.24 (a), Ed 2026-09-25), // replacing Q1502's *Activate 🏛️* @@ -1384,12 +1384,12 @@ window.COPY = (function () { }, pen: { title: 'Founder Actions', - why: 'As the founder of this document, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.', + why: 'As the founder, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.', waiting: 'Waiting on the save.', }, shield: { title: 'Founder Veto', - why: 'As the founder of this document, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.', + why: 'As the founder, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.', waiting: 'Waiting on the save.', }, // **a grant is accepted, not OK'd** (Q1501, Ed 2026-09-22; T44 amended From 95cc6c4017ee4e61a392fd3bab16bd99c72db558 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 3 Oct 2026 17:25:22 +0000 Subject: [PATCH 2/8] =?UTF-8?q?The=20Rules=20re-laid:=20founder=20clauses,?= =?UTF-8?q?=20five=20Proposals=20clauses,=20the=20Schedule=20(#223,=20G1?= =?UTF-8?q?=E2=80=93G6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✒️ and 🛡️ each a clause in the lead under the Founded line, naming the settings held or gone with their tab; 🎩 on the Founded line until 🍾; the Proposals preamble split into ✏️ ⏱️ ⚖️ 👥 🏛️; the quorum as the pass rule; Schedule (🍾 ⏰ 🥂) above the Text and Decisions gone; ORDER reads the Rules top to bottom. founding-walk's aside check follows G1's birth clauses. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018T9ZX2oB2xxzsK4Tgxpya7 --- design/band.js | 23 ++- design/begin.js | 3 +- design/copy.js | 45 ++++- design/feed.js | 2 +- design/session-view.html | 364 +++++++++++++++++++++----------------- design/setup.js | 4 +- scripts/founding-walk.mjs | 36 ++-- scripts/spec-check.mjs | 39 ++-- 8 files changed, 298 insertions(+), 218 deletions(-) diff --git a/design/band.js b/design/band.js index da40eae0..1d913e23 100644 --- a/design/band.js +++ b/design/band.js @@ -173,11 +173,13 @@ window.BAND = (function () { // clause is a list rather than a sentence. It carries markup (the avatar, the // 👑 span), so it rides `ctx.clauseFor` beside `rosterClause()` and never // `headFor`, which escapes what it is given. + // …and it no longer carries the time (#223, Ed 2026-10-03): the moment + // moved to 🍾's clause in the Schedule const foundedClause = () => - (!env.cs ? '' : '

Founded by ' + + (!env.cs ? '' : '

' + esc(PAGE_COPY.clause.foundedLead) + avHtml(founderInfo()) + ' ' + esc(founderInfo().n.trim() || 'Anonymous') + // 👑 by any reservation (Ed, Q379 wide); 📯 = holds nothing. - ' ' + founderMark() + esc(foundedAt()) + '.

'); + ' ' + founderMark() + '.

'); // the close's moment, the same shape as the founding's const closedAtWords = () => { if (!env.cs || !env.cs.closed || env.cs.closedAt == null) return ''; @@ -580,7 +582,7 @@ window.BAND = (function () { // stacked field. The chips a shape's unit once grew above the field // left with 🧭 (Q1363). opt(V, 'ending', 'ends', - 'No more changes to the document may be made after ' + + 'Proposals may be made until ' + '.') + rungOpt(V, 'ending', 'perpetual', ENDING_NEVER) + ''; })(), @@ -644,9 +646,9 @@ window.BAND = (function () { '').join('') + ''; return opt(V, 'rateBy', 'founder', - 'Members may make a new proposal ✏️ every ' + + 'Members may make a new ✏️ proposal every ' + ' ' + sel + '.', ''); })() + + (shown === '' ? '' : ' value="' + shown + '"') + '> ' + sel + ', up to 3 at a time.', ''); })() + '', chamber: () => (() => { const V = ladderView('chamber'); @@ -1086,7 +1088,9 @@ window.BAND = (function () { // other seat (Q1503) if (c.k === 'hat') { if (stateOf(c, ctx) === 'news') return 'watching'; - return amFounder() && !constituted() && docOpen() && founderCanCommit() ? 'setting' : 'watching'; + // …answerable before ✒️ is accepted (#223, G6: 🎩 is the walk's second + // step, ✒️ its third), so not pen-gated + return amFounder() && !constituted() && docOpen() ? 'setting' : 'watching'; } if (!MANAGED_KEYS.includes(c.k)) return null; if (c.record || c.isGate || c.door || c.kind === 'personal' || c.admit || c.release || @@ -1169,9 +1173,12 @@ window.BAND = (function () { attrs: ' data-confirm="1"', title: PAGE_COPY.setIt, until: powerParts(c).chosen ? null : 'choose' }); return acts; } + // **🎩 is answered above the pen** (#223, G6): the walk meets it before + // ✒️ is accepted, so it commits with no power, as ✋ and 🖼️ do (Q980) — + // a ✒️ on it would be a power not yet held (T44) if (c.k === 'hat') { - const ready = !!S.hatPick && S.hatPick !== hatCurrent() && mayPen() && docOpen(); - acts.push({ kind: 'commit', glyph: '✒️', glyphHtml: glyphHtml('✒️'), cls: 'btn-approve emojibtn', + const ready = !!S.hatPick && S.hatPick !== hatCurrent() && docOpen(); + acts.push({ kind: 'commit', glyph: '✓', glyphHtml: window.CARDS.TICK, cls: 'btn-approve', attrs: ' data-confirm="1"', title: PAGE_COPY.setIt, until: ready ? null : 'choose' }); return acts; } diff --git a/design/begin.js b/design/begin.js index 31cdeacb..a9e99e04 100644 --- a/design/begin.js +++ b/design/begin.js @@ -265,7 +265,8 @@ window.BEGIN = (function () { const BEGIN_ROWS = [ 'title', 'slug', 'chamber', 'admission', 'applications', 'lapse', 'removal', 'invite', 'remove', - 'rate', 'ending', 'quorum', 'authorship', 'judgments', + // the Rules' own order since #223 (G6): ⏰ in the Schedule, after 👤 👁️ + 'rate', 'quorum', 'authorship', 'judgments', 'ending', 'text', // `machines` is not here (Ed, 2026-09-09: *we are not having machines*): // it has had no card since 🤖 left the surface (R-078) and stays in the diff --git a/design/copy.js b/design/copy.js index 46d49675..206fc1d6 100644 --- a/design/copy.js +++ b/design/copy.js @@ -1121,6 +1121,37 @@ window.COPY = (function () { founderRefused: (what) => 'The Founder refused your proposal: ' + what + '.', couldNotBePut: 'Your proposal could not be put to the membership, and your ✏️ is back in your wallet.', }, + // **The Rules' clauses for the grants, the gates and the Schedule** (#223, + // Ed 2026-10-03, G1–G5, every sentence his, verbatim): the ✒️ and 🛡️ + // clauses in the lead, the five Proposals clauses, the Schedule's 🍾 and + // 🥂. `list` joins the setting names (`pw.noun`) the way his examples + // read: *the title, the link, the visibility rule and the text*. + clause: { + foundedLead: 'Founded by ', + penAll: 'The Founder may ✒️ change anything at will.', + penSome: (list) => 'The Founder may ✒️ change ' + list + ' at will.', + shieldAll: 'The Founder may 🛡️ refuse any proposal the membership pass.', + shieldSome: (list) => 'The Founder may 🛡️ refuse proposals the membership pass on ' + list + '.', + penBirth: 'The Founder (that’s you!) may ✒️ change anything at will.', + shieldBirth: 'The Founder (that’s you!) may 🛡️ refuse proposals the membership pass.', + // *Friday 2 October*, his example's shape: the weekday, then the day + weekDay: (weekday, day) => weekday + ' ' + day, + list: (names) => (names.length < 2 ? names.join('') + : names.slice(0, -1).join(', ') + ' and ' + names[names.length - 1]), + propose: 'Members may propose changes to the text and the rules.', + proposeBefore: 'When the document begins, members may propose changes to the text and the rules.', + vote: 'Members may vote on proposals.', + voteBefore: 'When the document begins, members may vote on proposals.', + rate: (phrase, cap) => 'Members may make a new ✏️ proposal every ' + phrase + ', up to ' + cap + ' at a time.', + constitutional: 'A 🏛️ proposal passes only when all members agree to it.', + beginWaiting: 'The members are waiting on the Founder to begin.', + begun: (time, day) => 'Members started making proposals at ' + time + ' on ' + day + '.', + endingAfter: (when) => 'Proposals may be made until ' + when + '.', + endingNever: 'Proposals may be made indefinitely.', + // *[N] members have signed* — the verb agrees with one (*1 member has*) + closed: (time, day, n) => 'Proposals closed at ' + time + ' on ' + day + '. ' + + n + (n === 1 ? ' member has signed.' : ' members have signed.'), + }, // the Proposals preamble (Y21): the gates' fragments, composed preamble: { beforeBegin: 'When the document begins, members may propose changes to rules and vote on proposals.', @@ -1151,10 +1182,12 @@ window.COPY = (function () { // member's answer card and the composer's lane (Q1137) — and then the // `(x of y)` that follows *of the membership*. `val.quorumPct` is still // the one writer of a share where a share is printed whole. + // …and since #223 (G4, Ed 2026-10-03 17:10) it is the pass rule, the one + // clause saying what carries a ✏️ proposal, in his words quorumRule: { - share: (share, tail) => 'A proposal ✏️ cannot pass until it is preferred by at least ' + - share + ' of the membership' + (tail || '') + '.', - count: (n) => 'A proposal ✏️ cannot pass until it is preferred by at least ' + n + ' members.', + share: (share, tail) => 'A ✏️ proposal passes once at least ' + + share + ' of the membership' + (tail || '') + ' have voted for it and it’s the most preferred option.', + count: (n) => 'A ✏️ proposal passes once at least ' + n + ' members have voted for it and it’s the most preferred option.', }, // **👥 is the one card that says what its number comes to** (Q1490, Ed // 2026-09-21 → why: R-139), a deliberate exception to Q1439 ruling u, @@ -1591,11 +1624,11 @@ window.COPY = (function () { // until the page reads these, which is a change to the regular page and // waits for its own pass. rule: { - endingNever: 'Changes to the document may be made perpetually.', - endingAfter: (when) => 'No more changes to the document may be made after ' + when + '.', + endingNever: 'Proposals may be made indefinitely.', + endingAfter: (when) => 'Proposals may be made until ' + when + '.', lapseNever: 'Inactive members never lapse and are still counted towards votes.', lapseAfter: (spell) => 'After ' + spell + ', inactive members lapse and automatically abstain from votes.', - rate: (phrase) => 'Members may make a new proposal ✏️ every ' + phrase + '.', + rate: (phrase, cap) => 'Members may make a new ✏️ proposal every ' + phrase + ', up to ' + cap + ' at a time.', unit: { days: 'day', hours: 'hour', minutes: 'minute' }, units: (n, unit) => n + ' ' + unit, address: (slug) => 'The document lives at docs.vote/d/' + slug + '.', diff --git a/design/feed.js b/design/feed.js index 372d35b4..f4cc213e 100644 --- a/design/feed.js +++ b/design/feed.js @@ -94,7 +94,7 @@ // the floor a race is held to, max(⌈n·E/100⌉, min(2, E)) — product // before quotient (issue #24), the seconder under it (Q1490) Math.max(Math.ceil((v.n * roster) / 100), Math.min(2, roster)), roster)); - case 'rate': return dripPhrase(v.dripMinutes) ? T.rule.rate(dripPhrase(v.dripMinutes)) : ''; + case 'rate': return dripPhrase(v.dripMinutes) ? T.rule.rate(dripPhrase(v.dripMinutes), v.cap) : ''; case 'lapse': return v.afterMs === null ? T.rule.lapseNever : (spell ? T.rule.lapseAfter(spell) : ''); case 'ending': return v.endsAtMs === null ? T.rule.endingNever : T.rule.endingAfter(C.longWhen(v.endsAtMs)); // the page's words, 24-hour (STYLE T16) diff --git a/design/session-view.html b/design/session-view.html index 829e6b06..ba5e0972 100644 --- a/design/session-view.html +++ b/design/session-view.html @@ -3164,7 +3164,9 @@

Untitled

// clause had nothing true for them) and served to them from the cork hide: () => !viewerIsMember() || !docOpen(), open: () => !!cs && viewerIsMember() && !!viewerRow().in, - early: () => !!cs && viewerIsMember() && !!viewerRow().in, + // …the Founder's at its place in the founding walk, after 👥 (#223, G6); + // an invitee's or an applicant's still on arrival, before any question + early: () => !!cs && !amFounder() && viewerIsMember() && !!viewerRow().in, // **a body of its own, about 🏛️ alone** (Q1502, Ed 2026-09-22): it // headed with the Proposals preamble — three rules about three things — // and asked for one OK; `proposalsClause` stays the constitution's @@ -3183,7 +3185,8 @@

Untitled

// by `holderLine` — which is the whole of Ed's argument for retiring them. { k: 'grant-pen', g: '✒️', t: PAGE_COPY.gate.pen.title, own: 'you', isGate: true, isGrant: true, grants: '✒️', textcard: true, - hide: () => !amFounder() || !docOpen(), + // …and with its clause, where the power is laid down everywhere (#223, G1b) + hide: () => !amFounder() || !docOpen() || !founderClauseStands('u'), open: () => !!cs && amFounder() && holdsPenAnywhere(), // Ed's sentence, card review 2026-09-02 why: PAGE_COPY.gate.pen.why, @@ -3207,7 +3210,7 @@

Untitled

// which is a thing the journey walk asserts. { k: 'grant-shield', g: '🛡️', t: PAGE_COPY.gate.shield.title, own: 'you', isGate: true, isGrant: true, grants: '🛡️', textcard: true, - hide: () => !amFounder() || !docOpen(), // the holder's, like the pen above + hide: () => !amFounder() || !docOpen() || !founderClauseStands('a'), // the holder's, like the pen above open: () => !!cs && amFounder() && holdsShieldAnywhere(), // Ed's sentence, card review 2026-09-02 why: PAGE_COPY.gate.shield.why, @@ -3350,7 +3353,12 @@

Untitled

routeOf: () => (removalPrice() === 'proposal' ? 'ordinary' : 'constitutional'), hide: () => !docOpen(), ready: () => true, door: true }, - { k: 'hat', g: '🎩', t: PAGE_COPY.cards.hat.t, n: PAGE_COPY.cards.hat.n, kind: 'personal', changeable: true }, + // **🎩 sits by the Founder and is hidden at 🍾** (#223, G2 / G2b, Ed + // 2026-10-03): a fact about membership, its tab on the Founded line until + // the start, after which the members list says the rest. Hidden, never + // unsettled — `settled()` still reads it decided once the document begins. + { k: 'hat', g: '🎩', t: PAGE_COPY.cards.hat.t, n: PAGE_COPY.cards.hat.n, kind: 'personal', changeable: true, + hide: () => constituted() }, // **The register and the door are two decisions** (Ed, 2026-08-18: *We // should separate "Membership" which is the register of people who are // members from "Membership Policy" which is how people become members @@ -4133,56 +4141,92 @@

Untitled

// member sees from arrival (`onFoundedLine`). The voice's *sentence* stays // merged into the preamble (Q750) — sentence 3 of `proposalsClause` — so // the preamble still states the constitutional route; only the tab moved. - const PROPOSAL_CHIPS = ['begin', 'canpropose', 'canjudge']; - // The open card is kept in its own group even if it has just gone invisible - // under the pointer: the strip a card opened into must not lose the tab you - // are looking at. - // **A release batch belongs to no clause, so it stands where 🍾 does** - // (entry 162). What one act laid down spans the constitution's sections — - // 🍾 itself lays down the Text's pair and every pending release at once — - // and the Proposals preamble is the page's own precedent for a card that is - // a *moment* rather than a rule. So the 👑 tab joins the four already in - // that stack and opens at the same paragraph, which is also what gives the - // card a home to render in: it draws no clause of its own. - const proposalChips = () => PROPOSAL_CHIPS.concat(releaseKeys()).map(card) - .filter((c) => c && (visible(c) || S.open === c.k)); - const proposalsClause = () => { - // sentence 1 — who may take part: 💡's own two branches joined to ⚖️'s. - // Before the press both gates say the same thing, so they join into one - // clause; *the document begins*, never *the session*, which is engine - // vocabulary the surface otherwise avoids. - let s1; - if (!constituted()) s1 = PAGE_COPY.preamble.beforeBegin; - // the defensive branch — begun, with proposing shut (a constituted - // document whose text never confirmed). 💡's own sentence, with what is - // true said first rather than a new one invented — and ⚖️ keeps its own - // condition here as it does below: judging is not open just because the - // document has begun. - else if (!canPropose()) s1 = (canJudge() ? PAGE_COPY.preamble.voteOnly - : PAGE_COPY.preamble.voteWhenDecided) + - PAGE_COPY.preamble.proposeAtBegin; - else { - // a delegated ordinary question may still be collecting after the - // start (🍾 waits only on the judge-gate ones), and the module keeps - // a member who owes it an answer from proposing until they give it - const prop = PAGE_COPY.preamble.proposeLead + (LIVE().some(owesAnswer) - ? PAGE_COPY.preamble.onceAnswered - : PAGE_COPY.preamble.asArrive); - s1 = canJudge() ? prop + PAGE_COPY.preamble.andVote - : prop + PAGE_COPY.preamble.voteTail; + // **Every grant and gate hangs on a clause of its own** (#223, Ed + // 2026-10-03, G1 · G3 · G5). The Proposals preamble — one paragraph of three + // sentences wearing 🍾 💡 ⚖️ and the 👑 news (Q748–Q750, Y21, Y23) — is + // retired: ✒️ and 🛡️ each have a clause in the lead under the link, after + // the Founded line; 💡 ⚖️ 🏛️ each theirs among Proposals' five clauses + // (✏️ ⏱️ ⚖️ 👥 🏛️); 🍾 and 🥂 theirs in the Schedule. Every sentence is + // Ed's, in `PAGE_COPY.clause`. + // **The setting names, in the Rules' own order** (G1b): the list a clause + // names when the Founder holds a power on some settings and not all. A key + // the catalogue gains later is named after these, so nothing goes unsaid. + // the Proposals section's five clauses, in Ed's order (#223, G3) + const PROPOSAL_CLAUSES = ['canpropose', 'rate', 'canjudge', 'quorum', 'grant-voice']; + const PW_NAME_ORDER = ['title', 'slug', 'chamber', 'admission', 'applications', + 'lapse', 'removal', 'rate', 'quorum', 'authorship', 'judgments', 'ending', 'text']; + // **The founder's clause for one power** (G1, G1b): *anything* where the + // hand is on every setting and both doors, the settings named where it is + // on some — the doors in their own whole sentences, Ed's — and nothing at + // all where it is on none, the clause and its tab going together. Before + // the save the founder holds both by construction, and the clause says + // whose hand that is (*that's you!*, STYLE T10). A closed document states + // no power at all (1541.52), so neither clause stands on one. + const founderPowerClause = (power) => { + const C = PAGE_COPY.clause; + if (!cs) return power === 'u' ? C.penBirth : C.shieldBirth; + if (!docOpen()) return ''; + // the settings the surface names and the two doors — never a catalogue + // key the surface has retired (🌡️ 🪜 🤖), which no reader could find + const keys = PW_NAME_ORDER.concat(DOOR_KEYS).filter((k) => PW_KEYS.includes(k)); + const holds = (k) => !!pwPair(k)[power]; + if (!keys.some(holds)) return ''; + if (keys.every(holds)) return power === 'u' ? C.penAll : C.shieldAll; + const named = PW_NAME_ORDER.filter((k) => keys.includes(k) && holds(k)).map(pwNoun); + return [named.length ? (power === 'u' ? C.penSome : C.shieldSome)(C.list(named)) : ''] + .concat(DOOR_KEYS.filter(holds).map((k) => founderMay([pwPhrase(k, power)]))) + .filter(Boolean).join(' '); + }; + // whether the clause stands at all — the test its tab shares (G1b) + const founderClauseStands = (power) => !!founderPowerClause(power); + // **A release batch hangs on the founder's clause it laid down** (G1, G3: + // *the 👑 news moves to the founder's clauses*): ✒️'s where it laid a ✒️ + // down and that clause still stands, else 🛡️'s on the same test — and + // where the power it laid down is gone everywhere, its clause gone with it, + // on the Founded line (*the 👑 news for the last power laid down hangs + // here*). The answer is a paragraph key: `grant-pen`, `grant-shield` or + // `founded`. + const releaseHostOf = (rk) => { + const c = card(rk); + const b = c && c.release ? releaseBatch(c.release) : null; + const laid = (power) => !!b && b.releases.some((r) => r.power === power); + if (laid('unilateral') && founderClauseStands('u')) return 'grant-pen'; + if (laid('assent') && founderClauseStands('a')) return 'grant-shield'; + if (!laid('unilateral') && !laid('assent')) { + if (founderClauseStands('u')) return 'grant-pen'; + if (founderClauseStands('a')) return 'grant-shield'; + } + return 'founded'; + }; + const releasesOn = (host) => releaseKeys().filter((rk) => releaseHostOf(rk) === host) + .map(card).filter((c) => c && (visible(c) || S.open === c.k)); + // *Friday 2 October*: the day in the page's own words, never the browser's + // locale (STYLE T16, Q1523) + const dayWords = (d0) => PAGE_COPY.clause.weekDay(window.COPY.grammar.longWhen.days[d0.getDay()], + window.CARDS.longDay(d0.getTime())); + // the 💡 ⚖️ 🍾 🥂 clauses' sentences, each its gate's own (G3, G5) + const gateClause = (c) => { + const C = PAGE_COPY.clause; + if (c.k === 'canpropose') return constituted() ? C.propose : C.proposeBefore; + if (c.k === 'canjudge') return constituted() ? C.vote : C.voteBefore; + if (c.isBegin) { + if (!constituted() || !cs || cs.constitutedAtT === null) return C.beginWaiting; + const d0 = new Date(cs.constitutedAtT); + const p2 = (x) => String(x).padStart(2, '0'); + // the date in the page's own words, never the browser's locale (STYLE T16, Q1523) + return C.begun(p2(d0.getHours()) + ':' + p2(d0.getMinutes()), dayWords(d0)); + } + if (c.isClosing) { + const at = cs && cs.closedAt != null ? new Date(cs.closedAt) : null; + const p2 = (x) => String(x).padStart(2, '0'); + return at ? C.closed(p2(at.getHours()) + ':' + p2(at.getMinutes()), + dayWords(at), closeSignatures().length) : ''; } - // sentences 2 and 3 — what it takes to pass, one route per line (Ed's - // card review, 2026-09-02: three lines with explicit breaks on the 🏛️ - // card — `breakLines` draws them there; the constitution's own paragraph - // flows them as prose). What the rewrite drops is *Nobody amends a rule - // at will, and no passed change waits on anyone's assent, unless a rule - // says so* — the default-powers sentence, cut by Ed with the review. - return s1 + '\n' + - PAGE_COPY.preamble.passOrdinary + '\n' + - PAGE_COPY.preamble.passConstitutional; - }; - const preamblePara = () => - '

' + proposalsClause() + '

'; + if (c.k === 'grant-voice') return C.constitutional; + if (c.k === 'grant-pen') return founderPowerClause('u'); + if (c.k === 'grant-shield') return founderPowerClause('a'); + return ''; + }; // The title's clause (403/404, revised Ed 2026-08-19: *move title tab to // the first clause once that clause appears*): the 🪶 value tab stands in // front of the two power tabs — the governance tab group proper — so once @@ -4679,13 +4723,19 @@

Untitled

// pen's own blocking rule — every card below it is committed with the very // pen it hands over — does not reach them: a name and a picture are personal, // bind nobody (§9.0c) and are committed with no power at all. + // **The founding walk reads the Rules top to bottom** (#223, G6, Ed + // 2026-10-03): 🪶 📍 📧 (✋ 🖼️ alongside), 🎩 on the Founded line, ✒️ then + // 🛡️ on their own clauses, 🌍; 🪪 🤝 💤 🥾; 💡 ⏱️ ⚖️ 👥 🏛️; 👤 👁️; ⏰ and + // then 🍾 — the Schedule's 🍾 standing last in the file though its clause + // opens the section, as it always has, and 📝 last but one. 💡 and ⚖️ are + // accepted for the founder at the start; 🏛️ is the founder-member's here, + // and an invitee's or applicant's on arrival (`early`). const ORDER = ['title', 'slug', 'myemail', 'myname', 'mypic', - 'grant-pen', 'grant-shield', 'chamber', - 'admission', 'applications', 'hat', 'lapse', 'removal', - 'canpropose', 'canjudge', 'grant-voice', 'rate', - 'ending', 'quorum', + 'hat', 'grant-pen', 'grant-shield', 'chamber', + 'admission', 'applications', 'lapse', 'removal', + 'canpropose', 'rate', 'canjudge', 'quorum', 'grant-voice', 'authorship', 'judgments', - 'text', 'begin']; + 'ending', 'text', 'begin']; // a card in the order waits for everything above it and nothing else: the // ORDER *is* the dependency list. Since Q619 (b) (2026-08-22) a `dep` array // survives only on the cards outside it — ✉️ ❌ the doors and 🥂, 🪜 having @@ -4774,6 +4824,9 @@

Untitled

// every purpose that counts them — the rail enumerates from `tasksFor`, and // so does the group's own note — but they draw no paragraph of their own: // their tabs stand on the Founded line, and that sentence is their clause. + // (since #223 the grants hang on clauses of their own — ✒️ 🛡️ in the lead, + // 🏛️ among the Proposals — so the name is the history; what it answers is + // still *a grant enumerated by the link that draws no paragraph there*) const onFoundedLine = (c) => !!c && (c.k === 'grant-pen' || c.k === 'grant-shield' || c.k === 'grant-voice'); // **Your identity tabs ride wherever you appear in the document** (Q759, Ed // 2026-08-23). Ordinarily that is your own row in the Members list; a clerk @@ -4785,6 +4838,11 @@

Untitled

// way out is the *Alumni* heading's act now, beside the people who took it const myIdCards = () => ['myname', 'mypic', 'myemail'].map(card).filter((c) => c && visible(c)); const idOnFounded = () => (cs && amFounder() && !viewerIsMember() ? myIdCards() : []); + // **The Founded line's pile** (#223): 🎩 until 🍾 (G2, G2b — hidden at the + // start, the members list saying the rest), the 👑 news for the last power + // laid down (G1), and a clerk's identity tabs (Q759) + const foundedChips = () => [card('hat')].filter((c) => c && (visible(c) || S.open === c.k)) + .concat(releasesOn('founded'), idOnFounded()); // One question card per delegable setting, born when its setting is handed // over and gone if it is taken back. Same glyph, same title — it is the // same question — told apart by whose pile it stands in. @@ -5079,8 +5137,8 @@

Untitled

// the admit judgments on *Applications for Membership* (Q1557), // where every vote on letting somebody in is listed, ❌ on *Proposed // for removal* — each still wearing its own ✒️/🛡️ pair, and each a - // pile rather than a button in a row. 🎩 stays on *Members*, being - // about the founder's own place in it. The stranger reads the + // pile rather than a button in a row. 🎩 left *Members* for the + // Founded line (#223, G2), sitting by the Founder. The stranger reads the // register as the door serves it, under *Members* and *Alumni*. // **The register tells the room who left** (Q901, E31–E32), under // *Alumni* since Q1557 (e) — document text, so it reads on the @@ -5099,11 +5157,11 @@

Untitled

(isStranger() ? memSub(PAGE_COPY.memHeads.members, strangerRows, [], true) // The invitations sent stand after the arrived rows and before - // your own (Q1557 (a)), and 📭 beside 🎩 (E34): the give-up is + // your own (Q1557 (a)), and 📭 at the head (E34): the give-up is // about somebody listed here, and this is where a reader looks : memSub(PAGE_COPY.memHeads.members, nobody + here.map((p) => memRow(p)).join('') + invited.map((p) => memRow(p)).join('') + meBlock, - ['hat'].concat(mailGiveUpKeys()), false, !!openId) + + mailGiveUpKeys(), false, !!openId) + // **Every vote on letting somebody in, one heading** (Q1557 (b)): // a member's proposal to invite, by its address, then a // stranger's own application. ✉️ heads it — its result in the @@ -5123,58 +5181,43 @@

Untitled

alumniSub; } }, // **Proposals reads second** (Q871, Q872, Ed 2026-08-26: *Proposals above - // Decisions* — reversing *Proposals reads fourth*, Ed 2026-08-21). That - // ruling was itself a restoration: the section had drifted to second and - // was put back to fourth because its whole arrival landed directly beneath - // the Membership clauses the founder was still working through. What has - // changed since is that the section no longer arrives at once — Q748 made - // its opening one clause wearing four tabs, and ORDER (not a wave graph) - // paces the two settings under it one at a time — so the objection the - // fourth-place ruling was answering no longer holds. What is left is the - // reading order, and it reads better this way: who may propose, and how - // often, before how the room decides what is proposed. Membership · - // Proposals · Decisions · Anonymity · The Document. - // The gates lead it (Ed, 2026-08-18); the preamble opens it - // (Ed, 2026-08-19 — moved from the head of the constitution, redrafted - // in his words); no intro lines — the sections are not big enough to - // need explanations, and the clauses ought to be self-explanatory - // (Ed, 2026-08-19) + // Decisions*): who may propose, and how often, before how the room + // decides what is proposed. Membership · Proposals · Anonymity · + // Schedule · The Document since #223 (G5: *the Decisions section goes + // away*). + // **Five clauses, one per power, in Ed's order** (#223, G3 · G4, Ed + // 2026-10-03): ✏️ wearing 💡, ⏱️, ⚖️ wearing ⚖️, 👥 — the quorum, now the + // pass rule, moved here from Decisions — and 🏛️ wearing its grant. The + // three that are rules about everybody (✏️ ⚖️ 🏛️) stand whenever the + // section does, their tabs joining as each grant or gate is served to the + // reader; ⏱️ and 👥 as the founding order reaches them. 🍾 moved to the + // Schedule, the 👑 news to the founder's clauses. { key: 'rate', title: 'Proposals', text: '', - // **The section opens with one clause wearing four tabs** (Q748). The - // preamble stands tab-less while the group is empty, so the sentence - // saying what beginning will do is readable from the moment the section - // is live; each of 🍾 💡 ⚖️ 🏛️ *joins the stack* as it arrives rather - // than a new paragraph appearing under it, and the whole group opens at - // this one paragraph (`chipsFor` / `PROPOSAL_CHIPS`). `body` therefore - // skips all four; `keys` keeps 🍾 💡 ⚖️, because the rail and the - // group's own count enumerate from the section's cards. 🏛️ stays out of - // `keys` deliberately — it is `visible` from its place in ORDER, and - // admitting it would make the whole section live that early and offer - // its news before 🍾 (F9). It is enumerated by `tasksFor`, as Q605 - // built it, and only its *clause* moves here. - lead: (H) => (cs ? (proposalChips().length ? H.para(card('begin')) : preamblePara()) : ''), - body: (H) => ['rate'].map(card) - .filter((c) => c && visible(c)).map((c) => H.para(c) + H.tasks(c)).join(''), - keys: ['begin', 'canpropose', 'canjudge', 'rate'], - // one 👑 card per act that laid powers down (entry 162) — news, never a - // task, and enumerated here because the rail reads a section's own keys: - // a card the section does not enumerate is a card nothing can serve - extraKeys: () => releaseKeys() }, - { key: 'deciding', title: 'Decisions', - text: '', - // ⏰ when it ends, 👥 how many must have voted. Two of the four left on - // 2026-09-15 (Q1362): 🌡️ *how sure the room must be at that point* and - // 🪜 *how that number gets there* were the confidence, and the rule that - // asked for one is gone — the wording the room prefers stands once the - // quorum is met, so 👥 is the whole of what this section decides. - keys: ['ending', 'closing', 'quorum'], - body: (h) => ['ending', 'closing', 'quorum'].map(card) - .filter((c) => c && visible(c)).map((c) => h.para(c) + h.tasks(c)).join('') }, + body: (H) => PROPOSAL_CLAUSES.map(card) + .filter((c) => c && (c.isGate || c.isGrant || visible(c))) + .map((c) => H.para(c) + (c.isGate || c.isGrant ? '' : H.tasks(c))).join(''), + keys: ['canpropose', 'rate', 'canjudge', 'quorum', 'grant-voice'] }, // "Anonymity" (Ed, 2026-08-19), not "Privacy" { key: 'privacy', title: 'Anonymity', text: '', keys: ['authorship', 'judgments'] }, + // **The Schedule, the last section of the Rules** (#223, G5, Ed + // 2026-10-03), just above the Text: 🍾 ⏰ 🥂. 🍾's clause stands whenever + // the section does — *The members are waiting on the Founder to begin* + // until the press, the moment after it — its tab joining when 🍾 is + // served; ⏰ moved here from Decisions; 🥂's clause once the document is + // closed. One 👑 card per act that laid powers down is enumerated here + // (entry 162) — news, never a task — and drawn on the founder's clause + // it hangs on (`releaseHostOf`): the rail reads a section's own keys, and + // a card no section enumerates is a card nothing can serve. + { key: 'schedule', title: 'Schedule', + text: '', + body: (H) => H.para(card('begin')) + + ['ending', 'closing'].map(card).filter((c) => c && visible(c)) + .map((c) => H.para(c) + H.tasks(c)).join(''), + keys: ['begin', 'ending', 'closing'], + extraKeys: () => releaseKeys() }, // rail-only: the title is the heading, the link leads the constitution, // and the starting text anchors the prose (Ed, 2026-08-18) { key: 'doc', title: 'The Document', railOnly: true, @@ -5221,7 +5264,8 @@

Untitled

// (Q1365; it was the first blind question's, Q605 (a)) — all three // enumerated here and drawn on the Founded line (Q639). .concat(c.k === 'slug' - ? [card('grant-pen'), card('grant-shield'), card('grant-voice')].filter(visible) : []); + // 🏛️ is the Proposals section's own key since #223, its clause there + ? [card('grant-pen'), card('grant-shield')].filter(visible) : []); // **The keys the rail can ever list** (Q775). Every section's own keys, plus // the three grants and the answer tasks `tasksFor` adds beside them — the // whole of what `servedCards` below can produce, read off the same literals @@ -5300,8 +5344,8 @@

Untitled

// at the 🍾 press; a founder who set no name is Anonymous like anybody // else (§9.0c). **It is where ✒️ and 🛡️ hang their tabs** (Q639 (a)). lead: (h) => titleGovPara(h) + - // the link's own answer-task still draws here; the two grants it - // carries do not — they are drawn on the Founded line below (Q639) + // the link's own answer-task still draws here + // — the grants it carries do not: they are drawn on their own clauses (visible(card('slug')) ? h.para(card('slug')) + tasksFor(card('slug')).filter((t) => !onFoundedLine(t)).map((t) => h.para(t)).join('') : '') + // **The birth's third clause** (Ed, 2026-08-21): the document says @@ -5309,39 +5353,30 @@

Untitled

// to say *Sent to…* and the founder can get back to their own // address from the document itself. It stands here for the whole of // the birth — open, unsent, and awaiting the link — because the tab - // must not move: it used to be rendered by the Membership section's - // own body while the card was open and by this line once the mail - // was out, so the paragraph jumped up the page at the send. + // must not move. (!cs && visible(card('myemail')) ? h.para(card('myemail')) : '') + - // 🌍 is about the document, not member privacy — the penultimate - // clause of the opening section (Ed, 2026-08-19) - (visible(card('chamber')) ? h.para(card('chamber')) + h.tasks(card('chamber')) : '') + - // **The grants stand here** (Q639 (a), Ed 2026-08-22). Their own two - // clauses — *The Founder holds the pen on 16 settings.* and its - // shield twin — are retired: the count is already stated on every - // setting the power turns (`holderLine`) and in the second person on - // the wallet's own tooltip, so the clause was the third telling and - // the one that went stale whenever a power was laid down. Every - // *settings* clause wears ✒️ and 🛡️ for its **own** powers, so a - // grant could not hang on one without putting two ✒️ tabs meaning - // different things in one stack; the Founded line carries none, sits - // in the opening run where the grants arrive, and already wears 👑 — - // the mark that means *holds either power*. The tabs make the crown - // clickable, which is the dividend rather than the cost. + // **The Founded line** (Q640, Q639 (a)): from the save, *Founded by + // [avatar] [name] 👑.* — the time moved to 🍾's clause (#223). It + // carries 🎩 until 🍾 (G2, G2b: *a fact about membership, but it sits + // by the Founder*), the 👑 news for the last power laid down (G1), and + // a clerk's ✋🖼️📧 (Q759) — the grants left it for their own clauses. (cs ? (() => { - // and a clerk's ✋🖼️📧 join them (Q759): the Founded line is where - // a clerk appears in the document, so it is where their identity - // tabs ride. Five tabs is the tallest this pile gets. - // …and 🏛️ rides here for every member (Q1365): the grant of the - // membership, on the line that says who founded the document - const gc = [card('grant-pen'), card('grant-shield'), card('grant-voice')].filter((c) => c && visible(c)) - .concat(idOnFounded()); - // a member, a stranger, a founder who holds nothing: no pile, and - // the line renders exactly as it always did - return gc.some((c) => S.open === c.k) ? h.paraWith(card(S.open), gc) + const gc = foundedChips(); + const open = gc.find((c) => S.open === c.k); + return open ? h.paraWith(open, gc) : '
' + (gc.length ? h.pile(gc) : '') + - '
' + foundedClause() + '
'; })() : ''), + '
' + foundedClause() + '
'; })() : '') + + // **✒️ and 🛡️, a clause each** (#223, G1, Ed 2026-10-03): in the lead, + // under the link, after the Founded line, each wearing its grant and + // the 👑 news that hangs there; at the birth, under the link, saying + // whose hand it is. A power held nowhere has no clause and no tab (G1b). + (visible(card('slug')) || cs + ? ['grant-pen', 'grant-shield'].map(card).filter((c) => c && gateClause(c)) + .map((c) => h.para(c)).join('') : '') + + // 🌍 is about the document, not member privacy — the last clause of + // the opening run, after the founder's (#223, G6's order) + (visible(card('chamber')) ? h.para(card('chamber')) + h.tasks(card('chamber')) : ''), // **The hairline and the title are facts about the document, not about // the 📄 task** (Ed, backlog 33: *immediately after the birth, when my // named document opens for the first time, the Text area should already @@ -7907,7 +7942,7 @@

Untitled

const endingAt = () => (endingVal() !== 'never' && endingVal()) || S.endsAt; // Ed's sentence (card review 2026-09-02); the freeze is real (SPEC §9.5) // and the card no longer mentions it - const ENDING_NEVER = 'Changes to the document may be made perpetually.'; + const ENDING_NEVER = PAGE_COPY.clause.endingNever; // **A number states its own sentence** (Q1112 (b), step 5): the numeric // clauses are named here, taking the value rather than reading the // document's, so the clause and the *You said* readback are the one @@ -7917,7 +7952,7 @@

Untitled

// none of these writes a second consequence sentence, which is the half of // that rule the surface still keeps. const ENDING_RULE = (v) => (v === 'never' || !v ? ENDING_NEVER - : 'No more changes to the document may be made after ' + endFmtOf(v) + '.'); + : PAGE_COPY.clause.endingAfter(endFmtOf(v))); // `form` is the value's own since Q1162 — a delegated resolution can land // in either form whatever the founder's frame says, so a caller with a // typed value passes its form and only the founder's own field defaults @@ -7957,8 +7992,8 @@

Untitled

return !p ? '' : p.n === 1 ? { days: 'day', hours: 'hour', minutes: 'minute' }[p.unit] : p.n + ' ' + p.unit; }; - const RATE_RULE = (drip) => - 'Members may make a new proposal ✏️ every ' + dripPhrase(drip) + '.'; + // …and the cap in its own sentence since #223 (G3, Ed 2026-10-03) + const RATE_RULE = (drip) => PAGE_COPY.clause.rate(dripPhrase(drip), RATE_START.cap); const DECIDED = { // the ⏰ clause is `ENDING_RULE`'s like the readback's — it was the one // numeric clause left spelling itself a second time beside its builder @@ -8032,8 +8067,9 @@

Untitled

if (!PW_KEYS.includes(k)) return ''; // at the birth the founder holds everything by construction — there is no // module yet, and nobody else to hold anything - // — and the title's is the one that says whose hand that is (backlog 140) - if (!cs) return ' ' + founderMay([pwPhrase(k, 'u')], k === 'title'); + // — whose hand that is, *(that's you!)*, the founder's own two clauses say + // since #223 (G1's *Before the save*); it was the title's (backlog 140) + if (!cs) return ' ' + founderMay([pwPhrase(k, 'u')]); // a closed document states no power at all (1541.52; answers Part 6.6): // the Rules paragraph keeps its rule and *this* as its subject if (!docOpen()) return ''; @@ -8088,21 +8124,12 @@

Untitled

const c = hostOf(c0); // a grant's paragraph states the document's rule (third person, the same // for every reader); the card that opens on it is yours - // 🍾's clause is the Proposals preamble (Ed, 2026-08-22): the tab hangs - // on the sentence the press makes true, and the moment itself stays on the - // Founded line, which carried it already. Since Q748 the preamble is - // **the whole opening of the section** and 💡 ⚖️ 🏛️ share it, so all four - // heads are the rule as the document states it (F15). - if (c.isBegin) return proposalsClause(); - if (c.k === 'canpropose' || c.k === 'canjudge') return proposalsClause(); - if (c.isClosing) return 'The document closed' + closedAtWords() + '. ' + - (closeSignatures().length === 0 ? 'Nobody has signed it yet.' - : closeSignatures().length + (closeSignatures().length === 1 ? ' member has' : ' members have') + ' signed it.'); - // 🏛️ draws no paragraph of its own since Q1365 — its tab is on the - // Founded line with ✒️ 🛡️ — but its card's head is still the preamble's - // three rule lines (Q750; the whole body of the card, Ed's QA 2026-09-02 - // pm), whichever paragraph the tab stands on - if (c.k === 'grant-voice') return proposalsClause(); + // **Each grant and gate states its own clause** (#223, G1 · G3 · G5, Ed + // 2026-10-03), the sentence its tab hangs on: 🍾 and 🥂 in the Schedule, + // 💡 ⚖️ 🏛️ among the Proposals, ✒️ and 🛡️ in the lead — the preamble + // they shared (Q748–Q750) and the grants' silence on the Founded line + // (Q639 (a)) are both retired. + if (c.isBegin || c.isClosing || c.isGrant || c.k === 'canpropose' || c.k === 'canjudge') return gateClause(c); // ✒️ and 🛡️ write no sentence (Q639 (a), Ed 2026-08-22). They used to // state a count — *The Founder holds the pen on 16 settings.* — which // `holderLine` already says on every setting the power turns, and which @@ -8420,7 +8447,14 @@

Untitled

// the Proposals opening is one clause carrying four tabs (Q748): the // group is the same whichever of the four you come in by, and `para` // opens the card at 🍾's paragraph for any of them - if (PROPOSAL_CHIPS.includes(c.k) || c.release) return proposalChips(); + // every grant and gate is a clause of its own since #223 (G1, G3, G5): + // its own tab, and on ✒️ 🛡️ the 👑 batches that hang there; a batch + // opens at its host's paragraph with that pile as its strip + if (c.release) { const host = releaseHostOf(c.k); + return host === 'founded' ? foundedChips() : chipsForRaw(card(host)); } + if (c.k === 'grant-pen' || c.k === 'grant-shield') return [c].filter((x) => visible(x) || S.open === x.k) + .concat(releasesOn(c.k)); + if (c.isGate || c.isGrant) return [c].filter((x) => visible(x) || S.open === x.k); // 🪜 shared 🌡️'s stack, having no clause of its own; both left the // surface on 2026-09-15 (Q1362), so every card is its own pile again const k = c.k; @@ -10659,9 +10693,11 @@

Untitled

// an acknowledgement card's line is the paragraph its tab hangs on: // the Founded line for a grant, the Proposals clause for 💡 ⚖️ 🍾 if (shellAck(c)) { - return { kind: 'para', key: k, html: c.isGrant ? foundedClause() : ctx.headFor(c) }; + return { kind: 'para', key: k, html: c.isGrant ? breakLines(linkify(esc(gateClause(c)))) : ctx.headFor(c) }; } - if (shellRelease(c)) return { kind: 'para', key: k, html: ctx.headFor(card('begin')) }; + // …and a 👑 batch's is the founder's clause it hangs on (#223) + if (shellRelease(c)) { const host = releaseHostOf(k); + return { kind: 'para', key: k, html: host === 'founded' ? foundedClause() : breakLines(linkify(esc(gateClause(card(host))))) }; } if (peopleNewsOf(c)) return { kind: 'para', key: k, html: '
' + (doorRowsNow[peopleNewsOf(c).rows] || '') + '
' }; // stage 3a's settings cards: the paragraph's own sentence, settled or // not, without its powers line — an answer card's is its setting's diff --git a/design/setup.js b/design/setup.js index 98e94bbf..ec41a5d6 100644 --- a/design/setup.js +++ b/design/setup.js @@ -1427,9 +1427,9 @@ window.SETUP = (function () { // The whole typed value still comes from the caller's own `ANSTYPED`. const unit = A.rateUnit || 'minutes'; const sel = unitSel(unit, 'data-ansunit="rate"'); - return 'Members may make a new proposal ✏️ every ' + + return 'Members may make a new ✏️ proposal every ' + ' ' + sel + '.'; + (typeof A.rate === 'number' ? ' value="' + A.rate + '"' : '') + '> ' + sel + ', up to 3 at a time.'; }, }; diff --git a/scripts/founding-walk.mjs b/scripts/founding-walk.mjs index 74e29cf1..92c5d571 100644 --- a/scripts/founding-walk.mjs +++ b/scripts/founding-walk.mjs @@ -242,33 +242,35 @@ const record = async (step, note) => { }; /** - * **At the birth the title clause says who the Founder is** (Ed, 2026-08-27, - * backlog 140). The birth's first clause is the founder's first meeting with - * the word *Founder*, and until this sentence nothing on the page has told - * them it means them — so it carries *(that’s you!)*, once, on the title and - * nowhere else. Three assertions, because the aside has three ways to be - * wrong: absent at the birth, still there after the save (where the clause is - * read by members who are *not* the Founder, and must be byte-identical to - * what it always was), or repeated down the band, where it reads as a tic. + * **At the birth the founder's two clauses say who the Founder is** (#223, + * Ed 2026-10-03, G1's *Before the save*: *The Founder (that's you!) may ✒️ + * change anything at will.* and its 🛡️ twin), taking the aside from the title + * clause, where it stood from backlog 140 (Ed, 2026-08-27). The birth is the + * founder's first meeting with the word *Founder*, so it carries *(that’s + * you!)* there, on ✒️'s and 🛡️'s clauses and nowhere else. Three assertions, + * because the aside has three ways to be wrong: absent at the birth, still + * there after the save (where the clauses are read by members who are *not* + * the Founder), or on any other clause down the band — the title's included. * The apostrophe is the page's own curly `Q`, which `snap()` preserves — it * only collapses whitespace — so a straight quote here would pass vacuously. */ const BIRTH_TITLE = - /^The document is titled “Hollow Oak Club Charter”\. The Founder \(that’s you!\) may amend this at will\.$/; + /^The document is titled “Hollow Oak Club Charter”\. The Founder may amend this at will\.$/; const SAVED_TITLE = 'The document is titled “Hollow Oak Club Charter”. The Founder may amend this at will.'; const ASIDE = 'that’s you'; +const ASIDE_CLAUSES = ['grant-pen', 'grant-shield']; const titleClauseAtBirth = async () => { const said = await clauseText('title'); if (!BIRTH_TITLE.test(said || '')) { - errors.push('the birth’s title clause does not tell the founder who the Founder is: ' + + errors.push('the birth’s title clause is not the plain rule: ' + (said === null ? '(no title clause in the band)' : said)); } }; const titleClauseAfterSave = async (s) => { const said = await clauseText('title'); if (said !== SAVED_TITLE) { - errors.push('the aside outlived the birth: ' + + errors.push('the title clause changed after the save: ' + (said === null ? '(no title clause in the band)' : said)); } const carried = (s.paras || []).filter((p) => String(p.text).includes(ASIDE)); @@ -277,11 +279,11 @@ const titleClauseAfterSave = async (s) => { carried.map((p) => p.k).join(', ') + ' still say it after the save'); } }; -const asideOnTitleAlone = (s) => { - const carried = (s.paras || []).filter((p) => String(p.text).includes(ASIDE)); - if (carried.length !== 1 || carried[0].k !== 'title') { - errors.push('the aside is on ' + carried.length + ' clauses at the birth (' + - (carried.map((p) => p.k).join(', ') || 'none') + '), and it belongs on the title alone'); +const asideOnFounderClauses = (s) => { + const carried = (s.paras || []).filter((p) => String(p.text).includes(ASIDE)).map((p) => p.k); + if (JSON.stringify(carried) !== JSON.stringify(ASIDE_CLAUSES)) { + errors.push('the aside is on ' + (carried.join(', ') || 'none') + + ' at the birth, and it belongs on ' + ASIDE_CLAUSES.join(' and ') + ' alone'); } }; @@ -299,7 +301,7 @@ await openCard('slug'); await record('open slug'); await clickIn('.setupcard [data-confirm]'); const atCommitSlug = await record('commit slug'); -asideOnTitleAlone(atCommitSlug); +asideOnFounderClauses(atCommitSlug); await openCard('myemail'); await record('open myemail'); diff --git a/scripts/spec-check.mjs b/scripts/spec-check.mjs index c695789c..8751a888 100644 --- a/scripts/spec-check.mjs +++ b/scripts/spec-check.mjs @@ -888,12 +888,11 @@ function checkOrder(pm) { const secs = [...page.matchAll(/\{ key: '([a-z]+)', title: '([^']*)'[\s\S]*?keys: \[([^\]]*)\]/g)] .map((m) => ({ key: m[1], keys: [...m[3].matchAll(/'([a-z-]+)'/g)].map((x) => x[1]) })); const secOf = (k) => (secs.find((s) => s.keys.includes(k)) || {}).key; - // 🏛️ was skipped here while its clause hung off whichever question the - // grant arrived with; Q750 pins the clause to the Proposals preamble, so - // there is a section to name. It is deliberately not in `SEC.rate.keys` - // (admitting it would make the whole section live from its place in ORDER), - // hence an override rather than a `secOf` lookup. - const hosts = { 'grant-pen': 'lead', 'grant-shield': 'lead', 'grant-voice': 'lead', title: 'lead', slug: 'lead', shape: 'lead', myemail: 'lead', chamber: 'lead' }; + // The lead is no section of `SEC` — it is the constitution group's own + // `lead` — so its cards are named here: the title, the link, 📧 at the + // birth, 🎩 on the Founded line (#223, G2), ✒️ and 🛡️ on their own clauses + // (G1) and 🌍. 🏛️ is the Proposals section's own key since #223 (G3). + const hosts = { 'grant-pen': 'lead', 'grant-shield': 'lead', hat: 'lead', title: 'lead', slug: 'lead', shape: 'lead', myemail: 'lead', chamber: 'lead' }; for (const r of rows) { const want = r.section.split(/[,—(]/)[0].trim(); const got = (r.key in hosts) ? hosts[r.key] : secOf(r.key); @@ -921,20 +920,22 @@ function checkOrder(pm) { const glyphs = {}; for (const m of page.matchAll(/\{ k: '([a-z-]+)', g: '([^']+)'/g)) glyphs[m[1]] = m[2]; for (const r of rows) if (glyphs[r.key] && glyphs[r.key] !== r.glyph) find('order', `${r.key}: glyph ${r.glyph} vs the card's ${glyphs[r.key]}`); - // **The Proposals opening is one clause carrying four tabs** (Y23, Q748). - // The page names the group in `PROPOSAL_CHIPS`; the band prose above names - // the glyph run the section opens with. Neither is derivable from the other, - // so the stack could drift from the document that describes it — and the - // group is what decides which tabs a reader can reach at all. - const chipsLit = page.match(/const PROPOSAL_CHIPS = \[([^\]]*)\]/); - if (!chipsLit) find('order', 'PROPOSAL_CHIPS is gone — the Proposals opening no longer names its own stack (Y23)'); + // **The Proposals section is five clauses, one per power** (#223, G3, Ed + // 2026-10-03), retiring Y23's one clause carrying four tabs. The page names + // them in `PROPOSAL_CLAUSES`; the band prose above names the glyph run the + // section reads in. Neither is derivable from the other, so the order could + // drift from the document that describes it. + const clausesLit = page.match(/const PROPOSAL_CLAUSES = \[([^\]]*)\]/); + if (!clausesLit) find('order', 'PROPOSAL_CLAUSES is gone — the Proposals section no longer names its own clauses (G3)'); else { - const chipKeys = [...chipsLit[1].matchAll(/'([a-z-]+)'/g)].map((m) => m[1]); - const prose = read('SURFACE.md').split('\n').find((l) => l.includes('the preamble wearing')) || ''; - const run = ((prose.match(/the preamble wearing ([^·)]+)/) || [, ''])[1] || '').trim(); - const want = chipKeys.map((k) => glyphs[k] || '?').join(' '); - if (run !== want) find('order', `SURFACE's Proposals run is '${run}', PROPOSAL_CHIPS is '${want}'`); - for (const k of chipKeys) if (!pm.ORDER.includes(k)) find('order', `PROPOSAL_CHIPS names '${k}', which is not in ORDER`); + const clauseKeys = [...clausesLit[1].matchAll(/'([a-z-]+)'/g)].map((m) => m[1]); + const prose = read('SURFACE.md').split('\n').find((l) => l.includes('the five clauses wearing')) || ''; + const run = ((prose.match(/the five clauses wearing ([^·)|]+)/) || [, ''])[1] || '').trim(); + const want = clauseKeys.map((k) => glyphs[k] || '?').join(' '); + if (run !== want) find('order', `SURFACE's Proposals run is '${run}', PROPOSAL_CLAUSES is '${want}'`); + for (const k of clauseKeys) if (!pm.ORDER.includes(k)) find('order', `PROPOSAL_CLAUSES names '${k}', which is not in ORDER`); + if (secs.find((s2) => s2.key === 'rate') && JSON.stringify(secs.find((s2) => s2.key === 'rate').keys) !== JSON.stringify(clauseKeys)) + find('order', `SEC.rate.keys is not PROPOSAL_CLAUSES: ${secs.find((s2) => s2.key === 'rate').keys.join(' ')} / ${clauseKeys.join(' ')}`); } // F16, the half a table cannot hold (Q639): ✒️ and 🛡️ write no clause of // their own — the count they used to state is already on every setting From d750e00da18edbed2789ac55ff96605bb9884bd3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 3 Oct 2026 17:39:04 +0000 Subject: [PATCH 3/8] =?UTF-8?q?The=20rules=20files=20follow=20G1=E2=80=93G?= =?UTF-8?q?6=20(#223);=20walks=20and=20probe=20follow=20the=20new=20walk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SURFACE §8's ORDER table, band table, F16 F20 F22, Y21 Y23 retired, §9's grant, gate, 🍾 and 🎩 rows; SPEC v0.147 (the preamble sentence); STYLE T10's birth aside on the founder clauses. spec-check re-points Y23's check at PROPOSAL_CLAUSES. setup-probe, journey and slider-walk walk the Rules top to bottom. The Proposals preamble's copy is retired. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018T9ZX2oB2xxzsK4Tgxpya7 --- README.md | 2 +- SPEC.md | 4 +- SURFACE.md | 71 +++++++++++++++++----------------- design/STYLE.md | 19 +++++----- design/copy.js | 14 ------- design/tools/setup-probe.js | 76 +++++++++++++++++++------------------ scripts/journey-walk.mjs | 15 +++++--- scripts/probe-coverage.mjs | 5 ++- scripts/slider-walk.mjs | 13 ++++--- scripts/spec-check.mjs | 2 +- 10 files changed, 109 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index 9c39d2f8..dc73cf73 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Rule files hold rules; the reasoning behind them lives in `design/`. Where two d | Document | What it is | Read it when | |---|---|---| -| [`SPEC.md`](SPEC.md) | The mechanism, v0.146 — tables and numbered rules, each pointing at its reasons as `→ why: R-nnn`. The single source of truth. | First, to understand what the engine does. | +| [`SPEC.md`](SPEC.md) | The mechanism, v0.147 — tables and numbered rules, each pointing at its reasons as `→ why: R-nnn`. The single source of truth. | First, to understand what the engine does. | | [`SURFACE.md`](SURFACE.md) | What the surface tells a member and what a control does: the event matrix, the marks, the wallets, the founding order, the card kinds. Asserted against the page's own tables by `npm run spec-check`. | Second, to understand what a member sees. | | [`CLAUDE.md`](CLAUDE.md) | The project's operative reference: the vocabulary, the glossary of every named part, and the post-mortems that bite. | Before contributing. | | [`design/STYLE.md`](design/STYLE.md) | The surface-copy checklist every string a member can read has to pass. | Before touching `copy.js`. | diff --git a/SPEC.md b/SPEC.md index 7a85e3de..70730dd7 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1,4 +1,4 @@ -# Group Drafting Engine — Specification v0.146 +# Group Drafting Engine — Specification v0.147 ### Working name deferred (direction: "draft") A compiler for group agreement. Input: a starting text, a roster, a constitution file. Output: the most-agreed text, plus a record of every disagreement, ranked and mapped. Institutional acts — provenance, adoption, ratification — belong to the convening context. The tool measures agreement; it does not confer legitimacy. @@ -309,7 +309,7 @@ The founding is optional in full: a convenor may set everything and delegate not | room proposes, convenor answers | — | held | | delegated | — | — | -How each state reads to a member is copy rather than mechanism: the power clause is `design/STYLE.md` T8's, in `design/copy.js`, and a delegated setting carries no governance sentence at all — the preamble's default applies. +How each state reads to a member is copy rather than mechanism: the power clause is `design/STYLE.md` T8's, in `design/copy.js`, and a delegated setting carries no governance sentence at all — the Founder's own ✒️ and 🛡️ clauses name what the Founder holds (SURFACE §8, F16). 1. **Birth.** Every setting is born convenor-held, both powers, question shut, value unset. → why: R-001 2. **Delegation is an act, one-way, the convenor's own, never a motion.** Before the start, on a delegable setting, it opens the founding question; otherwise — and always after the start — it is a **hand-over**: the value stands, only the holder changes. Each setting's hand-over moment is in §9.7.1 (*hand-over from*). → why: R-006, R-027 diff --git a/SURFACE.md b/SURFACE.md index bf5d3445..5f2d68cb 100644 --- a/SURFACE.md +++ b/SURFACE.md @@ -54,11 +54,11 @@ Vocabulary: **audience** — nobody / the actor / one member / every member owed | E1 | A founding question opens (delegated) | every arrived member, founder if a member | rail ask entry; task paragraph under the watching clause | an answer | ✓ | entry leaves; 🍾 carries the count (Q1176); revisable until resolved | chamber applications lapse removal ending quorum authorship judgments rate | | E2 | A founding question resolves | everyone who answered | clause (Q1176) | nothing (pre-start: no OK owed) | — | clause states the rule | — | | E3 | A setting is set pre-start | nobody | clause fades in | nothing | — | clause | title slug text hat myname mypic admission | -| E4 | The document begins 🍾 | every member | 💡 ⚖️ gate news cards, the Founded line; 🏛️ for anybody not yet handed a voice (E8) | **Accept ✏️** · **Accept ⚖️** each (Q1501) | the Accept | `ACK_KEYS` per seat; grants staged behind the constitutional OKs | begin canpropose canjudge | +| E4 | The document begins 🍾 | every member | 💡 ⚖️ gate news cards, 🍾's clause in the Schedule; 🏛️ for anybody not yet handed a voice (E8) | **Accept ✏️** · **Accept ⚖️** each (Q1501) | the Accept | `ACK_KEYS` per seat; grants staged behind the constitutional OKs | begin canpropose canjudge | | E5 | A constitutional setting set or changed post-start (pen or carried) | every member who had no say **and arrived when it was set**, lapsed included; a later joiner reads it as the document | news entry pinned ✔; clause change line (was / now, who, why); the card's body is the rule block, then *Set to · ‹value› · Decided by the members.* (or *Set by the founder when the document was made.*), then the change sentence and its reason — the one place that line is drawn (Q1232, 2026-09-07) | OK | OK | the record behind the rule's tab and the record's Amendments — no line on the clause (1564.5 (b), 2026-09-27) | — | | E6 | An ordinary setting first set | nobody | clause | nothing | — | clause | — | | E7 | An ordinary setting changed | as E5 | as E5; no mail | OK | OK | as E5 | — | -| E8 | A power arrives | the holder | news entry, wallet flight (the ✏️ storm from 💡's OK, per member — the founder's ✏️ arrives with no press and no storm, Y27); 🏛️ arrives when you first become a member — the founder-member at the save, an invitee at arrival, an admitted applicant at admission — as a tab on the Founded line beside ✒️ 🛡️, and **no blind question is served until its OK** (Q1365, Ed 2026-09-15, reversing Q605 and F20), **nor 💡 ✏️ or ⚖️ to a member** (Q1540, Ed 2026-09-24), so nobody proposes or votes before accepting 🏛️; **until ⚖️'s OK the charter holds no race for you** — no entry, tab, mark or wire, in any state — and the OK brings every one of them in at once (C9, Q1328) | **Accept ‹glyph›** — 🏛️'s **Activate 🏛️** (Q1501, Q1502) | the Accept | per seat; re-asked on each not-held → held | grant-pen grant-shield grant-voice canpropose | +| E8 | A power arrives | the holder | news entry, wallet flight (the ✏️ storm from 💡's OK, per member — the founder's ✏️ arrives with no press and no storm, Y27); 🏛️ arrives when you first become a member — the founder-member at its place in the founding walk, after 👥 (#223, G6), an invitee at arrival, an admitted applicant at admission — as a tab on the 🏛️ clause among the Proposals (G3), and **no blind question is served until its OK** (Q1365, Ed 2026-09-15, reversing Q605 and F20), **nor 💡 ✏️ or ⚖️ to a member** (Q1540, Ed 2026-09-24), so nobody proposes or votes before accepting 🏛️; **until ⚖️'s OK the charter holds no race for you** — no entry, tab, mark or wire, in any state — and the OK brings every one of them in at once (C9, Q1328) | **Accept ‹glyph›** — 🏛️'s **Activate 🏛️** (Q1501, Q1502) | the Accept | per seat; re-asked on each not-held → held | grant-pen grant-shield grant-voice canpropose | | E9 | A power is laid down | every member but the actor (Q918, Ed 2026-08-29) | the power card; **one news entry owed an OK, stating its whole batch — and none at 🍾** (C8a; Q571, Q1013, Q1184); clause deviation vanishes | OK | on commit / on OK | clause; record | — | | E10 | A constitutional motion is put — an invitation or removal too, where 🪪 / 🥾 stands at *assembly* (entry 94); and put again, to everyone but its mover, when a carry or the Founder's ✒️ moves the setting under it (Q1348, SPEC §9.6 | every active member | **the motion's own card** — a tab in the setting's pile behind the rule's own tab, and its own rail ask (M18, Q1367, Ed 2026-09-15: *other proposals should not be shown on the decision card but in tab stack and as more queue cards*) — none of it, for a member, until they have acknowledged 🏛️ (C9, Q1344); **several motions running on one setting are several tabs and several entries**, each card showing that motion alone; the setting's own tab opens the rule as it stands, and the composer | accept / keep / abstain | ✓ — an accept or an abstain files the entry ⏳ and stays revisable on the same tab until the settle; **a keep settles the motion at that press** (Q1473, Ed 2026-09-19; SPEC §9.6) and the entry becomes the ✖ record at once — news to its mover (E41), filed for everybody else; tab keeps its glyph | carried → E5 | invite remove | | E11 | An ordinary motion is put — an invitation or removal too, where 🪪 / 🥾 stands at *proposal* (entry 94) | every active member — not anyone whose membership has gone quiet (Q930, Ed 2026-08-29) | race card — none of it, for a member, until they have acknowledged ⚖️ (C9, Q1344). **The mover's own entry is theirs** (Q1370, Ed 2026-09-15; M3, §6's `yours` row): ✏️, force-kept, from the press — the mover stands at accept (K8), so their card is the two blocks with the proposed one marked *Proposed*, inert, and 🗑️ withdraw alone, no ✓; an invitation so put stands under *Applications for Membership* and in ✉️'s head under those words, untagged (Q1557) | a judgment | ✓ — closes and files ⏳ (Q576) | → E5 / E14 | invite remove | @@ -135,9 +135,9 @@ The card lifecycle, as the rows of the matrix a single card passes through — p | Y18 | A clause is not a button; the gutter tab is the only way into a card from the document — the text's 📝 included: a click in the prose in read mode beats it (K31) | C1 | clicking a clause puts a caret in it (in edit mode); in read mode it points at the door | K31 | | Y19 | **Retired on the settings cards** (answers.md Part 4 .19, Q1541 stage 3a): before Founder Actions is accepted the ✒️ commit is **not drawn at all**, and the card reads as the rule (§9's *watching* row) — and from the save no settings card is reachable before it at all, 🪶 and 📍's tabs withheld like every rule from 🌍 on (§8 `ORDER` rows 1–2, Q1560 (5)). The dark ✒️ with *Founder Actions ✒️ are waiting in your tasks — accept them and this turns.* (`penWait`) stands only on the cards stage 3b converts (🪪 🤝 🎩, the power cards) | C9 | Ed: *before an action is accepted we shouldn't show the button at all* | Q622; Part 4 .19 | | Y20 | The sealed record, the settled motion record — the mover's owed rejection (E41) included — and the backlog card have no 🗑️ (Q1522 (6)); nor have 🥂 and the departure news cards (E31, E32, E40), their row the OK alone while it is owed and nothing after (Q1527 (a), Ed 2026-09-24); every email card sends from the row's 📧 (reading 1194; the stranger's Apply since Q1390, the applicant's since Q1399); the applicant's Apply opens with *Begin* | C4 | nothing to put back, or the door has no provisional layer | Q613; Q744; CP5 (Q1100) | -| Y21 | 🍾's clause **is** the Proposals preamble, and heads the section while its task is last in the order | C14 | pressing 🍾 is what makes the preamble's first sentence true, so the tab belongs on it | Q616; Ed 2026-08-22 | +| Y21 | **Retired** (#223, G5, Ed 2026-10-03): 🍾's clause is its own, first in the Schedule — *The members are waiting on the Founder to begin* until the press, the moment after it — and it still heads its section while its task is last in the order | C14 | the press makes the clause true, so the tab belongs on it | Q616; #223 | | Y22 | ↻ is a character, styled as a drawn mark; ⏸ is this repo's own drawn glyph rather than the set's | §6 | ↻ has no partner whose weight it must equal, and one shape in two colours needs a shape a colour can reach; the set's ⏸ and ↻ are its two *buttons* — a plate with a white shape knocked out — which this alphabet has refused since 2026-08-17 | Ed 2026-08-17; Q1360 | -| Y23 | The Proposals opening is **one clause carrying three tabs** (🍾 💡 ⚖️; 🏛️'s tab is on the Founded line since Q1365): the gates and the voice grant contribute fragments, not paragraphs, and each gate joins the stack as it arrives | C14 | the four state one rule together — how a rule changes, and who may take part — so four stubs claimed four decisions where there is one | Q748–Q751 | +| Y23 | **Retired** (#223, G3, Ed 2026-10-03): the Proposals section is five clauses, one per power — ✏️ ⏱️ ⚖️ 👥 🏛️ — each gate and grant wearing its tab on its own clause, the preamble of three sentences gone | C14 | one clause per power reads as the rules it is | Q748–Q751; #223 | | Y24 | ✉️'s invitation field is drawn **only where the viewer's own word sends** (`directInvite`): the founder before the start; after it the founder while the **door's** ✒️ is held — `membershipReserved()`, which is `invite()`'s own test — and **any member while 🪪 stands at *pen***. Where it is not, the card composes the invitation as a motion at 🪪's price instead. And the field's send wears **✒️**, the mark of the word that sends — so the row is left with nothing to commit and only closes (§9.1) | C1 | a control that cannot act is worse than an absent one; the 🛡️ only refuses, so it can never open a door; the mark goes where the act is | Q812–Q813, entry 94, entry 37; §9.6a, R-048 | | Y25 | **Every command the document refuses says so** (Q1330, Ed 2026-09-11: *refusals should print an error on screen with a debug message*): where the page can name the card the command left from — the card open when it was sent — in **one sentence under that card**, the card re-opened, retired by the next keystroke on it — the module's own `(§…)` pointer dropped, a set reading *That could not be set: …* and everything else *That was refused: …*; and **always** one stagehand's line at the foot of the window, off the design system like the alpha flag — the sentence, then the command, its arguments, the document, the seat and the time — dismissed by a click and replaced by the next refusal. A wire that answers with a status and no sentence, or not at all, is refused the same way with the status for its reason. The host writes every refusal to its error log (`docs/OPERATING.md` §11). Until Q1330 this reached the two doors and a founder's set only. **A vote on the charter says so too** (Q1505, Ed 2026-09-23): a judgment the host refused or never answered is taken back (issue #37), and its pair card — re-opened once the press's own close has run, unless another card of the charter is open — carries *That was refused: …* just above its commit row, retired by the next choice on it; the connection itself is warned of before anybody acts (C17), so this is the rare press that fails in the instant it is made | C4 | every act on these cards was fire-and-forget, so a refusal and a success looked identical; a member is not expected to meet a refusal at all, so one that happens is a defect and has to be debuggable | Q811, Q815; backlog 51; Q1330; Q1505 **A pasted list is the one exception to the single sentence** (Q1342, Ed 2026-09-12: *one sentence per refused address, all standing*): ✉️ draws one line per refused address, naming the address and the reason, the lines standing together until the card closes or the next send, and the box keeps the refused addresses so a corrected paste is one edit away. | | Y26 | A 💤 change that returns lapsed members **names them in its own change line** — the change is the news (E5), and who it put back is part of what changed: one sentence after the was/now pair (`lapseReturned`), the names off the register from the module's own record of the set (`SettingView.returned`, filled by `member-returned` events the rule caused), on the member's news card and in the record behind the rule's tab; the founder's own lane says nothing, having no change line (E5) | C11 | a rule change that silently re-populates the electorate is a fact about people, not only about the rule | Ed, 2026-08-26 (Q902); §9.5a, entry 97 | @@ -182,7 +182,7 @@ Keys that are not settings: `hat` `myemail` `begin` (SPEC §9.7.1, decisions tha Lifted from SPEC.md; kept as a section by Ed's ruling (Q1004). -- **The head of the document** — the Rules block (Q1516 (5); `constitution-section` in code) carries the founder, the constituted-at time (*Founded by [name] 👑 at [time] on [date]*) and every setting's current value as a clause, governance stated by deviation under the Proposals preamble. (One bullet, by Ed's Q1004 and Q1238; the 👑 question is E12 and STYLE T8.) +- **The head of the document** — the Rules block (Q1516 (5); `constitution-section` in code) carries the founder (*Founded by [avatar] [name] 👑.*), the founder's ✒️ and 🛡️ clauses, every setting's current value as a clause with its governance stated by deviation, and the Schedule, whose 🍾 clause states the moment (*Members started making proposals at [time] on [day].*) (#223). (One bullet, by Ed's Q1004 and Q1238; the 👑 question is E12 and STYLE T8.) ## 6. The marks alphabet and the rail @@ -351,24 +351,24 @@ The refund flight and every grant's inbound flight take 640 ms. | 3 | myemail | 📧 | birth · identity | lead (pre-save); your own row after | yes | slug | Enter Your Email · Your Email | | 4 | myname | ✋ | personal | members — your own row | no | the save | Choose Your Name · Your Name | | 5 | mypic | 🖼️ | personal | members — your own row | no | the save | Choose Your Picture · Your Picture | -| 6 | grant-pen | ✒️ | grant | lead — a tab on the Founded line, no clause | yes | the save | Founder Actions | -| 7 | grant-shield | 🛡️ | grant | lead — a tab on the Founded line, no clause | no | the save | Founder Veto | -| 8 | chamber | 🌍 | constitutional · judge-gate | lead, penultimate | yes | the pen's OK | Who Can See the Document? · Visibility | -| 9 | admission | 🪪 | constitutional — **the price of admission** (entry 94) | members, first — its own clause, above 🤝 (Ed, 2026-08-26) | yes | chamber | How Does Somebody Join? · Admissions (the ✉️ door is the **remedy** while 🍾 waits on `one-voice`, F19) | -| 10 | applications | 🤝 | constitutional | members — above *Members*, ahead of 💤 🥾 (Q617) | yes | admission | Can Strangers Apply? · Applications | -| 11 | hat | 🎩 | decision, not a setting | members — a tab on the list; no sentence | yes | applications | Is the Founder a Member? · Founder’s Membership | -| 12 | lapse | 💤 | constitutional · judge-gate | members — above *Members*, with 🤝 (Q865) | yes | hat | Do Memberships Lapse? · Lapsing | +| 6 | hat | 🎩 | decision, not a setting | lead — a tab on the Founded line until 🍾, then hidden; no sentence (G2, G2b, #223) | yes | the save | Is the Founder a Member? · Founder’s Membership | +| 7 | grant-pen | ✒️ | grant | lead — its own clause after the Founded line, wearing 👑 news it laid down (G1); no clause and no tab where held nowhere (G1b) | yes | 🎩 | Founder Actions | +| 8 | grant-shield | 🛡️ | grant | lead — its own clause after ✒️'s, wearing 👑 news it laid down (G1); no clause and no tab where held nowhere (G1b) | no | the pen's OK | Founder Veto | +| 9 | chamber | 🌍 | constitutional · judge-gate | lead, last — after the founder's clauses (#223) | yes | the pen's OK | Who Can See the Document? · Visibility | +| 10 | admission | 🪪 | constitutional — **the price of admission** (entry 94) | members, first — its own clause, above 🤝 (Ed, 2026-08-26) | yes | chamber | How Does Somebody Join? · Admissions (the ✉️ door is the **remedy** while 🍾 waits on `one-voice`, F19) | +| 11 | applications | 🤝 | constitutional | members — above *Members*, ahead of 💤 🥾 (Q617) | yes | admission | Can Strangers Apply? · Applications | +| 12 | lapse | 💤 | constitutional · judge-gate | members — above *Members*, with 🤝 (Q865) | yes | applications | Do Memberships Lapse? · Lapsing | | 13 | removal | 🥾 | constitutional | members — the last rule above *Members* | yes | lapse | How Is a Member Removed? · Removals | -| 14 | canpropose | 💡 | gate (and the ✏️ grant) | rate — in the preamble's stack | no | 🍾 | Proposals | -| 15 | canjudge | ⚖️ | gate | rate — in the preamble's stack | no | 🍾 | Voting | -| 16 | grant-voice | 🏛️ | grant | lead — a tab on the Founded line beside ✒️ 🛡️, no clause (Q1365); its sentence is the preamble's third (Q750) | no | the moment you become a member — the save, your arrival, your admission (Q1365); **its OK gates every blind question** | Constitutional Proposals | -| 17 | rate | ⏱️ | ordinary, delegable | rate | yes | removal | Set the Proposal Rate · Proposal Rate | -| 18 | ending | ⏰ | constitutional, route inside it | deciding, first | yes | rate | When Does It End? · Ending | -| 19 | quorum | 👥 | constitutional · judge-gate | deciding, last | yes | ending | Choose the Quorum · Quorum | -| 20 | authorship | 👤 | constitutional · judge-gate | privacy | yes | quorum | Are Proposals Anonymous? · Anonymous Proposals | -| 21 | judgments | 👁️ | constitutional · judge-gate | privacy | yes | authorship | When Are Votes Revealed? · Vote Reveal | -| 22 | text | 📝 | ordinary | doc — the charter heading under the hairline; **never a task** — a card with two modes, settled from the save, its OK struck (K31, Q1082) | yes | judgments | Text | -| 23 | begin | 🍾 | decision, not a setting | rate — the preamble's stack, first (Y21, Y23) | no | until the founder can press it — `readiness().ready` and 🏛️ not still being served — **or until nothing but the ✉️ remedy is being served** (F5, F9, F18, F19), **or while every hold is a delegated question still collecting and the founder's own answers are given — then a ⏳ entry naming who is due** (F5, issue #76); members never see it pre-start | Begin | +| 14 | canpropose | 💡 | gate (and the ✏️ grant) | rate — the ✏️ clause, first (G3) | no | 🍾 | Proposals | +| 15 | rate | ⏱️ | ordinary, delegable | rate — second (G3) | yes | removal | Set the Proposal Rate · Proposal Rate | +| 16 | canjudge | ⚖️ | gate | rate — the ⚖️ clause, third (G3) | no | 🍾 | Voting | +| 17 | quorum | 👥 | constitutional · judge-gate | rate — the pass rule, fourth (G4) | yes | rate | Choose the Quorum · Quorum | +| 18 | grant-voice | 🏛️ | grant | rate — the 🏛️ clause, last (G3) | no | the founder-member: 👥, its place in the walk (G6); anybody else the moment they become a member — arrival, admission (Q1365) — before any question they are asked; **its OK gates every blind question** | Constitutional Proposals | +| 19 | authorship | 👤 | constitutional · judge-gate | privacy | yes | quorum | Are Proposals Anonymous? · Anonymous Proposals | +| 20 | judgments | 👁️ | constitutional · judge-gate | privacy | yes | authorship | When Are Votes Revealed? · Vote Reveal | +| 21 | ending | ⏰ | constitutional, route inside it | schedule — second, under 🍾's clause (G5) | yes | judgments | When Does It End? · Ending | +| 22 | text | 📝 | ordinary | doc — the charter heading under the hairline; **never a task** — a card with two modes, settled from the save, its OK struck (K31, Q1082) | yes | ending | Text | +| 23 | begin | 🍾 | decision, not a setting | schedule — its clause first (G5, #223) | no | until the founder can press it — `readiness().ready` and 🏛️ not still being served — **or until nothing but the ✉️ remedy is being served** (F5, F9, F18, F19), **or while every hold is a delegated question still collecting and the founder's own answers are given — then a ⏳ entry naming who is due** (F5, issue #76); members never see it pre-start | Begin | **Outside `ORDER`** — tabs that are not steps: @@ -378,18 +378,18 @@ The refund flight and every grant's inbound flight take 640 ms. | ✉️ · ❌ | ✉️ on *Applications for Membership* (Q1557), ❌ on *Proposed for removal*, both from the birth — **a door stands by its result** — each wearing its own ✒️/🛡️ pair over the act | the subsection · ✉️ stands in the founder's rail as a task once the Membership rules stand, until the first invitation or 🍾 (F23) | entries 94, 96, 181 | | the `adm:` admit cards | the *Applications for Membership* pile, behind ✉️ and its motions, one per applicant still asking; absent at 🪪 *pen*, where an application is admitted on submit | the applicant's row · a task at *proposal* and *assembly*, news at *pen* (E21); at *proposal* a race entry — 🪪 asking, ⏳ voted, the race's closeness as its fill (Q1371); the entry faced and named (Q1375) | entry 96 | | the ✒️/🛡️ power tabs | under every held-able setting — *Can the Founder Make Amendments at Will?* / *Does the Founder Have a Veto?*; on the doors *Can the Founder Invite / Remove at Will?* / *Does the Founder Have a Veto over Invitations / Removals?* | none · none — **and once the document has begun, a power the Founder does not hold has no tab at all**, for any reader (Q1404, Ed 2026-09-16; E39 and K32 retired); before 🍾 both tabs stand, a release being revisable until the start | Q615, Q1404 | -| 🥂 | *Decisions*, when closed | the closing card · pinned per member (E24) | — | +| 🥂 | *Schedule*, when closed (G5) | the closing card · pinned per member (E24) | — | **The band** (`SEC`), under the heading **Rules** — the section's name on the surface since Q1516 (5) (Ed, 2026-09-23: groups drafting a constitution read *the constitution of the constitution*; *Settings* set aside as reading like preferences), in the band and in the contents rail alike (`COPY.page.rulesHeading`); `constitution-section` and `#cs-constitution` stay the code's names — top to bottom: | section | `SEC` key | what it carries | |---|---|---| -| the opening run | `lead` | the title wearing 🪶 ✒️ 🛡️, the link, 📧 pre-save, 🌍, the Founded line wearing the three grants' ✒️ 🛡️ 🏛️ (Q1365) | -| **Membership** | `members` | the rules 🪪 · 🤝 · 💤 · 🥾, then one lvl-3 subsection per status, each carrying the control whose result it holds — *Members* with 🎩 and 📭, your row last (Y8) wearing ✋ 🖼️ 📧; *Applications for Membership* with ✉️, its power tabs, its motions and one admit card per person asking (Q1557); *Lapsed* with none; *Proposed for removal* with ❌ and its power tabs; *Alumni* with 🌂 and the 🥾 news (F21; entries 95 and 96; Q1557) | -| **Proposals** | `rate` | the preamble wearing 🍾 💡 ⚖️ · ⏱️ (Y21, Y23) | -| **Decisions** | `deciding` | ⏰ · 🥂 when closed · 👥 | +| the opening run | `lead` | the title wearing 🪶 ✒️ 🛡️, the link, 📧 pre-save, the Founded line wearing 🎩 until 🍾 and the 👑 news for the last power laid down (G1, G2), the ✒️ clause wearing its grant and the 👑 news that laid a ✒️ down, the 🛡️ clause the same (G1; none where the power is held nowhere, G1b), 🌍 (#223) | +| **Membership** | `members` | the rules 🪪 · 🤝 · 💤 · 🥾, then one lvl-3 subsection per status, each carrying the control whose result it holds — *Members* with 📭, your row last (Y8) wearing ✋ 🖼️ 📧; *Applications for Membership* with ✉️, its power tabs, its motions and one admit card per person asking (Q1557); *Lapsed* with none; *Proposed for removal* with ❌ and its power tabs; *Alumni* with 🌂 and the 🥾 news (F21; entries 95 and 96; Q1557) | +| **Proposals** | `rate` | the five clauses wearing 💡 ⏱️ ⚖️ 👥 🏛️ — ✏️ proposing, the rate, ⚖️ voting, the quorum as the pass rule, 🏛️ constitutional proposals (G3, G4, #223) | | **Anonymity** | `privacy` | 👤 👁️ | +| **Schedule** | `schedule` | the Rules' last section, just above the Text: 🍾 · ⏰ · 🥂 when closed (G5, #223) | | the hairline, then the charter | — | under its own name, the riding 📝 tab beside it — from the save, in `#ridetab`, never in the band's own row (K31) | | **The Document** | `doc` | a rail-only group: 🪶 📍 🌍 📝, 📝 never reaching the rail (K31) | @@ -428,7 +428,7 @@ Before 🌍 is decided its clause states the interim rule: *Until the Founder de - **F13 The constitution starts writing itself at the birth**; at the birth the founder holds everything by construction; the assent half waits for a membership. - **F14 What is born arrives**: a rail entry grows from zero (240 ms), a clause fades (840 ms), a batch cascades 55 ms apart capped at six, each column counting its own; reduced motion fades everything; a stagehand act mutes the pass. **Something that went and returns arrives again**: 🪶 and 📍's tabs, withheld from the save, fade in at the pen's OK as a clause does, once, by a birth key that exists only after the OK (`data-born`; Ed, Q1560 (5): *give them a nice fading in animation*). - **F15 A clause states *The Founder is deciding [what]* until decided, then the rule and its governance deviation; a settings card opens with that clause as its first line under the label *Current rule*, without the governance deviation, the rule wearing its standing pill once decided (Q1541 stage 3a, 1541.3, .10, .47 — Q1151's headless option-block card retired), and a heading-over-text card — the grants and the gates — no title head (Q1373); **a founder's power tab carries no head at the founding only**, its two blocks being the whole choice, and takes the rule as its head once the document has begun (Q1430, Ed 2026-09-17); 📝, personal cards and the blind answers keep their title.** -- **F16 The Founded line stands from the save and gains its moment at the press** — the *moment* is what is unknown before the start, not the founding — **and it is the clause ✒️ and 🛡️ hang their tabs on, neither writing a sentence of its own; 🎩 is settled once the document begins; the register asks for no minimum.** → why: Q639. +- **F16 The Founded line stands from the save and states no moment** — *Founded by [avatar] [name] 👑.*, the time being 🍾's clause's in the Schedule (#223) — **and it carries 🎩 until 🍾 (G2: hidden at the start, settled all the same) and the 👑 news for the last power laid down; ✒️ and 🛡️ each have a clause of their own beneath it, naming the settings held where the power is held on some, and gone with their tab where it is held on none (G1, G1b); the register asks for no minimum.** → why: Q639, #223. - **F17 A dead click beats the next task**: during the birth, a click landing on document space with no control under it — while a task is outstanding and no card is open — lifts and settles **both** that task's tab and its rail entry, twice, over 640 ms. It never opens the card, however often it is clicked, and **nothing ever beats unprompted**. Reduced motion holds the lift for the same span instead of moving. → why: Q650–Q653. @@ -436,11 +436,11 @@ Before 🌍 is decided its clause states the interim rule: *Until the Founder de - **F19 A wait the founding cannot end says why, and ✉️ is the remedy**: `readiness()` gives a reason beside each waiting question — `judge-gate`, `deps-unsettled` (the dependency named beside it), `invitation-open`, `one-voice`, `collecting` (`WaitingWhy`) — and **`one-voice` is the only one no amount of answering will clear**, a question handed to a membership of one having been handed to nobody. **`deps-unsettled` is served without the remedy** (entry 69): §9.0a serves a question only once its own dependencies stand, so a delegated question under an undecided dependency is waiting on another question and not on the room — ✉️ is not summoned for it, and 🍾 names the dependency and that it must settle first rather than an act. **No live example since 2026-09-15** (Q1362): 🌡️ and 🪜 were the only settings in the catalogue carrying a `deps` list, and both left the surface — the rule stands and nothing reaches it. Where the dependency is *itself* a `one-voice` hold, the ✉️ card that is served for **that** question says what is waiting on it in turn, so the second question is not read as a second problem. In that state 🍾 names it and both acts that end it, **✉️ stands as a task**, and the two are served **together**: the remedy is the dead end wearing a card, so it neither counts as *something else being served* (F5) nor as one of the *other tasks* that pace a founder's own delegated questions (F7). The ✉️ task is derived and acknowledges nothing — no OK, no `ACK_KEYS` key, nothing owed (F8) — and leaves the instant an invitation goes out (the reason becomes `invitation-open`) or the delegated setting is taken back. The remedy's card: §9's ✉️ row. → why: DECISIONS.md, *Begin says what it is waiting for* (Q826–Q830). -- **F20 — retired (Q1365, Ed 2026-09-15).** 🏛️ is served the moment you become a member, on the Founded line (E8, §8 row 16), so it needs no host question and no fallback; a founder-member is served it at the save. → why: DECISIONS.md, *Q1365*. +- **F20 — retired (Q1365, Ed 2026-09-15).** 🏛️ is served the moment you become a member (E8, §8 row 18), so it needs no host question and no fallback — on the 🏛️ clause among the Proposals since #223, and a founder-member at its place in the walk, after 👥 (G6). → why: DECISIONS.md, *Q1365*. - **F21 Membership is one subsection per status, and only the first is the membership.** Under the rules (🪪 🤝 💤 🥾) stand lvl-3 subsections — **Members** · **Applications for Membership** · **Lapsed** · **Proposed for removal** · **Alumni** (Q1557) — each the document's own text, not a card's. *Lapsed* stands only where 💤 lapses, and *Alumni* on its rows or wherever 🌂 is yours to press; **the two that carry a control stand when empty** (Applications for Membership, Proposed for removal), **each with a placeholder row saying so**. **A door stands by its result** (entry 96) — each a pile with its own ✒️/🛡️ tabs, never a button inside a row, and **no card draws the register**. *Members* is everybody else who has **arrived**, then every invitation sent and not yet followed, wearing *invited*, then your own row last (Y8, Q1316, Q1557), and *(nobody else here yet)* counts the arrived rows alone; *Applications for Membership* is every vote still running on letting somebody in. **A lapsed member moves to *Lapsed* and counts all the same** (Y10) — lapse is a stall, not a departure, and they abstain rather than leave; **a removal running against somebody names them under *Proposed for removal* whatever else is true of them**. **Every subsection folds**, like every other heading in the document (entry 96, Q406): the same `cs-` id, the same triangle, and never over an open card. The stranger reads the register under *Members* and *Alumni* alone — the per-subsection counts are unbuilt (Q508). Each status — its pile, its placeholder and its rows: §8's statuses table; your own ✋ 🖼️ 📧 card: §9's identity row. → why: DECISIONS.md, *The leaving rules before the register* (Q868–Q870, entry 95); *SURFACE's four long rules trimmed to their rulings* (Q1237). -- **F22 The rules about a membership stand above the register, and Proposals reads before Decisions**: 🤝 how people join, 💤 and 🥾 the two ways they leave, and only then *Members* and the list; and the whole Proposals run — 💡 ⚖️ 🏛️ ⏱️ — above ⏰ 👥 👤 👁️, 🍾 keeping its place last in `ORDER` though its clause opens Proposals, and 📝 last but one. → why: DECISIONS.md, *The leaving rules before the register* (Q865, Q871, Q617). +- **F22 The founding walk reads the Rules top to bottom** (#223, G6): 🪶 📍 📧 (✋ 🖼️ alongside), 🎩, ✒️ then 🛡️, 🌍; 🪪 🤝 💤 🥾 — the rules about a membership above the register, 🤝 how people join, 💤 and 🥾 the two ways they leave, and only then *Members* and the list; 💡 ⏱️ ⚖️ 👥 🏛️; 👤 👁️; ⏰ and then 🍾, 🍾 keeping its place last in `ORDER` though its clause opens the Schedule, and 📝 last but one. 💡 and ⚖️ are accepted for the founder at the start. → why: DECISIONS.md, *The leaving rules before the register* (Q865, Q871, Q617); #223. - **F23 The founder is asked to invite once the Membership rules stand**: the five rules of the section — 🪪 🤝 🎩 💤 🥾 — settled or handed to the membership, and no row on the register but the founder's. Then ✉️ stands in the founder's rail as a task. It is **derived and acknowledges nothing** — no OK, no `ACK_KEYS` key, nothing owed (F8) — it **blocks nothing** (F3), standing beside whatever the founding is asking next, and like the `one-voice` remedy it is counted neither as *something else being served* (F5/F18) nor as one of the *other tasks* that pace the founder's own delegated questions (F7): what is offered because the founder may want company must not withhold the questions or shut 🍾's last-resort door, so ✉️ and 🍾 stand side by side. Its card is the door's own, leading with the invitation box; the remedy's *why you are here* paragraph belongs to F19's case alone. It leaves at the first invitation or at 🍾, and 🍾 is never gated on one having been sent. → why: DECISIONS.md, *The founder is asked to invite once the Membership stands* (entry 181). @@ -451,8 +451,7 @@ Exceptions beyond §3: | the founder alone is served the delegated questions once nothing else is outstanding | F7 | an empty rail while the room fills is worse | Q408, Q413 | | 📧 post-save speaks in the second person | C10 | your own row | Y8 | | 🎩 ✋ 🖼️ 🪪 write no sentence | F13 | the answer is the list or a chip | — | -| 🌍 is asked sixth, ahead of Membership | F1 | it is the penultimate clause of the opening run | — | -| the locked 🎩 is read post-start: what stands as its first line, with its pill, and no other sentence and no row (Q1541 stage 3b) | C9 | a locked decision still has to be readable | CP9, CP11 | +| 🌍 is asked ahead of Membership | F1 | it is the last clause of the opening run, after the founder's (#223) | — | ## 9. The card kinds, the commit row, the composer @@ -489,9 +488,9 @@ Every card the surface draws: | 👑 question (the Text) | the founder's rail beside the clause — and the front tab of the clause's pile (1541.38: the strip led with the race's 💡) | ***Accept This Change?*** over the clause it rewrites | the membership-passed sentence, then the parked wording against the clause as one proposal block, *Proposed* or *Proposed by ‹name›* (Part 4 .8), under its speaker | none | none (1541.9) | 🛡️ refuse · ✒️ accept (CP5; 1541.38) | either power | the entry leaves | | park ⏳ (news; E36) | the ⏳ entry beside the clause, every seat but the Founder's and the author's | *Current text* (Part 4 .1) | one sentence, *Awaiting assent from the Founder 🛡️* — no wording, no speaker: they are the 👑 card's | none | none | OK while unread (`data-seen`, per seat); none once seen (1541.8 (a)) | OK | ⏳ until the park resolves, then the entry leaves; a race waiting behind the park keeps its own card, the waiting sentence a note (E36) | | news / owed OK | news entry ✔ | a settings card's news is the *watching* row's card (stage 3a) | as *watching*: the reason under the rule now standing, *Previous rule* beneath (Part 4 .11) — the change line retired (1564.5 (b)), the watch half too (Q1176) | none | none | OK | OK | grey; the clause keeps the line | -| gates 💡 ⚖️ | rail, hidden until 🍾, no subtitle (C13, Q1374); entry and tab read *Accept Proposals* / *Accept Voting* while owed, *Proposals* / *Voting* once accepted (Q1556 (14)) | drawn like a grant (Part 4 .24): labelled *Accept Proposals* / *Accept Voting* while owed, *Current rule* once accepted (.3, 6.1), over the Proposals clause the tab hangs on, never a title (Q1373) | none — label, line and Accept; no sentence names the commit (card review 2026-09-05, A5; T45, Q1129) | none | none | *Accept ✏️* on 💡, *Accept ⚖️* on ⚖️ (Q1501; Part 4 .24); none once accepted | the Accept (persisted) | the yours hue, sparkling in the rail, until accepted (Q1501); then grey ✔, gone from the rail | -| grants 🏛️ ✒️ 🛡️ | rail, staged, no subtitle (STYLE §4, card review 2026-09-02; C13); entry and tab read the *Accept …* words while owed, the power's name — *Founder Actions* · *Founder Veto* · *Constitutional Proposals* — once accepted (Q1556 (14)) | labelled *Accept Founder Actions* · *Accept the Founder Veto* · *Accept Constitutional Proposals* while owed (Part 4 .23), *Current rule* once accepted (.3, 6.1), over the Founded line the tab hangs on; no title head (Q1373) | what the power is, in Ed's 2026-09-02 sentences — no Founded line, no who-gave-it sentence (Q1170); 🏛️'s body about 🏛️ alone — what it is, that you are already a member, what activating it opens (Q1502; T45's one exception), `proposalsClause` staying the constitution's; the same accepted or not | none | none | *Accept ✒️* · *Accept 🛡️* · *Accept 🏛️* (1541.24), a click (Q1501; T44 amended for grants); none once accepted | the Accept → the flight | the yours hue, sparkling in the rail, until accepted (Q1501); then grey | -| 🍾 Begin | rail (founder) | labelled *Begin* (Part 6.8; Q1542), over the Proposals clause its tab hangs on | the batch, its first line what the power table was told to keep on the Text · the power table, one row per power-holding setting in document order (`BEGIN_ROWS`; Q1195 (c), R-098; F5): glyph and noun (the doors *Invites* / *Removals*; `machines`, retired, has no row), then ✒️ and 🛡️ as glyph toggles, no header row — plain for kept, the wallets' `--slash` strike for laid down, pressed to flip; struck and not a control where already promised away on the setting's own ✒️/🛡️ tab (a span since Q1541 stage 2; the tooltip says it comes back only there), read off the tab's own source; 📝 starts at *lay down*, every other row at *keep*, one direction only — *keep* never reclaims · the readiness readout (no *hold to begin* line since 2026-09-09 — the commit is a click and wears 🍾) | none — the toggle stands where the radio would (Q1181); no *Kept* / *Laid down* words | none — a toggle is undone by toggling it back (Q1556 (2)) | 🍾 (hold; T47), the Founder's alone, before the start — dark until it can be pressed, the reason its note, never in the body (Q1556 (3)): `one-voice` *Delegated questions need at least two responses.*; else members owing answers *Waiting for x members to answer questions.* (Part 4 .22); else `invitation-open` *Waiting for invited members to arrive.*; no note for `judge-gate` or `deps-unsettled` (F5, F19) | the hold | grey, restating the batch, no row (1541.8 (a)); the table gone | +| gates 💡 ⚖️ | rail, hidden until 🍾, no subtitle (C13, Q1374); entry and tab read *Accept Proposals* / *Accept Voting* while owed, *Proposals* / *Voting* once accepted (Q1556 (14)) | drawn like a grant (Part 4 .24): labelled *Accept Proposals* / *Accept Voting* while owed, *Current rule* once accepted (.3, 6.1), over the gate's own Proposals clause — ✏️'s for 💡, ⚖️'s for ⚖️ (G3) — never a title (Q1373) | none — label, line and Accept; no sentence names the commit (card review 2026-09-05, A5; T45, Q1129) | none | none | *Accept ✏️* on 💡, *Accept ⚖️* on ⚖️ (Q1501; Part 4 .24); none once accepted | the Accept (persisted) | the yours hue, sparkling in the rail, until accepted (Q1501); then grey ✔, gone from the rail | +| grants 🏛️ ✒️ 🛡️ | rail, staged, no subtitle (STYLE §4, card review 2026-09-02; C13); entry and tab read the *Accept …* words while owed, the power's name — *Founder Actions* · *Founder Veto* · *Constitutional Proposals* — once accepted (Q1556 (14)) | labelled *Accept Founder Actions* · *Accept the Founder Veto* · *Accept Constitutional Proposals* while owed (Part 4 .23), *Current rule* once accepted (.3, 6.1), over the clause the tab hangs on — ✒️'s and 🛡️'s in the lead, 🏛️'s among the Proposals (G1, G3); no title head (Q1373) | what the power is, in Ed's sentences (#223) — no who-gave-it sentence (Q1170); 🏛️'s about 🏛️ alone — what it is, one at a time, passing only when all members agree (Q1502, #223); the same accepted or not | none | none | *Accept ✒️* · *Accept 🛡️* · *Accept 🏛️* (1541.24), a click (Q1501; T44 amended for grants); none once accepted | the Accept → the flight | the yours hue, sparkling in the rail, until accepted (Q1501); then grey | +| 🍾 Begin | rail (founder) | labelled *Begin* (Part 6.8; Q1542), over 🍾's own clause in the Schedule (G5) | the batch, its first line what the power table was told to keep on the Text · the power table, one row per power-holding setting in document order (`BEGIN_ROWS`; Q1195 (c), R-098; F5): glyph and noun (the doors *Invites* / *Removals*; `machines`, retired, has no row), then ✒️ and 🛡️ as glyph toggles, no header row — plain for kept, the wallets' `--slash` strike for laid down, pressed to flip; struck and not a control where already promised away on the setting's own ✒️/🛡️ tab (a span since Q1541 stage 2; the tooltip says it comes back only there), read off the tab's own source; 📝 starts at *lay down*, every other row at *keep*, one direction only — *keep* never reclaims · the readiness readout (no *hold to begin* line since 2026-09-09 — the commit is a click and wears 🍾) | none — the toggle stands where the radio would (Q1181); no *Kept* / *Laid down* words | none — a toggle is undone by toggling it back (Q1556 (2)) | 🍾 (hold; T47), the Founder's alone, before the start — dark until it can be pressed, the reason its note, never in the body (Q1556 (3)): `one-voice` *Delegated questions need at least two responses.*; else members owing answers *Waiting for x members to answer questions.* (Part 4 .22); else `invitation-open` *Waiting for invited members to arrive.*; no note for `judge-gate` or `deps-unsettled` (F5, F19) | the hold | grey, restating the batch, no row (1541.8 (a)); the table gone | | 🥂 The Close | rail, pinned, per member | the sentence, labelled ***Add your closing comment*** while unsigned, *The Close* after | the moment once · the batch · your closing comment; one batch line per thing the close did, *‹n› motions were still open and did not pass* — either route's motions the clock found running (Q1450; E41 raises no card for them) — omitted at zero | none | nothing (Y20, Q1527) | OK = sign, while unsigned — the signature acknowledges everything else owed (C9) | OK → the farewell | — | | 🪪 Admissions | band tab, above 🤝 | the setting row's: *Current rule* over the price as it stands, with its pill (entry 94) — or the *watching* and composer rows by reader | the three options, each the clause it would set (Q1109), what stands omitted | Choose this (CP2) | 🗑️ dark until chosen | ✒️ | ✒️ | grey | | ✉️ Invite | the *Applications for Membership* pile, from the birth (Q1557); ✒️/🛡️ power tabs in its stack | labelled its ask, *Invite a Member* (Part 4; 1541.46 (a)); first line the subsection's rows — every vote still running on letting somebody in — or *There are no applications at the moment.* (1541.25 (a)) | where the viewer's word sends (Y24): one multi-line address box (Q1166), placeholder *name@example.com* / *one address per line*, no explanatory paragraphs, and the reason box where the Founder may also put it to the membership; a member's seat (the composer): 🪪's clause as the body, the same box and no *Invite* button (Q1187), one motion per address — at ✏️ price each line its own motion and ✏️, at 🏛️ price a single line — and the reason box; a refusal sentence under the box (Y25); the `one-voice` remedy leads (F19) · the plain task once the Membership rules stand (F23) | — | 🗑️ dark until an address is typed (1541.9); puts the card back, or discards the motion | the commit says which act (Q1166): ✒️ where the viewer's word sends (Y24), the route's ✏️ hold · 🏛️ hold where it composes at 🪪's price (🏛️ dark while yours is out), both where the viewer holds both, ✒️ immediately left (Q1154); ✒️ and 🏛️ dark over the empty box (Part 4 .18); the row follows the typing in place, never a render under the caret | commit, keeping what is typed | — | @@ -500,7 +499,7 @@ Every card the surface draws: | `adm:` an admission | the *Applications for Membership* pile, one card per applicant; a task at *assembly* and *proposal*, news at *pen* | labelled ✉️'s ask, *Invite a Member* (1567.3); first line the subsection's rows, the applicant's (face and name) among them while still asking; at *pen* they are a member from the asking, so the first line is the subsection as it stands (its sentence where nobody else asks), the body saying they joined, and the address is the name (Q912 (b)) | by 🪪's price, the address the body and the applicant's own words the sealed reason: *proposal* — *Admit them* / *Keep the membership as it is* / Indifferent, with the abstention clock (Q1460 (c)); *assembly* — *The membership stays as it is.*, ***Proposed*** *‹name› joins the membership.*, Indifferent, no blind note (issue #19); *pen* — one sentence saying they are already a member | *Prefer this* · Indifferent (CP2, CP4) | none (Q1500, 1541.9) | ✓ at *proposal*, 🏛️ at *assembly*; OK where it is news | ✓ / 🏛️ / OK | the race's own marks | | 🤝 Applications | band tab | the setting row's: *Current rule* over whether strangers may apply, with its pill (the cost is 🪪's) — or the *watching* and composer rows by reader | the other rung, *Invitation only* / *Anyone may apply* as the clause each would set, what stands omitted | Choose this (CP2) | 🗑️ dark until chosen | ✒️ | ✒️ | grey | | identity ✋ 🖼️ 📧 | your own row — the Founded line for a clerk, who has none (Q759); the birth run for 📧; the card opens in your row's place inside the list, at its foot (F21, entry 188, Q1316), *Applications for Membership* still below it; a folded *Members* renders its rows to hold it | labelled its ask — *Choose Your Name* · *Choose Your Picture* · *Enter Your Email* (Part 4; 1541.46 (a)) — over your row as the Members list draws it, its face and leading kept (grammar O6 (b)) | option blocks (card review 2026-09-02), what stands omitted (Q620) — it is your row, the first line, and where an answered Anonymous stands its block is left out too (#176): ✋ the name composer · *Anonymous* (Q1164: inert until typed; a blank is no answer); 🖼️ *Anonymous* first (Q1165: initials where you have a name), drawn as the anonymous avatar, never the word (card review 2026-09-05, A7) · *Upload an image* · *Pick an emoji*, the picker opening when its block is chosen; no labels, preview or helper paragraphs / 📧, once an address stands, that address on the first line under your row, and below the hairline one block: a new address's field, the one sent waiting in it until verified (issue #169); before one stands, the field alone | Choose this, per block (CP2); the first line wears the chosen radio wherever something stands — 📧's address, ✋'s name or answered blank, 🖼️'s picture or answered Anonymous — pressed while nothing new is chosen, choosing it again cancelling the change (1541.47, #169, #176); a card never answered has none (F6); the applicant's ✋ 🖼️ are not given it, nothing of theirs standing before Submit (Ed, 2026-10-02, #176) | 🗑️ puts back, never clears (1541.9) | ✓ (T47), dark over an answer that stands (#176); 📧: 🪶 at the birth, 📨 while unverified, ✓ once verified — dark over the empty box a verified address leaves (Part 4 .18); frozen after the close, the row and all beneath it (1541.28 (a)) | commit · send; 📧 re-opens on refusal | the row | -| 🎩 | the members paragraph — *Members* writes no sentence (F13), so its first line is its own (P14's exception) | labelled *Is the Founder a Member?* until the Founder answers (1541.46 (a)), *Current rule* after; first line *The Founder is part of the membership.* / *The Founder is not part of the membership.* — the fact, no consequences (card review 2026-09-02) — with its pill, or *The Founder is deciding whether to be part of the membership.* before an answer | the Founder's before 🍾: the other sentence as an option block, nothing pressed on open (F6); locked at 🍾 and from any other seat: first line and pill alone (CP11) | Choose this, and the pill | 🗑️ dark until chosen | ✒️ pre-start; locked: no row (CP9) | ✒️ | locked | +| 🎩 | the Founded line until 🍾, hidden after it (G2, G2b, #223) — no sentence of its own (F13), so its first line is its own (P14's exception) | labelled *Is the Founder a Member?* until the Founder answers (1541.46 (a)), *Current rule* after; first line *The Founder is part of the membership.* / *The Founder is not part of the membership.* — the fact, no consequences (card review 2026-09-02) — with its pill, or *The Founder is deciding whether to be part of the membership.* before an answer | the Founder's before 🍾: the other sentence as an option block, nothing pressed on open (F6); from any other seat: first line and pill alone (CP11) | Choose this, and the pill | 🗑️ dark until chosen | ✓ pre-start — answered above ✒️ in the walk, so no power (#223, G6); from any other seat: no row (CP9) | ✓ | hidden at 🍾 | | 📝 Text — the text section | the riding tab on the constitution's gutter from the save (K31), never the rail; it rests (Q1516 (2)), centred on the document's first line box as measured (`rideLine`), scrolling away with the heading in both modes; in edit mode the pile is the strip, ✒️ 🛡️ pressable, 📝 alone active (M12); **in read mode, beside the first clause's own tabs, it stacks above them** (#194, Ed 2026-10-02: *it should tuck into the card*) — the pile at rest, the strip of a card open there, 📝 a tab and the strip's gap above the first tab as measured (`ridePile`), and beside a card it takes a resting strip tab's tuck, never the active treatment (card-audit P38; `scripts/repro/ride-tab-tuck.mjs`); it carries the draft's count while one is pending. After 🍾, the floating 📝 (`edit-door`, `#editdoor`, Q1335): read mode only, for a member who may propose (C9 — none for the stranger, applicant, clerk, a member whose ✏️ is unacknowledged, or a closed document); the window's bottom-right corner, `--s5` off both edges, never the proposal row's place (Q1516 (3)); 4.5rem across, glyph at B6's size (Q1516 (4); card-audit D1, D4); may overlap the text (1541.17; P30's exception); the tab's tooltip, pressing it pressing the tab; sparkling in the *yours* hue until first pressed, reduced motion too (Q1516 (1), Q1501, a11y-audit A17), remembered per seat; the read-mode runway `#runway` | the section heading, the 📝 tab, the ✒️ 🛡️ power tabs beneath while held (Q1404) — no threshold line | the prose column. Read mode: no caret, no row, a click beats the tab. Edit mode: the column lifted onto a card (`--shadow-xl`) at the decision card's width, drawn by a pseudo-element so the column does not move, its outline `--measure-gutter` above the first line and running off the page's foot, the runway on the last segment alone (Q1336), `.doc`'s bottom border stepping aside; the caret live; a trailing gap block (*Start a new clause here.*); the viewport the card, tab at its top, row at its foot. The proposal-row: stuck to the window's foot, no ground, click-transparent between its buttons, obeying §9.1, its middle *2 places changed* (Q1089); circles `--s5` off the foot, 4.5rem across (1569.2), overlapping the text (1569.2 (c); P30); three faces (Q1380, Q1382) — the 📝 door; 🗑️ *discard all* · ✏️ *submit all*, the hold itself (Q1382); ❄️? · ✓ over an open patch site card, no 🗑️ (Q1500; `#patchrow`). The lane controls: one strip, B · *I*, at the card's top right (Q1294 (b)), sticky, riding with a ground and the tab's shadow, acting on the selection in the lane holding the caret, disabled where none, writing or removing the markdown marks (K22, Q1467); no lane controls of their own; none outside edit mode. The column is the source in edit mode (Q1467): each block its markdown line, marker and marks editable, dimmed and selectable, in the document's face at the marker's rank; the `[]` toggle retired. Before 🍾 the same strip (Q1313, `#prosectl`) and two views — read draws bold, italic and code and holds a typed `# ` or `- ` as the block's class (`relabel`, `textDivs`); edited, the exact characters, every rank kept (`srcDivs`), the caret carried as a source offset; `proseText` the one read-out for the stash, ✒️ and 🍾 | — | 🗑️ discards the whole draft; before 🍾 puts the column back to the text as it stands; with nothing changed, leaves edit mode (K31, #193) | before 🍾 the founder's ✒️ — `confirm-starting-text`, every press, no OK (Q1080); after, ✏️, with ✒️ beside it, pen first, where the Founder holds the pen (entries 160–161) — with nothing changed the way out of edit mode (K31, #193), once a place has changed opening the `editing-card`, which has no commit of its own in edit mode (1541.22 (a)); with that card open the row's glyph is its commit, the same flight by the draft's id (Q1296–Q1297) | 📝 again, a click on nothing outside the card (C2; the column, strip, row, riding tab and editing cards are inside; Q1315, both eras), or another card opening — the draft kept | grey; the ✒️ 🛡️ tabs beneath stay, where the pen is laid down; the tab carries the draft's count while one is pending | | the applicant's five | the applicant's rail | placeless — nothing of theirs is in the document yet — so each is labelled by its title and opens at the band's top, its first line what it asks: its opening sentence, or its address box (one drawing with the stranger's two); 🖼️, all blocks, carries the strip alone | apply: why · what the application holds so far — name, picture, words, each as given or not yet given (Q1366) · the needed/optional line; email (why · the address; 📧 in the row, Q1399) · name · picture · words — every one born blank, whoever the address was (Q1366: a departed seat's name and face are the register's; Submit is the whole identity it gives) | — | none but 🖼️'s, dark until a choice is made (1541.9); a submitted application has no row and no withdraw (1567.11; 1541.9 (b) does not apply) | *Begin* (Y20) → *Submit*, words on the glyph button's height; 📧 dark until a valid address; ✓ closes and keeps (Q1366), dark until the card is done | Submit · ✓ | ✏️ yours | | the stranger's two | the door's rail | placeless — the title the label, the first line what it asks: 📧 Log In the field itself (card review 2026-09-05, A5), Apply its why | 📧 Log In: the field alone, placeholder *you@example.com*; Apply: why · the address | — | 🗑️ dark until an address is typed, putting it back to empty (1541.9) | 📧 at the row's right on both (T47), dark over a malformed address (reading 1194; Apply since Q1390; Part 4 .18) | send | — | diff --git a/design/STYLE.md b/design/STYLE.md index 3a5d7e56..30f6cb57 100644 --- a/design/STYLE.md +++ b/design/STYLE.md @@ -52,7 +52,7 @@ Glyph names are stable (tabulated 2026-09-07, Q1208; the founding order's own gl | visibility | 🌍 | — | | text | 📝 | 📄 until backlog 204; the applicant's words are 👋 | | founder-is-member | 🎩 | — | -| proposing gate | 💡 | keeps its glyph on the card titled *Proposals* — Ed's ruling of 2026-09-01 (Q1121), the gate being what the tab and the preamble stack show | +| proposing gate | 💡 | keeps its glyph on the card titled *Proposals* — Ed's ruling of 2026-09-01 (Q1121), the gate being what the tab on the ✏️ clause shows (#223) | | voting gate | ⚖️ | — | | crown | 👑 | — | | horn | 📯 | — | @@ -104,10 +104,11 @@ Glyph names are stable (tabulated 2026-09-07, Q1208; the founding order's own gl redesign (1541.25 (a); the words 1567.2), because the door's card heads with the same line: *There are no applications at the moment.* · *Nobody is proposed for removal.* · *Nobody has left.* — SURFACE §8's statuses table (F21). -- T10's second exception, the birth's title clause, where the Founder meets - the word for the first time, says *(that’s you!)* once and only there (Ed, - 2026-08-27, entry 140) — only the Founder ever sees the birth, and after the - save the same clause is byte-identical to what every other reader gets. +- T10's second exception, the birth's two founder clauses, where the Founder + meets the word for the first time, say *(that’s you!)* there and nowhere else + (#223, Ed 2026-10-03, G1's *Before the save*, taking it from the title clause + of entry 140) — only the Founder ever sees the birth, and after the save + neither clause carries it. - **The membership takes a plural verb** (Ed, 2026-09-02, Q1150): *the membership will decide*, on every delegation sentence and anywhere else the word stands for the people rather than the count. **The tense is future** @@ -197,8 +198,8 @@ Glyph names are stable (tabulated 2026-09-07, Q1208; the founding order's own gl you · Ran out of time* — nothing was decided, so nothing ranks it. *Refused* is the Founder's 🛡️ alone; a wording the room closed early is *Rejected* (T8). **Nor does a heading-over-text card carry a title head** (Ed, - 2026-09-15, Q1373): the grants ✒️ 🛡️ open as the strip, the paragraph and - OK, and 🏛️ 💡 ⚖️ head with the Proposals clause, their rule. The card's name + 2026-09-15, Q1373): every grant and gate — ✒️ 🛡️ 🏛️ 💡 ⚖️ — heads with + the clause its tab hangs on, its rule (#223). The card's name survives on the rail entry, the tab tooltip and the record. ## 5. Bodies and notes @@ -208,7 +209,7 @@ Glyph names are stable (tabulated 2026-09-07, Q1208; the founding order's own gl - **T21 · Read-only copy must survive the spec it summarises.** "Fixed for the life of the document" predated motions and was false. **The lockline goes from the settings cards** (1541.14 (a); Q1541 stage 3a): who chose a rule is - its pill (T48), and what changing it takes is the constitution's preamble + its pill (T48), and what changing it takes is the Proposals clauses and the card's own commits. - **T22 · Section headings carry no intro prose.** - **T17 · The price is said in words exactly once**, at the act: *the edit is @@ -267,7 +268,7 @@ The rules the audit log (§7) audits against, lifted from CLAUDE.md's glossary a | T7 | A power option is a proposal block, not a radio label: the rule at document size with a full stop, its consequence as a note, a lane bar reading *Choose this / Chosen* | `powerLane` | | T8 | The power clause is one sentence in Ed's vocabulary: a member proposes; the membership passes and rejects; the Founder assents, refuses and amends. *pass* not *carry*; *refuse* is the Founder's word. The Founder **also proposes**, and the word for it is the membership's — a Founder putting a change to the room does the same thing a member does and is said to in the same words (entry 161) | `PW_PHRASE`; the 👑 question's buttons are *Refuse / Accept*; the pair's second commit is *✏️ Propose* / *🏛️ Ask all members*, unchanged | | T9 | One voice, only the object changing: per-setting phrases only where the generic would be untrue (policy, text) | `PW_PHRASE`, `PW_OPTS`, `PW_NOUN` | -| T10 | §3 · The document reads identically to every reader; "you" belongs to tasks and cards | *The Founder is checking their email for a link.* · *The Founder (that’s you!) may amend this at will.* (birth only) | +| T10 | §3 · The document reads identically to every reader; "you" belongs to tasks and cards | *The Founder is checking their email for a link.* · *The Founder (that’s you!) may ✒️ change anything at will.* (birth only) | | T11 | §3 · A paragraph states the document's rule, never your own answer | a delegated clause's *(x of y have answered so far)*, never a value | | T12 | §2 · A count, never a direction | *4 of 9 have answered* | | T13 | §2 · A value, never a guess | *The Founder is deciding [x]* | diff --git a/design/copy.js b/design/copy.js index 206fc1d6..bb6677ae 100644 --- a/design/copy.js +++ b/design/copy.js @@ -1152,20 +1152,6 @@ window.COPY = (function () { closed: (time, day, n) => 'Proposals closed at ' + time + ' on ' + day + '. ' + n + (n === 1 ? ' member has signed.' : ' members have signed.'), }, - // the Proposals preamble (Y21): the gates' fragments, composed - preamble: { - beforeBegin: 'When the document begins, members may propose changes to rules and vote on proposals.', - voteOnly: 'Members may vote on proposals.', - voteWhenDecided: 'Members may begin voting on proposals when all the rules have been decided.', - proposeAtBegin: ' They may propose changes to rules when the document begins.', - proposeLead: 'Members may propose changes to rules ', - onceAnswered: 'once they have answered the questions the Founder delegated', - asArrive: 'as soon as they arrive', - andVote: ', and may vote on proposals.', - voteTail: '. They may begin voting on proposals when all the rules have been decided.', - passOrdinary: 'A proposal ✏️ passes once it is preferred by enough of the membership, and by more than prefer the current text.', - passConstitutional: 'A constitutional proposal 🏛️ passes only when all members agree.', - }, // 👥's rule, in Ed's own words (Q1439, ruling t, 2026-09-18: *A proposal // cannot pass until it is preferred by at least 50% of the membership // (5 of 10).*). It read *At least 50% (5 of 10) of the membership must diff --git a/design/tools/setup-probe.js b/design/tools/setup-probe.js index 9cd6afd3..5cccc20a 100644 --- a/design/tools/setup-probe.js +++ b/design/tools/setup-probe.js @@ -198,6 +198,28 @@ ['type-email', () => typeInto('.setupcard input[type="email"]', 'ada@example.org')], ['send-verify', () => click('.setupcard [data-confirm]')], ['click-magic-link', () => click('[data-act="clickmail"]')], + // **The walk reads the Rules top to bottom** (#223, G6, Ed 2026-10-03): + // 🎩 on the Founded line, ✋ 🖼️ beside it, then ✒️ and 🛡️ on their own + // clauses, 🌍, Membership, Proposals, Anonymity, the Schedule + ['open-hat', () => openTab('hat')], + ['choose-hat', () => click('.setupcard [data-set="hatPick"][data-val="member"]')], + // 🎩 commits through the one commit control like every other setting + // (Q522, 2026-08-21), with ✓ since #223: it is the walk's second step, + // answered before ✒️ is accepted (G6) + ['confirm-hat', () => click('.setupcard [data-confirm]')], + ['open-name', () => openTab('myname')], + ['type-name', () => typeInto('.setupcard input[data-txt="myname"]', 'Ada Lovell')], + ['confirm-name', () => click('.setupcard [data-confirm]')], + // 🖼️ is step 10 of the founding order and the probe had never opened it + // (Q732, 2026-08-23) — which is how an emoji face rendered at 7px on the + // Founded line for four days with every check green. + ['open-picture', () => openTab('mypic')], + // the picker opens when its block is chosen since the card review of + // 2026-09-02 (Q1165: 🖼️ is Anonymous · Upload an image · Pick an emoji), + // so the block is chosen first and the fox clicked where it stands + ['choose-emoji-block', () => click('.setupcard [data-set="picPick"][data-val="emoji"]')], + ['choose-emoji', () => click('.setupcard .avopt[data-pic="e🦊"]')], + ['confirm-picture', () => click('.setupcard [data-confirm]')], ['open-the-pen', () => openTab('grant-pen')], ['ok-the-pen', () => click('.setupcard [data-ok]')], // 🛡️ takes its own place in ORDER, right behind the pen, and had never @@ -261,24 +283,6 @@ }], ['choose-join', () => click('.setupcard [data-set="joinBy"][data-val="invite"]')], ['confirm-applications', () => click('.setupcard [data-confirm]')], - ['open-hat', () => openTab('hat')], - ['choose-hat', () => click('.setupcard [data-set="hatPick"][data-val="member"]')], - // 🎩 commits through the one commit control like every other setting - // (Q522, 2026-08-21); it used to carry a data-hatgo of its own - ['confirm-hat', () => click('.setupcard [data-confirm]')], - ['open-name', () => openTab('myname')], - ['type-name', () => typeInto('.setupcard input[data-txt="myname"]', 'Ada Lovell')], - ['confirm-name', () => click('.setupcard [data-confirm]')], - // 🖼️ is step 10 of the founding order and the probe had never opened it - // (Q732, 2026-08-23) — which is how an emoji face rendered at 7px on the - // Founded line for four days with every check green. - ['open-picture', () => openTab('mypic')], - // the picker opens when its block is chosen since the card review of - // 2026-09-02 (Q1165: 🖼️ is Anonymous · Upload an image · Pick an emoji), - // so the block is chosen first and the fox clicked where it stands - ['choose-emoji-block', () => click('.setupcard [data-set="picPick"][data-val="emoji"]')], - ['choose-emoji', () => click('.setupcard .avopt[data-pic="e🦊"]')], - ['confirm-picture', () => click('.setupcard [data-confirm]')], /* **The rest of the founding, card by card** (Q915, Ed 2026-08-26). * `npm run probe-coverage` reported the scenario opened 12 of the * founding's cards, and the thirteen it never opened were exactly @@ -310,30 +314,32 @@ ['open-removal', () => openTab('removal')], ['choose-removal', () => click('.setupcard [data-set="removal"][data-val="proposal"]')], ['confirm-removal', () => click('.setupcard [data-confirm]')], - // 🏛️ is a grant, so it OKs rather than commits — and it is news to a - // founder who is a member, which this one is (🎩 above) - ['open-the-voice', () => openTab('grant-voice')], - ['ok-the-voice', () => click('.setupcard [data-ok]')], // ⏱️ is one number since Q1160 (Ed's card review, 2026-09-02): the // interval, typed inside the sentence — typing claims the rung (F6) ['open-rate', () => openTab('rate')], ['type-rate-drip', () => typeInto('.setupcard input[data-num="dripN"]', '45')], ['confirm-rate', () => click('.setupcard [data-confirm]')], - ['open-ending', () => openTab('ending')], - ['choose-ending', () => click('.setupcard [data-set="ending"][data-val="perpetual"]')], - ['confirm-ending', () => click('.setupcard [data-confirm]')], // 👥 collects form and number together since Q1162: the count block is // its own rule sentence, the number typed inside it ['open-quorum', () => openTab('quorum')], ['choose-quorum-form', () => click('.setupcard [data-set="quorumForm"][data-val="count"]')], ['type-quorum', () => typeInto('.setupcard input[data-num="quorumN"]', '3')], ['confirm-quorum', () => click('.setupcard [data-confirm]')], + // 🏛️ is a grant, so it OKs rather than commits — and it is news to a + // founder who is a member, which this one is (🎩 above); its clause is + // the Proposals section's last, after 👥 (#223, G3, G6) + ['open-the-voice', () => openTab('grant-voice')], + ['ok-the-voice', () => click('.setupcard [data-ok]')], ['open-authorship', () => openTab('authorship')], ['choose-authorship', () => click('.setupcard [data-set="authorship"][data-val="sealed"]')], ['confirm-authorship', () => click('.setupcard [data-confirm]')], ['open-judgments', () => openTab('judgments')], ['choose-judgments', () => click('.setupcard [data-set="judgments"][data-val="after"]')], ['confirm-judgments', () => click('.setupcard [data-confirm]')], + // ⏰ is the Schedule's, after 👁️ (#223, G5, G6) + ['open-ending', () => openTab('ending')], + ['choose-ending', () => click('.setupcard [data-set="ending"][data-val="perpetual"]')], + ['confirm-ending', () => click('.setupcard [data-confirm]')], // 📝 is the door into edit mode (backlog 204), not a card: it lifts the // column, the founder writes, the row's ✒️ saves, and 📝 again leaves ['open-text', () => openTab('text')], @@ -364,18 +370,14 @@ // untouched and `seat-founder` below finds the surface it always did. ['fast-forward', () => click('#devff')], ['seat-bo', () => setSeat('1')], - // …and they are reached through the pile, not by key. 💡 ⚖️ and 🏛️ stand - // behind 🍾 on the Proposals preamble's one paragraph (`PROPOSAL_CHIPS`), - // and `pileHtml` marks every chip after the first `inert` and emits no - // `data-tab` on it — so `openTab` cannot address either gate from the - // closed page, on any seat. Opening the front of the stack draws the - // strip live, and there the tabs carry their keys. No OK is pressed: - // opening is what coverage measures, the open card is what the region - // hashes compare, and Bo's own acknowledgements are left alone so - // `seat-founder` below finds the surface it always did. - ['open-preamble-bo', () => openTab('begin')], - ['open-canpropose', () => click('.setupcard .chipcol [data-tab="canpropose"]')], - ['open-canjudge', () => click('.setupcard .chipcol [data-tab="canjudge"]')], + // …and each by its own tab since #223 (G3): 💡 on the ✏️ clause, ⚖️ on + // the ⚖️ clause, each the front of its own pile — the preamble's one + // stack (`PROPOSAL_CHIPS`), which hid both behind 🍾, is retired. No OK + // is pressed: opening is what coverage measures, and Bo's own + // acknowledgements are left alone so `seat-founder` below finds the + // surface it always did. + ['open-canpropose', () => openTab('canpropose')], + ['open-canjudge', () => openTab('canjudge')], ['seat-founder', () => setSeat('0')], ]; diff --git a/scripts/journey-walk.mjs b/scripts/journey-walk.mjs index 4f7c543b..cfbeebe9 100644 --- a/scripts/journey-walk.mjs +++ b/scripts/journey-walk.mjs @@ -258,12 +258,16 @@ say('birth · saved at ' + page.url()); // with no power at all, so they stand at the save and block nothing (F2, F3). // **And 🏛️ is the save's too, for a founder who is a member** (Q1365, Ed // 2026-09-15: *you should get a 🏛️ grant when you first become a member*) — a -// grant is news and blocks nothing, so it stands beside the pen from the save. +// grant is news and blocks nothing, so it stood beside the pen from the save. +// **Since #223 the walk reads the Rules top to bottom** (G6, Ed 2026-10-03): +// 🎩 on the Founded line is the save's question, answered with no power, ✒️ +// arrives once it is answered, and the founder-member's 🏛️ at its own place +// after 👥 — an invitee's and an applicant's still on arrival. const atSave = await rail(); -const saveWant = ['grant-pen', 'grant-voice', 'myname', 'mypic']; +const saveWant = ['hat', 'myname', 'mypic']; const saveOk = JSON.stringify([...atSave].sort()) === JSON.stringify(saveWant); say('at save · rail ' + JSON.stringify(atSave) + - (saveOk ? '' : ' FAIL: expected the pen and the voice with ✋ and 🖼️, and nothing else')); + (saveOk ? '' : ' FAIL: expected 🎩 with ✋ and 🖼️, and nothing else')); if (!saveOk) stuck.push('rail at save'); // **The column carries the document's name from the save** (backlog 33, Ed: // *immediately after the birth, when my named document opens for the first @@ -998,7 +1002,8 @@ const secondSeatPreBegin = async () => { if (owed.length) stuck.push('pre-Begin acks in the member seat: ' + owed.join(',')); /* **…and 🏛️ reaches them as *Accept Constitutional Proposals*** (Q1502, * Ed 2026-09-22; the rail matching the card since Q1556 (14)): a body - * about 🏛️ alone, and the commit *Accept 🏛️* (Q1541.24). Read + * about 🏛️ alone, in Ed's words since #223, and the commit *Accept 🏛️* + * (Q1541.24). Read * and closed, never pressed — what the OK then opens is the member * questions walk's. */ const voice = await guestPage.evaluate(async () => { @@ -1016,7 +1021,7 @@ const secondSeatPreBegin = async () => { }); await guestPage.waitForTimeout(400); const voiceOk = !!voice && voice.word === 'Accept 🏛️' && /^Accept Constitutional Proposals\b/.test(voice.title || '') && - /You are already a member/.test(voice.body || ''); + /Members may only make one at a time, and it passes only when all members agree to it\./.test(voice.body || ''); say('activate · ' + (voiceOk ? 'the member’s 🏛️ grant reads “' + voice.word + '”, titled ' + voice.title : 'FAIL: ' + JSON.stringify(voice))); if (!voiceOk) stuck.push('the member’s 🏛️ grant (Q1502)'); diff --git a/scripts/probe-coverage.mjs b/scripts/probe-coverage.mjs index d147ff4d..0c87239d 100644 --- a/scripts/probe-coverage.mjs +++ b/scripts/probe-coverage.mjs @@ -46,8 +46,9 @@ const EXEMPT = { // 💡 and ⚖️ were exempted here on 2026-09-01, when SURFACE Y27 — **a gate // never withholds from the seat that set it** — took both off the founder's // rail and left the pair covered by nothing. The exemption is spent (F20): - // the founding scenario now opens both on Bo's seat, through the preamble - // pile, because a member is served them unacknowledged. An exemption is + // the founding scenario now opens both on Bo's seat, each by its own tab on + // its own Proposals clause (#223), because a member is served them + // unacknowledged. An exemption is // honest only while no seat can reach the card, and one could. }; diff --git a/scripts/slider-walk.mjs b/scripts/slider-walk.mjs index bf55d7e1..fa1b85c5 100644 --- a/scripts/slider-walk.mjs +++ b/scripts/slider-walk.mjs @@ -257,13 +257,16 @@ for (const key of ['quorum']) { // Ed's own sentence since Q1439 (ruling t, 2026-09-18, re-wording ruling // p): the quorum counts the members who *prefer* the proposal to the // current text, so *must vote on* is gone — and the verb is **pass**, - // STYLE T8's, with the emphasis on the bar. The share block ends in *of the - // membership*, the count block in *members*, so the assertion is the shared - // opening and then each block's own ending. + // STYLE T8's, with the emphasis on the bar. Since #223 (G4, Ed 2026-10-03 + // 17:10) it is the pass rule: *A ✏️ proposal passes once at least …*, the + // share block saying *of the membership*, the count block *members*, and + // both ending on the most preferred option — so the assertion is the shared + // opening, each block's own middle and the shared ending. check('each block is the rule with its number inline', born.picks.every((p) => p.box && - /^A proposal ✏️ cannot pass until it is preferred by at least/.test(p.label) && - (p.val === 'share' ? /of the membership\.$/ : /members\.$/).test(p.label)), + /^A ✏️ proposal passes once at least/.test(p.label) && + (p.val === 'share' ? / of the membership/ : / members have voted for it/).test(p.label) && + /have voted for it and it’s the most preferred option\.$/.test(p.label)), born.picks.map((p) => p.label.slice(0, 50)).join(' | ')); check('born untouched', !born.picks.some((p) => p.on) && born.picks.every((p) => p.box.value === ''), diff --git a/scripts/spec-check.mjs b/scripts/spec-check.mjs index 8751a888..e02f5b50 100644 --- a/scripts/spec-check.mjs +++ b/scripts/spec-check.mjs @@ -930,7 +930,7 @@ function checkOrder(pm) { else { const clauseKeys = [...clausesLit[1].matchAll(/'([a-z-]+)'/g)].map((m) => m[1]); const prose = read('SURFACE.md').split('\n').find((l) => l.includes('the five clauses wearing')) || ''; - const run = ((prose.match(/the five clauses wearing ([^·)|]+)/) || [, ''])[1] || '').trim(); + const run = ((prose.match(/the five clauses wearing ([^·)|—]+)/) || [, ''])[1] || '').trim(); const want = clauseKeys.map((k) => glyphs[k] || '?').join(' '); if (run !== want) find('order', `SURFACE's Proposals run is '${run}', PROPOSAL_CLAUSES is '${want}'`); for (const k of clauseKeys) if (!pm.ORDER.includes(k)) find('order', `PROPOSAL_CLAUSES names '${k}', which is not in ORDER`); From 8257fbad010af2f4ddebcc479466071a4ef99e90 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 3 Oct 2026 17:39:57 +0000 Subject: [PATCH 4/8] Re-freeze the copy goldens for #223's copy (copy-freeze) The grant bodies, the setting names, the doors' sentences, the founder, Proposals and Schedule clauses, the quorum as the pass rule, and the retired Proposals preamble. Nothing else moved. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018T9ZX2oB2xxzsK4Tgxpya7 --- design/tools/card-copy.golden.json | 308 +++++++++++---------------- design/tools/copy-source.golden.json | 67 +++--- 2 files changed, 160 insertions(+), 215 deletions(-) diff --git a/design/tools/card-copy.golden.json b/design/tools/card-copy.golden.json index 7be6173f..b276ffcd 100644 --- a/design/tools/card-copy.golden.json +++ b/design/tools/card-copy.golden.json @@ -1,6 +1,6 @@ { "meta": { - "cards": 276, + "cards": 274, "walks": [ "answers", "charter", @@ -210,9 +210,9 @@ }, "begin": { "eyebrow": "Begin", - "head": "When the document begins, members may propose changes to rules and vote on proposals.A proposal ✏️ passes once it is preferred by enough of the membership, and by more than prefer the current text.A constitutional proposal 🏛️ passes only when all members agree.", + "head": "The members are waiting on the Founder to begin.", "lock": "", - "body": "What beginning does, all at once.The Founder lays down ✒️ and 🛡️ on the Text — from here it changes by proposal alone.Members gain ✏️s on it, at the proposal rate.Voting opens.The moment is stamped as the founding, and the settings stop being re-set: from here they are amended.The document cannot begin while 🌍 Visibility is still being decided.🌍 Visibility is delegated to the membership, and you are its only member: a question answered by one voice has not been handed to anybody. Invite somebody at 🪪 Membership, or take it back on its own card and set it yourself.What the Founder keeps.Every power is the Founder’s until they lay it down. Whatever is not kept here goes the moment the document begins.🪶 Title✒️🛡️📍 Link✒️🛡️🌍 Visibility✒️🛡️🪪 Admissions✒️🛡️🤝 Applications✒️🛡️💤 Lapsing✒️🛡️🥾 Removals✒️🛡️✉️ Invite a Member✒️🛡️❌ Remove a Member✒️🛡️⏱️ Proposal Rate✒️🛡️⏰ Ending✒️🛡️👥 Quorum✒️🛡️👤 Anonymous Proposals✒️🛡️👁️ Vote Reveal✒️🛡️📝 Text✒️🛡️The questions🌍 1 out of 1 of the membership has voted on whether the document can only be seen by members or by anyone with the link.The peopleAda Lovell · answered everything", + "body": "What beginning does, all at once.The Founder lays down ✒️ and 🛡️ on the Text — from here it changes by proposal alone.Members gain ✏️s on it, at the proposal rate.Voting opens.The moment is stamped as the founding, and the settings stop being re-set: from here they are amended.The document cannot begin while 🌍 Visibility is still being decided.🌍 Visibility is delegated to the membership, and you are its only member: a question answered by one voice has not been handed to anybody. Invite somebody at 🪪 Membership, or take it back on its own card and set it yourself.What the Founder keeps.Every power is the Founder’s until they lay it down. Whatever is not kept here goes the moment the document begins.🪶 Title✒️🛡️📍 Link✒️🛡️🌍 Visibility✒️🛡️🪪 Admissions✒️🛡️🤝 Applications✒️🛡️💤 Lapsing✒️🛡️🥾 Removals✒️🛡️✉️ Invite a Member✒️🛡️❌ Remove a Member✒️🛡️⏱️ Proposal Rate✒️🛡️👥 Quorum✒️🛡️👤 Anonymous Proposals✒️🛡️👁️ Vote Reveal✒️🛡️⏰ Ending✒️🛡️📝 Text✒️🛡️The questions🌍 1 out of 1 of the membership has voted on whether the document can only be seen by members or by anyone with the link.The peopleAda Lovell · answered everything", "options": [], "inputs": [], "foot": [ @@ -281,17 +281,17 @@ "eyebrow": "Current rule", "head": "The Founder is deciding when the drafting process will end.", "lock": "", - "body": "No more changes to the document may be made after .Choose thisChanges to the document may be made perpetually.Choose thisThe membership will decide whether changes end on a set date or may be made perpetually.Choose this", + "body": "Proposals may be made until .Choose thisProposals may be made indefinitely.Choose thisThe membership will decide whether changes end on a set date or may be made perpetually.Choose this", "options": [ { "set": "ending", "val": "ends", - "label": "No more changes to the document may be made after ." + "label": "Proposals may be made until ." }, { "set": "ending", "val": "perpetual", - "label": "Changes to the document may be made perpetually." + "label": "Proposals may be made indefinitely." }, { "set": "ending", @@ -326,9 +326,9 @@ }, "grant-pen": { "eyebrow": "Accept Founder Actions", - "head": "Founded by AL Ada Lovell 👑.", + "head": "The Founder may ✒️ change anything at will.", "lock": "", - "body": "As the founder of this document, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.", + "body": "As the founder, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.", "options": [], "inputs": [], "foot": [ @@ -338,19 +338,17 @@ } ], "hints": [ - "Accept Founder Actions — close it", - "Accept Constitutional Proposals — yours to take", - "Some of the rules are reserved: changing them needs the founder’s assent" + "Accept Founder Actions — close it" ], "helpers": [ - "As the founder of this document, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to." + "As the founder, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to." ] }, "grant-shield": { "eyebrow": "Accept the Founder Veto", - "head": "Founded by AL Ada Lovell 👑.", + "head": "The Founder may 🛡️ refuse any proposal the membership pass.", "lock": "", - "body": "As the founder of this document, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.", + "body": "As the founder, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.", "options": [], "inputs": [], "foot": [ @@ -360,20 +358,17 @@ } ], "hints": [ - "Accept the Founder Veto — close it", - "Accept Constitutional Proposals — yours to take", - "Founder Actions — settled", - "Some of the rules are reserved: changing them needs the founder’s assent" + "Accept the Founder Veto — close it" ], "helpers": [ - "As the founder of this document, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to." + "As the founder, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to." ] }, "grant-voice": { "eyebrow": "Accept Constitutional Proposals", - "head": "Founded by AL Ada Lovell 👑.", + "head": "A 🏛️ proposal passes only when all members agree to it.", "lock": "", - "body": "A 🏛️ is a constitutional proposal: one at a time, returned whole, passing only when all members agree. You are already a member; activating it opens every question, proposal and vote on the rules.", + "body": "A 🏛️ is a constitutional proposal. Members may only make one at a time, and it passes only when all members agree to it.", "options": [], "inputs": [], "foot": [ @@ -383,13 +378,10 @@ } ], "hints": [ - "Accept Constitutional Proposals — close it", - "Founder Actions — settled", - "Founder Veto — settled", - "Some of the rules are reserved: changing them needs the founder’s assent" + "Accept Constitutional Proposals — close it" ], "helpers": [ - "A 🏛️ is a constitutional proposal: one at a time, returned whole, passing only when all members agree. You are already a member; activating it opens every question, proposal and vote on the rules." + "A 🏛️ is a constitutional proposal. Members may only make one at a time, and it passes only when all members agree to it." ] }, "hat": { @@ -416,7 +408,7 @@ "title": "Put it back as it stands" }, { - "label": "✒️", + "label": "Set it", "title": "Set it" } ], @@ -675,17 +667,17 @@ "eyebrow": "Current rule", "head": "The Founder is deciding how many members must prefer a proposal before it can pass.", "lock": "", - "body": "A proposal ✏️ cannot pass until it is preferred by at least % of the membership.Choose thisA proposal ✏️ cannot pass until it is preferred by at least members.Choose thisThe membership will decide how many members must prefer a proposal ✏️ before it can pass.Choose this", + "body": "A ✏️ proposal passes once at least % of the membership have voted for it and it’s the most preferred option.Choose thisA ✏️ proposal passes once at least members have voted for it and it’s the most preferred option.Choose thisThe membership will decide how many members must prefer a proposal ✏️ before it can pass.Choose this", "options": [ { "set": "quorumForm", "val": "share", - "label": "A proposal ✏️ cannot pass until it is preferred by at least % of the membership." + "label": "A ✏️ proposal passes once at least % of the membership have voted for it and it’s the most preferred option." }, { "set": "quorumForm", "val": "count", - "label": "A proposal ✏️ cannot pass until it is preferred by at least members." + "label": "A ✏️ proposal passes once at least members have voted for it and it’s the most preferred option." }, { "set": "quorumBy", @@ -729,12 +721,12 @@ "eyebrow": "Current rule", "head": "The Founder is deciding how often members may propose.", "lock": "", - "body": "Members may make a new proposal ✏️ every minuteshoursdays.Choose thisThe membership will decide how often members should be able to make proposals.Choose this", + "body": "Members may make a new ✏️ proposal every minuteshoursdays, up to 3 at a time.Choose thisThe membership will decide how often members should be able to make proposals.Choose this", "options": [ { "set": "rateBy", "val": "founder", - "label": "Members may make a new proposal ✏️ every minuteshoursdays." + "label": "Members may make a new ✏️ proposal every minuteshoursdays, up to 3 at a time." }, { "set": "rateBy", @@ -3238,17 +3230,17 @@ "eyebrow": "Current rule", "head": "The Founder is deciding when the drafting process will end.", "lock": "", - "body": "No more changes to the document may be made after .Choose thisChanges to the document may be made perpetually.Choose thisThe membership will decide whether changes end on a set date or may be made perpetually.Chosen", + "body": "Proposals may be made until .Choose thisProposals may be made indefinitely.Choose thisThe membership will decide whether changes end on a set date or may be made perpetually.Chosen", "options": [ { "set": "ending", "val": "ends", - "label": "No more changes to the document may be made after ." + "label": "Proposals may be made until ." }, { "set": "ending", "val": "perpetual", - "label": "Changes to the document may be made perpetually." + "label": "Proposals may be made indefinitely." }, { "set": "ending", @@ -3409,17 +3401,17 @@ "eyebrow": "Current rule", "head": "The Founder is deciding how many members must prefer a proposal before it can pass.", "lock": "", - "body": "A proposal ✏️ cannot pass until it is preferred by at least % of the membership.Choose thisA proposal ✏️ cannot pass until it is preferred by at least members.Choose thisThe membership will decide how many members must prefer a proposal ✏️ before it can pass.Chosen", + "body": "A ✏️ proposal passes once at least % of the membership have voted for it and it’s the most preferred option.Choose thisA ✏️ proposal passes once at least members have voted for it and it’s the most preferred option.Choose thisThe membership will decide how many members must prefer a proposal ✏️ before it can pass.Chosen", "options": [ { "set": "quorumForm", "val": "share", - "label": "A proposal ✏️ cannot pass until it is preferred by at least % of the membership." + "label": "A ✏️ proposal passes once at least % of the membership have voted for it and it’s the most preferred option." }, { "set": "quorumForm", "val": "count", - "label": "A proposal ✏️ cannot pass until it is preferred by at least members." + "label": "A ✏️ proposal passes once at least members have voted for it and it’s the most preferred option." }, { "set": "quorumBy", @@ -3463,12 +3455,12 @@ "eyebrow": "Current rule", "head": "The Founder is deciding how often members may propose.", "lock": "", - "body": "Members may make a new proposal ✏️ every minuteshoursdays.Choose thisThe membership will decide how often members should be able to make proposals.Chosen", + "body": "Members may make a new ✏️ proposal every minuteshoursdays, up to 3 at a time.Choose thisThe membership will decide how often members should be able to make proposals.Chosen", "options": [ { "set": "rateBy", "val": "founder", - "label": "Members may make a new proposal ✏️ every minuteshoursdays." + "label": "Members may make a new ✏️ proposal every minuteshoursdays, up to 3 at a time." }, { "set": "rateBy", @@ -3756,9 +3748,9 @@ }, "begin": { "eyebrow": "Begin", - "head": "When the document begins, members may propose changes to rules and vote on proposals.A proposal ✏️ passes once it is preferred by enough of the membership, and by more than prefer the current text.A constitutional proposal 🏛️ passes only when all members agree.", + "head": "The members are waiting on the Founder to begin.", "lock": "", - "body": "What beginning does, all at once.The Founder lays down ✒️ and 🛡️ on the Text — from here it changes by proposal alone.Members gain ✏️s on it, at the proposal rate.Voting opens.The moment is stamped as the founding, and the settings stop being re-set: from here they are amended.What the Founder keeps.Every power is the Founder’s until they lay it down. Whatever is not kept here goes the moment the document begins.🪶 Title✒️🛡️📍 Link✒️🛡️🌍 Visibility✒️🛡️🪪 Admissions✒️🛡️🤝 Applications✒️🛡️💤 Lapsing✒️🛡️🥾 Removals✒️🛡️✉️ Invite a Member✒️🛡️❌ Remove a Member✒️🛡️⏱️ Proposal Rate✒️🛡️⏰ Ending✒️🛡️👥 Quorum✒️🛡️👤 Anonymous Proposals✒️🛡️👁️ Vote Reveal✒️🛡️📝 Text✒️🛡️The questionsThe peopleAda Lovell · nothing owedNobody is kept waiting by this: whoever has not answered can still answer after the start, and the document takes what was said.", + "body": "What beginning does, all at once.The Founder lays down ✒️ and 🛡️ on the Text — from here it changes by proposal alone.Members gain ✏️s on it, at the proposal rate.Voting opens.The moment is stamped as the founding, and the settings stop being re-set: from here they are amended.What the Founder keeps.Every power is the Founder’s until they lay it down. Whatever is not kept here goes the moment the document begins.🪶 Title✒️🛡️📍 Link✒️🛡️🌍 Visibility✒️🛡️🪪 Admissions✒️🛡️🤝 Applications✒️🛡️💤 Lapsing✒️🛡️🥾 Removals✒️🛡️✉️ Invite a Member✒️🛡️❌ Remove a Member✒️🛡️⏱️ Proposal Rate✒️🛡️👥 Quorum✒️🛡️👤 Anonymous Proposals✒️🛡️👁️ Vote Reveal✒️🛡️⏰ Ending✒️🛡️📝 Text✒️🛡️The questionsThe peopleAda Lovell · nothing owedNobody is kept waiting by this: whoever has not answered can still answer after the start, and the document takes what was said.", "options": [], "inputs": [], "foot": [ @@ -3825,17 +3817,17 @@ "eyebrow": "Current rule", "head": "The Founder is deciding when the drafting process will end.", "lock": "", - "body": "No more changes to the document may be made after .Choose thisChanges to the document may be made perpetually.Choose thisThe membership will decide whether changes end on a set date or may be made perpetually.Choose this", + "body": "Proposals may be made until .Choose thisProposals may be made indefinitely.Choose thisThe membership will decide whether changes end on a set date or may be made perpetually.Choose this", "options": [ { "set": "ending", "val": "ends", - "label": "No more changes to the document may be made after ." + "label": "Proposals may be made until ." }, { "set": "ending", "val": "perpetual", - "label": "Changes to the document may be made perpetually." + "label": "Proposals may be made indefinitely." }, { "set": "ending", @@ -3870,9 +3862,9 @@ }, "grant-pen": { "eyebrow": "Accept Founder Actions", - "head": "Founded by AL Ada Lovell 👑.", + "head": "The Founder may ✒️ change anything at will.", "lock": "", - "body": "As the founder of this document, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.", + "body": "As the founder, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.", "options": [], "inputs": [], "foot": [ @@ -3882,19 +3874,17 @@ } ], "hints": [ - "Accept Founder Actions — close it", - "Accept Constitutional Proposals — yours to take", - "Some of the rules are reserved: changing them needs the founder’s assent" + "Accept Founder Actions — close it" ], "helpers": [ - "As the founder of this document, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to." + "As the founder, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to." ] }, "grant-shield": { "eyebrow": "Accept the Founder Veto", - "head": "Founded by AL Ada Lovell 👑.", + "head": "The Founder may 🛡️ refuse any proposal the membership pass.", "lock": "", - "body": "As the founder of this document, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.", + "body": "As the founder, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.", "options": [], "inputs": [], "foot": [ @@ -3904,20 +3894,17 @@ } ], "hints": [ - "Accept the Founder Veto — close it", - "Accept Constitutional Proposals — yours to take", - "Founder Actions — settled", - "Some of the rules are reserved: changing them needs the founder’s assent" + "Accept the Founder Veto — close it" ], "helpers": [ - "As the founder of this document, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to." + "As the founder, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to." ] }, "grant-voice": { "eyebrow": "Accept Constitutional Proposals", - "head": "Founded by AL Ada Lovell 👑.", + "head": "A 🏛️ proposal passes only when all members agree to it.", "lock": "", - "body": "A 🏛️ is a constitutional proposal: one at a time, returned whole, passing only when all members agree. You are already a member; activating it opens every question, proposal and vote on the rules.", + "body": "A 🏛️ is a constitutional proposal. Members may only make one at a time, and it passes only when all members agree to it.", "options": [], "inputs": [], "foot": [ @@ -3927,13 +3914,10 @@ } ], "hints": [ - "Accept Constitutional Proposals — close it", - "Founder Actions — settled", - "Founder Veto — settled", - "Some of the rules are reserved: changing them needs the founder’s assent" + "Accept Constitutional Proposals — close it" ], "helpers": [ - "A 🏛️ is a constitutional proposal: one at a time, returned whole, passing only when all members agree. You are already a member; activating it opens every question, proposal and vote on the rules." + "A 🏛️ is a constitutional proposal. Members may only make one at a time, and it passes only when all members agree to it." ] }, "hat": { @@ -3960,7 +3944,7 @@ "title": "Put it back as it stands" }, { - "label": "✒️", + "label": "Set it", "title": "Set it" } ], @@ -4217,17 +4201,17 @@ "eyebrow": "Current rule", "head": "The Founder is deciding how many members must prefer a proposal before it can pass.", "lock": "", - "body": "A proposal ✏️ cannot pass until it is preferred by at least % of the membership.Choose thisA proposal ✏️ cannot pass until it is preferred by at least members.Choose thisThe membership will decide how many members must prefer a proposal ✏️ before it can pass.Choose this", + "body": "A ✏️ proposal passes once at least % of the membership have voted for it and it’s the most preferred option.Choose thisA ✏️ proposal passes once at least members have voted for it and it’s the most preferred option.Choose thisThe membership will decide how many members must prefer a proposal ✏️ before it can pass.Choose this", "options": [ { "set": "quorumForm", "val": "share", - "label": "A proposal ✏️ cannot pass until it is preferred by at least % of the membership." + "label": "A ✏️ proposal passes once at least % of the membership have voted for it and it’s the most preferred option." }, { "set": "quorumForm", "val": "count", - "label": "A proposal ✏️ cannot pass until it is preferred by at least members." + "label": "A ✏️ proposal passes once at least members have voted for it and it’s the most preferred option." }, { "set": "quorumBy", @@ -4271,12 +4255,12 @@ "eyebrow": "Current rule", "head": "The Founder is deciding how often members may propose.", "lock": "", - "body": "Members may make a new proposal ✏️ every minuteshoursdays.Choose thisThe membership will decide how often members should be able to make proposals.Choose this", + "body": "Members may make a new ✏️ proposal every minuteshoursdays, up to 3 at a time.Choose thisThe membership will decide how often members should be able to make proposals.Choose this", "options": [ { "set": "rateBy", "val": "founder", - "label": "Members may make a new proposal ✏️ every minuteshoursdays." + "label": "Members may make a new ✏️ proposal every minuteshoursdays, up to 3 at a time." }, { "set": "rateBy", @@ -4541,46 +4525,40 @@ }, "begin": { "eyebrow": "Begin", - "head": "Members may propose changes to rules as soon as they arrive, and may vote on proposals.A proposal ✏️ passes once it is preferred by enough of the membership, and by more than prefer the current text.A constitutional proposal 🏛️ passes only when all members agree.", + "head": "Members started making proposals at HH:MM on Weekday D Month.", "lock": "", "body": "What beginning did.The Founder lays down ✒️ and 🛡️ on the Text — from here it changes by proposal alone.Members gain ✏️s on it, at the proposal rate.Voting opens.The moment is stamped as the founding, and the settings stop being re-set: from here they are amended.", "options": [], "inputs": [], "foot": [], "hints": [ - "Begin — close it", - "Proposals — settled", - "Voting — settled" + "Begin — close it" ], "helpers": [] }, "canjudge": { "eyebrow": "Current rule", - "head": "Members may propose changes to rules as soon as they arrive, and may vote on proposals.A proposal ✏️ passes once it is preferred by enough of the membership, and by more than prefer the current text.A constitutional proposal 🏛️ passes only when all members agree.", + "head": "Members may vote on proposals.", "lock": "", "body": "", "options": [], "inputs": [], "foot": [], "hints": [ - "Begin — settled", - "Proposals — settled", "Voting — close it" ], "helpers": [] }, "canpropose": { "eyebrow": "Current rule", - "head": "Members may propose changes to rules as soon as they arrive, and may vote on proposals.A proposal ✏️ passes once it is preferred by enough of the membership, and by more than prefer the current text.A constitutional proposal 🏛️ passes only when all members agree.", + "head": "Members may propose changes to the text and the rules.", "lock": "", "body": "", "options": [], "inputs": [], "foot": [], "hints": [ - "Begin — settled", - "Proposals — close it", - "Voting — settled" + "Proposals — close it" ], "helpers": [] }, @@ -4609,7 +4587,7 @@ }, "ending": { "eyebrow": "Current rule", - "head": "No more changes to the document may be made after Weekday, D Month, HH:MM.Chosen by the membership", + "head": "Proposals may be made until Weekday, D Month, HH:MM.Chosen by the membership", "lock": "", "body": "The drafting process will end on .Propose thisThe drafting process has no set end — it runs until too few members are active.Propose this", "options": [ @@ -4648,39 +4626,25 @@ }, "grant-voice": { "eyebrow": "Current rule", - "head": "Founded by AB Ash Bellamy 👑 at HH:MM on D Month.", + "head": "A 🏛️ proposal passes only when all members agree to it.", "lock": "", - "body": "The Founder invited you into the membership, and Constitutional Proposals came with it.A 🏛️ is a constitutional proposal: one at a time, returned whole, passing only when all members agree. You are already a member; activating it opens every question, proposal and vote on the rules.", + "body": "The Founder invited you into the membership, and Constitutional Proposals came with it.A 🏛️ is a constitutional proposal. Members may only make one at a time, and it passes only when all members agree to it.", "options": [], "inputs": [], "foot": [], "hints": [ - "Constitutional Proposals — close it", - "Some of the rules are reserved: changing them needs the founder’s assent" + "Constitutional Proposals — close it" ], "helpers": [ "The Founder invited you into the membership, and Constitutional Proposals came with it.", - "A 🏛️ is a constitutional proposal: one at a time, returned whole, passing only when all members agree. You are already a member; activating it opens every question, proposal and vote on the rules." + "A 🏛️ is a constitutional proposal. Members may only make one at a time, and it passes only when all members agree to it." ] }, - "hat": { - "eyebrow": "Current rule", - "head": "The Founder is part of the membership.Chosen by the Founder ✒️", - "lock": "", - "body": "", - "options": [], - "inputs": [], - "foot": [], - "hints": [ - "Founder’s Membership — close it" - ], - "helpers": [] - }, "held:mo-2": { "eyebrow": "Rejected · Weekday, D Month, HH:MM", - "head": "Members may make a new proposal ✏️ every 3 hours.", + "head": "Members may make a new ✏️ proposal every 3 hours, up to 3 at a time.", "lock": "", - "body": "Proposed by youMembers may make a new proposal ✏️ every 90 minutes.AnonymousHalf the wait would let people write while the thought is warm.", + "body": "Proposed by youMembers may make a new ✏️ proposal every 90 minutes, up to 3 at a time.AnonymousHalf the wait would let people write while the thought is warm.", "options": [], "inputs": [], "foot": [ @@ -4906,9 +4870,9 @@ }, "mo:mo-7": { "eyebrow": "Current rule", - "head": "Members may make a new proposal ✏️ every 3 hours.", + "head": "Members may make a new ✏️ proposal every 3 hours, up to 3 at a time.", "lock": "", - "body": "Proposed by youMembers may make a new proposal ✏️ every 6 hours.AnonymousFewer, better proposals — let the wait be longer.", + "body": "Proposed by youMembers may make a new ✏️ proposal every 6 hours, up to 3 at a time.AnonymousFewer, better proposals — let the wait be longer.", "options": [], "inputs": [], "foot": [ @@ -5064,7 +5028,7 @@ }, "pw:a:invite": { "eyebrow": "Does the Founder Have a Veto over Invitations?", - "head": "The Founder may refuse invitations and applications that the membership pass.Chosen by the Founder ✒️", + "head": "The Founder may block invitations that the membership have decided to make.Chosen by the Founder ✒️", "lock": "", "body": "", "options": [], @@ -5097,7 +5061,7 @@ }, "pw:a:remove": { "eyebrow": "Does the Founder Have a Veto over Removals?", - "head": "The Founder may refuse removals that the membership pass.Chosen by the Founder ✒️", + "head": "The Founder may block removals that the membership have decided to make.Chosen by the Founder ✒️", "lock": "", "body": "", "options": [], @@ -5192,7 +5156,7 @@ }, "pw:u:remove": { "eyebrow": "Can the Founder Remove at Will?", - "head": "The Founder may remove members at will.Chosen by the Founder ✒️", + "head": "The Founder may remove people at will.Chosen by the Founder ✒️", "lock": "", "body": "", "options": [], @@ -5237,9 +5201,9 @@ }, "quorum": { "eyebrow": "Current rule", - "head": "A proposal ✏️ cannot pass until it is preferred by at least 38% of the membership (n of m).Chosen by the membership", + "head": "A ✏️ proposal passes once at least 38% of the membership (n of m) have voted for it and it’s the most preferred option.Chosen by the membership", "lock": "", - "body": "A proposal ✏️ cannot pass until it is preferred by at least % of the membership.Propose this", + "body": "A ✏️ proposal passes once at least % of the membership have voted for it and it’s the most preferred option.Propose this", "options": [], "inputs": [ { @@ -5272,7 +5236,7 @@ }, "rate": { "eyebrow": "Current rule", - "head": "Members may make a new proposal ✏️ every 3 hours.Chosen by the Founder ✒️", + "head": "Members may make a new ✏️ proposal every 3 hours, up to 3 at a time.Chosen by the Founder ✒️", "lock": "", "body": "Members may make a new proposal ✏️ every minutes.Propose this", "options": [], @@ -5327,7 +5291,7 @@ }, "rec:quorum:0": { "eyebrow": "Passed · Weekday, D Month, HH:MM", - "head": "A proposal ✏️ cannot pass until it is preferred by at least 38% of the membership (n of m).", + "head": "A ✏️ proposal passes once at least 38% of the membership (n of m) have voted for it and it’s the most preferred option.", "lock": "", "body": "AnonymousThe charter should not change until more of us have had our say.", "options": [], @@ -5542,7 +5506,7 @@ }, "ending": { "eyebrow": "Current rule", - "head": "No more changes to the document may be made after Weekday, D Month, HH:MM.Chosen by the membership", + "head": "Proposals may be made until Weekday, D Month, HH:MM.Chosen by the membership", "lock": "", "body": "", "options": [], @@ -5616,9 +5580,9 @@ }, "mo:mo-7": { "eyebrow": "Current rule", - "head": "Members may make a new proposal ✏️ every 3 hours.", + "head": "Members may make a new ✏️ proposal every 3 hours, up to 3 at a time.", "lock": "", - "body": "ProposedMembers may make a new proposal ✏️ every 6 hours.AnonymousFewer, better proposals — let the wait be longer.", + "body": "ProposedMembers may make a new ✏️ proposal every 6 hours, up to 3 at a time.AnonymousFewer, better proposals — let the wait be longer.", "options": [], "inputs": [], "foot": [], @@ -5758,7 +5722,7 @@ }, "quorum": { "eyebrow": "Current rule", - "head": "A proposal ✏️ cannot pass until it is preferred by at least 38% of the membership (n of m).Chosen by the membership", + "head": "A ✏️ proposal passes once at least 38% of the membership (n of m) have voted for it and it’s the most preferred option.Chosen by the membership", "lock": "", "body": "", "options": [], @@ -5772,7 +5736,7 @@ }, "rate": { "eyebrow": "Current rule", - "head": "Members may make a new proposal ✏️ every 3 hours.Chosen by the Founder ✒️", + "head": "Members may make a new ✏️ proposal every 3 hours, up to 3 at a time.Chosen by the Founder ✒️", "lock": "", "body": "", "options": [], @@ -5806,7 +5770,7 @@ }, "rec:quorum:0": { "eyebrow": "Passed · Weekday, D Month, HH:MM", - "head": "A proposal ✏️ cannot pass until it is preferred by at least 38% of the membership (n of m).", + "head": "A ✏️ proposal passes once at least 38% of the membership (n of m) have voted for it and it’s the most preferred option.", "lock": "", "body": "AnonymousThe charter should not change until more of us have had our say.", "options": [], @@ -5821,9 +5785,9 @@ }, "rec:rate:0": { "eyebrow": "Rejected · Weekday, D Month, HH:MM", - "head": "Members may make a new proposal ✏️ every 3 hours.", + "head": "Members may make a new ✏️ proposal every 3 hours, up to 3 at a time.", "lock": "", - "body": "ProposedMembers may make a new proposal ✏️ every 90 minutes.AnonymousHalf the wait would let people write while the thought is warm.", + "body": "ProposedMembers may make a new ✏️ proposal every 90 minutes, up to 3 at a time.AnonymousHalf the wait would let people write while the thought is warm.", "options": [], "inputs": [], "foot": [], @@ -6036,46 +6000,40 @@ }, "begin": { "eyebrow": "Begin", - "head": "Members may propose changes to rules as soon as they arrive, and may vote on proposals.A proposal ✏️ passes once it is preferred by enough of the membership, and by more than prefer the current text.A constitutional proposal 🏛️ passes only when all members agree.", + "head": "Members started making proposals at HH:MM on Weekday D Month.", "lock": "", "body": "What beginning did.The Founder lays down ✒️ and 🛡️ on the Text — from here it changes by proposal alone.Members gain ✏️s on it, at the proposal rate.Voting opens.The moment is stamped as the founding, and the settings stop being re-set: from here they are amended.", "options": [], "inputs": [], "foot": [], "hints": [ - "Begin — close it", - "Proposals — settled", - "Voting — settled" + "Begin — close it" ], "helpers": [] }, "canjudge": { "eyebrow": "Current rule", - "head": "Members may propose changes to rules as soon as they arrive, and may vote on proposals.A proposal ✏️ passes once it is preferred by enough of the membership, and by more than prefer the current text.A constitutional proposal 🏛️ passes only when all members agree.", + "head": "Members may vote on proposals.", "lock": "", "body": "", "options": [], "inputs": [], "foot": [], "hints": [ - "Begin — settled", - "Proposals — settled", "Voting — close it" ], "helpers": [] }, "canpropose": { "eyebrow": "Current rule", - "head": "Members may propose changes to rules as soon as they arrive, and may vote on proposals.A proposal ✏️ passes once it is preferred by enough of the membership, and by more than prefer the current text.A constitutional proposal 🏛️ passes only when all members agree.", + "head": "Members may propose changes to the text and the rules.", "lock": "", "body": "", "options": [], "inputs": [], "foot": [], "hints": [ - "Begin — settled", - "Proposals — close it", - "Voting — settled" + "Proposals — close it" ], "helpers": [] }, @@ -6123,7 +6081,7 @@ }, "ending": { "eyebrow": "Current rule", - "head": "No more changes to the document may be made after Weekday, D Month, HH:MM.Chosen by the membership", + "head": "Proposals may be made until Weekday, D Month, HH:MM.Chosen by the membership", "lock": "", "body": "The drafting process will end on .Propose thisThe drafting process has no set end — it runs until too few members are active.Propose this", "options": [ @@ -6162,71 +6120,49 @@ }, "grant-pen": { "eyebrow": "Current rule", - "head": "Founded by AB Ash Bellamy 👑 at HH:MM on D Month.", + "head": "The Founder may ✒️ change the title, the link, the visibility rule and the proposal rate at will. The Founder may invite people at will. The Founder may remove people at will.", "lock": "", - "body": "As the founder of this document, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.", + "body": "As the founder, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.", "options": [], "inputs": [], "foot": [], "hints": [ - "Founder Actions — close it", - "Founder Veto — settled", - "Constitutional Proposals — settled", - "Some of the rules are reserved: changing them needs the founder’s assent" + "Founder Actions — close it" ], "helpers": [ - "As the founder of this document, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to." + "As the founder, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to." ] }, "grant-shield": { "eyebrow": "Current rule", - "head": "Founded by AB Ash Bellamy 👑 at HH:MM on D Month.", + "head": "The Founder may 🛡️ refuse proposals the membership pass on the title, the link, the visibility rule and the proposal rate. The Founder may block invitations that the membership have decided to make. The Founder may block removals that the membership have decided to make.", "lock": "", - "body": "As the founder of this document, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.", + "body": "As the founder, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.", "options": [], "inputs": [], "foot": [], "hints": [ - "Founder Actions — settled", - "Founder Veto — close it", - "Constitutional Proposals — settled", - "Some of the rules are reserved: changing them needs the founder’s assent" + "Founder Veto — close it" ], "helpers": [ - "As the founder of this document, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to." + "As the founder, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to." ] }, "grant-voice": { "eyebrow": "Current rule", - "head": "Founded by AB Ash Bellamy 👑 at HH:MM on D Month.", + "head": "A 🏛️ proposal passes only when all members agree to it.", "lock": "", - "body": "A 🏛️ is a constitutional proposal: one at a time, returned whole, passing only when all members agree. You are already a member; activating it opens every question, proposal and vote on the rules.", + "body": "A 🏛️ is a constitutional proposal. Members may only make one at a time, and it passes only when all members agree to it.", "options": [], "inputs": [], "foot": [], "hints": [ - "Founder Actions — settled", - "Founder Veto — settled", - "Constitutional Proposals — close it", - "Some of the rules are reserved: changing them needs the founder’s assent" + "Constitutional Proposals — close it" ], "helpers": [ - "A 🏛️ is a constitutional proposal: one at a time, returned whole, passing only when all members agree. You are already a member; activating it opens every question, proposal and vote on the rules." + "A 🏛️ is a constitutional proposal. Members may only make one at a time, and it passes only when all members agree to it." ] }, - "hat": { - "eyebrow": "Current rule", - "head": "The Founder is part of the membership.Chosen by the Founder ✒️", - "lock": "", - "body": "", - "options": [], - "inputs": [], - "foot": [], - "hints": [ - "Founder’s Membership — close it" - ], - "helpers": [] - }, "held:mo-4": { "eyebrow": "Rejected · Weekday, D Month, HH:MM", "head": "sorrel@example.org", @@ -6469,9 +6405,9 @@ }, "mo:mo-7": { "eyebrow": "Current rule", - "head": "Members may make a new proposal ✏️ every 3 hours.Prefer this", + "head": "Members may make a new ✏️ proposal every 3 hours, up to 3 at a time.Prefer this", "lock": "", - "body": "ProposedMembers may make a new proposal ✏️ every 6 hours.AnonymousFewer, better proposals — let the wait be longer.Prefer thisIndifferent", + "body": "ProposedMembers may make a new ✏️ proposal every 6 hours, up to 3 at a time.AnonymousFewer, better proposals — let the wait be longer.Prefer thisIndifferent", "options": [ { "set": null, @@ -6481,7 +6417,7 @@ { "set": null, "val": "proposed", - "label": "Members may make a new proposal ✏️ every 6 hours." + "label": "Members may make a new ✏️ proposal every 6 hours, up to 3 at a time." }, { "set": null, @@ -6660,14 +6596,14 @@ }, "pw:a:invite": { "eyebrow": "Does the Founder Have a Veto over Invitations?", - "head": "The Founder may refuse invitations and applications that the membership pass.Chosen by the Founder ✒️", + "head": "The Founder may block invitations that the membership have decided to make.Chosen by the Founder ✒️", "lock": "", - "body": "The Founder may not refuse invitations and applications that the membership pass.One way — it cannot be taken back.Choose this", + "body": "The Founder may not block invitations that the membership have decided to make.One way — it cannot be taken back.Choose this", "options": [ { "set": "pw:a:invite", "val": "given", - "label": "The Founder may not refuse invitations and applications that the membership pass." + "label": "The Founder may not block invitations that the membership have decided to make." } ], "inputs": [], @@ -6732,14 +6668,14 @@ }, "pw:a:remove": { "eyebrow": "Does the Founder Have a Veto over Removals?", - "head": "The Founder may refuse removals that the membership pass.Chosen by the Founder ✒️", + "head": "The Founder may block removals that the membership have decided to make.Chosen by the Founder ✒️", "lock": "", - "body": "The Founder may not refuse removals that the membership pass.One way — it cannot be taken back.Choose this", + "body": "The Founder may not block removals that the membership have decided to make.One way — it cannot be taken back.Choose this", "options": [ { "set": "pw:a:remove", "val": "given", - "label": "The Founder may not refuse removals that the membership pass." + "label": "The Founder may not block removals that the membership have decided to make." } ], "inputs": [], @@ -6942,14 +6878,14 @@ }, "pw:u:remove": { "eyebrow": "Can the Founder Remove at Will?", - "head": "The Founder may remove members at will.Chosen by the Founder ✒️", + "head": "The Founder may remove people at will.Chosen by the Founder ✒️", "lock": "", - "body": "The Founder may not remove members at will.One way — it cannot be taken back.Choose this", + "body": "The Founder may not remove people at will.One way — it cannot be taken back.Choose this", "options": [ { "set": "pw:u:remove", "val": "given", - "label": "The Founder may not remove members at will." + "label": "The Founder may not remove people at will." } ], "inputs": [], @@ -7044,9 +6980,9 @@ }, "quorum": { "eyebrow": "Current rule", - "head": "A proposal ✏️ cannot pass until it is preferred by at least 38% of the membership (n of m).Chosen by the membership", + "head": "A ✏️ proposal passes once at least 38% of the membership (n of m) have voted for it and it’s the most preferred option.Chosen by the membership", "lock": "", - "body": "A proposal ✏️ cannot pass until it is preferred by at least % of the membership.Propose this", + "body": "A ✏️ proposal passes once at least % of the membership have voted for it and it’s the most preferred option.Propose this", "options": [], "inputs": [ { @@ -7079,14 +7015,14 @@ }, "rate": { "eyebrow": "Current rule", - "head": "Members may make a new proposal ✏️ every 3 hours.Chosen by the Founder ✒️", + "head": "Members may make a new ✏️ proposal every 3 hours, up to 3 at a time.Chosen by the Founder ✒️", "lock": "", - "body": "Members may make a new proposal ✏️ every minuteshoursdays.Choose ✒️ or Propose ✏️ this", + "body": "Members may make a new ✏️ proposal every minuteshoursdays, up to 3 at a time.Choose ✒️ or Propose ✏️ this", "options": [ { "set": "rateBy", "val": "founder", - "label": "Members may make a new proposal ✏️ every minuteshoursdays." + "label": "Members may make a new ✏️ proposal every minuteshoursdays, up to 3 at a time." } ], "inputs": [ @@ -7144,7 +7080,7 @@ }, "rec:quorum:0": { "eyebrow": "Passed · Weekday, D Month, HH:MM", - "head": "A proposal ✏️ cannot pass until it is preferred by at least 38% of the membership (n of m).", + "head": "A ✏️ proposal passes once at least 38% of the membership (n of m) have voted for it and it’s the most preferred option.", "lock": "", "body": "AnonymousThe charter should not change until more of us have had our say.", "options": [], @@ -7159,9 +7095,9 @@ }, "rec:rate:0": { "eyebrow": "Rejected · Weekday, D Month, HH:MM", - "head": "Members may make a new proposal ✏️ every 3 hours.", + "head": "Members may make a new ✏️ proposal every 3 hours, up to 3 at a time.", "lock": "", - "body": "ProposedMembers may make a new proposal ✏️ every 90 minutes.AnonymousHalf the wait would let people write while the thought is warm.", + "body": "ProposedMembers may make a new ✏️ proposal every 90 minutes, up to 3 at a time.AnonymousHalf the wait would let people write while the thought is warm.", "options": [], "inputs": [], "foot": [], diff --git a/design/tools/copy-source.golden.json b/design/tools/copy-source.golden.json index a966f999..d3384e0b 100644 --- a/design/tools/copy-source.golden.json +++ b/design/tools/copy-source.golden.json @@ -1,6 +1,6 @@ { "meta": { - "entries": 725 + "entries": 734 }, "entries": { "RULES.admission.assembly": "Any member may propose to invite someone to join the membership, but all members must agree 🏛️.", @@ -48,11 +48,11 @@ "feed.redacted": "[redacted]", "feed.removed": "This clause was removed.", "feed.rule.address": "ƒ (slug) => 'The document lives at docs.vote/d/' + slug + '.'", - "feed.rule.endingAfter": "ƒ (when) => 'No more changes to the document may be made after ' + when + '.'", - "feed.rule.endingNever": "Changes to the document may be made perpetually.", + "feed.rule.endingAfter": "ƒ (when) => 'Proposals may be made until ' + when + '.'", + "feed.rule.endingNever": "Proposals may be made indefinitely.", "feed.rule.lapseAfter": "ƒ (spell) => 'After ' + spell + ', inactive members lapse and automatically abstain from votes.'", "feed.rule.lapseNever": "Inactive members never lapse and are still counted towards votes.", - "feed.rule.rate": "ƒ (phrase) => 'Members may make a new proposal ✏️ every ' + phrase + '.'", + "feed.rule.rate": "ƒ (phrase, cap) => 'Members may make a new ✏️ proposal every ' + phrase + ', up to ' + cap + ' at a time.'", "feed.rule.unit.days": "day", "feed.rule.unit.hours": "hour", "feed.rule.unit.minutes": "minute", @@ -257,6 +257,26 @@ "page.cards.text.t": "Text", "page.cards.title.n": "Title", "page.cards.title.t": "Name the Document", + "page.clause.beginWaiting": "The members are waiting on the Founder to begin.", + "page.clause.begun": "ƒ (time, day) => 'Members started making proposals at ' + time + ' on ' + day + '.'", + "page.clause.closed": "ƒ (time, day, n) => 'Proposals closed at ' + time + ' on ' + day + '. ' +\n n + (n === 1 ? ' member has signed.' : ' members have signed.')", + "page.clause.constitutional": "A 🏛️ proposal passes only when all members agree to it.", + "page.clause.endingAfter": "ƒ (when) => 'Proposals may be made until ' + when + '.'", + "page.clause.endingNever": "Proposals may be made indefinitely.", + "page.clause.foundedLead": "Founded by ", + "page.clause.list": "ƒ (names) => (names.length < 2 ? names.join('')\n : names.slice(0, -1).join(', ') + ' and ' + names[names.length - 1])", + "page.clause.penAll": "The Founder may ✒️ change anything at will.", + "page.clause.penBirth": "The Founder (that’s you!) may ✒️ change anything at will.", + "page.clause.penSome": "ƒ (list) => 'The Founder may ✒️ change ' + list + ' at will.'", + "page.clause.propose": "Members may propose changes to the text and the rules.", + "page.clause.proposeBefore": "When the document begins, members may propose changes to the text and the rules.", + "page.clause.rate": "ƒ (phrase, cap) => 'Members may make a new ✏️ proposal every ' + phrase + ', up to ' + cap + ' at a time.'", + "page.clause.shieldAll": "The Founder may 🛡️ refuse any proposal the membership pass.", + "page.clause.shieldBirth": "The Founder (that’s you!) may 🛡️ refuse proposals the membership pass.", + "page.clause.shieldSome": "ƒ (list) => 'The Founder may 🛡️ refuse proposals the membership pass on ' + list + '.'", + "page.clause.vote": "Members may vote on proposals.", + "page.clause.voteBefore": "When the document begins, members may vote on proposals.", + "page.clause.weekDay": "ƒ (weekday, day) => weekday + ' ' + day", "page.clerkNoPencil": "You are not a member, so there is no ✏️ for you to spend — this one is yours to set.", "page.closeBatch.stillOpenMany": "motions were still open and did not pass", "page.closeBatch.stillOpenOne": "motion was still open and did not pass", @@ -306,23 +326,23 @@ "page.gate.canjudge.done": "Open — the rules are settled.", "page.gate.canjudge.title": "Voting", "page.gate.canjudge.waiting": "Waiting on the rules.", - "page.gate.canjudge.why": "A vote is your say on a proposal: you are shown two at a time and choose the one you prefer, or neither.", + "page.gate.canjudge.why": "A vote is your say on a proposed change to the document.", "page.gate.canpropose.done": "Open — members can propose as soon as they arrive.", "page.gate.canpropose.title": "Proposals", "page.gate.canpropose.waiting": "Waiting on the start.", - "page.gate.canpropose.why": "A proposal is a change you write to the document, for the membership to vote on.", + "page.gate.canpropose.why": "A proposal is a change to the document that the membership vote on.", "page.gate.closing.signTitle": "OK signs the document; your comment goes on the record", "page.gate.closing.title": "The Close", "page.gate.pen.title": "Founder Actions", "page.gate.pen.waiting": "Waiting on the save.", - "page.gate.pen.why": "As the founder of this document, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.", + "page.gate.pen.why": "As the founder, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.", "page.gate.shield.title": "Founder Veto", "page.gate.shield.waiting": "Waiting on the save.", - "page.gate.shield.why": "As the founder of this document, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.", + "page.gate.shield.why": "As the founder, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.", "page.gate.voice.accept": "Accept 🏛️", "page.gate.voice.title": "Constitutional Proposals", "page.gate.voice.waiting": "Waiting on your arrival.", - "page.gate.voice.why": "A 🏛️ is a constitutional proposal: one at a time, returned whole, passing only when all members agree. You are already a member; activating it opens every question, proposal and vote on the rules.", + "page.gate.voice.why": "A 🏛️ is a constitutional proposal. Members may only make one at a time, and it passes only when all members agree to it.", "page.heldNews.couldNotBePut": "Your proposal could not be put to the membership, and your ✏️ is back in your wallet.", "page.heldNews.founderRefused": "ƒ (what) => 'The Founder refused your proposal: ' + what + '.'", "page.heldNews.membershipRejected": "ƒ (what) => 'The membership rejected your proposal: ' + what + '.'", @@ -377,17 +397,6 @@ "page.picNote.tooBig": "That picture is too big to open here.", "page.picNote.tooHeavy": "That picture will not compress small enough — try a simpler one.", "page.picNote.unreadable": "That picture could not be opened.", - "page.preamble.andVote": ", and may vote on proposals.", - "page.preamble.asArrive": "as soon as they arrive", - "page.preamble.beforeBegin": "When the document begins, members may propose changes to rules and vote on proposals.", - "page.preamble.onceAnswered": "once they have answered the questions the Founder delegated", - "page.preamble.passConstitutional": "A constitutional proposal 🏛️ passes only when all members agree.", - "page.preamble.passOrdinary": "A proposal ✏️ passes once it is preferred by enough of the membership, and by more than prefer the current text.", - "page.preamble.proposeAtBegin": " They may propose changes to rules when the document begins.", - "page.preamble.proposeLead": "Members may propose changes to rules ", - "page.preamble.voteOnly": "Members may vote on proposals.", - "page.preamble.voteTail": ". They may begin voting on proposals when all the rules have been decided.", - "page.preamble.voteWhenDecided": "Members may begin voting on proposals when all the rules have been decided.", "page.prose.below": ", below.", "page.prose.heading": "heading", "page.prose.nothingYet": "Nothing written yet.", @@ -413,24 +422,24 @@ "page.pw.notes.vetoNeedsPen": "A veto can only be held where the Founder still amends it at will — take back the ✒️ first.", "page.pw.noun.admission": "the admissions rule", "page.pw.noun.applications": "the applications rule", - "page.pw.noun.authorship": "the anonymity rule", + "page.pw.noun.authorship": "anonymity of proposals", "page.pw.noun.chamber": "the visibility rule", - "page.pw.noun.ending": "the ending", + "page.pw.noun.ending": "the end time", "page.pw.noun.fallback": "this", "page.pw.noun.invite": "invitations", - "page.pw.noun.judgments": "the vote-reveal rule", + "page.pw.noun.judgments": "anonymity of votes", "page.pw.noun.lapse": "the lapse rule", - "page.pw.noun.quorum": "the quorum rule", + "page.pw.noun.quorum": "the quorum", "page.pw.noun.rate": "the proposal rate", "page.pw.noun.removal": "the removal rule", "page.pw.noun.remove": "removals", "page.pw.noun.slug": "the link", "page.pw.noun.text": "the text", "page.pw.noun.title": "the title", - "page.pw.phrase.invite.a": "refuse invitations and applications that the membership pass", + "page.pw.phrase.invite.a": "block invitations that the membership have decided to make", "page.pw.phrase.invite.u": "invite people at will", - "page.pw.phrase.remove.a": "refuse removals that the membership pass", - "page.pw.phrase.remove.u": "remove members at will", + "page.pw.phrase.remove.a": "block removals that the membership have decided to make", + "page.pw.phrase.remove.u": "remove people at will", "page.pw.phrase.star.a": "refuse proposals that the membership pass", "page.pw.phrase.star.u": "amend this at will", "page.pw.phrase.text.a": "refuse changes to the text that the membership pass", @@ -444,8 +453,8 @@ "page.pwTitle.removeA": "Does the Founder Have a Veto over Removals?", "page.pwTitle.removeU": "Can the Founder Remove at Will?", "page.quorumFloorMin": "The minimum quorum is 2: the author and one other member.", - "page.quorumRule.count": "ƒ (n) => 'A proposal ✏️ cannot pass until it is preferred by at least ' + n + ' members.'", - "page.quorumRule.share": "ƒ (share, tail) => 'A proposal ✏️ cannot pass until it is preferred by at least ' +\n share + ' of the membership' + (tail || '') + '.'", + "page.quorumRule.count": "ƒ (n) => 'A ✏️ proposal passes once at least ' + n + ' members have voted for it and it’s the most preferred option.'", + "page.quorumRule.share": "ƒ (share, tail) => 'A ✏️ proposal passes once at least ' +\n share + ' of the membership' + (tail || '') + ' have voted for it and it’s the most preferred option.'", "page.quorumStop": "ƒ (k) => 'With this quorum, ' + (k === 1 ? 'one member' : k + ' members') +\n ' preferring the current text can stop a proposal.'", "page.railVal.admission.assembly": "all agree", "page.railVal.admission.pen": "any member invites", From 9b54c227195b8d9c14b0c1549d2b84af90ac654f Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 3 Oct 2026 18:02:28 +0000 Subject: [PATCH 5/8] Re-freeze the setup reference and the founding golden (qa:freeze, #223) The band changes on purpose: the founder clauses, the five Proposals clauses, the Schedule, and the walk reading the Rules top to bottom. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018T9ZX2oB2xxzsK4Tgxpya7 --- design/reference/band.js | 23 +- design/reference/begin.js | 3 +- design/reference/cards.js | 10 +- design/reference/composer.js | 49 ++- design/reference/constitution.js | 46 +++ design/reference/copy.js | 75 ++-- design/reference/edit-mode.js | 47 ++- design/reference/live.js | 7 + design/reference/paper.js | 92 ++++- design/reference/session-view.html | 440 +++++++++++++--------- design/reference/session.js | 211 +++++++++-- design/reference/setup.js | 4 +- design/reference/system.css | 142 +++++++- design/tools/founding-walk.golden.json | 481 ++++++++++++++++--------- 14 files changed, 1205 insertions(+), 425 deletions(-) diff --git a/design/reference/band.js b/design/reference/band.js index da40eae0..1d913e23 100644 --- a/design/reference/band.js +++ b/design/reference/band.js @@ -173,11 +173,13 @@ window.BAND = (function () { // clause is a list rather than a sentence. It carries markup (the avatar, the // 👑 span), so it rides `ctx.clauseFor` beside `rosterClause()` and never // `headFor`, which escapes what it is given. + // …and it no longer carries the time (#223, Ed 2026-10-03): the moment + // moved to 🍾's clause in the Schedule const foundedClause = () => - (!env.cs ? '' : '

Founded by ' + + (!env.cs ? '' : '

' + esc(PAGE_COPY.clause.foundedLead) + avHtml(founderInfo()) + ' ' + esc(founderInfo().n.trim() || 'Anonymous') + // 👑 by any reservation (Ed, Q379 wide); 📯 = holds nothing. - ' ' + founderMark() + esc(foundedAt()) + '.

'); + ' ' + founderMark() + '.

'); // the close's moment, the same shape as the founding's const closedAtWords = () => { if (!env.cs || !env.cs.closed || env.cs.closedAt == null) return ''; @@ -580,7 +582,7 @@ window.BAND = (function () { // stacked field. The chips a shape's unit once grew above the field // left with 🧭 (Q1363). opt(V, 'ending', 'ends', - 'No more changes to the document may be made after ' + + 'Proposals may be made until ' + '.') + rungOpt(V, 'ending', 'perpetual', ENDING_NEVER) + ''; })(), @@ -644,9 +646,9 @@ window.BAND = (function () { '').join('') + ''; return opt(V, 'rateBy', 'founder', - 'Members may make a new proposal ✏️ every ' + + 'Members may make a new ✏️ proposal every ' + ' ' + sel + '.', ''); })() + + (shown === '' ? '' : ' value="' + shown + '"') + '> ' + sel + ', up to 3 at a time.', ''); })() + '', chamber: () => (() => { const V = ladderView('chamber'); @@ -1086,7 +1088,9 @@ window.BAND = (function () { // other seat (Q1503) if (c.k === 'hat') { if (stateOf(c, ctx) === 'news') return 'watching'; - return amFounder() && !constituted() && docOpen() && founderCanCommit() ? 'setting' : 'watching'; + // …answerable before ✒️ is accepted (#223, G6: 🎩 is the walk's second + // step, ✒️ its third), so not pen-gated + return amFounder() && !constituted() && docOpen() ? 'setting' : 'watching'; } if (!MANAGED_KEYS.includes(c.k)) return null; if (c.record || c.isGate || c.door || c.kind === 'personal' || c.admit || c.release || @@ -1169,9 +1173,12 @@ window.BAND = (function () { attrs: ' data-confirm="1"', title: PAGE_COPY.setIt, until: powerParts(c).chosen ? null : 'choose' }); return acts; } + // **🎩 is answered above the pen** (#223, G6): the walk meets it before + // ✒️ is accepted, so it commits with no power, as ✋ and 🖼️ do (Q980) — + // a ✒️ on it would be a power not yet held (T44) if (c.k === 'hat') { - const ready = !!S.hatPick && S.hatPick !== hatCurrent() && mayPen() && docOpen(); - acts.push({ kind: 'commit', glyph: '✒️', glyphHtml: glyphHtml('✒️'), cls: 'btn-approve emojibtn', + const ready = !!S.hatPick && S.hatPick !== hatCurrent() && docOpen(); + acts.push({ kind: 'commit', glyph: '✓', glyphHtml: window.CARDS.TICK, cls: 'btn-approve', attrs: ' data-confirm="1"', title: PAGE_COPY.setIt, until: ready ? null : 'choose' }); return acts; } diff --git a/design/reference/begin.js b/design/reference/begin.js index 31cdeacb..a9e99e04 100644 --- a/design/reference/begin.js +++ b/design/reference/begin.js @@ -265,7 +265,8 @@ window.BEGIN = (function () { const BEGIN_ROWS = [ 'title', 'slug', 'chamber', 'admission', 'applications', 'lapse', 'removal', 'invite', 'remove', - 'rate', 'ending', 'quorum', 'authorship', 'judgments', + // the Rules' own order since #223 (G6): ⏰ in the Schedule, after 👤 👁️ + 'rate', 'quorum', 'authorship', 'judgments', 'ending', 'text', // `machines` is not here (Ed, 2026-09-09: *we are not having machines*): // it has had no card since 🤖 left the surface (R-078) and stays in the diff --git a/design/reference/cards.js b/design/reference/cards.js index c60614d5..19d39d8f 100644 --- a/design/reference/cards.js +++ b/design/reference/cards.js @@ -2241,7 +2241,15 @@ window.CARDS = (function () { // opens the draft with that character already applied. Which is // `always-on-typing`, applied to a box instead of to a paragraph — the same // idea and, it turns out, the same function underneath. - function laneBoxHtml(d, site, blank) { + // `part` (MOBILE.md §6a.1, Ed 1585.1): `'lane'` is the editor alone, for + // the phone's card whose lane stands in the clause's own box; `'why'` the + // reasoning alone, drawn beneath it. Absent, the whole box as ever. + function laneBoxHtml(d, site, blank, part) { + if (part === 'lane') { + return '
' + laneBlocks(site.text, originText(site)) + '
'; + } + if (part === 'why') return whyBoxHtml((d && d.rationale) || '', ' data-why'); // **No controls of its own since Q1294 (b)** (Ed, 2026-09-10: *top right // of the edit box, identical to the existing composer control. You can // put bold and italic there too*): the lane is the text and nothing diff --git a/design/reference/composer.js b/design/reference/composer.js index aeb93a4d..866e9041 100644 --- a/design/reference/composer.js +++ b/design/reference/composer.js @@ -589,6 +589,46 @@ window.COMPOSER = (function () { startDraft(key, null, { text: src.slice(0, a) + ins + src.slice(b), caret: a + ins.length }); } + // **A tap on a clause makes it its lane** (MOBILE.md §6a.1, Ed 1585.1, + // 2026-10-02): on a coarse pointer the column never takes a caret, so in + // edit mode the tap is the door — the clause opens as a draft of itself, + // nothing changed yet, the caret where the finger landed (the point + // mapped to a source offset exactly as a keystroke's caret is). The same + // opening as a keystroke's, with no character applied. + function startDraftFromTap(p, x, y) { + const key = p.dataset.key; + if (!key) return false; + const src = markerFor(key) + currentTextFor(key); + let caret = src.length; + let r = null; + if (document.caretRangeFromPoint) r = document.caretRangeFromPoint(x, y); + else if (document.caretPositionFromPoint) { + const c = document.caretPositionFromPoint(x, y); + if (c) { r = document.createRange(); r.setStart(c.offsetNode, c.offset); r.collapse(true); } + } + if (r && p.contains(r.startContainer)) { + const off = offsetIn(p, r.startContainer, r.startOffset); + if (off != null) caret = Math.min(off, src.length); + } + // **the words do not move** (§6a.6 (2)): the head is a `.anch`'s box, + // its words 6px inside it, where an untouched clause's words stand at + // its own top — so the scroll takes up whatever the opening moved them + const wordsTop = (el) => { + const w = el && document.createTreeWalker(el, NodeFilter.SHOW_TEXT); + for (let n = w && w.nextNode(); n; n = w.nextNode()) { + if (!n.nodeValue.trim() || (n.parentElement && n.parentElement.closest('.chipcol, .nocaret'))) continue; + const q = document.createRange(); q.selectNodeContents(n); + const rs = q.getClientRects(); if (rs.length) return rs[0].top; + } + return null; + }; + const t0 = wordsTop(p); + startDraft(key, null, { text: src, caret }); + const t1 = wordsTop(env.doc.querySelector('[data-lane="' + key + '"]')); + if (t0 != null && t1 != null && Math.abs(t1 - t0) > 0.5) scrollBy(0, t1 - t0); + return true; + } + // **Backspace at the start of a clause joins it to the one above** (Q1302, // Ed's bot room 2026-09-10: *I should be able to backspace at the start of a // clause to join it to the previous clause*), and Delete at its end joins @@ -1004,6 +1044,13 @@ window.COMPOSER = (function () { block: '
' + esc(window.COPY.shell.yourDraft) + '' + anonSwitchHtml(d) + '
' + laneBoxHtml(d, s) + '
', + // the phone's form (MOBILE.md §6a.1, Ed 1585.1): the lane alone, to + // stand in the clause's own box, and beneath it the switch and the + // reasoning — the same parts, the head left out + lane: s.lost ? null : laneBoxHtml(d, s, null, 'lane'), + below: '
' + + (SIGNING() ? '
' + anonSwitchHtml(d) + '
' : '') + + laneBoxHtml(d, s, null, 'why') + '
', // what the card says under the words: a site the text moved out from // under (Q1463), and only the two facts that change what the row's ✏️ // *does* (Ed, 2026-08-17) — it joins a race, it goes in as one change @@ -1053,7 +1100,7 @@ window.COMPOSER = (function () { return { DRAFT_ID, draftOf, docIndexOfKey, siteFor, syncDraftKeys, dropDraft, dropDraftSite, caretRangeIn, selectedBlocks, laneCaret, placeCaret, - startDraft, startDraftFromTyping, startDraftFromRun, + startDraft, startDraftFromTyping, startDraftFromTap, startDraftFromRun, laneRemark, syncEditCtl, markSelection, commitBtnHtml, proposalRowHtml, proposeCtlTitles, draftRowState, setDraftSigned, draftSigned, ownParts, editParts, cardCommitActs }; diff --git a/design/reference/constitution.js b/design/reference/constitution.js index c7303809..dd5d83a9 100644 --- a/design/reference/constitution.js +++ b/design/reference/constitution.js @@ -3517,6 +3517,52 @@ var CONSTITUTION = (() => { } } } + /** + * **When `tick` next has something to do** (plan-scaling Stage 1, issue + * #210): the earliest moment any branch of `tick` above would emit, read + * off the same state and the same arithmetic, or null where none ever will + * until something else is folded — unbegun, closed, or no ending and no + * 💤 clock. A host may then skip this document's ticks before that moment + * and lose nothing, since `tick` at any earlier `t` emits nothing. + * + * **Every clock here is a state, never a moment that passes**: a lapse + * point already behind `t` still names a member in E, and a warning point + * behind it still names a lead shorter than the one sent, until `tick` + * folds them — so a point that was never ticked comes back from here as + * due, and the answer needs no *since when*. Kept beside `tick` because it + * is `tick` read backwards: a branch added there is a branch owed here. + */ + nextClockT() { + if (this.constitutedT === null || this.closedFlag) return null; + let next = Infinity; + const ending = this.settings.get("ending").value; + if (ending && ending.endsAtMs !== null) next = Math.min(next, ending.endsAtMs); + const lapse = this.settings.get("lapse").value; + if (lapse && lapse.afterMs !== null) { + const pointsOf = (lastT, warnedLead, warns) => { + const due = lapseDue(lastT, lapse.afterMs); + let at = due.lapseAtT; + if (warns) { + for (const w of due.warnAt) { + if (warnedLead === null || w.lead < warnedLead) at = Math.min(at, w.t); + } + } + return at; + }; + for (const m of this.members.values()) { + if (!inE(m)) continue; + next = Math.min(next, pointsOf(m.lastActivityT, m.lapseWarnedLead, true)); + } + if (!this.crownLapsedFlag && this.holdsAnythingReserved()) { + next = Math.min(next, pointsOf( + this.convenor.lastActivityT, + this.convenor.lapseWarnedLead, + !this.members.has(this.convenor.id) + )); + } + } + return next === Infinity ? null : next; + } holdsAnythingReserved() { for (const st of this.settings.values()) { if (st.holder === "convenor") return true; diff --git a/design/reference/copy.js b/design/reference/copy.js index 272ef3d0..bb6677ae 100644 --- a/design/reference/copy.js +++ b/design/reference/copy.js @@ -997,8 +997,8 @@ window.COPY = (function () { pw: { phrase: { star: { u: 'amend this at will', a: 'refuse proposals that the membership pass' }, - invite: { u: 'invite people at will', a: 'refuse invitations and applications that the membership pass' }, - remove: { u: 'remove members at will', a: 'refuse removals that the membership pass' }, + invite: { u: 'invite people at will', a: 'block invitations that the membership have decided to make' }, + remove: { u: 'remove people at will', a: 'block removals that the membership have decided to make' }, text: { u: 'amend the text at will', a: 'refuse changes to the text that the membership pass' }, }, // **On the decision card the phrase names its setting** (Q1429, Ed @@ -1025,8 +1025,8 @@ window.COPY = (function () { // (entry 94) and are only ever the veto sentence's. noun: { title: 'the title', slug: 'the link', text: 'the text', - ending: 'the ending', quorum: 'the quorum rule', - authorship: 'the anonymity rule', judgments: 'the vote-reveal rule', + ending: 'the end time', quorum: 'the quorum', + authorship: 'anonymity of proposals', judgments: 'anonymity of votes', chamber: 'the visibility rule', rate: 'the proposal rate', lapse: 'the lapse rule', removal: 'the removal rule', admission: 'the admissions rule', applications: 'the applications rule', @@ -1121,19 +1121,36 @@ window.COPY = (function () { founderRefused: (what) => 'The Founder refused your proposal: ' + what + '.', couldNotBePut: 'Your proposal could not be put to the membership, and your ✏️ is back in your wallet.', }, - // the Proposals preamble (Y21): the gates' fragments, composed - preamble: { - beforeBegin: 'When the document begins, members may propose changes to rules and vote on proposals.', - voteOnly: 'Members may vote on proposals.', - voteWhenDecided: 'Members may begin voting on proposals when all the rules have been decided.', - proposeAtBegin: ' They may propose changes to rules when the document begins.', - proposeLead: 'Members may propose changes to rules ', - onceAnswered: 'once they have answered the questions the Founder delegated', - asArrive: 'as soon as they arrive', - andVote: ', and may vote on proposals.', - voteTail: '. They may begin voting on proposals when all the rules have been decided.', - passOrdinary: 'A proposal ✏️ passes once it is preferred by enough of the membership, and by more than prefer the current text.', - passConstitutional: 'A constitutional proposal 🏛️ passes only when all members agree.', + // **The Rules' clauses for the grants, the gates and the Schedule** (#223, + // Ed 2026-10-03, G1–G5, every sentence his, verbatim): the ✒️ and 🛡️ + // clauses in the lead, the five Proposals clauses, the Schedule's 🍾 and + // 🥂. `list` joins the setting names (`pw.noun`) the way his examples + // read: *the title, the link, the visibility rule and the text*. + clause: { + foundedLead: 'Founded by ', + penAll: 'The Founder may ✒️ change anything at will.', + penSome: (list) => 'The Founder may ✒️ change ' + list + ' at will.', + shieldAll: 'The Founder may 🛡️ refuse any proposal the membership pass.', + shieldSome: (list) => 'The Founder may 🛡️ refuse proposals the membership pass on ' + list + '.', + penBirth: 'The Founder (that’s you!) may ✒️ change anything at will.', + shieldBirth: 'The Founder (that’s you!) may 🛡️ refuse proposals the membership pass.', + // *Friday 2 October*, his example's shape: the weekday, then the day + weekDay: (weekday, day) => weekday + ' ' + day, + list: (names) => (names.length < 2 ? names.join('') + : names.slice(0, -1).join(', ') + ' and ' + names[names.length - 1]), + propose: 'Members may propose changes to the text and the rules.', + proposeBefore: 'When the document begins, members may propose changes to the text and the rules.', + vote: 'Members may vote on proposals.', + voteBefore: 'When the document begins, members may vote on proposals.', + rate: (phrase, cap) => 'Members may make a new ✏️ proposal every ' + phrase + ', up to ' + cap + ' at a time.', + constitutional: 'A 🏛️ proposal passes only when all members agree to it.', + beginWaiting: 'The members are waiting on the Founder to begin.', + begun: (time, day) => 'Members started making proposals at ' + time + ' on ' + day + '.', + endingAfter: (when) => 'Proposals may be made until ' + when + '.', + endingNever: 'Proposals may be made indefinitely.', + // *[N] members have signed* — the verb agrees with one (*1 member has*) + closed: (time, day, n) => 'Proposals closed at ' + time + ' on ' + day + '. ' + + n + (n === 1 ? ' member has signed.' : ' members have signed.'), }, // 👥's rule, in Ed's own words (Q1439, ruling t, 2026-09-18: *A proposal // cannot pass until it is preferred by at least 50% of the membership @@ -1151,10 +1168,12 @@ window.COPY = (function () { // member's answer card and the composer's lane (Q1137) — and then the // `(x of y)` that follows *of the membership*. `val.quorumPct` is still // the one writer of a share where a share is printed whole. + // …and since #223 (G4, Ed 2026-10-03 17:10) it is the pass rule, the one + // clause saying what carries a ✏️ proposal, in his words quorumRule: { - share: (share, tail) => 'A proposal ✏️ cannot pass until it is preferred by at least ' + - share + ' of the membership' + (tail || '') + '.', - count: (n) => 'A proposal ✏️ cannot pass until it is preferred by at least ' + n + ' members.', + share: (share, tail) => 'A ✏️ proposal passes once at least ' + + share + ' of the membership' + (tail || '') + ' have voted for it and it’s the most preferred option.', + count: (n) => 'A ✏️ proposal passes once at least ' + n + ' members have voted for it and it’s the most preferred option.', }, // **👥 is the one card that says what its number comes to** (Q1490, Ed // 2026-09-21 → why: R-139), a deliberate exception to Q1439 ruling u, @@ -1357,13 +1376,13 @@ window.COPY = (function () { gate: { canpropose: { title: 'Proposals', - why: 'A proposal is a change you write to the document, for the membership to vote on.', + why: 'A proposal is a change to the document that the membership vote on.', waiting: 'Waiting on the start.', done: 'Open — members can propose as soon as they arrive.', }, canjudge: { title: 'Voting', - why: 'A vote is your say on a proposal: you are shown two at a time and choose the one you prefer, or neither.', + why: 'A vote is your say on a proposed change to the document.', waiting: 'Waiting on the rules.', done: 'Open — the rules are settled.', }, @@ -1376,7 +1395,7 @@ window.COPY = (function () { // and tab read *Accept Constitutional Proposals*, the card's own // words, and *Activate Your Membership* went title: 'Constitutional Proposals', - why: 'A 🏛️ is a constitutional proposal: one at a time, returned whole, passing only when all members agree. You are already a member; activating it opens every question, proposal and vote on the rules.', + why: 'A 🏛️ is a constitutional proposal. Members may only make one at a time, and it passes only when all members agree to it.', waiting: 'Waiting on your arrival.', // *Accept 🏛️* like every grant (Q1541.24 (a), Ed 2026-09-25), // replacing Q1502's *Activate 🏛️* @@ -1384,12 +1403,12 @@ window.COPY = (function () { }, pen: { title: 'Founder Actions', - why: 'As the founder of this document, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.', + why: 'As the founder, you have the power to change settings and edit the document at will. Founder Actions are denoted by ✒️. You can give up these powers later if you choose to.', waiting: 'Waiting on the save.', }, shield: { title: 'Founder Veto', - why: 'As the founder of this document, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.', + why: 'As the founder, you have the power to veto choices that the membership make. Founder Veto is denoted by 🛡️. You can give up this power later if you choose to.', waiting: 'Waiting on the save.', }, // **a grant is accepted, not OK'd** (Q1501, Ed 2026-09-22; T44 amended @@ -1591,11 +1610,11 @@ window.COPY = (function () { // until the page reads these, which is a change to the regular page and // waits for its own pass. rule: { - endingNever: 'Changes to the document may be made perpetually.', - endingAfter: (when) => 'No more changes to the document may be made after ' + when + '.', + endingNever: 'Proposals may be made indefinitely.', + endingAfter: (when) => 'Proposals may be made until ' + when + '.', lapseNever: 'Inactive members never lapse and are still counted towards votes.', lapseAfter: (spell) => 'After ' + spell + ', inactive members lapse and automatically abstain from votes.', - rate: (phrase) => 'Members may make a new proposal ✏️ every ' + phrase + '.', + rate: (phrase, cap) => 'Members may make a new ✏️ proposal every ' + phrase + ', up to ' + cap + ' at a time.', unit: { days: 'day', hours: 'hour', minutes: 'minute' }, units: (n, unit) => n + ' ' + unit, address: (slug) => 'The document lives at docs.vote/d/' + slug + '.', diff --git a/design/reference/edit-mode.js b/design/reference/edit-mode.js index 00e2d2e4..892a98c8 100644 --- a/design/reference/edit-mode.js +++ b/design/reference/edit-mode.js @@ -202,6 +202,7 @@ window.EDIT_MODE = (function () { } window.PATCH.set(rt, tmp.innerHTML); rt.classList.toggle('editing', !!env.S.editMode); + rt.classList.toggle('tucked', !!(ridePile() || {}).card); // **the rest position is a measurement, not a constant** (the band's // gotcha about `fitBand`): the pile is lifted from the sticky box's // natural top — the band's bottom edge — back up to the **first line of @@ -222,8 +223,17 @@ window.EDIT_MODE = (function () { // clamp swallowed. const tab = rt.querySelector('.achip'); const tabH = tab ? tab.getBoundingClientRect().height : 30; + // **beside the first clause's own tabs, it stacks above them** (#194, + // Ed 2026-10-02: *it should tuck into the card*): a card open on the + // first clause puts 📝 at the head of its strip, tucked; a pile there + // at rest puts 📝 above the pile, which it had covered 1px off + const pile = ridePile(); rt.style.position = 'static'; - const up = rt.getBoundingClientRect().top - (line.top + (line.lineBox - tabH) / 2); + // the whole of 📝's pile stands clear, the ✒️ 🛡️ slivers beneath it + // a Founder's included + const ownCol = rt.querySelector('.chipcol'); + const pileH = ownCol ? ownCol.getBoundingClientRect().height : tabH; + const up = rt.getBoundingClientRect().top - (pile ? pile.top - pile.gap - pileH : line.top + (line.lineBox - tabH) / 2); rt.style.position = ''; rt.style.setProperty('--ride-up', up.toFixed(2) + 'px'); } @@ -248,6 +258,33 @@ window.EDIT_MODE = (function () { const r = col.getBoundingClientRect(), pt = parseFloat(getComputedStyle(col).paddingTop) || 0; return { top: r.top + pt, bottom: r.top + pt + 24, lineBox: 24 }; } + // **the first clause's own tabs**, when 📝 stands beside them (#194): the + // strip of a card open on the text's first block, or that block's pile at + // rest. Null in edit mode (the text is the card, K31), with no tab there, + // and before 🍾. `gap` is the strip's own, read off a strip — a pile is + // drawn with none, so its class comes off for the one reading. + function ridePile() { + if (env.S.editMode || !constituted()) return null; + const charter = document.getElementById('charter'); + if (!charter) return null; + let first = null; + for (const el of charter.children) { + first = el.classList.contains('prose') ? el.firstElementChild : el; + if (first) break; + } + if (!first) return null; + const card = first.classList.contains('sugg'); + const col = first.querySelector(card ? '.clausehead .chipcol' : ':scope > .chipcol'); + const tab = col && col.querySelector('.achip'); + if (!tab) return null; + const stack = col.classList.contains('stack'); + if (stack) col.classList.remove('stack'); + const gap = parseFloat(getComputedStyle(col).rowGap) || 0; + if (stack) col.classList.add('stack'); + // the column's top, not the tab's: the tab under the pointer that + // opened the card wears its hover lift, a transform the rect includes + return { top: col.getBoundingClientRect().top, gap, card }; + } // **The tab no longer rides** (Q1516 (2), Ed 2026-09-23: the floating // button being enough): it rests at the text's heading and scrolls away // with it, in read mode and in edit mode alike, so there is no detached @@ -275,8 +312,9 @@ window.EDIT_MODE = (function () { // patched (stage 10): the Founder's ✒️ here is a hold, and a render // under it keeps the button the pointer went down on window.PATCH.set(pr, show ? SESSION.proposalRowHtml({ - count: 0, pen: true, disabled: !proseDirty(), - discardDisabled: !proseDirty(), + // never dark (#193): with nothing changed both ends leave edit mode + count: 0, pen: true, disabled: false, + discardDisabled: false, discardTitle: PAGE_COPY.proseRow.discard, title: proseDirty() ? PAGE_COPY.proseRow.save : PAGE_COPY.proseRow.saved, }) : ''); @@ -286,10 +324,7 @@ window.EDIT_MODE = (function () { const b = pr && pr.querySelector('[data-act="row-commit"]'); if (!b) return; const dirty = proseDirty(); - b.disabled = !dirty; b.title = dirty ? PAGE_COPY.proseRow.save : PAGE_COPY.proseRow.saved; - const bin = pr.querySelector('[data-act="row-discard"]'); - if (bin) bin.disabled = !dirty; } // ---- the column's strip before the start (Q1313) -------------------------- // **The same strip as after 🍾** (Ed, 2026-09-11 14:08): B · I at the top diff --git a/design/reference/live.js b/design/reference/live.js index 98cf3653..77437958 100644 --- a/design/reference/live.js +++ b/design/reference/live.js @@ -1279,6 +1279,10 @@ window.LIVE = (function () { return { retry: true }; }).then((data) => { if (data && data.retry) { setTimeout(liveBoot, 4000); return; } + // **the opening skeleton is lifted by the first render** (Q1559): + // until one, the page wears `html.opening` from its and shows + // the bars in place of the birth's *Untitled*; a retry above keeps it + const opened = () => document.documentElement.classList.remove('opening'); if (!data) return; // **The stagehand's controls are asked for only where the host says // it has them** (Q1349, Ed 2026-09-12): `devMail` rides every view @@ -1303,6 +1307,7 @@ window.LIVE = (function () { // at once, so a phone that scanned is one tap from a seat if (data.demoJoin && new URLSearchParams(location.search).get('try') === '1') S.open = 'strtry'; render(); + opened(); api.pushOpen(LIVESLUG); setInterval(() => { // **Nothing rebuilds under a press.** A pen hold is a gesture in @@ -1332,6 +1337,7 @@ window.LIVE = (function () { hydrateApplicant(data.applicant); hydrateS(); render(); + opened(); api.pushOpen(LIVESLUG); setInterval(() => api.pollTick(), 4000); return; @@ -1359,6 +1365,7 @@ window.LIVE = (function () { if (data.text) setProse(data.text); else if (data.provisionalText) setProse(data.provisionalText); render(); + opened(); api.pushOpen(LIVESLUG); setInterval(() => { // **Nothing rebuilds under a press.** A pen hold is a gesture in diff --git a/design/reference/paper.js b/design/reference/paper.js index f6071958..46630a40 100644 --- a/design/reference/paper.js +++ b/design/reference/paper.js @@ -178,6 +178,96 @@ place(rules, left, top, width, breakTop - top); place(text, left, breakBottom, width, bottom - breakBottom); desk.dataset.sheets = '2'; + openAtText(breakBottom); + } + + // ---- the opening place (issue #197; the default since #212) -------------- + // Ed, 2026-10-02: *when the page opens, it aligns near the top of The Text + // page, and people could just scroll upwards* — the Rules being, in the + // members' word, a menu. SURFACE M26. The page opens with the Text sheet's + // top `--sheet-margin` under the bar: the desk gap and the Rules' blank foot + // show above it, never a Rules line. Behind `?rules=below` from #197 until + // Ed's *merge and make it the default* (2026-10-03, #212). + // + // **Decided once, at the first lay that has the break** — the docsep is + // drawn only once the page holds a document, so this is the boot's first + // render, whichever boot — and only for a begun one (`.doc.begun`, which + // the closed page and the stranger's door wear too): a founding page opens + // at the top, and a 🍾 pressed later is not an opening. A fragment in the + // address, or a card already open (`?try=1`'s 👋), is a place of its own and + // wins. + // + // **Pinned, not jumped**: `overflow-anchor` is off (system.css, *the page + // holds its own scroll*), and the faces landing, the band settling and the + // poll all move the Text after the first render, so every lay re-pins it, + // instantly, until the reader does anything at all — a wheel, a touch, a + // key, a press, or a scroll this code did not make. + // + // **A reload or a return keeps whatever restores the reader's place**: the + // browser's own restoration, landing after `load`. It lands where the + // reader was only where the page is already whole by then (the fixture); + // on the live path the column arrives on the view's fetch and the browser + // restores against a page a fraction of its height, which lands nowhere in + // particular (233px for a reader who left at 1800, measured on the demo). + // So the place is noted as the page is left, and on a reload or a return + // the decision waits for the restoration: where the browser put the reader + // back, it stands; where it did not, the page opens at the Text. + // **`?open=top` is a dev seam, not a member's option** (#212): the old + // opening, for a walk or a probe whose measurements are frozen at scroll 0. + const SEAM_TOP = new URLSearchParams(location.search).get('open') === 'top'; + const ON = !SEAM_TOP; + let pinState = ON ? 'undecided' : 'off'; + let pinnedY = null; + const PLACE_KEY = 'open-at:' + location.pathname + location.search; + const navType = (() => { + try { const n = performance.getEntriesByType('navigation')[0]; return n ? n.type : 'navigate'; } catch (e) { return 'navigate'; } + })(); + let leftAt = 0; + if (ON && (navType === 'reload' || navType === 'back_forward')) { + try { leftAt = Number(sessionStorage.getItem(PLACE_KEY)) || 0; } catch (e) { /* no place noted */ } + } + // the restoration is the browser's, a frame or so after `load`; a timer, + // never rAF, for the backgrounded tabs (CLAUDE.md, *Checking a mockup*) + let restoreSettled = !leftAt; + if (!restoreSettled) { + const settle = () => setTimeout(() => { restoreSettled = true; if (window.PAPER) window.PAPER.lay(); }, 150); + if (document.readyState === 'complete') settle(); else addEventListener('load', settle); + } + if (ON) { + addEventListener('pagehide', () => { + try { sessionStorage.setItem(PLACE_KEY, String(Math.round(window.scrollY))); } catch (e) { /* the place goes unnoted */ } + }); + } + function unpin() { + if (pinState !== 'pinned') return; + pinState = 'released'; + for (const t of ['wheel', 'touchstart', 'keydown', 'pointerdown']) removeEventListener(t, unpin, true); + } + function openAtText(breakBottom) { + if (pinState === 'undecided') { + if (!restoreSettled) return; + const restored = leftAt > 0 && Math.abs(window.scrollY - leftAt) <= 2; + const take = doc.classList.contains('begun') && !location.hash && !doc.querySelector('.gshell') && !restored; + pinState = take ? 'pinned' : 'off'; + if (!take) return; + for (const t of ['wheel', 'touchstart', 'keydown', 'pointerdown']) addEventListener(t, unpin, { capture: true, passive: true }); + addEventListener('scroll', () => { + if (pinState === 'pinned' && pinnedY != null && Math.abs(window.scrollY - pinnedY) > 1) unpin(); + }, { passive: true }); + } + if (pinState !== 'pinned') return; + // a card opened by anything — an evaluated click carries no pointerdown — + // is a place of its own, as at the decision + if (doc.querySelector('.gshell')) { unpin(); return; } + const max = document.documentElement.scrollHeight - innerHeight; + // …and so is a scroll made since the last pin and not yet announced by + // its event (a walk's `scrollTo(0, 0)`, a travel's first frame): the + // reader's, unless it is the page clamping a scroll it no longer has room for + if (pinnedY != null && Math.abs(window.scrollY - pinnedY) > 1 && window.scrollY < max - 1) { unpin(); return; } + const nav = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--nav-h')) || 58; + const y = Math.max(0, Math.min(max, Math.round(breakBottom - nav - token('--sheet-margin')))); + pinnedY = y; + if (Math.abs(window.scrollY - y) > 1) window.scrollTo(window.scrollX, y); } let queued = false; @@ -200,7 +290,7 @@ // the page lays itself out over several ticks after boot (the fixture's // washes, the fonts); a last pass once it has settled setTimeout(lay, 300); setTimeout(lay, 1200); - window.PAPER = { lay }; + window.PAPER = { lay, pinState: () => pinState }; } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); diff --git a/design/reference/session-view.html b/design/reference/session-view.html index 346c343e..ba5e0972 100644 --- a/design/reference/session-view.html +++ b/design/reference/session-view.html @@ -1,6 +1,6 @@ - + +