IronClaw

IronClaw tells unpaired Telegram users to connect before commands

IronClaw now checks pairing before command admission so Telegram’s automatic /start receives the connect notice instead of a command inventory. A related device-link repair distinguishes an administrator who has not configured the deployment from a user account failure.

← Back to homeOriginal source ↗

nearai/ironclaw PR #8073 is the inspected primary source: “fix(device-link): say "not configured by administrator" instead of blaming the user's account.” The related records were inspected as supporting context rather than independent confirmation.

The facts

- nearai/ironclaw PR #8073 was created 2026-09-04T20:10:17Z and was closed at inspection; its title is “fix(device-link): say "not configured by administrator" instead of blaming the user's account.” - nearai/ironclaw PR #8054 was created 2026-09-03T18:03:00Z and was closed at inspection; its title is “fix(assistant): check pairing before command admission so first contact gets the connect notice.” - nearai/ironclaw issue #7956 was created 2026-08-27T20:02:41Z and was closed at inspection; its title is “Telegram: unpaired sender's /start gets the command inventory instead of the connect/pairing notice.” - The repository reported 12604 stars, 1510 open issues, default branch main, and last push 2026-09-05T00:41:41Z. - The latest tagged-release baseline checked was ironclaw-v1.4.0 published 2026-08-28T11:19:24Z.

What changed

nearai/ironclaw PR #8073 reports: Summary On a deployment whose administrator has not configured Telegram personal-account access (telegram_api_id/telegram_api_hash), the personal-account setup path failed with "Linking Telegram account cannot be completed for this account. Something went wrong while linking." — blaming the user's account for an operator condition. Adds DeviceLinkErrorCode::NotConfigured (wire not_configured, non-restartable) to the closed device-link error vocabulary, and DeviceLinkError::NotConfigured for adapters. The Telegram adapter now returns NotConfigured when the MTProto app identity is absent, without touching session custody; the flow driver records AuthErrorCode::MalformedConfig (previously BackendUnavailable) so the audit trail names configuration, not an outage. User-facing copy now names who can act and where: the prompt view says "{name} linking has not been set up by an administrator on this deployment.", and the WebUI panel appends "An administrator can enable it under Admin → Configuration → {name}." — localized in all 10 locales against each locale's own nav.admin / admin.tab.configuration labels. The "This {name} account cannot be linked." terminal line is suppressed for this code. Architecture ratchet re-pin** (per the gate's own instruction): ironclaw_extension_contracts' §11.2.3 size ceiling is re-pinned 12_867 → 13_026 in reborn_dependency_boundaries.rs. This PR contributes +13 production lines (the NotConfigured variant + error carrier and its code/restartable arms — closed-vocabulary DTO growth only; classification lives in ironclaw_auth, rendering in the WebUI); the remainder of the delta was growth main had already banked inside the ceiling's +150 working-slack window since the 2026-08-31 pin, which

nearai/ironclaw PR #8054 reports: Summary Unpaired Telegram users' first contact (the Start button sends a bare /start) was answered with the "Available commands" inventory; only the *next* message surfaced the pairing/connect notice. Root cause: the product workflow ran command admission before any pairing/binding lookup, so an unknown command from an unpaired actor died as InvalidRequest (→ inventory help) instead of BindingRequired (→ connect notice). Adds a read-only ProductBindingResolver::ensure_actor_bound pre-check (contract crate; default no-op since resolve_binding still gates every dispatch) and calls it in ironclaw_assistant's workflow Command branch before admission, so an unpaired actor's slash command fails closed with BindingRequired exactly like an unpaired user message. Widens the run-delivery observer's trigger predicates (envelope_is_direct_chat / envelope_addresses_the_bot) to read Command payloads — previously only UserMessage — so the channel's connect_required nudge actually posts for a rejected command. Amends contract doc rule 4.2 (docs/internal/reborn/contracts/conversation-binding.md): unpaired fails closed for slash commands too, checked before admission. The fix is channel-neutral: it covers Telegram's /start and any other channel's DM slash command from an unpaired sender identically. Resolves: and now displays the following after /start: Change Type [x] Bug fix [ ] New feature [ ] Refactor [x] Documentation [ ] CI/Infrastructure [ ] Security [ ] Dependencies Linked Issue Fixes #7956 Validation [x] cargo fmt --all -- --check [x] cargo clippy -p ironclaw_product_contracts -p ironclaw_assistant -p ironclaw_extension_host --all-targets --all-features -- -D warnings [x] cargo build (via test builds) [x] Relevant

nearai/ironclaw issue #7956 reports: Problem Opening the Telegram workspace bot for the first time (before pairing) greets the user with the command inventory instead of the connect/pairing notice: 1. User taps **Start** in Telegram → client sends /start (no pairing code). 2. Bot replies Available commands:\n/interrupt\n/model\n/new\n/status\n/stop. 3. User sends any message → bot replies Connect this Telegram account to the workspace bot from the Telegram extension in IronClaw, then message me again. The first message the user sees lists commands they cannot use; the actual next step (pairing) only shows up on the *second* message. Expected: an unpaired sender's first contact — /start, any slash command, or plain text — gets the connect_required notice, and the command inventory is only shown once the sender is paired. Cause Command admission runs before the pairing/binding lookup, so an unpaired sender's slash command is rejected as an unknown command rather than as BindingRequired: crates/extensions/packages/telegram/src/payload.rs — /start (bare) classifies as ProductTriggerReason::BotCommand. crates/extensions/ironclaw_extension_host/src/channel_pairing.rs candidate_code — /start with no code is NotHandled by the pairing interceptor, so it flows to the workflow as a command. crates/product/ironclaw_assistant/src/workflow.rs ProductInboundPayload::Command branch — command_admission_service.admit() runs **before** dispatch_product_command() (where binding_service resolves the paired user). start is not in the manifest channel.commands list, so admission returns InvalidRequest. crates/product/ironclaw_assistant/src/run_delivery/observer.rs post_command_feedback — InvalidRequest renders command_help_text ("Available commands: …"). BindingRequired deliberately defers to post_connect_nudge_if_unbound_user_message, but that path is never reached for commands from an unbound sender. The same

Why it matters

First contact teaches users who owns the next action. Listing unusable commands or blaming the account leaks policy detail and sends the operator to the wrong repair surface. The practical test is whether the system remains bounded and its receipts still describe the action after failure, retry or restart.

Current

The primary record was closed when captured on 2026-09-05. The tagged-release baseline was ironclaw-v1.4.0 published 2026-08-28T11:19:24Z. Repository metadata, source bodies, current pull requests and issues, releases, Google News, Hacker News, Lobsters, Metamesh and the rendered ClawCharts row were inspected. ClawCharts selected the subject; it did not prove the claim.

Evidence

The primary URL is https://github.com/nearai/ironclaw/pull/8073. Supporting records are https://github.com/nearai/ironclaw/pull/8054 and https://github.com/nearai/ironclaw/issues/7956. Source bodies, timestamps and states are preserved in the daily evidence bundle. Test counts and reproductions remain attributed to their authors unless identified as independently rerun.

Source boundary

Open work is described as open, closed work as closed, and operator reports as reports. A pull request is evidence of proposed or reviewed direction, not proof of a shipped release. Search residue, package mirrors and historically published source spines were excluded.

Operator take

Resolve binding before parsing channel commands, fail closed without constructing a turn, classify missing deployment configuration as terminal operator work, and preserve rollback caveats for persisted error vocabularies. Preserve a before-state receipt, make the smallest reversible change, and verify the original failure independently.

Caveat

Public project records are mutable. Status, scope and evidence can change after publication. This brief records the inspected state and does not authorize changes to a reader’s deployment.

Source inspected; source state, environment and release boundary remain explicit.