Time to code the bot.
Turn small, recurring team frictions into shared evidence, time-boxed improvement trials, and lessons the team can reuse.
Splinterlog is a Slack-native friction lab. It is not a stand-up bot, reminder bot, poll, generic task list, retrospective facilitator, or AI chat wrapper. A teammate catches a small workflow snag, other people add independent signals, and the team turns the strongest splinter into one measurable experiment. On the check date, Splinterlog asks what actually happened and keeps the lesson.
Teams lose surprising amounts of time to tiny repeated annoyances: copying release notes into three places, remembering a slightly different checklist, hunting for the newest handoff template, or repeating a manual status update. Each incident feels too small for a ticket or retrospective, so the pattern stays invisible.
Splinterlog gives those paper cuts a lightweight path:
- Catch the friction while it is fresh.
- Signal that it affects more than one person.
- Trial one small change with a success measure and review date.
- Learn from the outcome, including mixed and unsuccessful experiments.
The bot's story is deliberately circular. A failed trial does not disappear or become fake success; the splinter reopens with a recorded lesson so the team can try a better change.
Most team bots begin with a meeting, task, poll, or reminder. Splinterlog begins with micro-friction and uses teammate evidence to decide whether a small experiment is worth running. Its commands share one persistent state model instead of acting like unrelated utilities.
The implementation does not depend on an external AI service. Its originality comes from the collaboration workflow, Slack interactions, state transitions, evidence rules, scheduled review, and the language of splinters, signals, trials, and lessons.
Slack's developer documentation warns that slash commands are not truly namespaced: if two apps install the same command, the most recently installed command wins. The initial naming review therefore avoided Slack's built-ins such as /remind, broad words such as /help and /stats, and common bot categories and command families around polls, retrospectives, stand-ups, tracking, and decisions.
The shared /splinterlog-* namespace is memorable, project-specific, and had no matching Slack Marketplace result during the July 2026 naming check. That lowers collision risk but is not a global uniqueness guarantee. Before publishing publicly, repeat the Marketplace and web search and check the target workspace's installed commands.
Useful naming references:
- Slack: implementing slash commands
- Slack: app design and command naming
- Slack: built-in shortcuts and commands
| Command or function | What it does | Example |
|---|---|---|
/splinterlog-catch |
Opens a guided modal and posts a real, persistent friction card. | Run it in the channel where the snag happens. |
/splinterlog-board |
Shows open, trial, learned, or all splinters ranked by teammate signals. |
/splinterlog-board open |
/splinterlog-trial |
Opens a modal for a measurable experiment tied to one open splinter. | /splinterlog-trial 4 |
/splinterlog-guide |
Shows compact onboarding, examples, and the central workflow. | /splinterlog-guide |
| Catch a splinter message shortcut | Opens capture from an existing Slack message and stores its timestamp as context. | Open a message's shortcuts menu. |
| This snags me too button | Adds one signal per person and refreshes the shared count. | Click a friction card. |
| Scheduled trial check | Posts one review card when a running trial reaches its check date. | Use Record what happened. |
| App Home | Shows the workflow, meaningful totals, and the five most-signaled open splinters. | Open Splinterlog from Slack's Apps area. |
There are more than three genuinely different functions across the complete bot:
- direct slash-command discovery and capture;
- interactive multi-user evidence and modal workflows;
- scheduled, event-driven trial review;
- App Home publication in response to Slack events.
All slash commands use the collision-resistant splinterlog namespace. There is intentionally no generic /help, /stats, /settings, or /remind command.
These are cautious design estimates, not competition-score guarantees.
| Feature | Originality | Technical value | Usability | Why it does not feel generic |
|---|---|---|---|---|
| Friction capture | 7/9 | 7/9 | 9/9 | It captures recurring cost and frequency, not a generic task or note. |
| Shared signal board | 8/9 | 7/9 | 8/9 | One signal per teammate turns repeated experience into evidence without becoming a poll. |
| Improvement trials | 8/9 | 8/9 | 8/9 | Hypothesis, action, success measure, owner, and check date form one connected experiment. |
| Scheduled lesson loop | 8/9 | 8/9 | 8/9 | The bot asks what happened and preserves mixed or failed results instead of sending a reminder only. |
| App Home and guide | 6/9 | 6/9 | 9/9 | They explain and surface the same friction workflow rather than acting as a generic dashboard. |
You can demonstrate the complete story with two people in one test channel:
- Run
/splinterlog-guideand show the concise onboarding blocks. - Run
/splinterlog-catchand capture ?Release notes are copied by hand.? - Have a second teammate click This snags me too twice. The first click adds one signal; the second explains that the signal was already counted.
- Run
/splinterlog-board openand show that the signaled item rises to the top. - Click Try a small fix and create a trial with today's date.
- Run the scheduler once or wait for its interval. Open Record what happened.
- Record
helped,mixed, ordid-not-helpand show the lesson post and updated board state. - Open App Home and show the summary and top open splinters.
For a credential-free proof, run the seven local tests. They exercise real SQLite writes, ranking, deduplication, permission checks, trial state changes, daily nudge state, and rollback.
Splinterlog uses a small, direct Python architecture:
Slack Socket Mode
|
v
slack_app.py ---- Block Kit ----> blocks.py
|
v
service.py <---- scheduler.py
|
v
database.py ----> SQLite
app.pyloads settings, initializes the database, starts the scheduler, and opens Socket Mode.splinterlog/slack_app.pyacknowledges Slack payloads, checks retry keys, translates UI values, and returns friendly errors.splinterlog/blocks.pybuilds reusable modals, cards, board views, review prompts, and App Home content.splinterlog/service.pyowns validation, workspace scoping, state transitions, signal uniqueness, and permission rules.splinterlog/database.pyowns SQLite schema creation, transactions, rollback, and idempotency keys.splinterlog/scheduler.pyfinds due trials, retries one Slack rate limit, posts review cards, and records daily nudge state.
The code favors ordinary functions, dictionaries, a dataclass, and short classes. It should be readable to a newer Python developer while still separating Slack UI, business rules, persistence, and background work.
| Table | Purpose |
|---|---|
frictions |
Workspace-scoped splinters, cost, frequency, source timestamp, and current state. |
signals |
One supporting signal per person per splinter. |
trials |
Owner, hypothesis, action, success measure, check date, outcome, lesson, and nudge state. |
processed_actions |
Idempotency keys for commands, views, buttons, shortcuts, and events. |
SQLite foreign keys and transactions are enabled on every connection. A failed multi-step operation rolls back. The bot never responds to an error by deleting or rewriting the project.
The manifest requests three bot scopes:
| Scope | Why it is needed |
|---|---|
commands |
Register the four slash commands and message shortcut. |
chat:write |
Post friction cards, trial confirmations, review prompts, lessons, and ephemeral feedback. |
users:read |
Verify whether a non-owner resolving a trial is a workspace admin or owner. |
The app-level Socket Mode token needs connections:write. Splinterlog does not request message-history scopes, file access, channel administration, or user tokens. The message shortcut stores a source timestamp, not a copy of the original message body.
- Open Slack API - Your Apps.
- Choose Create New App, then From an app manifest.
- Select the test workspace and paste
slack-manifest.yaml. - Review the commands, shortcut, App Home event, interactivity, and three bot scopes.
- Create the app and install it to the workspace.
- Under Socket Mode, create an app-level token with
connections:write. - Copy the bot token, app token, and signing secret.
If an admin controls app installation, send the manifest and scope table with the approval request. A missing scope is reported to users with a reinstall instruction.
Windows PowerShell:
py -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
Copy-Item .env.example .envEdit .env with the real Slack credentials:
SLACK_BOT_TOKEN=xoxb-...
SLACK_APP_TOKEN=xapp-...
SLACK_SIGNING_SECRET=...
SPLINTERLOG_DB_PATH=splinterlog.db
SPLINTERLOG_REVIEW_INTERVAL_SECONDS=60
LOG_LEVEL=INFO| Variable | Required | Meaning |
|---|---|---|
SLACK_BOT_TOKEN |
Yes | Bot token created when the app is installed. |
SLACK_APP_TOKEN |
Yes | App-level Socket Mode token with connections:write. |
SLACK_SIGNING_SECRET |
Yes | Slack signing secret used by Bolt. |
SPLINTERLOG_DB_PATH |
No | SQLite path; defaults to splinterlog.db. |
SPLINTERLOG_REVIEW_INTERVAL_SECONDS |
No | Positive scheduler interval; defaults to 60. |
LOG_LEVEL |
No | Python log level; defaults to INFO. |
python app.pyA successful start logs the selected database path and opens a Socket Mode connection. Invite Splinterlog to the demonstration channel if workspace policy requires it, then run /splinterlog-guide.
Run every local test:
python -m unittest discover -s tests -vRun a syntax/import check:
python -m compileall -q .
python -c "import app; import splinterlog.slack_app; import splinterlog.scheduler"The current suite has seven passing tests covering:
- capture validation and persistence;
- workspace isolation and signal-based ranking;
- one signal per teammate;
- trial ownership, resolution, and learned state;
- due-trial nudge suppression for the rest of the day;
- one-time action-key claims;
- transaction rollback after a simulated failure.
The tests use temporary SQLite files and do not need Slack credentials or network access.
| Situation | Behavior |
|---|---|
| Missing or invalid arguments | Modal field error or ephemeral example with the exact expected format. |
| Missing Slack permission | Safe denial and a request to have an admin reinstall the manifest. |
| Slack retry or duplicate button/view | Persistent idempotency key prevents a second state change. |
| Same person signals twice | The count stays unchanged and the user gets private feedback. |
| Two trials requested for one splinter | The service rejects the second running trial. |
| Non-owner resolves a trial | Denied unless Slack confirms workspace admin or owner status. |
| Slack rate limit | Scheduler waits for Retry-After and retries once; commands return a retry message. |
| Missing or deleted channel | The database state remains safe; posting failure is logged with IDs. |
| Unexpected exception | Logged with payload type; a private ?nothing was deleted? notice is attempted when possible. |
| Process stops | SQLite state remains; the scheduler restarts from due, not-yet-nudged trials. |
- SQL values are parameterized. The only interpolated SQL fragment is selected from an internal status allowlist.
- Every friction/trial read used by Slack is scoped through the workspace ID.
- Secrets are loaded from environment variables and are never included in Slack blocks or logs.
- No external AI or analytics service receives workspace content.
- The bot stores Slack IDs and user-provided friction/trial text. Teams should apply their own retention and access policy before production use.
app.py
slack-manifest.yaml
requirements.txt
splinterlog/
blocks.py
config.py
database.py
scheduler.py
service.py
slack_app.py
tests/
test_database.py
test_service.py
docs/
ARCHITECTURE.md
DEVELOPMENT_LOG.md
DEMO_GUIDE.md
- SQLite schema creation is suitable for this version, but production upgrades need explicit migrations.
- The in-process scheduler assumes one bot process. Multiple replicas need a database-backed job lease before posting.
- A permanently missing channel remains due and will be retried on later loops; there is no admin repair screen yet.
- The source message timestamp is stored but not rendered as a permalink.
- There is no edit, archive, export, retention, or workspace-settings interface in version 0.1.0.
- Admin verification depends on
users:readand workspace role flags. - A real Slack workspace demo and screenshots still require valid credentials; none are fabricated in this repository.
- Add versioned database migrations and a backup/restore command.
- Add workspace timezone and review-channel settings in App Home.
- Add a distributed scheduler lease before supporting multiple processes.
- Add safe edit/archive actions with confirmation and undo.
- Turn source timestamps into permission-safe Slack permalinks.
- Add mocked Slack payload tests for every handler and rate-limit response.
docs/ARCHITECTURE.md- boundaries, data flow, state transitions, and reliability choices.docs/DEVELOPMENT_LOG.md- what changed, real challenges, fixes, lessons, and next steps.docs/DEMO_GUIDE.md- a short live demo, test data, and screenshot/GIF plan.slack-manifest.yaml- commands, shortcut, events, settings, and least-privilege scopes.tests/- executable proof of core state and failure behavior.
Built with Slack Bolt for Python, Slack Block Kit, Python's built-in sqlite3, and a lot of attention to small team paper cuts.