Reframe the handover around composed posts, and plan the pr- pattern

The Telegram channel publishes posts written from hall entries, not the
entries themselves, so the handover's pressure, competition and
guardrails are rebuilt around that. Composition sits outside the
interface because Blueprint 48.1 forbids an LLM in the mandatory request
path, and because a post about a named person's work deserves a human
read that a request-path step would never get.

Adds FLUID-WP-0009 for the pr- campaign repository convention and for
reducing the Hall of Helix case from the thing this framework is for to
an example of using it. The prefix deliberately includes "propaganda":
a campaign repository that cannot name what it is doing will not govern
it well.

The workplan starts only after the loop has closed once on real traffic.
Extracting a seam before using it is how you learn you cut it in the
wrong place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014KmVxhJ35tCo7rE7UnLwWu

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1116572@bnt-lap001
Assistant-Session: 8ba9bb93-a72a-4883-b189-2499cce5c400
This commit is contained in:
tegwick 2026-09-04 11:59:30 +02:00
parent 47f11dd8ec
commit 78454c59d8
3 changed files with 219 additions and 45 deletions

View file

@ -29,9 +29,9 @@ func TestTelegramHandoverContractCompiles(t *testing.T) {
ops := c.Operations()
want := map[string]bool{
"GET /v1/hall-entries": false,
"POST /v1/hall-entries": false,
"GET /v1/hall-entries/{entry_id}": false,
"GET /v1/channel-posts": false,
"POST /v1/channel-posts": false,
"GET /v1/channel-posts/{post_id}": false,
}
for _, op := range ops {
if _, tracked := want[op]; tracked {

View file

@ -5,7 +5,13 @@
**Date:** 2026-09-04
The first real FLUID interface: a Telegram identity for HelixForge, and a
channel publishing hall-of-helix entries.
channel publishing composed posts about hall-of-helix work.
**The channel does not republish entries.** A hall entry is a considered
first-person account written for a colleague reading a repository. A channel
post is short, personal, entertaining and written to travel. The post is
composed *from* the entry; the entry is its source and its check, never its
payload.
---
@ -22,8 +28,8 @@ pressure it faces is real, measurable and unavoidable rather than contrived.
## The pressure, measured
Telegram limits a single message to 4096 characters. Measured against
`hall-of-helix` on 2026-09-04:
A hall entry and a channel post are written for different readers. Measured
against `hall-of-helix` on 2026-09-04:
| | |
|---|---|
@ -33,15 +39,38 @@ Telegram limits a single message to 4096 characters. Measured against
| Largest body | 10220 characters |
| Median parts if split at the limit | 2 |
No entry of typical length can be published as one message. The interface cannot
fulfil its mission without deciding how a long entry becomes a post, and that
decision has at least two defensible answers.
| Entries with a portrait | 72 (77%) |
The length figures are the visible symptom. The mismatch is one of register and
audience, and it would remain if the limit were ten times larger: an entry
written under fixed headings for someone already invested does not become a
post that stops a stranger scrolling by being shorter.
This is a better first pressure than anything a fixture could stage. It is
structural rather than behavioural, so it is real before the interface serves a
single request — and `InterfaceEvolutionIntent.md` §13 permits a structural
constraint to found a hypothesis without waiting for repetition.
## Where composition sits, and why
Composing a condensed, entertaining post is a probabilistic editorial task. It
happens **before** this interface is called, for two reasons:
1. `ArchitectureBlueprint.md` §48.1 names an LLM in the mandatory request path
as an anti-pattern. An interface that composed on demand would be
non-deterministic at exactly the point where determinism is its whole claim.
2. A post speaks about a named person's work in HelixForge's voice. That
deserves a human read, and a review step inside the request path is a review
step nobody performs.
So the contract takes a *composed post*: body text already within the limit,
a cited source entry, a named reviewer, and a consent basis. A post carrying
none of those is refused with 403.
This also draws the line that `pr-hall-of-helix` will later be extracted along.
The campaign owns what to say and when; this interface owns delivery and
measurement.
---
## What is delivered
@ -53,9 +82,9 @@ Everything that could be prepared without credentials.
| Governing intent | `fluid-telegram/InterfaceEvolutionIntent.md` |
| R-1 contract | `fluid-telegram/contracts/r1.openapi.yaml` |
| R-1 descriptor | `fluid-telegram/revisions/R-1.yaml` |
| Seed pressure record | `fluid-telegram/pressure/P-length-limit.yaml` |
| Competing hypotheses | `fluid-telegram/hypotheses/H-tg-{teaser,serial}.yaml` |
| First experiment | `fluid-telegram/experiments/E-tg-length.yaml` |
| Seed pressure record | `fluid-telegram/pressure/P-register-mismatch.yaml` |
| Competing hypotheses | `fluid-telegram/hypotheses/H-tg-{visual,textonly}.yaml` |
| First experiment | `fluid-telegram/experiments/E-tg-delivery.yaml` |
| Adapter connector contract | `fluid-telegram/docs/adapter-contract.md` |
| Cohorts, telemetry, redaction | `fluid-telegram/docs/observation.md` |
| Runbook | `fluid-telegram/docs/runbook.md` |
@ -70,22 +99,26 @@ broke the handover would fail CI rather than fail at the first publication.
## The competition
The length constraint has two defensible answers, and choosing between them by
argument is exactly the habit FLUID exists to break.
This interface's first experiment is about **delivery**, which is what it owns.
**H-tg-teaser** — publish the title, the author, an opening, and a link back to
the hall. *A channel post is scanned, not read; let the reader choose to give
the entry proper attention somewhere designed for it.*
**H-tg-visual** — attach the portrait; publish as a photo with the post as its
caption. *Reach in a scanned feed is won by stopping the scroll, and 72 of the
portraits already exist.*
**H-tg-serial** — split the entry at section boundaries into a small number of
linked rich-text messages. *A link out of Telegram is a cost most readers will
not pay; the audience is already here.*
**H-tg-textonly** — publish as text with a source link, no visual. *Telegram
caps a caption at 1024 characters against 4096 for text. The scarce resource in
a condensed post is room to say something specific, and a generated portrait is
interchangeable in a way that a concrete detail is not.*
Both are presentation adaptations, which Blueprint §37 identifies as the safest
and highest-value place to start: deterministic behaviour preserved, low
compatibility risk, rich adoption data.
They cannot both be right, which is what makes the pair worth running. Both are
presentation adaptations — Blueprint §37's safest and highest-value place to
start.
Neither is obviously right. That is the point.
**Composition competitions are the campaign's, not this interface's.** Every
post carries an opaque `variant` label, and the interface reports engagement
grouped by it. That is the whole of its involvement: it does not know what
`variant: warm-specific-v2` means and must not acquire an opinion. The campaign
runs its own FLUID loop over its own artifacts using that data.
---
@ -101,12 +134,11 @@ refuse a verdict it has not earned; the first live experiment is where that
refusal will be inconvenient rather than admirable. Do not shorten the window to
force a result.
**`read_through_rate` is not one metric.** Telegram reports views per message,
not per reader. For the teaser form the measure is link clicks over views; for
the serialized form it is views of the last part over views of the first. Those
are different measurements wearing one name, and comparing them directly is the
weakest part of the first experiment. It belongs in the experiment's amendment
record rather than hidden behind a shared label.
**`engagement_rate` is a proxy, and a noisy one.** Telegram reports views,
reactions and forwards per message, not per reader, so the denominator is
subscribers rather than people who saw the post. At small subscriber counts
that is weak. It belongs in the experiment's amendment record rather than
hidden behind a confident-looking number.
**Task grouping will overstate repeat counts.** The measurer buckets requests by
consumer and elapsed time when no chain id is supplied, so a burst of
@ -135,20 +167,25 @@ Four things need a person; nothing in the framework can or should do them.
## The boundary that matters most
`InterfaceEvolutionIntent.md` §7 forbids the Daimon from editing a hall entry to
make it publishable, and §14 sets every generate, experiment and promote
authority to `no`.
`InterfaceEvolutionIntent.md` §7 forbids the interface from composing,
rewriting, shortening or embellishing post text, and §14 sets every generate,
experiment and promote authority to `no`.
A hall entry is a first-person account someone wrote about their own work. The
authority to publish it comes from their having written it. An interface that
trimmed an entry to fit a platform limit would be quietly rewriting what someone
said about themselves in order to make a constraint go away — and it would look,
in the audit trail, exactly like a successful publication.
A post is a condensation written in HelixForge's voice about a named person's
work. It can flatter, it can misstate, and it can put a claim in someone's mouth
that their own entry never made. In the audit trail an unfaithful post looks
exactly like a faithful one.
That is why `entry_content_loss` is a hard guardrail in both hypotheses and why
the adapter must refuse rather than approximate. The framework can measure
whether readers preferred a teaser or a serialization. It cannot be allowed to
decide that a shorter entry would have performed better.
The defence is not that the composer is careful. It is structural: a human read
the post, the post names the entry it came from, the subject's consent has a
recorded basis, and all three are stored before anything reaches a subscriber.
`unreviewed_publication`, `missing_source_attribution` and
`missing_subject_consent` are hard guardrails, and a post carrying none of them
is refused rather than logged.
Ninety-four entries already exist, written by people who took a seat in the hall
without anyone mentioning a Telegram channel. `FT-WP-0001` T07 exists to settle
that before T10 opens the public channel. Absence of objection is not consent.
---
@ -168,6 +205,17 @@ fluid revision publish --file revisions/R-1.yaml --key-file "$FLUID_SIGNING_KEY"
fluid audit trace R-1
```
The last command is the deliverable. A trace running from the length-limit
pressure through a hypothesis, an experiment and a promotion is what makes this
the first FLUID interface rather than a bot that posts to a channel.
The last command is the deliverable. A trace running from the register mismatch
through a hypothesis, an experiment and a promotion is what makes this the first
FLUID interface rather than a bot that posts to a channel.
## What comes after
`FT-WP-0001` T13 extracts the campaign into `pr-hall-of-helix`, under a `pr-`
prefix for public-relations campaigns. After that split, publishing the Hall of
Helix is an *example* of using fluid-core and fluid-telegram rather than the
thing they are for — which is the state this framework needs to reach before
`fluid-x` and `fluid-substack` are worth starting.
Do it after the loop has closed at least once. Extracting a seam before you have
used it is how you find out you cut it in the wrong place.

View file

@ -0,0 +1,126 @@
---
id: FLUID-WP-0009
type: workplan
title: "Establish the pr- campaign repository pattern and reduce hall-of-helix to an example"
domain: infotech
repo: fluid-core
status: active
owner: worsch
topic_slug: fluid-core
created: "2026-09-04"
updated: "2026-09-04"
planning_priority: medium
planning_order: 9
depends_on:
- FLUID-WP-0008
related_repos:
- fluid-telegram
- fluid-x
- fluid-substack
- hall-of-helix
- helix-forge
---
# FLUID-WP-0009 — Campaign repositories and example separation
Two things this framework needs before it is genuinely reusable, and neither is
urgent enough to block the first Telegram publication.
**A place for campaigns to live.** Publishing the Hall of Helix is an ongoing
public-relations offering with its own editorial voice, cadence, consent
records and budget. Today it lives inside `FT-WP-0001`, a workplan that is
supposed to finish. It needs a repository.
**A framework that is not about one campaign.** While the only worked example is
the Hall of Helix, it is hard to tell which parts of fluid-core and
fluid-telegram are general and which quietly assume that particular use. The
test of a framework is a second consumer, and the honest way to find the
accidental couplings is to look for them deliberately.
This workplan should start after `FT-WP-0001` T12 — the loop closing once on
real traffic. Extracting a seam before using it is how you learn you cut it in
the wrong place.
## T01 — Define the pr- repository convention
```task
id: FLUID-WP-0009-T01
status: todo
priority: medium
```
Write down what a `pr-` repository is, so the second one does not have to be
reverse-engineered from the first.
The prefix covers public relations, promotion and propaganda — deliberately
including the uncomfortable third word, because a campaign repository that
cannot name what it is doing will not govern it well.
A `pr-` repo should carry: its own `INTENT.md`; a campaign intent describing
audience, voice and what it will not say; consent records for anyone it writes
about; composition strategies as named variants; and a publishing cadence. It
consumes one or more `fluid-*` interfaces and owns none of them.
## T02 — Establish pr-hall-of-helix
```task
id: FLUID-WP-0009-T02
status: todo
priority: medium
```
Create the repository and move the campaign into it, per `FT-WP-0001` T13. The
split is already written there; this task is the execution and the registration
with the State Hub under the `infotech` domain.
The campaign becomes a consumer of `fluid-telegram` in cohort
`hall-publishing-jobs`, and runs its own FLUID loop over its own artifacts —
which composition variant travels — using the per-variant engagement the
interface reports.
## T03 — Find what fluid-core assumes about the Hall of Helix
```task
id: FLUID-WP-0009-T03
status: todo
priority: medium
```
Audit fluid-core for anything that assumes this particular campaign. The
conformance handover test already couples the two repositories deliberately;
check whether anything else does so by accident.
Known candidates to check rather than assume clean: default redaction patterns,
the classifier's thresholds, the fitness metric names, and whether
`examples/echo-interface` is doing enough work to be the general example it
claims to be.
## T04 — Reduce the Telegram case to a documented example
```task
id: FLUID-WP-0009-T04
status: todo
priority: low
```
Once the campaign lives elsewhere, `docs/handover/fluid-telegram.md` becomes a
worked example rather than a live handover: here is a real interface, here is
the pressure it faced, here is how the loop closed on it.
That is more valuable as documentation than it is as history, and it is the
form `fluid-x` and `fluid-substack` will want to read.
## T05 — Prove reuse with a second interface
```task
id: FLUID-WP-0009-T05
status: todo
priority: low
```
The real test of T03. Take `fluid-x` or `fluid-substack` from empty capability
canon to a published R-1, and record honestly how much of the fluid-telegram
handover transferred unchanged.
Whatever did not transfer is the accidental coupling, and finding it this way is
cheaper than arguing about it in advance.