Repository navigation
RFC: Goal Channel collaboration for LoopX goals #2817
Description
Summary
This RFC proposes Goal Channel as the LoopX-owned abstraction for an external collaboration channel bound to exactly one LoopX goal.
The first provider target is Lark/Feishu:
- create or reuse one group chat for a goal;
- create or reuse a Lark Base Kanban projection;
- pin a compact Goal Control message and Kanban link in the group;
- send bounded human-gate notifications;
- sync accepted LoopX state back to the Kanban projection.
The channel is not the source of truth. It is the visible collaboration entry point and feedback surface for a LoopX goal, while LoopX remains authoritative for goal state, todos, human gates, quota, evidence, receipts, and accepted state transitions.
Problem
LoopX already has useful Lark pieces:
lark-kanbanprojects LoopX todos and status into Lark Base;- Lark notification paths already prove send, readback, idempotency, and profile checks in narrower domains.
These pieces do not yet compose into the desired collaboration shape:
- A goal gets one external collaboration channel.
- Progress, gates, and Kanban are visible in the same place.
- Human-gate prompts can be delivered where collaborators already work.
- LoopX keeps the authoritative goal, todo, gate, and evidence state.
Without a first-class Goal Channel abstraction, the Lark group, Base board, pinned status, and notification receipts can drift apart.
Goals
- Define a provider-neutral LoopX concept for the external collaboration channel of one goal.
- Keep Lark group chat, Kanban, pinned messages, and gate notifications bound to one
goal_id. - Preserve LoopX as the only writer of canonical goal, todo, gate, evidence, and quota state.
- Make provider writes explicit, previewable, idempotent, and readback verified.
- Let humans see and answer gate prompts in the channel without granting the channel broad write authority.
- Allow later provider adapters such as Slack, GitHub, or Linear without renaming the core concept.
Non-Goals
- Replacing
lark-kanban; Goal Channel composes it. - Making Lark, Slack, GitHub, or any external tool the source of truth.
- Shipping a LoopX-managed global Lark app in the open-source CLI.
- Requiring one fixed bot identity across all users or tenants.
- Treating arbitrary chat text as an accepted state transition.
- Copying raw chat history, private message ids, local paths, credentials, or raw provider payloads into public packets.
- Solving full remote runner orchestration in this RFC.
Proposed CLI Surface
loopx goal-channel setup --provider lark --goal-id <goal-id>
loopx goal-channel doctor --goal-id <goal-id>
loopx goal-channel sync --goal-id <goal-id>
loopx goal-channel notify-gate --goal-id <goal-id>goal-channel is both the durable control-plane object and the user-facing CLI. This RFC intentionally does not introduce a separate tag command surface.
Lark Provider Binding
A Lark Goal Channel binding should be local-private and project-scoped, for example:
{
"schema_version": "loopx_goal_channel_lark_binding_v0",
"goal_id": "loopx-goal",
"provider": "lark",
"enabled": true,
"channel": {
"chat_id": "oc_<private-chat-id>",
"chat_name": "LoopX - loopx-goal",
"pinned_message_id": "om_<private-message-id>"
},
"kanban": {
"base_token": "<private-base-token>",
"table_id": "tbl...",
"view_ids": {
"Kanban": "vew...",
"User Gates": "vew..."
}
},
"identity": {
"mode": "project_bot",
"sender_profile": "loopx-project-bot",
"sender_identity": "bot",
"bot_display_name": "LoopX Bot"
},
"receipts": {}
}The file belongs under .loopx/ or another ignored local-private path. Public packets must not expose chat ids, member ids, message ids, profile names, raw Lark payloads, local paths, or credentials.
Identity Model
Open-source LoopX should default to Bring Your Own provider identity:
- users create or select their own Lark app or bot in their tenant;
- users authenticate it through
lark-clior a future provider-specific profile manager; - LoopX stores only the local profile reference and compact verification state;
- LoopX never ships a fixed cross-tenant bot as an implicit dependency.
Recommended modes:
| Mode | Intended use | Tradeoff |
|---|---|---|
local_user |
Fast local trial, create Base as the user | Easy setup, weaker team audit boundary |
project_bot |
Recommended team mode | Requires app/bot setup but gives stable identity |
managed_app |
Future hosted product | Best UX, requires tenant install, compliance, and operations |
Smallest Useful Slice
The MVP should:
- Add
loopx goal-channelwithsetup,doctor,sync, andnotify-gate. - Implement only the Lark provider.
- Reuse existing
lark-kanbansetup/sync andloopx-larkextension activation checks. - Create or reuse one Lark group for one existing goal.
- Send and pin one compact Goal Control message.
- Send a human-gate notification with idempotency and readback.
- Store local-private binding and receipts under
.loopx/.
This slice proves the external collaboration entry point.
Validation
The MVP must prove:
- setup is dry-run by default and performs external writes only with
--execute; - one goal maps to one local-private Lark binding;
- extension activation is checked before private config is read;
- a Kanban board can be reused or created and then synced;
- a Goal Control message is sent, pinned, and readback verified;
- human-gate notification respects cooldown and idempotency;
- repeated notification retries do not duplicate visible messages;
- doctor reports missing bot auth, missing channel, missing Kanban, or stale extension activation with typed blockers;
- local-private binding files remain ignored and untracked;
- public packets do not contain chat ids, member ids, message ids, profile names, local paths, raw provider payloads, or credentials.
Open Questions
- Should
goal-channel setupcreate a Lark group by default, or require--create-channelas a separate explicit external write? - Should the first pinned Goal Control message be immutable plus follow-up status messages, or updated in place when the provider supports it?
- Should gate notifications be triggered only by an explicit command in MVP, or can heartbeat call
goal-channel notify-gateas a post-writeback action after the binding is verified?
Activity