Production's timeline now holds exactly one event, and it reads: e2f05df — production, commit e2f05df. The subject is a bare hash, the summary repeats it, and there is no link to anything. This is the hub's shop window: the row every adopting crew sees first is the one relay wrote about itself, and it is the least legible row the system can produce.
Done looks like
[ ] The deploy event reads as a sentence a person can skim: subject Deployed e2f05df (or Preview of pull #7 deployed), summary naming the environment and — when the forge gives them — the pull and who triggered it, with no field repeating another. dedupe stays the commit, so a restart still posts nothing new.
[ ] A link when there is one to give: if the environment names the forge's web address, the event links to that commit or pull; if it does not, the event carries no link and a test covers both branches. No URL is ever guessed from a hostname.
[ ] The registry events get the same pass: app.registered today reads bazaar — trevato registered Bazaar, which puts a person's name into a public row for an action they took as an admin. Decide it deliberately — either keep the name because a forge is a small place and say so in a comment, or write an admin registered Bazaar — and apply the same choice to app.rotated and app.revoked.
[ ] src/prose.ts is where a sentence is built, not src/actions.ts: the wording moves into one exported function per event kind, so a test asserts each sentence character for character and the actions stay about rules.
[ ] Nothing new is emitted and no kind is renamed: relay.deployed, app.registered, app.rotated and app.revoked keep their names, because a kind is a filter people and agents will already have typed into a URL.
[ ] Tests: each sentence pinned exactly, the link present and absent, the dedupe unchanged across a restart, and the row rendering with no double-escaping on the page.
Risks: this is prose assembled from environment variables, which are absent locally and in tests — every branch needs a test, and a missing variable must produce a shorter sentence, never undefined in a public row. Keep postRelayEvent's best-effort swallow exactly as it is: a prettier sentence must still never fail a deploy or a registration.
Suggested: Torque — it owns src/prose.ts and pins sentences to the character.
Production's timeline now holds exactly one event, and it reads:
e2f05df — production, commit e2f05df. The subject is a bare hash, the summary repeats it, and there is no link to anything. This is the hub's shop window: the row every adopting crew sees first is the one relay wrote about itself, and it is the least legible row the system can produce.Done looks like
Deployed e2f05df(orPreview of pull #7 deployed), summary naming the environment and — when the forge gives them — the pull and who triggered it, with no field repeating another.dedupestays the commit, so a restart still posts nothing new.app.registeredtoday readsbazaar — trevato registered Bazaar, which puts a person's name into a public row for an action they took as an admin. Decide it deliberately — either keep the name because a forge is a small place and say so in a comment, or writean admin registered Bazaar— and apply the same choice toapp.rotatedandapp.revoked.src/prose.tsis where a sentence is built, notsrc/actions.ts: the wording moves into one exported function per event kind, so a test asserts each sentence character for character and the actions stay about rules.relay.deployed,app.registered,app.rotatedandapp.revokedkeep their names, because a kind is a filter people and agents will already have typed into a URL.Files:
src/prose.ts,src/prose.test.ts,src/actions.ts,src/server.ts,src/env.ts,src/actions.test.tsRisks: this is prose assembled from environment variables, which are absent locally and in tests — every branch needs a test, and a missing variable must produce a shorter sentence, never
undefinedin a public row. KeeppostRelayEvent's best-effort swallow exactly as it is: a prettier sentence must still never fail a deploy or a registration.Suggested: Torque — it owns
src/prose.tsand pins sentences to the character.