Skip to content

feat(grammar): allow bare !post — narrative from device metadata (#124)#159

Merged
brockamer merged 1 commit into
mainfrom
feat/124-bare-post
Apr 29, 2026
Merged

feat(grammar): allow bare !post — narrative from device metadata (#124)#159
brockamer merged 1 commit into
mainfrom
feat/124-bare-post

Conversation

@brockamer
Copy link
Copy Markdown
Owner

Summary

  • Bare !post (no caption) now parses and produces a journal post — previously returned undefined and replied "Unknown command. Try !help", wasting a satellite send.
  • The LLM builds the narrative purely from enrichment context (place name, weather, time) under a no-note system prompt that explicitly forbids inventing activities, feelings, or specifics not present in metadata. Observational third-person voice instead of first-person.
  • note is now string | undefined on the post ParsedCommand variant. Whitespace-only notes are treated as bare (cleaner than the empty-string ambiguity).
  • Closes Phase 2.5's last open issue — milestone goes 0/3 once this lands.

Surfaced 2026-04-27 during #111 production traffic turn-on (operator typed bare !post expecting "TrailScribe figures it out").

Acceptance criteria (from issue body)

  • src/core/grammar.ts — bare !post parses as { type: "post" } (note omitted at the type level — distinguishes "bare" from "empty after trim")
  • src/core/types.tsnote?: string on the post variant
  • src/core/commands/post.ts — branches on cmd.note === undefined; defensive empty-narrative fallback (Posted from {placeName} or Posted from the field)
  • src/core/narrative.tsSYSTEM_PROMPT split into WITH_NOTE / NO_NOTE; buildUserPrompt omits the Note: line when absent
  • Vitest cases — 1 grammar test (replaces the negative case), 3 narrative-prompt tests, 2 end-to-end pipeline tests (!post with GPS, bare + no-GPS edge case)
  • docs/field-commands.md documents the bare mode
  • !help reply text updates: !post [note] — blog

Tests: 332/332 (was 327; +5 new). Typecheck + lint clean.

Cost envelope (open question from issue body)

The risk note flagged whether bare-mode input tokens stay under PRD §6's <$0.05/transaction. Plan: probe staging with one bare !post + one with-note !post, compare ledger deltas. Will post the numbers as a PR comment before requesting merge.

Test plan

  • Deploy to staging via pnpm deploy:staging (pre-approved, dry-run first)
  • Probe bare !post against staging Worker; capture token usage from ledger
  • Probe with-note !post against staging; compare deltas
  • Confirm bare path stays under <$0.05/transaction; if not, investigate prompt-trimming before merge
  • Real-device verification deferred until P2-16 (operator + Mini in field)

🤖 Generated with Claude Code

#124)

Bare !post (no caption) is now parseable; previously it returned undefined
and replied "Unknown command. Try !help", wasting a satellite send. The
LLM constructs the narrative purely from enrichment context (place name,
weather, time) under a no-note system prompt that explicitly forbids
inventing activities or feelings — observational voice, anchored only to
metadata. Surfaced 2026-04-27 during the #111 production traffic turn-on
when the operator sent a bare !post expecting "TrailScribe figures it out".

Closes Phase 2.5's last open issue (milestone now 0/3).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@brockamer
Copy link
Copy Markdown
Owner Author

Staging cost probe — done

Flipped staging IPC_INBOUND_DRY_RUN to true, redeployed (version b6be26e6), fired one bare !post and one with-note !post against the staging Worker, then read the ledger:2026-04 rollup before/after. Reverted IPC_INBOUND_DRY_RUN to false and redeployed (version 76591947) — staging is back to its original config; wrangler.toml has no diff.

Ledger delta (2 calls combined: 1 bare + 1 with-note)

Metric Pre Post Δ
post requests 50 52 +2
prompt_tokens 22689 23621 +932
completion_tokens 13417 13667 +250
post usd_cost $0.15664170 $0.16318770 +$0.00654600

Per-transaction mean: ~$0.003. That's 6.5% of the PRD §6 $0.05 envelope — well inside target. Both calls returned 200; bare took ~26s, with-note took ~12s (the bare path's longer latency is the model elaborating from sparse metadata vs. anchoring on a caption).

The probe doesn't separate per-call tokens (the ledger rollup is an aggregate), but the combined-mean already answers the open question: bare-mode cost is comfortably inside envelope. If the with-note path's typical token count (~240 prompt / ~180 completion in unit tests) is the baseline, the bare path's share of the +932/+250 delta is at most ~750/+250 = roughly 1.5× the with-note prompt count — which is the expected direction (model has more enrichment to lean on, not less) and still nowhere near the budget ceiling.

P2-16 real-device verification will confirm field UX (does the LLM produce a readable observational narrative with no caption?), but cost is no longer a merge blocker.

Ready for your merge approval.

@brockamer brockamer merged commit 8f430d8 into main Apr 29, 2026
1 check passed
@brockamer brockamer deleted the feat/124-bare-post branch April 29, 2026 19:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant