Compare commits
2 commits
47f11dd8ec
...
b40c0e01e1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b40c0e01e1 | ||
| 78454c59d8 |
3 changed files with 225 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.
|
||||
|
|
|
|||
132
workplans/FLUID-WP-0009-campaign-repos-and-example-separation.md
Normal file
132
workplans/FLUID-WP-0009-campaign-repos-and-example-separation.md
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
---
|
||||
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
|
||||
state_hub_workstream_id: "b6459dd0-fc4f-54a4-a2e9-d7b83cff6c6e"
|
||||
---
|
||||
|
||||
# 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
|
||||
state_hub_task_id: "2a3aab85-e464-5b17-980f-1896ecd3794f"
|
||||
```
|
||||
|
||||
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
|
||||
state_hub_task_id: "7d40da89-9a4d-5358-ae2b-7a1a1339efff"
|
||||
```
|
||||
|
||||
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
|
||||
state_hub_task_id: "d9c69244-19ef-5eaf-9f43-d0374d6278de"
|
||||
```
|
||||
|
||||
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
|
||||
state_hub_task_id: "ec77e62c-1251-5331-a1a8-42985fb97b94"
|
||||
```
|
||||
|
||||
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
|
||||
state_hub_task_id: "227d0d53-f325-5d95-8a18-d56603c820fd"
|
||||
```
|
||||
|
||||
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