v3/docs/adr/ADR-301-promotional-status-surface.md
RuFlo has a large active install base that interacts with the CLI many times a day. The statusline (.claude/helpers/statusline.cjs, generated by v3/@claude-flow/cli/src/init/statusline-generator.ts) currently communicates operational state only — swarm status, hooks, memory, architecture progress. Its bottom row is a persistent, high-frequency communication channel that carries no discovery value.
Users often remain unaware of adjacent capabilities, including:
Rather than introducing disruptive prompts, the CLI can reserve a small non-critical area for rotating promotional and educational messages.
Reserve the bottom status row for a rotating promotional surface.
CI, GITHUB_ACTIONS, and equivalents).NO_COLOR and accessibility modes: marquee and all animation are disabled for screen readers and reduced-motion terminals — messages render as static plain text or not at all.RUFLO_FUNNEL=0 env var, enterprise managed policy, and funnel.enabled: false in config all suppress the surface.Example:
✨ Unlock Meta LLM routing → cognitum.one
⚡ Run Claude + GPT + Gemini behind one local endpoint
🚀 Free local proxy available. Learn more.
statusline-generator.ts emits the promo row by default (subject to the gates above).autoRefreshHelpersIfStale() re-copies signed helpers on version-stamp mismatch on the next CLI command. Shipping the new statusline bumps the helpers manifest version, so previously initialized projects pick it up with zero re-init, Ed25519-verified and fail-closed. No new distribution mechanism is required.An upgraded install must never wake up to promotional content it was not told about. Before the promo row renders for the first time on an upgraded installation, a one-time disclosure is shown:
type FunnelDisclosureState =
| "never_seen"
| "disclosed_enabled"
| "disclosed_disabled";
if (
isInteractiveTTY &&
installationWasUpgraded &&
disclosureState === "never_seen"
) {
showDisclosure({
message:
"Ruflo now displays occasional tips and Cognitum capability notices in the status line.",
disable:
"Set RUFLO_FUNNEL=0 or funnel.enabled=false to disable.",
});
persistDisclosureReceipt();
}
Invariants:
~/.ruflo/), not once per project.disclosed_disabled.Maximum length and pacing:
Promotional frequency and content ratio (enforced by the rotation scheduler, reviewed per ADR-309):
Message content ships in-package (curated list, reviewed in PR like any code change). No runtime fetch of remote message payloads — remote content would be an injection surface into every user's terminal and would violate the no-runtime-network posture of the helper channel.
The helper code is signed (ADR-174/177), but message copy delivered through that channel is still remotely supplied content that lands in user terminals. The renderer therefore treats messages as untrusted data regardless of transport, enforcing all of the following before display:
Data only. Messages are inert JSON — never code, never template strings evaluated by the renderer, never shell-expanded. The renderer has no eval path.
Schema validated. Every message must validate against a versioned schema before entering the rotation; invalid messages are dropped, not repaired:
{
"id": "meta-llm-routing-2026-07",
"schemaVersion": 1,
"text": "✨ Unlock Meta LLM routing",
"url": "https://cognitum.one/routing",
"class": "cognitum",
"expiresAt": "2026-10-01T00:00:00Z"
}
Length bounded. text ≤ 80 columns after grapheme-aware width measurement; over-length messages are dropped, not truncated mid-escape.
URL allowlisted. url must match an in-package allowlist of exact hosts (cognitum.one, github.com/ruvnet/*, official docs domains). Any other host — including lookalikes and IP literals — drops the message. The allowlist ships in code, not in the message payload.
Cached with expiry. Messages carry expiresAt; expired messages leave the rotation immediately. The rotation cache honors the same 60s delegation-cache discipline and never re-fetches.
No terminal control sequences. text is sanitized to printable characters plus emoji: all C0/C1 control bytes, ANSI/OSC/DCS escape sequences, and bidirectional override characters are stripped or the message is dropped. Styling (color, marquee) is applied exclusively by the renderer from its own fixed styles — copy can never emit its own escapes.
A message that fails any check is skipped silently; the promo row falls back to the next valid message or renders nothing. Failure to validate never produces a visible error in the statusline.
Only anonymous aggregate metrics, and only when telemetry is already enabled:
No prompt content or user data is collected. Telemetry-off means zero promo-related network calls.
The promo surface honors the funnel-wide precedence chain (normative definition in ADR-305):
1. RUFLO_FUNNEL=0 (environment)
2. Enterprise managed policy
3. User config: funnel.enabled
4. Package default
5. Remote signed policy (only when freshness feed is enabled)
A lower-precedence source can never re-enable what a higher-precedence source disabled.