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:
parent
47f11dd8ec
commit
78454c59d8
3 changed files with 219 additions and 45 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
126
workplans/FLUID-WP-0009-campaign-repos-and-example-separation.md
Normal file
126
workplans/FLUID-WP-0009-campaign-repos-and-example-separation.md
Normal 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue