fix: flow message silently stored a flag as the body - #95
Merged
Merged
Conversation
The body was a blind positional (args[1]) with no flag-guard, and Go's flag.Parse stops at the first positional — so `flow message tgt --urgent "real body"` stored the body as literal "--urgent" (real text dropped, urgent lost), and the `--body` agents keep inventing stored "--body". Rewrite the parser to be order-independent: --urgent anywhere, a real `--body`/`-m`/`--message` flag, `--` to end flags, and an explicit error on unknown flags instead of silently corrupting the body. All three natural forms now work: flow message tgt "body" --urgent flow message tgt --urgent "body" flow message tgt --body "body" Regression test added. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012iRktytszcMqAmiS84SvKL
anshulsao
force-pushed
the
fix/message-body-parsing
branch
from
September 4, 2026 07:13
dfe0a3d to
d7a30e4
Compare
rr0hit
added a commit
that referenced
this pull request
Sep 7, 2026
Port Anshul's #95 flag-parsing correction into this branch's docs: the mail-model rewrite of §4.18 had dropped it. references/messaging.md now states the tolerant command form (body positional or --body; --urgent/ --body/--reply-to may come in any order; unknown flags rejected, never stored as the body), and the app.go --help note lists --reply-to as a known flag. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
rr0hit
added a commit
that referenced
this pull request
Sep 7, 2026
…, 500-char cap (#96) * feat: pop-only inbox API; self-send gate; reserved assignee self -> user - flow inbox pop is the ONLY consumption API: ack and due subcommands removed, along with the whole escalation apparatus (next_notify_at / attempts columns for fresh installs, DueBusMessages, BumpNotifyAttempt, AckMessageByID). Popping answers, delivers, and clears; agents and notifier scripts just loop it. Ack-on-reply (the UserPromptSubmit hook) and wait metrics remain — that is behavior, not API surface. - No sender can message its own address: a bound session is rejected addressing its own inbox, and the human their own queue. - Reserved assignee renamed self -> user (agents misread 'message self' as talking to themselves). Existing rows migrated idempotently on open; the one-shot post->broadcast kind migration is deleted (no released DB ever needed it). - Usage, skill §4.18, and references/messaging.md rewritten to match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFGNJQo5xNBBrT7rZMuUTu * feat(bus): mail-model inbox — --all, read <id>, pop --keep-unread, message --reply-to Additive, non-breaking additions on top of the pop-only bus: - `flow inbox --all` lists the whole retained queue (read + unread); default `flow inbox` still lists only unread. - `flow inbox read <id>` shows any message by id (incl. consumed) and marks it read — acks a specific message out of pop's oldest-first order. - `flow inbox pop --wait --keep-unread` is the reader/relay wake: returns the oldest unread, claims it `delivered` (loop-safe) but never acks, so a forwarder can pass mail along without consuming the human's answer. - `flow message --reply-to <id>` stamps a parent id (new nullable `reply_to` column, migrated on open); the receiver sees the lineage. - Mail nomenclature (unread/read) in human output and `--json` (`mail`). - Retention: a delivered-but-unacked human message is immortal like pending — an unanswered question never vanishes, whoever forwarded it. Skill §4.18 + references/messaging.md rewritten: mail nomenclature, two-purpose framing (reach-out-to-user / peer-collab), two Monitor recipes (consumer pop-wait, reader pop-wait --keep-unread). Vanishing-message bug ruled out as durability: the ids were auto-acked by the sending session's UserPromptSubmit hook (acked_by='prompt') before the relay forwarded them, not lost from storage. `--keep-unread` closes the delivery-starvation gap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(bus): raise body cap to 500; auto-mode progress guidance - Bump busBodyMax 200 -> 500 (non-breaking; short messages unaffected). The 200 cap forced constant splitting for the Telegram relay. - Skill (SKILL.md §4.18 + references/messaging.md): document that headless `flow do --auto` agents should post regular progress via `flow message user`/`flow broadcast`, not only when blocked — a silent run looks stuck. Update the two "≤200 chars" citations to "≤500". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(bus): delta-gated inbox nudge at PreToolUse Surface a just-arrived DIRECTED inbox message right before a tool runs, so a mid-turn "stop, don't do X" reaches the agent before the action it would cancel — the case SessionStart/UserPromptSubmit/Stop nudges surface too late. The existing pending-count notice is NOT delta-gated (raw COUNT(*) of pending rows), so wiring it as-is into PreToolUse — which fires on every tool call — would re-nudge the same set every call. Instead add a bus_surfaced high-water mark: PendingDirectedUnsurfacedForTask returns only messages not yet announced, MarkSurfaced records them, so each message is surfaced at most once and quiet tool calls stay silent. Broadcasts are excluded (FYIs, not action-changing); urgent messages lead. Inform-only — hooks never consume. Wires `flow hook pre-tool-use` into settings.json via a new Install/UninstallPreToolUseHook on the harness interface (claude installs; codex no-ops), and on skill install/uninstall/auto-upgrade. bus_surfaced marks are cleaned on task close-out and pruned in SweepBus. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(bus): PreToolUse nudge surfaces broadcasts too (any unread) The PreToolUse delta-gated nudge previously surfaced only directed messages, excluding broadcasts as FYIs. Per refinement, it now fires on ANY unread item (message OR broadcast) addressed to the session: rename PendingDirectedUnsurfacedForTask -> PendingUnsurfacedForTask and drop the kind='message' filter. Delta-gating (bus_surfaced high-water mark), urgent lead, and inform-only (never consumes) are unchanged, so each new unread still surfaces exactly once with no per-call spam. Test flipped from "ignores broadcasts" to "surfaces broadcasts" (once, then delta-gated); wording + docs (messaging.md) updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(bus): align skill + help with #95's corrected message command Port Anshul's #95 flag-parsing correction into this branch's docs: the mail-model rewrite of §4.18 had dropped it. references/messaging.md now states the tolerant command form (body positional or --body; --urgent/ --body/--reply-to may come in any order; unknown flags rejected, never stored as the body), and the app.go --help note lists --reply-to as a known flag. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
flow messagetook the body as a blind positional (args[1]) with no flag-guard, and Go'sflag.Parsestops at the first positional. So any flag written before the body became the literal body, and the real text (and--urgent) were silently dropped:Agents hit this constantly — they write the flag first (natural CLI order) or invent a
--bodyflag — so real messages were being corrupted and delivered as literal--urgent/--body, with no error.The fix
Rewrite the arg parser to be order-independent and give the body a real name:
--urgentrecognised anywhere in the args--body/-m/--messageflag for the text (plus--body=…)--to end flag parsing (so a body may start with-)All three natural forms now work:
Tests
TestMessageParsingToleratesFlagOrderAndBodyFlagcovers flag-first,--body, positional, and the unknown-flag rejection (nothing stored).TestMessageRejectsFlagAddressAndClosedTasks: a known flag before the address is now valid (address = first positional); the real protection is that unknown flags error. Closed/done-task rejection unchanged.🤖 Generated with Claude Code