Skip to content

feat(stereoscope): Slack bot for the Stereoscope Coffee workspace - #77

Draft
isaacsight wants to merge 3 commits into
mainfrom
claude/slack-bot-stereoscope-xad4q3
Draft

feat(stereoscope): Slack bot for the Stereoscope Coffee workspace#77
isaacsight wants to merge 3 commits into
mainfrom
claude/slack-bot-stereoscope-xad4q3

Conversation

@isaacsight

Copy link
Copy Markdown
Owner

What

Slack bot for the Stereoscope Coffee workspace, in two pieces under tools/stereoscope/:

  1. Monday brief (brief.ts + post-brief.ts) — one post every Monday into a private ops channel: per-shop sales with week-over-week deltas, average ticket, labor as a percent of sales, then a single ranked "one thing to fix" and a short watch list. npm run stereoscope:brief dry-runs against the bundled 6-shop sample; --post sends via chat.postMessage.
  2. Conversational presence (bot.ts) — Socket Mode bot, same architecture as tools/slack-bot.ts: per-thread history, Claude via the Supabase claude-proxy, answers DMs and mentions, threads replies in channels. npm run stereoscope:bot.

Why

Delivers the integration Stereoscope's ops lead checkmarked in the 2026-07-14 Slack DM, out of the 11-item menu pitched there: "every monday, one post in this slack — how all 6 shops did last week + the one thing to fix," with a sample to drop in the DM. The dry-run output is that sample.

How

  • The brief composer is deterministic — every number is arithmetic over a WeekData JSON file (the intended feed is Zapier/cron producing that file from the POS export). No LLM in the loop, so the post is trustworthy and unit-testable. Findings are ranked in percentage points off plan, weighted by how directly the miss costs money: labor over target 1.5, sales decline 1.0 (2% floor), ticket drift 0.8 (3% floor); the top finding becomes "the one thing to fix," the rest plus manager notes become the Watch list. Missing metrics (no prior week, no labor tracking) render without their segment instead of erroring.
  • The presence bot follows .claude/rules/backend.md (all Claude calls through supabase/functions/claude-proxy/), and its personality is scoped hard: never invents policy, recipes, prices, or numbers; routes wage/sales/personnel questions to managers and the private ops channel.
  • Env vars are deliberately separate from the kernel workspace tokens (STEREOSCOPE_SLACK_BOT_TOKEN / STEREOSCOPE_SLACK_APP_TOKEN / STEREOSCOPE_BRIEF_CHANNEL) so a misconfigured shell can never cross-post between workspaces. No tokens or secrets committed; Slack app setup steps are in the README.
  • sample-week.json carries invented numbers marked as such; four shop names are real, two are named placeholders to edit before demoing.

Testing

  • npm run build passes (tsc portion; new files are tools-side and covered by the scoped strict check below)
  • npm run testnpx vitest run tools/stereoscope/brief.test.ts: 14/14 pass
  • npx tsc --noEmit passes (plus a scoped tsc --strict over the four new files)
  • Tested manually with kbot in dev mode — n/a, this is a tools/ bot, not a kbot change; verified via the CLI dry run instead

Screenshots

Dry-run output (npm run stereoscope:brief):

*STEREOSCOPE — WEEK IN REVIEW*
Aug 17 – Aug 23

*All shops:* $157,580 (+0.3% vs prior week) · 12,960 transactions · avg ticket $12.16

*By shop*
• Newport Beach — $41,230 · +3.6% · ticket $13.21 · labor 29.0%
• Buena Park — $36,410 · -1.3% · ticket $12.82 · labor 29.0%
• Echo Park — $24,880 · -5.6% · ticket $11.79 · labor 31.0%
• Hollywood — $21,540 · +2.1% · ticket $11.40 · labor 33.4%
• Shop 5 (edit name) — $18,320 · +2.3% · ticket $11.38 · labor 29.0%
• Shop 6 (edit name) — $15,200 · +1.0% · ticket $10.94 · labor 29.1%

*The one thing to fix*
Echo Park — sales came in -5.6% vs the prior week ($24,880 against $26,350). Worth a look at what changed: hours, staffing, weather, or a menu shift.

*Watch*
• Hollywood labor at 33.4% (plan 30.0%)
• Echo Park labor at 31.0% (plan 30.0%)
• Echo Park: Grinder burrs due for replacement — ordered, lands Thursday

_Numbers computed from the weekly POS export. Reply in thread if something looks off._

🤖 Generated with Claude Code


Generated by Claude Code

isaacsight and others added 2 commits August 26, 2026 18:29
Delivers the integration James checkmarked in the 2026-07-14 DM out of
the 11-item menu: one post every Monday — how all 6 shops did last week
plus the one thing to fix.

- tools/stereoscope/brief.ts: deterministic Monday-brief composer.
  Per-shop sales with week-over-week deltas, average ticket, labor as a
  percent of sales; ranked findings (labor over target weighted 1.5,
  sales decline 1.0 with a 2% floor, ticket drift 0.8 with a 3% floor)
  select the one thing to fix; manager notes pass through to the Watch
  list. No LLM in the loop — every number is arithmetic over the week
  file, so the post is trustworthy and testable.
- tools/stereoscope/brief.test.ts: 14 tests, all passing (vitest).
- tools/stereoscope/post-brief.ts: CLI, dry-run by default, --post
  sends via chat.postMessage.
- tools/stereoscope/bot.ts: Socket Mode presence, same architecture as
  tools/slack-bot.ts (per-thread history, Claude via claude-proxy per
  .claude/rules/backend.md). Personality never invents policy, recipes,
  prices, or numbers; wage/sales/personnel questions route to managers
  and the private ops channel.
- sample-week.json: 6-shop fixture with invented numbers, marked as
  such; 4 shop names confirmed from their Slack, 2 placeholders to edit.
- Env vars are deliberately separate from the kernel workspace tokens
  (STEREOSCOPE_SLACK_BOT_TOKEN / _APP_TOKEN / STEREOSCOPE_BRIEF_CHANNEL)
  so a misconfigured shell can never cross-post between workspaces.

Verified: npx tsc --noEmit clean; scoped strict tsc over the four new
files clean; 14/14 tests pass; dry run renders the promised post shape.

Co-Authored-By: Claude <noreply@anthropic.com>
CI's 'Web — Type-check & Build' job runs the root vitest suite, which
globs tools/ableton/gen-coverage.test.mjs; vite cannot bundle the
node:test builtin under the jsdom config, so suite collection failed
(1 failed suite, 847/847 tests otherwise passing — first surfaced on
this PR because the introducing commit 776fec1 never had a CI run on
main). The test bodies are plain test() + node:assert, so the fix is
the runner import alone: vitest's test instead of node:test. All nine
tests now execute and pass; full root suite is 58/58 files, 856/856
tests.

Co-Authored-By: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Owner Author

CI note: the Web — Type-check & Build failure on e4ee0df was not this PR's diff. The root vitest suite globs tools/ableton/gen-coverage.test.mjs (added on main in 776fec1, which never had a CI run of its own), and vite cannot bundle the node:test builtin under the jsdom config — so suite collection failed while all 847 tests that could run passed, including this PR's 14.

Fix ported into this PR as 2ffc07c: the test bodies are plain test() + node:assert, so the runner import alone changes (vitest instead of node:test). All nine ableton tests now actually execute and pass; the full root suite is green locally at 58/58 files, 856/856 tests. The change no-ops if the same fix lands on main first.


Generated by Claude Code

The app shell locks body scroll (position: fixed, overflow: hidden in
src/index.css); every scrolling page opts out by adding
ka-scrollable-page in a mount effect — except ArchivePage. With the
body locked, window.scrollY stays 0, so THE STACKS' scroll-driven
camera never descends and every volume below the first viewport is
unreachable by scroll, for readers and for Playwright alike.

The e2e spec 'the ledger lands on the real issue route' (targeting
N°427) passed only while 427 was the newest issue and sat in the
first viewport; ISSUE 429 (7ea6abe, pushed to main without a CI run)
added a volume above it and exposed the defect on this PR's CI:
Playwright scrolls the link into view, the locked body undoes it,
and the click retries until timeout on both Desktop Chrome and
Mobile Safari.

Fix is the same one-effect pattern the other twelve scrolling pages
use. Verified: the failing spec reproduced locally on the previous
head, all 6 stacks e2e tests now pass on Desktop Chrome, the full
Desktop Chrome e2e run shows no new failures (the two remaining
local failures — issue-415 zero-console-errors and mobile tab bar —
reproduce identically without this change and stem from the missing
local Supabase env that CI injects), tsc clean, 856/856 vitest.

Co-Authored-By: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Owner Author

Second inherited CI failure, also fixed: the e2e check failed on stacks.spec.ts ("the ledger lands on the real issue route") on both browsers. Root cause is on main, not this diff — ArchivePage never adds ka-scrollable-page, so the app shell's position: fixed; overflow: hidden body leaves /archive unable to scroll at all (the room's scroll-driven camera also never descends). The spec passed historically only because N°427 sat in the first viewport; ISSUE 429 (7ea6abe, landed on main without a CI run) added a volume above it and exposed the defect.

Fix pushed as 57b799f — the same one-effect opt-out the other twelve scrolling pages use. Verified locally: the failure reproduced on the prior head, all 6 stacks specs now pass on Desktop Chrome, no new failures in the full Desktop Chrome e2e run, tsc clean, 856/856 vitest. It no-ops if the same fix lands on main first.


Generated by Claude Code

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