From bbbe8ec312bfa96c22b97752ec03a24154568f5b Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Sun, 11 Oct 2026 10:34:08 -0400 Subject: [PATCH] Writing for players: the editing passes' lessons, and the template written by them The conventions gain a list of the habits players read as generated text (a semicolon in every paragraph, statement-colon-explanation, "The" plus an abstract noun as a heading, clipped fragments in threes, closing aphorisms, "X, not Y", the page talking about itself), and of the errors that come from inherited text: hidden information where there is none, points where there are none, "both" at a larger table, unchecked claims, and ledger-stored words reworded. new-game.sh points at the list. The template follows it: the about link loses 'a labor of love', the tabs are Origins and Games so far, and the hall, lobby, room, report slip, table talk, guide and demo board read as Waving Hands' and Hnefatafl's now do. The landing page and family.json carry the two games' new taglines and lose their semicolons and the tip jar's aphorism. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0141G6xqLeNRYEtviLWSB5Up --- CONVENTIONS.md | 64 +++++++++++++++---- family.json | 4 +- gamehall/site/index.html | 16 ++--- new-game.sh | 3 +- template/src/app.html | 6 +- template/src/lib/components/Board.svelte | 4 +- template/src/lib/components/Hall.svelte | 18 +++--- template/src/lib/components/Lobby.svelte | 8 +-- template/src/lib/components/ReportSlip.svelte | 4 +- template/src/lib/components/TableTalk.svelte | 2 +- template/src/routes/guide/+page.svelte | 12 ++-- template/src/routes/join/[code]/+page.svelte | 4 +- template/src/routes/rules/+page.svelte | 2 +- 13 files changed, 95 insertions(+), 52 deletions(-) diff --git a/CONVENTIONS.md b/CONVENTIONS.md index 1b187b2..3eb023a 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -11,10 +11,10 @@ README. *Open a table*, *or join one* with a four-letter code, then *your games* (the ledger of tables this browser holds a seat at, with whose move it is and unread talk; for someone coming back it comes first, and finished - games wait further down), *your reports to the keeper*, and *about this game — - a labor of love*, which opens the about panel: a dialog of tabs (The - game, The tally, More games, Send word; a game may add its own, as Dip's - Playing here), `TabbedDialog.svelte`, with each game's words in its hall. + games wait further down), *your reports to the keeper*, and *about this game*, + which opens the about panel: a dialog of tabs (Origins, Games so far, + More games, Send word; a game may rename the first two in its own world, + as Waving Hands' Duels so far, or add its own, as Dip's Playing here), `TabbedDialog.svelte`, with each game's words in its hall. The address names the dialog (`#about`, `#about/tally`), so a plain link opens it; a game may put its guide and rules in dialogs the same way, as Dip does. Its strip of tabs is `Tabs.svelte`. @@ -86,8 +86,8 @@ README. - **The hosting line** follows the family list in the about panel: the games are free and will stay free, the servers cost a few dollars a month, and ko-fi.com/kestrelsnest is where anyone may chip in. It says - nothing is unlocked by it and nothing withheld without it, and it - never names a game's rights holder: a tip is for the hosting, not the + every game stays whole either way, and it never names a game's rights + holder: a tip is for the hosting, not the game. - **Preferences** are what a browser remembers about its player: a panel @@ -237,16 +237,58 @@ thing will happen), and the image carries it. - **Our own words.** An older implementation's prose is a guide to the tone, never a source to copy. - **Plain chrome.** Headings, tabs, captions and notes say what is there - in ordinary words. Avoid the construction a label, a colon, then an - explanation ("The ledger: every gesture..."), and headings made of "The" - and an abstract noun ("The game", "The tally"); readers take both as the - mark of generated text. The images live in what happens, not in the - labels around it. + in ordinary words. The images live in what happens, not in the labels + around it. The patterns readers take as generated text are listed under + "Writing for players" below. - **Changing text is not changing rules.** Chronicle wording is not part of the ledger, so it ships without a rules revision; the deploy gate compares outcomes, not sentences. A test that pins a phrase should pin the fact inside it. +## Writing for players + +Players have learned the habits of generated text, and once they spot one +they trust the rest of the page less. Every game built from this kit has +needed an editing pass to remove them. Write without them from the start, +and read each new page against this list before it ships. + +- **One semicolon is fine. A semicolon in every paragraph is a tell.** Use + two sentences, or join with "and" or "so". Lists of rules may keep their + semicolons. +- **No statement, colon, explanation.** "The table is yours: seat a bot, + invite a friend" becomes "You can seat a bot or invite a friend". The + same goes for a label and a colon at the start of a line ("Shieldwall: + two or more pieces..."), and for a heading that ends in a colon. +- **No headings made of "The" and an abstract noun** ("The game", "The + tally"). Name what is there ("Origins", "Games so far"). +- **No clipped fragments in threes.** "Four towers. One crown. No second + chances." Write one sentence instead. +- **No closing aphorisms.** A paragraph ends on its last fact, not on a line + meant to sound wise ("This is the whole art", "Nothing is unlocked by it + and nothing is withheld without it"). +- **No "X, not Y" flourishes** ("drawn, not borrowed"). Say what it is. +- **No page that talks about itself.** Avoid "This page walks the room", "the + page keeps watch" and "a walk through the page". Describe the game and + what the player does. +- **No spaced em dashes** in the interface, and few anywhere. +- **Nothing copied from another game that is untrue here.** Text carried over + from the kit or a sibling game is the most common source of errors. Look + for hidden information in a game with none, points in a game without + them, a bot's name from another game, and "both" at a table that seats + more than two. Read every inherited line as if it were new. +- **Every claim checked.** A date, a count, "only" or "never" is checked + against the source and the engine. "A thousand years before chess" was + wrong, and so was "a shield is the only defence". +- **Words for any table size.** Use "every player" for what each does on a + turn, and plurals for opponents ("your rivals"), unless the game seats + exactly two. +- **Words the ledger stores are fixed.** An end-of-game reason written to a + ledger line is matched later by exact text. Rewording + one needs a migration, not just an edit. + +The game's own voice (above) is not on this list. The chronicle's images, +the in-world names and the keeper are what the editing passes protect. + ## The move - **Every history reads top to bottom**, oldest first, and scrolls to the diff --git a/family.json b/family.json index 83cbeac..3305950 100644 --- a/family.json +++ b/family.json @@ -14,7 +14,7 @@ "slug": "waving-hands", "name": "Waving Hands", "url": "https://hands.kestrelsnest.social", - "pitch": "Duelling wizards. Two hands each. The right run of gestures is a spell.", + "pitch": "Wizards duel with their hands, and the right run of gestures is a spell.", "players": "2 to 12", "origin": "Richard Bartle's pencil-and-paper duel, 1977" }, @@ -22,7 +22,7 @@ "slug": "hnefatafl", "name": "Hnefatafl", "url": "https://tafl.kestrelsnest.social", - "pitch": "The Viking board game. One king, one throne, four corners, and a ring of attackers closing in.", + "pitch": "The Viking board game of a king breaking for safety and a ring of attackers closing in.", "players": "2", "origin": "the Norse tafl game, reconstructed from Linnaeus's 1732 notes; Copenhagen rules or Tablut" }, diff --git a/gamehall/site/index.html b/gamehall/site/index.html index a3c00e5..9dacc3c 100644 --- a/gamehall/site/index.html +++ b/gamehall/site/index.html @@ -159,7 +159,7 @@
The Waving Hands share card: a column of gesture letters beside the title.

Waving Hands

-

Duelling wizards. Two hands each. The right run of gestures is a spell.

+

Wizards duel with their hands, and the right run of gestures is a spell.

2 to 12 players · Richard Bartle's pencil-and-paper duel, 1977

Play at hands.kestrelsnest.social →
@@ -169,7 +169,7 @@
A hnefatafl board at the start of a game: the king on the throne, defenders around him, attackers on the four edges.

Hnefatafl

-

The Viking board game. One king, one throne, four corners, and a ring of attackers closing in.

+

The Viking board game of a king breaking for safety and a ring of attackers closing in.

2 players · the Norse tafl game, by the Copenhagen rules or Linnaeus's 1732 Tablut

Play at tafl.kestrelsnest.social →
@@ -191,11 +191,11 @@

How a table works

    -
  • Open a table and you get a four-letter code and a link. Send it to a friend; they sit down and you begin.
  • -
  • No one home? Every game seats the house: an automaton, a housecarl, a rival wizard, an envoy. Play now, alone.
  • -
  • Nothing to sign up for. Your browser holds your seat. Close the tab and come back; the game is where you left it.
  • +
  • Open a table and you get a four-letter code and a link. Send it to a friend, and begin once they sit down.
  • +
  • No one home? Every game can seat the house, whether an automaton, a housecarl, a rival wizard or an envoy, so you can play right away on your own.
  • +
  • Nothing to sign up for. Your browser holds your seat. Close the tab and come back, and the game is where you left it.
  • Watchers welcome. Anyone with the link can sit in the Peanut Gallery and, if the host allows, join the table talk.
  • -
  • Something went wrong? Every table has a report button that reaches the keeper, pinned to the exact moment.
  • +
  • Something went wrong? Every table has a report button that reaches the keeper with the exact moment attached.
@@ -208,12 +208,12 @@

The hosting

-

The games are free and will stay free. They run on small servers that cost a few dollars a month; anyone who wants to chip in toward the hosting can at ko-fi.com/kestrelsnest. Nothing is unlocked by it and nothing is withheld without it.

+

The games are free and will stay free. They run on small servers that cost a few dollars a month, and anyone who wants to chip in toward the hosting can do so at ko-fi.com/kestrelsnest. Every game stays whole either way.

-

Wiz-War is Tom Jolly's design; the rights rest with Steve Jackson Games. Waving Hands is Richard Bartle's. Diplomacy is Allan B. Calhamer's design, and its name is a trademark of Hasbro. Nothing here is official or endorsed by Steve Jackson Games, Richard Bartle or Hasbro. Hnefatafl belongs to everyone; it is a thousand years old.

+

Wiz-War is Tom Jolly's design, and the rights rest with Steve Jackson Games. Waving Hands is Richard Bartle's. Diplomacy is Allan B. Calhamer's design, and its name is a trademark of Hasbro. Nothing here is official or endorsed by Steve Jackson Games, Richard Bartle or Hasbro. Hnefatafl belongs to everyone, and is more than a thousand years old.

diff --git a/new-game.sh b/new-game.sh index 8511026..d09278f 100755 --- a/new-game.sh +++ b/new-game.sh @@ -38,7 +38,8 @@ Next: 2. Fill in the hall's pitch, tag and about panel (src/lib/components/Hall.svelte), the rules page, the guide, src/app.html's description, and the palette in src/app.css. Write every line a player reads in the game's own voice: an image with each - fact (docs/conventions.md, "The voice"). + fact (docs/conventions.md, "The voice"). Then read every page against + "Writing for players" in the same file, the template's own lines included. Add static/og.png (1200 by 630) and static/apple-touch-icon.png (180 by 180): app.html already points at them, and the static fallback serves the hall's HTML for a missing file, so a share card without the image breaks quietly. diff --git a/template/src/app.html b/template/src/app.html index df1d460..92238d9 100644 --- a/template/src/app.html +++ b/template/src/app.html @@ -7,21 +7,21 @@ __NAME__ - + - + - + %sveltekit.head% diff --git a/template/src/lib/components/Board.svelte b/template/src/lib/components/Board.svelte index b3b4034..37dd270 100644 --- a/template/src/lib/components/Board.svelte +++ b/template/src/lib/components/Board.svelte @@ -67,8 +67,8 @@ {:else}
-

Round {g.rounds.length + 1}: name a number

-

Every player lays a card face down; the highest card nobody else laid takes the point. First to {TARGET}.

+

Name a number for round {g.rounds.length + 1}

+

Every player lays a card face down, and the highest card nobody else laid takes the point. The first to {TARGET} points wins.

{#each Array.from({ length: HIGHEST }, (_, i) => i + 1) as n (n)} diff --git a/template/src/lib/components/Hall.svelte b/template/src/lib/components/Hall.svelte index 63bd274..0aa144f 100644 --- a/template/src/lib/components/Hall.svelte +++ b/template/src/lib/components/Hall.svelte @@ -196,9 +196,9 @@
0}>

__NAME__

-

One line that says what the game is.

+

One sentence that says what the game is, in its own world.

Two or three sentences for someone who has never heard of it: what you do on a turn, what makes it fun, and that it is free to play here against the bot or with friends.

- +
@@ -232,7 +232,7 @@
-

A code opens the door either way: take a seat if one is free, or watch from the Peanut Gallery.

+

With a code you can take a seat if one is free, or watch from the Peanut Gallery.

or bring a seat from another device @@ -309,7 +309,7 @@

Where the game comes from, who made the original and when, and how the keeper of this page first met it.

What this version keeps and what it changes. Where the rules text it follows lives (the rules page), and any borrowed art or text with its notice.

-

It is free and keeps no accounts. A game between people lives on a small server as an append-only ledger of moves, so it can be replayed from its first move, and each player is shown only what the rules let them see.

+

It is free and keeps no accounts. A game between people lives on a small server as an append-only ledger of moves, so it can be replayed from its first move. Each player is shown only what the rules let them see (drop this sentence if nothing in the game is hidden).

{/snippet} {#snippet aboutTally()} @@ -329,15 +329,15 @@ {/if}

- The games of Kestrel's Hall are free and will stay free. They run on small servers that cost a few dollars a month; anyone who wants to chip in toward the hosting can at - ko-fi.com/kestrelsnest. Nothing is unlocked by it and nothing is withheld without it. + The games of Kestrel's Hall are free and will stay free. They run on small servers that cost a few dollars a month, and anyone who wants to chip in toward the hosting can do so at + ko-fi.com/kestrelsnest. Every game stays whole either way.

__NAME__ is its designer's.

{/snippet} {#snippet aboutWord()}
-

A rule read wrong, a bug, a game you would like to tell of: write to eric@ericwagoner.com, @kestrelsnest.social on Bluesky, or @eric@toots.kestrelsnest.social on Mastodon. The keeper of this hall roosts at kestrelsnest.social.

+

If a rule reads wrong, something breaks, or you have a game to tell of, write to eric@ericwagoner.com, @kestrelsnest.social on Bluesky, or @eric@toots.kestrelsnest.social on Mastodon. The keeper of this hall roosts at kestrelsnest.social.

{/snippet} {hostName} is gathering players. Sit down above, or watch from the gallery until the game begins.

{:else} -

The table is full. You watch from the Peanut Gallery; the game begins when {hostName} says.

+

The table is full, so you watch from the Peanut Gallery. The game begins when {hostName} says.

{/if} {:else}

room {v.roomId}

@@ -84,13 +84,13 @@
  • {v.size - v.seats.length - v.expected.length} of {v.size} seats empty{#if v.audience > 0}; {v.audience} in the gallery{v.galleryTalk ? ', allowed to talk' : ''}{/if}
  • {#if v.rematchOf} -

    A rematch of table {v.rematchOf}: the same company, seats held for those not yet back.

    +

    This is a rematch of table {v.rematchOf}, with seats held for those not yet back.

    {/if} {#if choices && !room.spectating}

    Your ranking

    - Best first. When the game begins, the deal reads everyone's ranking; nobody sees another player's. + Best first. When the game begins, the deal reads everyone's ranking, and nobody sees another player's. {#if !v.yourRanking}Until you move one, you have no preference and take what is left.{:else}Your ranking is kept.{/if}

      @@ -132,7 +132,7 @@

      {v.keeper} keeps this site and answers every call, sometimes within the minute, sometimes after a night's sleep.{#if keeperHour} It is {keeperHour} where {v.keeper} lives.{/if} The seat is held either way.

      {:else if v.challenge} -

      {v.keeper} has been called to the table by {room.nameOf(v.challenge.by)}.{#if keeperHour} It is {keeperHour} there.{/if} A seat is held: if they can come now they will take it; if they are asleep or away they will leave a word below when they can. Meanwhile the table is yours: seat a bot, invite a friend, or open another game in a new tab and play on. This table keeps its place in your hall until you come back.

      +

      {v.keeper} has been called to the table by {room.nameOf(v.challenge.by)}.{#if keeperHour} It is {keeperHour} there.{/if} A seat is held for them. If they can come now, they will take it. If they are asleep or away, they will leave a word below when they can. In the meantime you can seat a bot, invite a friend, or play another game in a new tab. This table keeps its place in your hall until you come back.

      {/if} {/if} {#if room.error}

      {room.error}

      {/if} diff --git a/template/src/lib/components/ReportSlip.svelte b/template/src/lib/components/ReportSlip.svelte index e0374af..2d50b44 100644 --- a/template/src/lib/components/ReportSlip.svelte +++ b/template/src/lib/components/ReportSlip.svelte @@ -61,7 +61,7 @@

      Something amiss?

      {#if sent} -

      Recorded beside the room's ledger, with the round. Thank you; the keeper will study the moment.{#if error} {error}{/if}

      +

      Recorded beside the room's ledger, with the turn. Thank you. The keeper will look into it.{#if error} {error}{/if}

      {:else} -

      The room code and the round ride along; the keeper can replay the game to this moment.

      +

      The room code and the turn are sent with it, so the keeper can replay the game to this moment.

      {#if error}

      {error}

      {/if}
      diff --git a/template/src/lib/components/TableTalk.svelte b/template/src/lib/components/TableTalk.svelte index 42cb23e..b2e0f1f 100644 --- a/template/src/lib/components/TableTalk.svelte +++ b/template/src/lib/components/TableTalk.svelte @@ -60,7 +60,7 @@ {:else if room.spectating} -

      The gallery listens; the host has not let it talk.

      +

      The host has not let the gallery talk at this table.

      {:else}
      diff --git a/template/src/routes/guide/+page.svelte b/template/src/routes/guide/+page.svelte index c3fb32c..bb18188 100644 --- a/template/src/routes/guide/+page.svelte +++ b/template/src/routes/guide/+page.svelte @@ -18,7 +18,7 @@
      {#if room}Back to your game{' · '}{/if}Back to the hall · The rules

      How to play

      -

      Two sentences that set the scene and say what this page walks through.

      +

      Two sentences that set the scene: who the player is, and what they are trying to do.