fluid-telegram/workplans/FT-WP-0001-telegram-identity-and-hall-channel.md
tegwick 5a785b9d9f Ground the repo docs and move presence provisioning to a declared spec
Align AGENTS.md with the FT-WP- prefix the hub already registered, and
rewrite INTENT.md and SCOPE.md, which were still the generated stubs and
carried none of the composed-post framing the interface is built around.

Fix two artifact inconsistencies: R-1's retry comment still described
POST /v1/hall-entries idempotent on entry_id, from before the reframe to
composed posts; and the seed pressure record listed only one of the two
hypotheses that cite it. Rename it to match its id.

Replace the manual T01-T03 route with FT-WP-0002. The Bot API cannot
create a bot or a channel, so provisioning is an MTProto client acting as
a designated operator account (Canon INT-03), driven from a declared
presence spec owned by the campaign. One bounded human bootstrap remains
and docs/provisioning.md says why it cannot be removed.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1361245@bnt-lap001
Assistant-Session: b3b428ef-f3e6-4688-b091-01f71461d66a
2026-09-04 19:21:08 +02:00

335 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: FT-WP-0001
type: workplan
title: "Establish HelixForge's Telegram identity and publish the Hall of Helix"
domain: infotech
repo: fluid-telegram
status: active
owner: worsch
topic_slug: fluid-telegram
created: "2026-09-04"
updated: "2026-09-04"
planning_priority: high
planning_order: 1
depends_on:
- FLUID-WP-0008
related_repos:
- fluid-core
- helix-forge
- hall-of-helix
state_hub_workstream_id: "a660ed65-700e-5b54-8d91-a556b73518f0"
---
# FT-WP-0001 — Telegram identity and the Hall of Helix channel
Establish a Telegram identity for HelixForge and a channel publishing composed
posts about hall-of-helix work, as the first real FLUID interface.
**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 and is a different piece of writing; the entry is the
source and the check, never the payload.
Composition happens before this interface is called. That boundary is
architectural: composing is a probabilistic editorial task, and
`ArchitectureBlueprint.md` §48.1 forbids an LLM in the mandatory request path.
It is also editorial: a post speaks about a named person's work in HelixForge's
voice, and that deserves a human read before it goes out.
Everything that could be prepared without credentials has been: the governing
intent, the R-1 contract, the adapter's connector contract, the observation
configuration, the seed pressure record, and two competing hypotheses for the
first experiment. See `docs/` and the handover package at
`fluid-core/docs/handover/fluid-telegram.md`.
What remains needs either a person with a Telegram account or a running
adapter.
**T01T03 have moved.** They were written as manual steps; they are now
provisioned from a declared specification by `FT-WP-0002`, and the three tasks
below are kept as the record of what must be true rather than as instructions to
follow. The Bot API cannot create a bot or a channel, so provisioning is an
MTProto client acting as a designated operator account (Canon INT-03); one
bounded human bootstrap remains, and `docs/provisioning.md` says exactly what it
is and why it cannot be removed.
## T01 — Register the bot
```task
id: FT-WP-0001-T01
status: todo
priority: high
state_hub_task_id: "a5449e86-3181-54b6-a0fe-e5c761b3e4ba"
```
**Superseded by `FT-WP-0002` T04.** The bot is registered by the provisioner
from the declared presence spec, not by hand: BotFather `/newbot` driven over
MTProto, name, about text, description and avatar set from the spec, and the
token written straight to OpenBao without passing through a terminal. Consistent
with `HF-WP-0002` (bao.coulomb.social).
What survives from the original step is the requirement, not the method: a
reader encountering the bot cold should recognise it as HelixForge. That is now
`bot.name` / `bot.about` / `bot.description` in the spec, where it is reviewable
before it is applied.
Close this task when `FT-WP-0002` T04 has run. See `docs/provisioning.md`.
## T02 — Create the channels
```task
id: FT-WP-0001-T02
status: todo
priority: high
state_hub_task_id: "597ab1b7-5903-5a5d-a510-0a6946325404"
```
**Superseded by `FT-WP-0002` T04.** Both channels are created by the
provisioner via `channels.createChannel`, private test channel first (Canon
PUB-01), with the bot added as administrator through `channels.editAdmin`.
The ordering and the rights are no longer conventions a person is trusted to
follow. `apply` will not touch the public channel until the test channel has
recorded a successful publication, and it grants **Post Messages** and refuses
anything wider *even if the spec asks for it* — the intent forbids the system
from holding a right it may not exercise, so the grant is the right place to
enforce that.
Every verification below still runs against the private channel, and nothing
reaches the public channel until a person has looked at a rendering.
Close this task when `FT-WP-0002` T04 has run.
## T03 — Generate and store the redaction salt
```task
id: FT-WP-0001-T03
status: todo
priority: high
state_hub_task_id: "bbe5da73-e604-5dfe-b25f-fba802f67618"
```
**Superseded by `FT-WP-0002` T05.** The provisioner generates 32 bytes if and
only if the OpenBao path is empty, and has no path that overwrites one. Injected
as `FLUID_REDACTION_SALT`.
It must never change: see `docs/observation.md` for why rotating it silently
invalidates every longitudinal comparison. Making that a property of the tool
rather than a note in a workplan is the whole reason the step moved.
Close this task when `FT-WP-0002` T05 has run.
## T04 — Record the intent and publish R-1
```task
id: FT-WP-0001-T04
status: todo
priority: high
state_hub_task_id: "13a92a96-2f87-5de2-a69c-7edc21f3a0c7"
```
```bash
export FLUID_INTERFACE=helix-forge-telegram-publishing
fluid intent put --version IEI-1 --file InterfaceEvolutionIntent.md --activate
# fill the digest in revisions/R-1.yaml, then
fluid revision publish --file revisions/R-1.yaml --key-file "$FLUID_SIGNING_KEY" \
--adaptation-classes presentation --approved-by "$USER" --traffic-share 1.0
```
Generate a real signing key pair for this; `--ephemeral-key` is development
only, and a revision signed with one stops verifying after a restart.
## T05 — Build the adapter
```task
id: FT-WP-0001-T05
status: todo
priority: high
state_hub_task_id: "c89782da-9896-5957-b729-7c35165a0067"
```
Implement the three operations in `contracts/r1.openapi.yaml` against the
connector contract in `docs/adapter-contract.md`. TypeScript with grammY is the
natural choice; the wire contract makes the language irrelevant to fluid-core.
Three requirements carry the weight: publication is idempotent on `post_id`; a
post arriving without `reviewed_by` or a consent basis is refused with 403; and
a post that exceeds the limit for the form being published is refused rather
than truncated. The adapter renders and delivers. It never writes.
## T06 — Establish the composition step
```task
id: FT-WP-0001-T06
status: todo
priority: high
state_hub_task_id: "317bef32-e82e-586a-8947-8cbb330f6c98"
```
Build the step that turns a hall entry into a post: condensed, personal,
entertaining, reach-optimized, carrying a link back to the source entry.
For now this lives here, as a script plus a review queue. It is temporary by
design — T13 extracts it into `pr-hall-of-helix` — so keep the seam clean. The
composition step's only interface to publishing is `POST /v1/channel-posts`,
and it must not reach into the adapter or the evidence store.
Each composition records the `variant` label of the strategy that produced it.
Without that label the campaign cannot later compare strategies, and inferring
it from timestamps is the kind of reconstruction that quietly stops being
accurate.
Human review is not a nicety here. A generated condensation can put a claim in
someone's mouth that their own entry never made, and in the audit trail an
unfaithful post looks exactly like a faithful one. The reviewer reads the post
against the entry, and their name goes in `reviewed_by`.
## T07 — Record the consent basis for hall participants
```task
id: FT-WP-0001-T07
status: todo
priority: high
state_hub_task_id: "6a91ea08-8814-58f3-8297-a7ca63405294"
```
Ninety-four entries already exist, written by people and agent sessions who
took a seat in the hall without anyone mentioning a Telegram channel.
Decide and record the basis on which HelixForge may write publicly about that
work. If `hall-of-helix/ENTRY.md` is to carry a standing agreement, add it and
say plainly that it is revocable. Contributors who predate it should be asked
rather than assumed into it.
Absence of objection is not consent, and the interface refuses a post carrying
no basis, so this blocks T10.
## T08 — Publish one post to the private channel
```task
id: FT-WP-0001-T08
status: todo
priority: high
state_hub_task_id: "5e5cd89a-e7b1-5aaf-9331-ece424afe5e3"
```
The first real test. Compose one post from one entry, have it reviewed, publish
it to the private channel, and confirm by looking at it:
- the post reads well and is worth a stranger's attention;
- it does not claim anything the source entry does not support;
- the source link works and the attribution is right;
- re-publishing the same `post_id` edits rather than duplicates;
- the evidence store holds the post text but no Telegram user identity and no
bot token;
- `fluid audit trace R-1` reconstructs the publication, naming the reviewer and
the source entry.
The second bullet is the one that needs a person. Everything else can be
automated; whether a condensation is fair to the person it describes cannot.
## T09 — Implement both delivery forms as R-2 and R-3
```task
id: FT-WP-0001-T09
status: todo
priority: medium
state_hub_task_id: "649e7df6-6c07-5cc8-a022-d1eadcdb7236"
```
R-2 implements `H-tg-visual` — portrait attached, and Telegram's 1024-character
caption limit with it. R-3 implements `H-tg-textonly` — a text message with the
full 4096 available. Both are presentation adaptations over the same contract,
so neither needs a contract change. Verify both against the private channel
before either sees a subscriber.
The two arms impose different length limits on the composer, which is the one
place delivery and composition genuinely couple.
## T10 — Open the public channel and start publishing
```task
id: FT-WP-0001-T10
status: todo
priority: medium
state_hub_task_id: "b469b9b8-a1a8-5f76-a1bb-dd51dd4d2ce5"
```
Point the production adapter at the public channel. Blocked on T07: no post
goes out about anyone's work without a recorded consent basis.
Do not batch-publish the backlog. Ninety-four posts in a burst is a rate-limit
problem, an unkind introduction for a new subscriber, and — more to the point —
ninety-four compositions nobody has had time to read properly. Publish
selectively and at a human pace, and let the backlog be a source to draw on
rather than a queue to drain.
## T11 — Run the first delivery experiment
```task
id: FT-WP-0001-T11
status: todo
priority: low
state_hub_task_id: "eb09fcaf-f1bc-5e38-918e-c66f638d356f"
```
Start `E-tg-delivery` once the channel has roughly 100 subscribers. Before then
the evaluator will report INCONCLUSIVE, and that is the correct answer rather
than a reason to shorten the window.
At one post per week, a 90-day experiment yields about twelve posts. That is
thin, and the honest expectation is that the first run is inconclusive and
informs a better-powered second one. Record that expectation in the
experiment's amendments rather than discovering it as a disappointment.
## T12 — Close the loop
```task
id: FT-WP-0001-T12
status: todo
priority: low
state_hub_task_id: "9f8a7724-3e5c-5a36-9e3e-e6837bd0ffdd"
```
Record the hypothesis outcome, resolve `CG-tg-delivery`, and promote the winner.
Then confirm `fluid audit trace` reconstructs the whole chain, from the register
mismatch through to the promoted revision.
That trace is the deliverable. It is what makes this the first FLUID interface
rather than a bot that posts to a channel.
## T13 — Extract the campaign into pr-hall-of-helix
```task
id: FT-WP-0001-T13
status: todo
priority: medium
state_hub_task_id: "d471516c-2f20-525f-8d79-7814160851fb"
```
Everything about *what to say and when* moves to a new repository,
`pr-hall-of-helix`, under the `pr-` prefix for public-relations campaigns. What
stays here is the deterministic delivery interface.
The split follows the boundary the intent already draws (§7):
| Moves to `pr-hall-of-helix` | Stays in `fluid-telegram` |
|---|---|
| Composition and the editorial voice | The R-1 contract and its revisions |
| Choosing which work is worth a post | The adapter |
| Variant strategies and their competition | Delivery experiments (`CG-tg-delivery`) |
| Consent records and the review queue | Per-variant engagement reporting |
| The publishing cadence | Bot and channel configuration |
The campaign becomes a *consumer* of this interface — cohort
`hall-publishing-jobs` — and runs its own FLUID loop over its own artifacts,
using the per-variant engagement this interface reports.
Two things this buys. `fluid-telegram` becomes reusable for any HelixForge
campaign rather than being about the Hall of Helix specifically. And the Hall of
Helix campaign becomes an ongoing offering with its own intent, budget and
cadence, rather than living inside a workplan that was supposed to finish.
Do this once the loop has closed at least once (T12). Extracting a seam before
you have used it is how you find out you cut it in the wrong place.