Compare commits
385 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c2f477dde6 | |||
| b098c8ff9f | |||
| 7fea543f60 | |||
| f8bb23a6d4 | |||
| 0016841770 | |||
| 3639d64da6 | |||
| ebdcf18134 | |||
| d83f2ae4c1 | |||
| f6c0d35f11 | |||
| facbf51f05 | |||
| 847ebc292f | |||
| 065ca2bd8f | |||
| 34988855bc | |||
| 7fc788baea | |||
| 40575924b5 | |||
| 4f56fa6cbe | |||
| 52513b55ff | |||
| 1ae2fa9b9b | |||
| 6077f74dbd | |||
| 8ab2e19e98 | |||
| b44f3a33e3 | |||
| 57407aed51 | |||
| a29677b3c7 | |||
| 95df450fca | |||
| 6c6bc35785 | |||
| 7652bbba9c | |||
| efd89a26ac | |||
| 71d4746009 | |||
| 98b5475160 | |||
| 740f1b5e6e | |||
| c0bab1e671 | |||
| 306810e4d5 | |||
| 1193b7ac9a | |||
| 05db012aaf | |||
| bc3c852509 | |||
| 04051282da | |||
| 081b33a8a6 | |||
| cf5424f9a6 | |||
| 1ec9aaab8a | |||
| d6f82393f5 | |||
| f4ce0e017b | |||
| c0ea5f0cb8 | |||
| d375c4b1d3 | |||
| 4ea4be1462 | |||
| bc1c1cd2c9 | |||
| c1e4c40f31 | |||
| 203e8f22be | |||
| 665c036a56 | |||
| 73304f08ac | |||
| fe9fc69f96 | |||
| 3b0542cd41 | |||
| 102919e0b3 | |||
| d63aa0a3c2 | |||
| 7f7db57933 | |||
| 043791194f | |||
| 710f839c65 | |||
| b0e21abb6e | |||
| 6b71c07f5b | |||
| 9cff3c753d | |||
| 6acd7f5249 | |||
| 26f5b25f22 | |||
| 8bc0bd592e | |||
| 7c25aca39b | |||
| 5173bf4aef | |||
| 7f7f49d757 | |||
| e88b9bbc63 | |||
| db22ab2229 | |||
| c30ad79398 | |||
| 16853328fa | |||
| 8ac8cdba88 | |||
| c046edebda | |||
| eef9669c95 | |||
| a9cdd3a354 | |||
| b4eeb499e9 | |||
| fca46c3806 | |||
| 2510fe44bc | |||
| e152cf3cb8 | |||
| 7d3b256fff | |||
| f6d632139e | |||
| 204a99c2e7 | |||
| f28a03e42e | |||
| 26f4edadcc | |||
| fd58631e65 | |||
| f70399a28f | |||
| 02d08faaa2 | |||
| 2dc77479ad | |||
| c5761ae968 | |||
| 67754781ca | |||
| 3436507a21 | |||
| 93bc08574b | |||
| 3571511349 | |||
| 04214ca155 | |||
| 4d48df152c | |||
| e0a70a4c1c | |||
| bae50fbc5b | |||
| 8998dac593 | |||
| e6b300e70e | |||
| b22e2b9274 | |||
| 24b1aa6e7f | |||
| 85e37e03a8 | |||
| f22a2666b8 | |||
| 507889627a | |||
| c4cee72938 | |||
| 33cb363651 | |||
| cd3ded278d | |||
| b5bf75aa5a | |||
| f6b5b1b01a | |||
| 26d34245f9 | |||
| de54265c35 | |||
| a52d1e098f | |||
| 015fa48c32 | |||
| abc30c93d1 | |||
| cf2faa9bff | |||
| 92aa1ebccf | |||
| 877f69c897 | |||
| 710b604b7c | |||
| ab4ce2db92 | |||
| 61f6fd60a8 | |||
| e66149e07c | |||
| 108a697483 | |||
| 1a7f419ecf | |||
| 96b1ce373b | |||
| 58e41f7e0b | |||
| c9a2fa58eb | |||
| 64c0f190cf | |||
| fc443ed987 | |||
| 7939a19816 | |||
| 46b5087157 | |||
| a8d6524b56 | |||
| 61d63db84c | |||
| aa4ec8c779 | |||
| 3777042ad3 | |||
| feb340beba | |||
| 23369c514d | |||
| 832da16b6f | |||
| 131964cbc3 | |||
| 81db0504ed | |||
| 584a44a516 | |||
| c7c4a57533 | |||
| 0a67c28f8c | |||
| 6476492caa | |||
| faf8734ea8 | |||
| 862f0704be | |||
| d3df1586c6 | |||
| 8a1996e0e4 | |||
| 61f5a0c3be | |||
| d7bc785de1 | |||
| eaac665a9f | |||
| d702aa59c4 | |||
| 9df9a1454a | |||
| 47163d235c | |||
| a7342fc9d3 | |||
| 5dd8feb75c | |||
| f5024b2648 | |||
| 6521c83eec | |||
| 65e3643655 | |||
| fc95b57a78 | |||
| 7c1a970b13 | |||
| 64e2df20b7 | |||
| 90e3612fd3 | |||
| 962bfe37c6 | |||
| f05c6a42b0 | |||
| 077288e7b7 | |||
| 580ab1ce68 | |||
| 71a6c72614 | |||
| fae7b3be20 | |||
| 775b9ac7e3 | |||
| 5a87d55dd4 | |||
| 0457fbfecc | |||
| 13b16138b5 | |||
| 8249896449 | |||
| dbd932bf46 | |||
| eef49678ce | |||
| 58ee82c988 | |||
| 49ac23044a | |||
| 84a775be77 | |||
| 60c0c95f38 | |||
| a1a30bcc42 | |||
| 96ebd7ecb8 | |||
| 25c8b1ec25 | |||
| bcb7cfabee | |||
| d4dad1d556 | |||
| 195c7c51c4 | |||
| 968d973cff | |||
| 4394ab3fed | |||
| 11bbfca3da | |||
| a9aa88b655 | |||
| b62974dd88 | |||
| ac52a8bb4e | |||
| 18755aac96 | |||
| 5d37421f70 | |||
| 224d269971 | |||
| 6146a173f1 | |||
| 821345d266 | |||
| 0fa63e2de3 | |||
| d8cbec8268 | |||
| 618a2779ff | |||
| 721d12bcfe | |||
| df6d2db327 | |||
| 49285c1865 | |||
| 0c15be43b8 | |||
| 9408bd2cdf | |||
| a24e4c5c85 | |||
| c0133fe733 | |||
| 752c3904bf | |||
| bac53ac09a | |||
| b2ef2eca5f | |||
| fb05f71e76 | |||
| 438be196c9 | |||
| f1b4894d6e | |||
| bd281fd749 | |||
| 79edc28334 | |||
| 92c53704f0 | |||
| 7223fa2f10 | |||
| dedf951b17 | |||
| aad583defd | |||
| 88b02cf746 | |||
| 1a9833d820 | |||
| a904cda629 | |||
| c755c03f0e | |||
| a8630f3e1b | |||
| 9fb1bd5711 | |||
| 0b3ce0f33e | |||
| f4b7573f0a | |||
| bb801ba826 | |||
| 53634d638d | |||
| b50e7cff00 | |||
| 68973b0bb8 | |||
| 34bbf5a122 | |||
| ed3c5f9c95 | |||
| 59d1a2c069 | |||
| 52e73bfbea | |||
| 4e590401a5 | |||
| 6b6815325d | |||
| f874783b09 | |||
| 292f9cdfe2 | |||
| 1cce46d3fa | |||
| e85c06df19 | |||
| 8b85ca743e | |||
| 1a7b6c7342 | |||
| 4a94158ef2 | |||
| f10ea1ecf2 | |||
| 1a3b69301a | |||
| 6d3eab92fd | |||
| f6920a87ad | |||
| 5f9d903987 | |||
| ea916d27f4 | |||
| 970b9bcd9d | |||
| a5ee6890f5 | |||
| 41dc3292bb | |||
| 3766f8b464 | |||
| 0c85ecc85c | |||
| 2c29a4d2b8 | |||
| 454d694d24 | |||
| 96bedd70dc | |||
| fffdd5c5ea | |||
| 4805598932 | |||
| 3d55e2fcc6 | |||
| 96b31d1a48 | |||
| 11168fa426 | |||
| c2c2d65889 | |||
| 5c8c4b7ff3 | |||
| fbab93f493 | |||
| 78ff6d104e | |||
| fcc2b9c3eb | |||
| 0c4239501a | |||
| 13b6ecd958 | |||
| 1816a94617 | |||
| 56d3373e69 | |||
| efdb0c5814 | |||
| b8365275d8 | |||
| 6ddfd29927 | |||
| 01b157a2e4 | |||
| 99a59d7ad1 | |||
| eb8adb6225 | |||
| 2262f2ca6b | |||
| 2bb36d0e68 | |||
| 86102f8ad6 | |||
| edf47601c4 | |||
| b606e1de92 | |||
| 0d5f0de876 | |||
| bb41f3951c | |||
| e3d7931f17 | |||
| 87b5648123 | |||
| 506bdd4df8 | |||
| a9bec3c29e | |||
| 69936f457f | |||
| 24dd3d9fa9 | |||
| bc45a91b3e | |||
| db7c11508e | |||
| 47173e0d3a | |||
| f610ef6046 | |||
| 89f776b978 | |||
| e4227cf673 | |||
| f346712dd1 | |||
| f9419e5ea7 | |||
| c32bab03a4 | |||
| ea23042698 | |||
| 3825b03fda | |||
| d6cfd18e6a | |||
| 01ac8a8345 | |||
| 153f8812d7 | |||
| 01c7c39872 | |||
| eec8f3ac15 | |||
| 28626ab80a | |||
| 4262af7faa | |||
| 628b60ad15 | |||
| c504738949 | |||
| 0d5b9724c1 | |||
| b189ca845c | |||
| 8094d32cbb | |||
| 1c2824fa31 | |||
| af72f593e8 | |||
| ac8112bf0b | |||
| 9bf4b65707 | |||
| 240ebf055a | |||
| 293a2fcfb6 | |||
| 4ccc3d9149 | |||
| eef0f3ee7d | |||
| 9dc7c21b05 | |||
| 76369de391 | |||
| b747cc0fab | |||
| f74a0425a9 | |||
| b0b21765d9 | |||
| 9075bc1a84 | |||
| 9f873dc839 | |||
| 3774c3dca7 | |||
| cd095f9a99 | |||
| fe0f560b58 | |||
| 0416bf343c | |||
| c3e4f85903 | |||
| 52d9d0f9ce | |||
| 996ba82682 | |||
| 1f4152c894 | |||
| 02b481ee4c | |||
| 9c339c118f | |||
| 4790aac286 | |||
| b2d92d6059 | |||
| 71887af2d3 | |||
| 5db9965962 | |||
| e109e1ba5c | |||
| 3554594d8d | |||
| a6c8cf0daf | |||
| 30a56d03e5 | |||
| 4734bd943f | |||
| a1dd88579b | |||
| 759a289894 | |||
| be3b5b0b60 | |||
| fbf391684a | |||
| 65546a42b7 | |||
| 4e014d45c3 | |||
| 4f39de437f | |||
| 79ee6eb0dc | |||
| c930c537bc | |||
| f129a38704 | |||
| 4344081b54 | |||
| 52c5344ce5 | |||
| 35bd196790 | |||
| 65c8dc19d6 | |||
| 645a141d2d | |||
| 11d1980920 | |||
| 83879cfa9e | |||
| 972d8f3c12 | |||
| 4b1167025c | |||
| 23eb752e3b | |||
| 7aa1d0e322 | |||
| a6dcd48da9 | |||
| 87958b0a2a | |||
| ea061d868d | |||
| 6a03ca725e | |||
| 0cd7c15227 | |||
| 0cb43a4de4 | |||
| cb663b620b | |||
| 0653af701c | |||
| b1a96990c4 | |||
| e46f855ab3 | |||
| d838dd7127 | |||
| 02ee48911e | |||
| 6429b20974 | |||
| dcf0be2998 | |||
| efea81b487 | |||
| 491ca19a0b | |||
| 243b75e966 | |||
| 7693cc820c | |||
| ba4af4179e |
@@ -0,0 +1,11 @@
|
|||||||
|
# Docker build context exclusions
|
||||||
|
**/target/
|
||||||
|
**/node_modules/
|
||||||
|
frontend/dist/
|
||||||
|
.storkit/worktrees/
|
||||||
|
.storkit/logs/
|
||||||
|
.storkit/work/6_archived/
|
||||||
|
.git/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
.DS_Store
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
# App specific (root-level; storkit subdirectory patterns live in .storkit/.gitignore)
|
# App specific (root-level; storkit subdirectory patterns live in .storkit/.gitignore)
|
||||||
store.json
|
store.json
|
||||||
.storkit_port
|
.storkit_port
|
||||||
|
.storkit/bot.toml.bak
|
||||||
|
|
||||||
# Rust stuff
|
# Rust stuff
|
||||||
target
|
target
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ frontend/
|
|||||||
node_modules/
|
node_modules/
|
||||||
.claude/
|
.claude/
|
||||||
.git/
|
.git/
|
||||||
.story_kit/
|
.storkit/
|
||||||
store.json
|
store.json
|
||||||
.story_kit_port
|
.storkit_port
|
||||||
|
|||||||
@@ -20,3 +20,6 @@ coverage/
|
|||||||
|
|
||||||
# Token usage log (generated at runtime, contains cost data)
|
# Token usage log (generated at runtime, contains cost data)
|
||||||
token_usage.jsonl
|
token_usage.jsonl
|
||||||
|
|
||||||
|
# Chat service logs
|
||||||
|
whatsapp_history.json
|
||||||
|
|||||||
+23
-1
@@ -228,7 +228,29 @@ If a user hands you this document and says "Apply this process to my project":
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Code Quality
|
## 6. Chat Bot Configuration
|
||||||
|
|
||||||
|
Story Kit includes a chat bot that can be connected to one messaging platform at a time. The bot handles commands, LLM conversations, and pipeline notifications.
|
||||||
|
|
||||||
|
**Only one transport can be active at a time.** To configure the bot, copy the appropriate example file to `.storkit/bot.toml`:
|
||||||
|
|
||||||
|
| Transport | Example file | Webhook endpoint |
|
||||||
|
|-----------|-------------|-----------------|
|
||||||
|
| Matrix | `bot.toml.matrix.example` | *(uses Matrix sync, no webhook)* |
|
||||||
|
| WhatsApp (Meta Cloud API) | `bot.toml.whatsapp-meta.example` | `/webhook/whatsapp` |
|
||||||
|
| WhatsApp (Twilio) | `bot.toml.whatsapp-twilio.example` | `/webhook/whatsapp` |
|
||||||
|
| Slack | `bot.toml.slack.example` | `/webhook/slack` |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .storkit/bot.toml.matrix.example .storkit/bot.toml
|
||||||
|
# Edit bot.toml with your credentials
|
||||||
|
```
|
||||||
|
|
||||||
|
The `bot.toml` file is gitignored (it contains secrets). The example files are checked in for reference.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Code Quality
|
||||||
|
|
||||||
**MANDATORY:** Before completing Step 3 (Verification) of any story, you MUST run all applicable linters, formatters, and test suites and fix ALL errors and warnings. Zero tolerance for warnings or errors.
|
**MANDATORY:** Before completing Step 3 (Verification) of any story, you MUST run all applicable linters, formatters, and test suites and fix ALL errors and warnings. Zero tolerance for warnings or errors.
|
||||||
|
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
homeserver = "https://matrix.example.com"
|
|
||||||
username = "@botname:example.com"
|
|
||||||
password = "your-bot-password"
|
|
||||||
|
|
||||||
# List one or more rooms to listen in. Use a single-element list for one room.
|
|
||||||
room_ids = ["!roomid:example.com"]
|
|
||||||
|
|
||||||
# Optional: the deprecated single-room key is still accepted for backwards compat.
|
|
||||||
# room_id = "!roomid:example.com"
|
|
||||||
|
|
||||||
allowed_users = ["@youruser:example.com"]
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
# Maximum conversation turns to remember per room (default: 20).
|
|
||||||
# history_size = 20
|
|
||||||
|
|
||||||
# Rooms where the bot responds to all messages (not just addressed ones).
|
|
||||||
# This list is updated automatically when users toggle ambient mode at runtime.
|
|
||||||
# ambient_rooms = ["!roomid:example.com"]
|
|
||||||
|
|
||||||
# ── WhatsApp Business API ──────────────────────────────────────────────
|
|
||||||
# Set transport = "whatsapp" to use WhatsApp instead of Matrix.
|
|
||||||
# The webhook endpoint will be available at /webhook/whatsapp.
|
|
||||||
# You must configure this URL in the Meta Developer Dashboard.
|
|
||||||
#
|
|
||||||
# transport = "whatsapp"
|
|
||||||
# whatsapp_phone_number_id = "123456789012345"
|
|
||||||
# whatsapp_access_token = "EAAx..."
|
|
||||||
# whatsapp_verify_token = "my-secret-verify-token"
|
|
||||||
#
|
|
||||||
# ── 24-hour messaging window & notification templates ─────────────────
|
|
||||||
# WhatsApp only allows free-form text messages within 24 hours of the last
|
|
||||||
# inbound message from a user. For proactive pipeline notifications sent
|
|
||||||
# after the window expires, an approved Meta message template is used.
|
|
||||||
#
|
|
||||||
# Register the template in the Meta Business Manager:
|
|
||||||
# 1. Go to Business Settings → WhatsApp → Message Templates → Create.
|
|
||||||
# 2. Category: UTILITY
|
|
||||||
# 3. Template name: pipeline_notification (or your chosen name below)
|
|
||||||
# 4. Language: English (en_US)
|
|
||||||
# 5. Body text (example):
|
|
||||||
# Story *{{1}}* has moved to *{{2}}*.
|
|
||||||
# Where {{1}} = story name, {{2}} = pipeline stage.
|
|
||||||
# 6. Submit for review. Meta typically approves utility templates within
|
|
||||||
# minutes; transactional categories may take longer.
|
|
||||||
#
|
|
||||||
# Once approved, set the name below (default: "pipeline_notification"):
|
|
||||||
# whatsapp_notification_template = "pipeline_notification"
|
|
||||||
|
|
||||||
# ── Slack Bot API ─────────────────────────────────────────────────────
|
|
||||||
# Set transport = "slack" to use Slack instead of Matrix.
|
|
||||||
# The webhook endpoint will be available at /webhook/slack.
|
|
||||||
# Configure this URL in the Slack App → Event Subscriptions → Request URL.
|
|
||||||
#
|
|
||||||
# Required Slack App scopes: chat:write, chat:update
|
|
||||||
# Subscribe to bot events: message.channels, message.groups, message.im
|
|
||||||
#
|
|
||||||
# transport = "slack"
|
|
||||||
# slack_bot_token = "xoxb-..."
|
|
||||||
# slack_signing_secret = "your-signing-secret"
|
|
||||||
# slack_channel_ids = ["C01ABCDEF"]
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Matrix Transport
|
||||||
|
# Copy this file to bot.toml and fill in your values.
|
||||||
|
# Only one transport can be active at a time.
|
||||||
|
|
||||||
|
enabled = true
|
||||||
|
transport = "matrix"
|
||||||
|
|
||||||
|
homeserver = "https://matrix.example.com"
|
||||||
|
username = "@botname:example.com"
|
||||||
|
password = "your-bot-password"
|
||||||
|
|
||||||
|
# List one or more rooms to listen in.
|
||||||
|
room_ids = ["!roomid:example.com"]
|
||||||
|
|
||||||
|
# Users allowed to interact with the bot (fail-closed: empty = nobody).
|
||||||
|
allowed_users = ["@youruser:example.com"]
|
||||||
|
|
||||||
|
# Bot display name in chat.
|
||||||
|
# display_name = "Assistant"
|
||||||
|
|
||||||
|
# Maximum conversation turns to remember per room (default: 20).
|
||||||
|
# history_size = 20
|
||||||
|
|
||||||
|
# Rooms where the bot responds to all messages (not just addressed ones).
|
||||||
|
# This list is updated automatically when users toggle ambient mode at runtime.
|
||||||
|
# ambient_rooms = ["!roomid:example.com"]
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Slack Transport
|
||||||
|
# Copy this file to bot.toml and fill in your values.
|
||||||
|
# Only one transport can be active at a time.
|
||||||
|
#
|
||||||
|
# Setup:
|
||||||
|
# 1. Create a Slack App at api.slack.com/apps
|
||||||
|
# 2. Add OAuth scopes: chat:write, chat:update
|
||||||
|
# 3. Subscribe to bot events: message.channels, message.groups, message.im
|
||||||
|
# 4. Install the app to your workspace
|
||||||
|
# 5. Set your webhook URL in Event Subscriptions: https://your-server/webhook/slack
|
||||||
|
|
||||||
|
enabled = true
|
||||||
|
transport = "slack"
|
||||||
|
|
||||||
|
slack_bot_token = "xoxb-..."
|
||||||
|
slack_signing_secret = "your-signing-secret"
|
||||||
|
slack_channel_ids = ["C01ABCDEF"]
|
||||||
|
|
||||||
|
# Bot display name (used in formatted messages).
|
||||||
|
# display_name = "Assistant"
|
||||||
|
|
||||||
|
# Maximum conversation turns to remember per channel (default: 20).
|
||||||
|
# history_size = 20
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# WhatsApp Transport (Meta Cloud API)
|
||||||
|
# Copy this file to bot.toml and fill in your values.
|
||||||
|
# Only one transport can be active at a time.
|
||||||
|
#
|
||||||
|
# Setup:
|
||||||
|
# 1. Create a Meta Business App at developers.facebook.com
|
||||||
|
# 2. Add the WhatsApp product
|
||||||
|
# 3. Copy your Phone Number ID and generate a permanent access token
|
||||||
|
# 4. Register your webhook URL: https://your-server/webhook/whatsapp
|
||||||
|
# 5. Set the verify token below to match what you configure in Meta's dashboard
|
||||||
|
|
||||||
|
enabled = true
|
||||||
|
transport = "whatsapp"
|
||||||
|
whatsapp_provider = "meta"
|
||||||
|
|
||||||
|
whatsapp_phone_number_id = "123456789012345"
|
||||||
|
whatsapp_access_token = "EAAx..."
|
||||||
|
whatsapp_verify_token = "my-secret-verify-token"
|
||||||
|
|
||||||
|
# Optional: name of the approved Meta message template used for notifications
|
||||||
|
# sent outside the 24-hour messaging window (default: "pipeline_notification").
|
||||||
|
# whatsapp_notification_template = "pipeline_notification"
|
||||||
|
|
||||||
|
# Bot display name (used in formatted messages).
|
||||||
|
# display_name = "Assistant"
|
||||||
|
|
||||||
|
# Maximum conversation turns to remember per user (default: 20).
|
||||||
|
# history_size = 20
|
||||||
|
|
||||||
|
# Optional: restrict which phone numbers can interact with the bot.
|
||||||
|
# When set, only listed numbers are processed; all others are silently ignored.
|
||||||
|
# When absent or empty, all numbers are allowed (open by default).
|
||||||
|
# whatsapp_allowed_phones = ["+15551234567", "+15559876543"]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# WhatsApp Transport (Twilio)
|
||||||
|
# Copy this file to bot.toml and fill in your values.
|
||||||
|
# Only one transport can be active at a time.
|
||||||
|
#
|
||||||
|
# Setup:
|
||||||
|
# 1. Sign up at twilio.com
|
||||||
|
# 2. Activate the WhatsApp sandbox (Messaging > Try it out > Send a WhatsApp message)
|
||||||
|
# 3. Send the sandbox join code from your WhatsApp to the sandbox number
|
||||||
|
# 4. Copy your Account SID, Auth Token, and sandbox number below
|
||||||
|
# 5. Set your webhook URL in the Twilio console: https://your-server/webhook/whatsapp
|
||||||
|
|
||||||
|
enabled = true
|
||||||
|
transport = "whatsapp"
|
||||||
|
whatsapp_provider = "twilio"
|
||||||
|
|
||||||
|
twilio_account_sid = "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
twilio_auth_token = "your_auth_token"
|
||||||
|
twilio_whatsapp_number = "+14155238886"
|
||||||
|
|
||||||
|
# Bot display name (used in formatted messages).
|
||||||
|
# display_name = "Assistant"
|
||||||
|
|
||||||
|
# Maximum conversation turns to remember per user (default: 20).
|
||||||
|
# history_size = 20
|
||||||
|
|
||||||
|
# Optional: restrict which phone numbers can interact with the bot.
|
||||||
|
# When set, only listed numbers are processed; all others are silently ignored.
|
||||||
|
# When absent or empty, all numbers are allowed (open by default).
|
||||||
|
# whatsapp_allowed_phones = ["+15551234567", "+15559876543"]
|
||||||
+11
-6
@@ -11,12 +11,17 @@ max_coders = 3
|
|||||||
|
|
||||||
# Maximum retries per story per pipeline stage before marking as blocked.
|
# Maximum retries per story per pipeline stage before marking as blocked.
|
||||||
# Set to 0 to disable retry limits.
|
# Set to 0 to disable retry limits.
|
||||||
max_retries = 2
|
max_retries = 3
|
||||||
|
|
||||||
|
# Base branch name for this project. Worktree creation, merges, and agent prompts
|
||||||
|
# use this value for {{base_branch}}. When not set, falls back to auto-detection
|
||||||
|
# (reads current HEAD branch).
|
||||||
|
base_branch = "master"
|
||||||
|
|
||||||
[[component]]
|
[[component]]
|
||||||
name = "frontend"
|
name = "frontend"
|
||||||
path = "frontend"
|
path = "frontend"
|
||||||
setup = ["npm install", "npm run build"]
|
setup = ["npm ci", "npm run build"]
|
||||||
teardown = []
|
teardown = []
|
||||||
|
|
||||||
[[component]]
|
[[component]]
|
||||||
@@ -33,7 +38,7 @@ model = "sonnet"
|
|||||||
max_turns = 50
|
max_turns = 50
|
||||||
max_budget_usd = 5.00
|
max_budget_usd = 5.00
|
||||||
prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. The story details are in your prompt above. Follow the SDTW process through implementation and verification (Steps 1-3). The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop. If the user asks to review your changes, tell them to run: cd \"{{worktree_path}}\" && git difftool {{base_branch}}...HEAD\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates (cargo clippy + tests) when your process exits and advance the pipeline based on the results.\n\n## Bug Workflow: Root Cause First\nWhen working on bugs:\n1. Investigate the root cause before writing any fix. Use `git bisect` to find the breaking commit or `git log` to trace history. Read the relevant code before touching anything.\n2. Fix the root cause with a surgical, minimal change. Do NOT add new abstractions, wrappers, or workarounds when a targeted fix to the original code is possible.\n3. Write commit messages that explain what broke and why, not just what was changed.\n4. If you cannot determine the root cause after thorough investigation, document what you tried and why it was inconclusive — do not guess and ship a speculative fix."
|
prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. The story details are in your prompt above. Follow the SDTW process through implementation and verification (Steps 1-3). The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop. If the user asks to review your changes, tell them to run: cd \"{{worktree_path}}\" && git difftool {{base_branch}}...HEAD\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates (cargo clippy + tests) when your process exits and advance the pipeline based on the results.\n\n## Bug Workflow: Root Cause First\nWhen working on bugs:\n1. Investigate the root cause before writing any fix. Use `git bisect` to find the breaking commit or `git log` to trace history. Read the relevant code before touching anything.\n2. Fix the root cause with a surgical, minimal change. Do NOT add new abstractions, wrappers, or workarounds when a targeted fix to the original code is possible.\n3. Write commit messages that explain what broke and why, not just what was changed.\n4. If you cannot determine the root cause after thorough investigation, document what you tried and why it was inconclusive — do not guess and ship a speculative fix."
|
||||||
system_prompt = "You are a full-stack engineer working autonomously in a git worktree. Follow the Story-Driven Test Workflow strictly. Run cargo clippy and biome checks before considering work complete. Commit all your work before finishing - use a descriptive commit message. Do not accept stories, move them to archived, or merge to master - a human will do that. Do not coordinate with other agents - focus on your assigned story. The server automatically runs acceptance gates when your process exits. For bugs, always find and fix the root cause. Use git bisect to find breaking commits. Do not layer new code on top of existing code when a surgical fix is possible. If root cause is unclear after investigation, document what you tried rather than guessing."
|
system_prompt = "You are a full-stack engineer working autonomously in a git worktree. Follow the Story-Driven Test Workflow strictly. Run cargo clippy --all-targets --all-features and biome checks before considering work complete. Commit all your work before finishing - use a descriptive commit message. Do not accept stories, move them to archived, or merge to master - a human will do that. Do not coordinate with other agents - focus on your assigned story. The server automatically runs acceptance gates when your process exits. For bugs, always find and fix the root cause. Use git bisect to find breaking commits. Do not layer new code on top of existing code when a surgical fix is possible. If root cause is unclear after investigation, document what you tried rather than guessing."
|
||||||
|
|
||||||
[[agent]]
|
[[agent]]
|
||||||
name = "coder-2"
|
name = "coder-2"
|
||||||
@@ -43,7 +48,7 @@ model = "sonnet"
|
|||||||
max_turns = 50
|
max_turns = 50
|
||||||
max_budget_usd = 5.00
|
max_budget_usd = 5.00
|
||||||
prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. The story details are in your prompt above. Follow the SDTW process through implementation and verification (Steps 1-3). The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop. If the user asks to review your changes, tell them to run: cd \"{{worktree_path}}\" && git difftool {{base_branch}}...HEAD\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates (cargo clippy + tests) when your process exits and advance the pipeline based on the results.\n\n## Bug Workflow: Root Cause First\nWhen working on bugs:\n1. Investigate the root cause before writing any fix. Use `git bisect` to find the breaking commit or `git log` to trace history. Read the relevant code before touching anything.\n2. Fix the root cause with a surgical, minimal change. Do NOT add new abstractions, wrappers, or workarounds when a targeted fix to the original code is possible.\n3. Write commit messages that explain what broke and why, not just what was changed.\n4. If you cannot determine the root cause after thorough investigation, document what you tried and why it was inconclusive — do not guess and ship a speculative fix."
|
prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. The story details are in your prompt above. Follow the SDTW process through implementation and verification (Steps 1-3). The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop. If the user asks to review your changes, tell them to run: cd \"{{worktree_path}}\" && git difftool {{base_branch}}...HEAD\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates (cargo clippy + tests) when your process exits and advance the pipeline based on the results.\n\n## Bug Workflow: Root Cause First\nWhen working on bugs:\n1. Investigate the root cause before writing any fix. Use `git bisect` to find the breaking commit or `git log` to trace history. Read the relevant code before touching anything.\n2. Fix the root cause with a surgical, minimal change. Do NOT add new abstractions, wrappers, or workarounds when a targeted fix to the original code is possible.\n3. Write commit messages that explain what broke and why, not just what was changed.\n4. If you cannot determine the root cause after thorough investigation, document what you tried and why it was inconclusive — do not guess and ship a speculative fix."
|
||||||
system_prompt = "You are a full-stack engineer working autonomously in a git worktree. Follow the Story-Driven Test Workflow strictly. Run cargo clippy and biome checks before considering work complete. Commit all your work before finishing - use a descriptive commit message. Do not accept stories, move them to archived, or merge to master - a human will do that. Do not coordinate with other agents - focus on your assigned story. The server automatically runs acceptance gates when your process exits. For bugs, always find and fix the root cause. Use git bisect to find breaking commits. Do not layer new code on top of existing code when a surgical fix is possible. If root cause is unclear after investigation, document what you tried rather than guessing."
|
system_prompt = "You are a full-stack engineer working autonomously in a git worktree. Follow the Story-Driven Test Workflow strictly. Run cargo clippy --all-targets --all-features and biome checks before considering work complete. Commit all your work before finishing - use a descriptive commit message. Do not accept stories, move them to archived, or merge to master - a human will do that. Do not coordinate with other agents - focus on your assigned story. The server automatically runs acceptance gates when your process exits. For bugs, always find and fix the root cause. Use git bisect to find breaking commits. Do not layer new code on top of existing code when a surgical fix is possible. If root cause is unclear after investigation, document what you tried rather than guessing."
|
||||||
|
|
||||||
[[agent]]
|
[[agent]]
|
||||||
name = "coder-3"
|
name = "coder-3"
|
||||||
@@ -53,7 +58,7 @@ model = "sonnet"
|
|||||||
max_turns = 50
|
max_turns = 50
|
||||||
max_budget_usd = 5.00
|
max_budget_usd = 5.00
|
||||||
prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. The story details are in your prompt above. Follow the SDTW process through implementation and verification (Steps 1-3). The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop. If the user asks to review your changes, tell them to run: cd \"{{worktree_path}}\" && git difftool {{base_branch}}...HEAD\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates (cargo clippy + tests) when your process exits and advance the pipeline based on the results.\n\n## Bug Workflow: Root Cause First\nWhen working on bugs:\n1. Investigate the root cause before writing any fix. Use `git bisect` to find the breaking commit or `git log` to trace history. Read the relevant code before touching anything.\n2. Fix the root cause with a surgical, minimal change. Do NOT add new abstractions, wrappers, or workarounds when a targeted fix to the original code is possible.\n3. Write commit messages that explain what broke and why, not just what was changed.\n4. If you cannot determine the root cause after thorough investigation, document what you tried and why it was inconclusive — do not guess and ship a speculative fix."
|
prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. The story details are in your prompt above. Follow the SDTW process through implementation and verification (Steps 1-3). The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop. If the user asks to review your changes, tell them to run: cd \"{{worktree_path}}\" && git difftool {{base_branch}}...HEAD\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates (cargo clippy + tests) when your process exits and advance the pipeline based on the results.\n\n## Bug Workflow: Root Cause First\nWhen working on bugs:\n1. Investigate the root cause before writing any fix. Use `git bisect` to find the breaking commit or `git log` to trace history. Read the relevant code before touching anything.\n2. Fix the root cause with a surgical, minimal change. Do NOT add new abstractions, wrappers, or workarounds when a targeted fix to the original code is possible.\n3. Write commit messages that explain what broke and why, not just what was changed.\n4. If you cannot determine the root cause after thorough investigation, document what you tried and why it was inconclusive — do not guess and ship a speculative fix."
|
||||||
system_prompt = "You are a full-stack engineer working autonomously in a git worktree. Follow the Story-Driven Test Workflow strictly. Run cargo clippy and biome checks before considering work complete. Commit all your work before finishing - use a descriptive commit message. Do not accept stories, move them to archived, or merge to master - a human will do that. Do not coordinate with other agents - focus on your assigned story. The server automatically runs acceptance gates when your process exits. For bugs, always find and fix the root cause. Use git bisect to find breaking commits. Do not layer new code on top of existing code when a surgical fix is possible. If root cause is unclear after investigation, document what you tried rather than guessing."
|
system_prompt = "You are a full-stack engineer working autonomously in a git worktree. Follow the Story-Driven Test Workflow strictly. Run cargo clippy --all-targets --all-features and biome checks before considering work complete. Commit all your work before finishing - use a descriptive commit message. Do not accept stories, move them to archived, or merge to master - a human will do that. Do not coordinate with other agents - focus on your assigned story. The server automatically runs acceptance gates when your process exits. For bugs, always find and fix the root cause. Use git bisect to find breaking commits. Do not layer new code on top of existing code when a surgical fix is possible. If root cause is unclear after investigation, document what you tried rather than guessing."
|
||||||
|
|
||||||
[[agent]]
|
[[agent]]
|
||||||
name = "qa-2"
|
name = "qa-2"
|
||||||
@@ -130,7 +135,7 @@ model = "opus"
|
|||||||
max_turns = 80
|
max_turns = 80
|
||||||
max_budget_usd = 20.00
|
max_budget_usd = 20.00
|
||||||
prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. The story details are in your prompt above. Follow the SDTW process through implementation and verification (Steps 1-3). The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop. If the user asks to review your changes, tell them to run: cd \"{{worktree_path}}\" && git difftool {{base_branch}}...HEAD\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates (cargo clippy + tests) when your process exits and advance the pipeline based on the results.\n\n## Bug Workflow: Root Cause First\nWhen working on bugs:\n1. Investigate the root cause before writing any fix. Use `git bisect` to find the breaking commit or `git log` to trace history. Read the relevant code before touching anything.\n2. Fix the root cause with a surgical, minimal change. Do NOT add new abstractions, wrappers, or workarounds when a targeted fix to the original code is possible.\n3. Write commit messages that explain what broke and why, not just what was changed.\n4. If you cannot determine the root cause after thorough investigation, document what you tried and why it was inconclusive — do not guess and ship a speculative fix."
|
prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. The story details are in your prompt above. Follow the SDTW process through implementation and verification (Steps 1-3). The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop. If the user asks to review your changes, tell them to run: cd \"{{worktree_path}}\" && git difftool {{base_branch}}...HEAD\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates (cargo clippy + tests) when your process exits and advance the pipeline based on the results.\n\n## Bug Workflow: Root Cause First\nWhen working on bugs:\n1. Investigate the root cause before writing any fix. Use `git bisect` to find the breaking commit or `git log` to trace history. Read the relevant code before touching anything.\n2. Fix the root cause with a surgical, minimal change. Do NOT add new abstractions, wrappers, or workarounds when a targeted fix to the original code is possible.\n3. Write commit messages that explain what broke and why, not just what was changed.\n4. If you cannot determine the root cause after thorough investigation, document what you tried and why it was inconclusive — do not guess and ship a speculative fix."
|
||||||
system_prompt = "You are a senior full-stack engineer working autonomously in a git worktree. You handle complex tasks requiring deep architectural understanding. Follow the Story-Driven Test Workflow strictly. Run cargo clippy and biome checks before considering work complete. Commit all your work before finishing - use a descriptive commit message. Do not accept stories, move them to archived, or merge to master - a human will do that. Do not coordinate with other agents - focus on your assigned story. The server automatically runs acceptance gates when your process exits. For bugs, always find and fix the root cause. Use git bisect to find breaking commits. Do not layer new code on top of existing code when a surgical fix is possible. If root cause is unclear after investigation, document what you tried rather than guessing."
|
system_prompt = "You are a senior full-stack engineer working autonomously in a git worktree. You handle complex tasks requiring deep architectural understanding. Follow the Story-Driven Test Workflow strictly. Run cargo clippy --all-targets --all-features and biome checks before considering work complete. Commit all your work before finishing - use a descriptive commit message. Do not accept stories, move them to archived, or merge to master - a human will do that. Do not coordinate with other agents - focus on your assigned story. The server automatically runs acceptance gates when your process exits. For bugs, always find and fix the root cause. Use git bisect to find breaking commits. Do not layer new code on top of existing code when a surgical fix is possible. If root cause is unclear after investigation, document what you tried rather than guessing."
|
||||||
|
|
||||||
[[agent]]
|
[[agent]]
|
||||||
name = "qa"
|
name = "qa"
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Example project.toml — copy to .storkit/project.toml and customise.
|
||||||
|
# This file is checked in; project.toml itself is gitignored (it may contain
|
||||||
|
# instance-specific settings).
|
||||||
|
|
||||||
|
# Project-wide default QA mode: "server", "agent", or "human".
|
||||||
|
# Per-story `qa` front matter overrides this setting.
|
||||||
|
default_qa = "server"
|
||||||
|
|
||||||
|
# Default model for coder agents. Only agents with this model are auto-assigned.
|
||||||
|
# Opus coders are reserved for explicit per-story `agent:` front matter requests.
|
||||||
|
default_coder_model = "sonnet"
|
||||||
|
|
||||||
|
# Maximum concurrent coder agents. Stories wait in 2_current/ when all slots are full.
|
||||||
|
max_coders = 3
|
||||||
|
|
||||||
|
# Maximum retries per story per pipeline stage before marking as blocked.
|
||||||
|
# Set to 0 to disable retry limits.
|
||||||
|
max_retries = 2
|
||||||
|
|
||||||
|
# Base branch name for this project. Worktree creation, merges, and agent prompts
|
||||||
|
# use this value for {{base_branch}}. When not set, falls back to auto-detection
|
||||||
|
# (reads current HEAD branch).
|
||||||
|
base_branch = "main"
|
||||||
|
|
||||||
|
[[component]]
|
||||||
|
name = "server"
|
||||||
|
path = "."
|
||||||
|
setup = ["cargo build"]
|
||||||
|
teardown = []
|
||||||
|
|
||||||
|
[[agent]]
|
||||||
|
name = "coder-1"
|
||||||
|
role = "Full-stack engineer"
|
||||||
|
stage = "coder"
|
||||||
|
model = "sonnet"
|
||||||
|
max_turns = 50
|
||||||
|
max_budget_usd = 5.00
|
||||||
|
prompt = """
|
||||||
|
You are working in a git worktree on story {{story_id}}.
|
||||||
|
Read CLAUDE.md first, then .storkit/README.md to understand the dev process.
|
||||||
|
Run: cd "{{worktree_path}}" && git difftool {{base_branch}}...HEAD
|
||||||
|
Commit all your work before your process exits.
|
||||||
|
"""
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: "Gate pipeline transitions on ensure_acceptance"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Story 169: Gate pipeline transitions on ensure_acceptance
|
|
||||||
|
|
||||||
## User Story
|
|
||||||
|
|
||||||
As a project owner, I want story progression to be blocked unless ensure_acceptance passes, so that agents can't skip the testing workflow.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- [ ] move_story_to_merge rejects stories that haven't passed ensure_acceptance
|
|
||||||
- [ ] accept_story rejects stories that haven't passed ensure_acceptance
|
|
||||||
- [ ] Rejection returns a clear error message telling the agent what's missing
|
|
||||||
- [ ] Existing passing stories (all criteria checked, tests recorded) still flow through normally
|
|
||||||
|
|
||||||
## Out of Scope
|
|
||||||
|
|
||||||
- TBD
|
|
||||||
-69
@@ -1,69 +0,0 @@
|
|||||||
---
|
|
||||||
name: "Evaluate Docker/OrbStack for agent isolation and resource limiting"
|
|
||||||
agent: coder-opus
|
|
||||||
---
|
|
||||||
|
|
||||||
# Spike 329: Evaluate Docker/OrbStack for agent isolation and resource limiting
|
|
||||||
|
|
||||||
## Question
|
|
||||||
|
|
||||||
Investigate running the entire storkit system (server, Matrix bot, agents, web UI) inside a single Docker container, using OrbStack as the macOS runtime for better performance. The goal is to isolate storkit from the host machine — not to isolate agents from each other.
|
|
||||||
|
|
||||||
Currently storkit runs as bare processes on the host with full filesystem and network access. A single container would provide:
|
|
||||||
|
|
||||||
1. **Host isolation** — storkit can't touch anything outside the container
|
|
||||||
2. **Clean install/uninstall** — `docker run` to start, `docker rm` to remove
|
|
||||||
3. **Reproducible environment** — same container works on any machine
|
|
||||||
4. **Distributable product** — `docker pull storkit` for new users
|
|
||||||
5. **Resource limits** — cap total CPU/memory for the whole system
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
Docker Container (single)
|
|
||||||
├── storkit server
|
|
||||||
│ ├── Matrix bot
|
|
||||||
│ ├── WhatsApp webhook
|
|
||||||
│ ├── Slack webhook
|
|
||||||
│ ├── Web UI
|
|
||||||
│ └── MCP server
|
|
||||||
├── Agent processes (coder-1, coder-2, coder-opus, qa, mergemaster)
|
|
||||||
├── Rust toolchain + Node.js + Claude Code CLI
|
|
||||||
└── /workspace (bind-mounted project repo from host)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key questions to answer:
|
|
||||||
|
|
||||||
- **Performance**: How much slower are cargo builds inside the container on macOS? Compare Docker Desktop vs OrbStack for bind-mounted volumes.
|
|
||||||
- **Dockerfile**: What's the minimal image for the full stack? Rust toolchain + Node.js + Claude Code CLI + cargo-nextest + git.
|
|
||||||
- **Bind mounts**: The project repo is bind-mounted from the host. Any filesystem performance concerns with OrbStack?
|
|
||||||
- **Networking**: Container exposes web UI port (3000). Matrix/WhatsApp/Slack connect outbound. Any issues?
|
|
||||||
- **API key**: Pass ANTHROPIC_API_KEY as env var to the container.
|
|
||||||
- **Git**: Git operations happen inside the container on the bind-mounted repo. Commits are visible on the host immediately.
|
|
||||||
- **Cargo cache**: Use a named Docker volume for ~/.cargo/registry so dependencies persist across container restarts.
|
|
||||||
- **Claude Code state**: Where does Claude Code store its session data? Needs to persist or be in a volume.
|
|
||||||
- **OrbStack vs Docker Desktop**: Is OrbStack required for acceptable performance, or does Docker Desktop work too?
|
|
||||||
- **Server restart**: Does `rebuild_and_restart` work inside a container (re-exec with new binary)?
|
|
||||||
|
|
||||||
## Deliverable:
|
|
||||||
A proof-of-concept Dockerfile, docker-compose.yml, and a short write-up with findings and performance benchmarks.
|
|
||||||
|
|
||||||
## Hypothesis
|
|
||||||
|
|
||||||
- TBD
|
|
||||||
|
|
||||||
## Timebox
|
|
||||||
|
|
||||||
- TBD
|
|
||||||
|
|
||||||
## Investigation Plan
|
|
||||||
|
|
||||||
- TBD
|
|
||||||
|
|
||||||
## Findings
|
|
||||||
|
|
||||||
- TBD
|
|
||||||
|
|
||||||
## Recommendation
|
|
||||||
|
|
||||||
- TBD
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
---
|
|
||||||
name: Agent Security and Sandboxing
|
|
||||||
---
|
|
||||||
# Story 34: Agent Security and Sandboxing
|
|
||||||
|
|
||||||
## User Story
|
|
||||||
**As a** supervisor orchestrating multiple autonomous agents,
|
|
||||||
**I want to** constrain what each agent can access and do,
|
|
||||||
**So that** agents can't escape their worktree, damage shared state, or perform unintended actions.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
- [ ] Agent creation accepts an `allowed_tools` list to restrict Claude Code tool access per agent.
|
|
||||||
- [ ] Agent creation accepts a `disallowed_tools` list as an alternative to allowlisting.
|
|
||||||
- [ ] Agents without Bash access can still perform useful coding work (Read, Edit, Write, Glob, Grep).
|
|
||||||
- [ ] Investigate replacing direct Bash/shell access with Rust-implemented tool proxies that enforce boundaries:
|
|
||||||
- Scoped `exec_shell` that only runs allowlisted commands (e.g., `cargo test`, `npm test`) within the agent's worktree.
|
|
||||||
- Scoped `read_file` / `write_file` that reject paths outside the agent's worktree root.
|
|
||||||
- Scoped `git` operations that only work within the agent's worktree.
|
|
||||||
- [ ] Evaluate `--max-turns` and `--max-budget-usd` as safety limits for runaway agents.
|
|
||||||
- [ ] Document the trust model: what the supervisor controls vs what agents can do autonomously.
|
|
||||||
|
|
||||||
## Questions to Explore
|
|
||||||
- Can we use MCP (Model Context Protocol) to expose our Rust-implemented tools to Claude Code, replacing its built-in Bash/filesystem tools with scoped versions?
|
|
||||||
- What's the right granularity for shell allowlists — command-level (`cargo test`) or pattern-level (`cargo *`)?
|
|
||||||
- Should agents have read access outside their worktree (e.g., to reference shared specs) but write access only within it?
|
|
||||||
- Is OS-level sandboxing (Docker, macOS sandbox profiles) worth the complexity for a personal tool?
|
|
||||||
|
|
||||||
## Out of Scope
|
|
||||||
- Multi-user authentication or authorization (single-user personal tool).
|
|
||||||
- Network-level isolation between agents.
|
|
||||||
- Encrypting agent communication channels (all local).
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: "WhatsApp webhook HMAC signature verification"
|
||||||
|
retry_count: 3
|
||||||
|
blocked: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 388: WhatsApp webhook HMAC signature verification
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a bot operator, I want incoming WhatsApp webhook requests to be cryptographically verified, so that forged requests from unauthorized sources are rejected.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Meta webhooks: validate X-Hub-Signature-256 HMAC-SHA256 header using the app secret before processing
|
||||||
|
- [ ] Twilio webhooks: validate request signature using the auth token before processing
|
||||||
|
- [ ] Requests with missing or invalid signatures are rejected with 403 Forbidden
|
||||||
|
- [ ] Verification is fail-closed: if signature checking is configured, unsigned requests are rejected
|
||||||
|
- [ ] Existing bot.toml config is extended with any needed secrets (e.g. Meta app_secret for HMAC verification)
|
||||||
|
- [ ] MUST use audited crypto crates (hmac, sha2, sha1, base64) — no hand-rolled cryptographic primitives
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
name: "Fly.io Machines API integration for multi-tenant storkit SaaS"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Spike 408: Fly.io Machines API integration for multi-tenant storkit SaaS
|
||||||
|
|
||||||
|
## Question
|
||||||
|
|
||||||
|
Can we build a working Rust integration that creates and manages per-tenant Fly.io Machines, attaches volumes, injects Claude credentials, and proxies JWT-authenticated HTTP/WebSocket traffic to the right machine?
|
||||||
|
|
||||||
|
## Hypothesis
|
||||||
|
|
||||||
|
A thin Rust service using `reqwest` for the Machines API and `axum` for the reverse proxy is sufficient. No heavyweight orchestration framework needed.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Fly.io account with API token (set `FLY_API_TOKEN` env var)
|
||||||
|
- Spike 407 findings reviewed
|
||||||
|
|
||||||
|
## Timebox
|
||||||
|
|
||||||
|
4 hours
|
||||||
|
|
||||||
|
## Investigation Plan
|
||||||
|
|
||||||
|
- [ ] Create a minimal Rust crate in `spikes/fly_machines/` — do not touch production code
|
||||||
|
- [ ] Implement machine lifecycle: create, start, stop, destroy via Fly Machines REST API using `reqwest`
|
||||||
|
- [ ] Test attaching a persistent volume to a machine and verify it persists across stop/start
|
||||||
|
- [ ] Test secret injection — pass a dummy `credentials.json` as a Fly secret and verify it's readable inside the machine
|
||||||
|
- [ ] Sketch the auth proxy: JWT validation → machine lookup → reverse proxy to machine's private IP; verify WebSocket proxying works
|
||||||
|
- [ ] Measure actual cold start time for a minimal storkit container image
|
||||||
|
- [ ] Document any API quirks, rate limits, or sharp edges discovered during testing
|
||||||
|
|
||||||
|
## Findings
|
||||||
|
|
||||||
|
- TBD
|
||||||
|
|
||||||
|
## Recommendation
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
name: "Multi-account OAuth token rotation on rate limit"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 411: Multi-account OAuth token rotation on rate limit
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a storkit user with multiple Claude Max subscriptions, I want the system to automatically rotate to a different account when one gets rate limited, so that agents and chat don't stall out waiting for limits to reset.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] OAuth login flow stores credentials per-account (keyed by email), not overwriting previous accounts
|
||||||
|
- [ ] GET /oauth/status returns all stored accounts and their status (active, rate-limited, expired)
|
||||||
|
- [ ] When the active account hits a rate limit, storkit automatically swaps to the next available account's refresh token, refreshes, and retries
|
||||||
|
- [ ] The bot sends a notification in Matrix/WhatsApp when it swaps accounts
|
||||||
|
- [ ] If all accounts are rate limited, the bot surfaces a clear message with the time until the earliest reset
|
||||||
|
- [ ] A new /oauth/authorize login adds to the account pool rather than replacing the current credentials
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: "Recheck bot command to re-run gates without restarting agent"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 412: Recheck bot command to re-run gates without restarting agent
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a user, I want to send `recheck <number>` to the bot so that it re-runs acceptance gates on an existing worktree without spawning a new agent, so I can unblock stories that failed due to environment issues without wasting agent turns.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] recheck command is registered in chat/commands/mod.rs and appears in help output
|
||||||
|
- [ ] `recheck <number>` runs run_acceptance_gates on the story's existing worktree
|
||||||
|
- [ ] If gates pass, the story advances through the pipeline (same as if a coder completed successfully)
|
||||||
|
- [ ] If gates fail, the error output is returned to the user (not silently retried)
|
||||||
|
- [ ] If no worktree exists for the story, returns a clear error
|
||||||
|
- [ ] Does not spawn a new agent or increment retry_count
|
||||||
|
- [ ] Works from all transports (Matrix, WhatsApp, Slack)
|
||||||
|
- [ ] Works from web UI slash commands
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
name: "Mergemaster pipeline marks story done without verifying code landed on master"
|
||||||
|
retry_count: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 426: Mergemaster pipeline marks story done without verifying code landed on master
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The mergemaster pipeline can mark a story as done even when the feature code never makes it to master. The cherry-pick step in merge.rs may fail or be skipped, but the pipeline still advances the story to done via the filesystem watcher. There is no post-merge verification that the code actually exists on master before marking done.
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
Observed on stories 422 and 403. For 422: mergemaster created merge-queue branch, resolved 2 conflicts in chat/commands/mod.rs and http/mcp/mod.rs, passed quality gates, created merge-queue commit cb2ef6b (4 files, 333 insertions including unblock.rs). But the done commit on master (05db012) only moves the story file — zero code changes. There is no 'storkit: merge 422' commit on master at all. The feature branch (db3157f) still has the code but it was never cherry-picked onto master.
|
||||||
|
|
||||||
|
## Manual Merge Notes
|
||||||
|
|
||||||
|
When manually cherry-picking 422 onto master, two conflicts arose:
|
||||||
|
|
||||||
|
1. `server/src/chat/commands/mod.rs` — both 421 (timer) and 422 (unblock) added entries to the same BotCommand registry. Resolution: keep both.
|
||||||
|
2. `server/src/http/mcp/mod.rs` — 420 (loc_file) and 422 (unblock) both bumped the tool count assertion from 49→50. Resolution: keep loc_file assertion, bump count to 51.
|
||||||
|
|
||||||
|
Additionally, the cherry-pick could not proceed at all because master was on the `merge-queue/424` branch with 3 unresolved files (notifications.rs, ws.rs, watcher.rs). A concurrent in-progress merge left the working tree dirty, which likely caused the original cherry-pick to fail silently. This suggests a race condition: the filesystem watcher commits (story file moves) can leave master in a state where the cherry-pick step in merge.rs fails.
|
||||||
|
|
||||||
|
## Full Audit of Done Stories (2026-03-28)
|
||||||
|
|
||||||
|
Audited all 9 stories in `5_done/` to check whether their code actually landed on master:
|
||||||
|
|
||||||
|
| Story | Merge Commit | Code on Master |
|
||||||
|
|-------|-------------|----------------|
|
||||||
|
| 417 — Split matrix/bot.rs | `665c036` (9 files, +1973/-1926) | YES |
|
||||||
|
| 418 — Split pool/auto_assign.rs | `d375c4b` (7 files, +1901/-1813) | YES |
|
||||||
|
| 419 — Matrix bot network error | `1193b7a` (1 file, +121/-3) | YES |
|
||||||
|
| 420 — loc file command | `d6f8239` (5 files, +112/-32) | YES |
|
||||||
|
| 421 — Timer command | `cf5424f` (7 files, +836) | YES |
|
||||||
|
| 422 — Unblock command | `6c6bc35` (4 files, +336) — manual cherry-pick | YES |
|
||||||
|
| 423 — Auto-schedule timer on rate limit | `b44f3a3` + `8ab2e19` (6 files, +375/-8) — manual cherry-pick | YES |
|
||||||
|
| **424 — Rate limit traffic light** | **None** | **NO — moved back to backlog for redo** |
|
||||||
|
| 425 — Chat notification on story block | `98b5475` (5 files, +184/-15) | YES |
|
||||||
|
| **427 — Text normalization for line breaks** | **None** | **NO — phantom done, code never landed** |
|
||||||
|
|
||||||
|
**4 out of 10 stories (422, 423, 424, 427) had broken merges.** 422 and 423 were fixed via manual cherry-pick. 424 was moved back to backlog for a fresh run. 427 also hit the same bug — marked done without code on master.
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
Story moved to done with no code on master. The merge-queue commit exists on a detached branch but was never applied to master. No merge commit appears in git log on master.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
Pipeline should verify that the cherry-pick produced a merge commit on master before advancing to done. If cherry-pick fails or is missing, the story should remain in merge stage with a merge_failure flag.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Pipeline must not move a story to done unless a merge commit containing the feature code exists on master
|
||||||
|
- [ ] If cherry-pick fails or produces no code diff on master, the merge must be reported as failed
|
||||||
|
- [ ] Add a post-merge verification step that checks git log on master for the expected merge commit before advancing to done
|
||||||
|
- [ ] When verification fails, emit a merge_failure and leave the story in the merge stage for retry
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: "Server-side text normalization for chat message line breaks"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 427: Server-side text normalization for chat message line breaks
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a user reading bot messages in Matrix, I want single newlines between sentences to render correctly, so that messages don't show up with words joined together like "sentence one.Sentence two".
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Add a text normalization step before markdown-to-HTML conversion in the Matrix transport that converts single newlines between non-empty prose lines into double newlines
|
||||||
|
- [ ] Preserve intentional single-newline formatting in bullet lists, headings, table rows, and code fences
|
||||||
|
- [ ] Apply the same normalization in WhatsApp and Slack transports
|
||||||
|
- [ ] Unit tests covering prose paragraphs, bullet lists, code blocks, and mixed content
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
name: Live Test Gate Updates
|
|
||||||
---
|
|
||||||
|
|
||||||
# Story 57: Live Test Gate Updates
|
|
||||||
|
|
||||||
## User Story
|
|
||||||
|
|
||||||
As a user, I want the Gate and Todo panels to update automatically when tests are recorded or acceptance is checked, so I can see progress without manually refreshing.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- [ ] Server broadcasts a `{"type": "notification", "topic": "tests"}` event over `/ws` when tests are recorded, acceptance is checked, or coverage is collected
|
|
||||||
- [ ] GatePanel auto-refreshes its data when it receives a `tests` notification
|
|
||||||
- [ ] TodoPanel auto-refreshes its data when it receives a `tests` notification
|
|
||||||
- [ ] Manual refresh buttons continue to work
|
|
||||||
- [ ] Panels do not flicker or lose scroll position on auto-refresh
|
|
||||||
- [ ] End-to-end test: record test results via MCP, verify Gate panel updates without manual refresh
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: "Split matrix/bot.rs into focused modules"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Refactor 417: Split matrix/bot.rs into focused modules
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
- TBD
|
||||||
|
|
||||||
|
## Desired State
|
||||||
|
|
||||||
|
Refactor the monolithic server/src/chat/transport/matrix/bot.rs (1926 lines) into focused submodules.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] history.rs contains ConversationRole, ConversationEntry, RoomConversation, PersistedHistory, load_history, save_history and their unit tests
|
||||||
|
- [ ] context.rs contains BotContext struct
|
||||||
|
- [ ] run.rs contains run_bot main event loop
|
||||||
|
- [ ] messages.rs contains on_room_message, handle_message, format_user_prompt, is_permission_approval and their unit tests
|
||||||
|
- [ ] mentions.rs contains mentions_bot, contains_word, is_reply_to_bot and their unit tests
|
||||||
|
- [ ] verification.rs contains check_sender_verified, on_to_device_verification_request, handle_sas_verification and their unit tests
|
||||||
|
- [ ] format.rs contains markdown_to_html, format_startup_announcement and their unit tests
|
||||||
|
- [ ] mod.rs re-exports all public types
|
||||||
|
- [ ] Unit tests live in their respective module files
|
||||||
|
- [ ] No public API changes — all existing imports continue to work
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
name: "Split pool/auto_assign.rs into submodules"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Refactor 418: Split pool/auto_assign.rs into submodules
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
- TBD
|
||||||
|
|
||||||
|
## Desired State
|
||||||
|
|
||||||
|
Refactor the monolithic server/src/agents/pool/auto_assign.rs (1813 lines) into focused submodules.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] auto_assign.rs contains auto_assign_available_work and its unit tests
|
||||||
|
- [ ] reconcile.rs contains reconcile_on_startup and its unit tests
|
||||||
|
- [ ] watchdog.rs contains run_watchdog_once, spawn_watchdog, check_orphaned_agents and their unit tests
|
||||||
|
- [ ] scan.rs contains scan_stage_items, is_story_assigned_for_stage, count_active_agents_for_stage, find_free_agent_for_stage, is_agent_free and their unit tests
|
||||||
|
- [ ] story_checks.rs contains read_story_front_matter_agent, has_review_hold, is_story_blocked, has_merge_failure and their unit tests
|
||||||
|
- [ ] mod.rs wires the submodules and re-exports all public items
|
||||||
|
- [ ] Unit tests live in their respective module files
|
||||||
|
- [ ] No public API changes — all existing imports continue to work
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
name: "Matrix bot crashes on transient network error instead of retrying"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 419: Matrix bot crashes on transient network error instead of retrying
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The Matrix bot treats a transient sync error as fatal and stops entirely. A single failed HTTP request to the homeserver kills the bot, requiring a full server rebuild to recover.
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Run storkit with Matrix bot enabled\n2. Homeserver becomes temporarily unreachable (network blip, DNS hiccup, server restart)\n3. Bot hits sync error and crashes
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
Bot logs "Fatal error: Matrix sync error: error sending request for url (...)" and stops responding. No retry, no recovery.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
Bot logs a warning, backs off with exponential delay, and retries the sync. Only crash on unrecoverable errors (invalid credentials, banned, etc).
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Transient network errors (connection refused, timeout, DNS failure) trigger a retry with exponential backoff
|
||||||
|
- [ ] Bot logs a warning on each failed retry attempt
|
||||||
|
- [ ] Bot resumes normal operation once the homeserver is reachable again
|
||||||
|
- [ ] Unrecoverable errors (401, 403) still cause a clean shutdown with a clear error message
|
||||||
|
- [ ] Bot sends a notification after recovering from a network outage
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
name: "loc for a specified file — bot command and web UI slash command"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 420: loc for a specified file — bot command and web UI slash command
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a developer, I want to send `loc <filepath>` to the bot or use it as a slash command in the web UI to see the line count for a specific file, so I can quickly check how large a file is without leaving my workflow.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] loc <filepath> returns the line count for the specified file
|
||||||
|
- [ ] Relative paths are resolved against the project root
|
||||||
|
- [ ] If the file does not exist, returns a clear error
|
||||||
|
- [ ] Works from all transports (Matrix, WhatsApp, Slack)
|
||||||
|
- [ ] Works as a slash command in the web UI
|
||||||
|
- [ ] loc with no argument retains existing behavior (top files by line count)
|
||||||
|
- [ ] Exposed as an MCP tool so agents can query file line counts programmatically
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: "Timer command for deferred agent start"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 421: Timer command for deferred agent start
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a ..., I want ..., so that ...
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Bot command `timer <story_id> <HH:MM>` schedules a one-shot deferred start for the given story at the next occurrence of that time (server-local timezone)
|
||||||
|
- [ ] Bot command `timer list` shows all pending timers with story ID and scheduled time
|
||||||
|
- [ ] Bot command `timer cancel <story_id>` removes the pending timer for that story
|
||||||
|
- [ ] Timers are persisted to .storkit/timers.json so they survive server restarts
|
||||||
|
- [ ] A 30s tick loop (tokio task, same pattern as watchdog) checks for due timers and calls start_agent when triggered
|
||||||
|
- [ ] When a timer fires, the story must already be in current — timer does not move stories between stages
|
||||||
|
- [ ] Fired timers are removed after execution (one-shot, not recurring)
|
||||||
|
- [ ] Multiple timers for the same time are supported and respect agent slot contention via auto-assign
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
name: "Unblock command to reset blocked stories"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 422: Unblock command to reset blocked stories
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a ..., I want ..., so that ...
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Bot command `unblock <story_id>` clears blocked flag and resets retry_count to 0 on the story front matter
|
||||||
|
- [ ] Replies with confirmation including story ID and name
|
||||||
|
- [ ] Returns clear error if story is not found or not blocked
|
||||||
|
- [ ] Works from all transports (Matrix, WhatsApp, Slack)
|
||||||
|
- [ ] Exposed as an MCP tool so agents can unblock stories programmatically
|
||||||
|
- [ ] Works as a slash command in the web UI
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
name: "Auto-schedule timer on rate limit to resume after reset"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 423: Auto-schedule timer on rate limit to resume after reset
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a ..., I want ..., so that ...
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] When a rate_limit_event with a hard block (not just allowed_warning) is received from the PTY stream, parse the reset time from rate_limit_info
|
||||||
|
- [ ] Automatically create a timer (via TimerStore from story 421) for the blocked story at the parsed reset time
|
||||||
|
- [ ] If a timer already exists for that story, update it to the later reset time rather than creating a duplicate
|
||||||
|
- [ ] Log the auto-scheduled timer with story ID, agent name, and scheduled resume time
|
||||||
|
- [ ] Notify chat transports that the story was rate-limited and will auto-resume at the scheduled time
|
||||||
|
- [ ] When the timer fires and restarts the agent, the existing worktree and committed work are preserved
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
name: "Rate limit traffic light status and hard block alerts"
|
||||||
|
agent: coder-opus
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 424: Rate limit traffic light status and hard block alerts
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a ..., I want ..., so that ...
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Remove repetitive per-message throttle warnings (allowed_warning) from chat transports entirely
|
||||||
|
- [ ] Pipeline status messages show a coloured dot next to each work item: green for running normally, yellow for throttled, red for hard blocked, white/grey for idle/no agent
|
||||||
|
- [ ] Hard block events (429 / rate_limit_exceeded) still send an individual chat notification with a red icon, including the reset time
|
||||||
|
- [ ] Throttle and block state tracked per-agent so the status dot updates in real time
|
||||||
|
- [ ] Server-side logging of throttle warnings is preserved for debugging
|
||||||
|
- [ ] Traffic light dots in status report should be small/compact, not large emoji
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: "Chat notification when a story blocks with reason"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 425: Chat notification when a story blocks with reason
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a project owner monitoring agent progress via chat, I want to receive a notification when a story gets blocked, including the reason, so that I can decide whether to unblock it or investigate the failure.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] When a story transitions to blocked state, send a chat notification to all configured transports
|
||||||
|
- [ ] Notification includes the story ID, story name, and the reason for blocking (e.g. gate failure output, max retries exceeded, empty diff)
|
||||||
|
- [ ] Notification uses a red or warning icon to distinguish from normal status messages
|
||||||
|
- [ ] Works across Matrix, WhatsApp, and Slack transports
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: "Split pool/pipeline.rs into submodules"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Refactor 428: Split pool/pipeline.rs into submodules
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
- TBD
|
||||||
|
|
||||||
|
## Desired State
|
||||||
|
|
||||||
|
Refactor the monolithic server/src/agents/pool/pipeline.rs (1789 lines) into focused submodules.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] advance.rs contains run_pipeline_advance, spawn_pipeline_advance, should_block_story and their unit tests
|
||||||
|
- [ ] completion.rs contains run_server_owned_completion, report_completion and their unit tests
|
||||||
|
- [ ] merge.rs contains start_merge_agent_work, run_merge_pipeline, get_merge_status, set_merge_failure_reported and their unit tests
|
||||||
|
- [ ] mod.rs re-exports all public items and wires the submodules
|
||||||
|
- [ ] Unit tests live in their respective module files
|
||||||
|
- [ ] No public API changes — all existing imports continue to work
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
name: "Work item titles render too large in expanded view"
|
name: "Work item titles render too large in expanded view"
|
||||||
merge_failure: "Merge pipeline infrastructure failure: squash merge committed successfully on merge-queue branch, but cherry-pick onto master failed with 'fatal: bad revision merge-queue/237_bug_work_item_titles_render_too_large_in_expanded_view'. The merge worktree setup also failed (ENOENT for .story_kit/merge_workspace — pnpm install, pnpm build, cargo check all skipped). The merge-queue branch appears to have been cleaned up before the cherry-pick step could reference it. Master is untouched."
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Bug 237: Work item titles render too large in expanded view
|
# Bug 237: Work item titles render too large in expanded view
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
name: "Add refactor work item type"
|
name: "Add refactor work item type"
|
||||||
merge_failure: "merge_agent_work tool returned empty output on two attempts. The merge-queue branch (merge-queue/254_story_add_refactor_work_item_type) was created with squash merge commit 27d24b2, and the merge workspace worktree exists at .story_kit/merge_workspace, but the pipeline never completed (no success/failure logged after MERGE-DEBUG calls). The stale merge workspace worktree may be blocking completion. Possibly related to bug 250 (merge pipeline cherry-pick fails with bad revision on merge-queue branch). Human intervention needed to: 1) clean up the merge-queue worktree and branch, 2) investigate why the merge pipeline hangs after creating the squash merge commit, 3) retry the merge."
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Story 254: Add refactor work item type
|
# Story 254: Add refactor work item type
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
name: "Show agent logs in expanded story popup"
|
name: "Show agent logs in expanded story popup"
|
||||||
merge_failure: "merge_agent_work tool returned empty output. The merge pipeline created the merge-queue branch (merge-queue/255_story_show_agent_logs_in_expanded_story_popup) and merge workspace worktree at .story_kit/merge_workspace, but hung without completing. This is the same issue that affected story 254 — likely related to bug 250 (merge pipeline cherry-pick fails with bad revision on merge-queue branch). The stale merge workspace worktree on the merge-queue branch may be blocking completion. Human intervention needed to: 1) clean up the merge workspace worktree and merge-queue branch, 2) investigate the root cause in the merge pipeline (possibly the cherry-pick/fast-forward step after squash merge), 3) retry the merge."
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Story 255: Show agent logs in expanded story popup
|
# Story 255: Show agent logs in expanded story popup
|
||||||
|
|||||||
+212
@@ -0,0 +1,212 @@
|
|||||||
|
---
|
||||||
|
name: "Evaluate Docker/OrbStack for agent isolation and resource limiting"
|
||||||
|
agent: "coder-opus"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Spike 329: Evaluate Docker/OrbStack for agent isolation and resource limiting
|
||||||
|
|
||||||
|
## Question
|
||||||
|
|
||||||
|
Investigate running the entire storkit system (server, Matrix bot, agents, web UI) inside a single Docker container, using OrbStack as the macOS runtime for better performance. The goal is to isolate storkit from the host machine — not to isolate agents from each other.
|
||||||
|
|
||||||
|
**Important context:** Storkit developing itself is the dogfood edge case. The primary use case is storkit managing agents that develop *other* projects, driven by multiple users in chat rooms (Matrix, WhatsApp, Slack). Isolation must account for untrusted codebases, multi-user command surfaces, and running against arbitrary repos — not just the single-developer self-hosted setup.
|
||||||
|
|
||||||
|
Currently storkit runs as bare processes on the host with full filesystem and network access. A single container would provide:
|
||||||
|
|
||||||
|
1. **Host isolation** — storkit can't touch anything outside the container
|
||||||
|
2. **Clean install/uninstall** — `docker run` to start, `docker rm` to remove
|
||||||
|
3. **Reproducible environment** — same container works on any machine
|
||||||
|
4. **Distributable product** — `docker pull storkit` for new users
|
||||||
|
5. **Resource limits** — cap total CPU/memory for the whole system
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Docker Container (single)
|
||||||
|
├── storkit server
|
||||||
|
│ ├── Matrix bot
|
||||||
|
│ ├── WhatsApp webhook
|
||||||
|
│ ├── Slack webhook
|
||||||
|
│ ├── Web UI
|
||||||
|
│ └── MCP server
|
||||||
|
├── Agent processes (coder-1, coder-2, coder-opus, qa, mergemaster)
|
||||||
|
├── Rust toolchain + Node.js + Claude Code CLI
|
||||||
|
└── /workspace (bind-mounted project repo from host)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key questions to answer:
|
||||||
|
|
||||||
|
- **Performance**: How much slower are cargo builds inside the container on macOS? Compare Docker Desktop vs OrbStack for bind-mounted volumes.
|
||||||
|
- **Dockerfile**: What's the minimal image for the full stack? Rust toolchain + Node.js + Claude Code CLI + cargo-nextest + git.
|
||||||
|
- **Bind mounts**: The project repo is bind-mounted from the host. Any filesystem performance concerns with OrbStack?
|
||||||
|
- **Networking**: Container exposes web UI port (3000). Matrix/WhatsApp/Slack connect outbound. Any issues?
|
||||||
|
- **API key**: Pass ANTHROPIC_API_KEY as env var to the container.
|
||||||
|
- **Git**: Git operations happen inside the container on the bind-mounted repo. Commits are visible on the host immediately.
|
||||||
|
- **Cargo cache**: Use a named Docker volume for ~/.cargo/registry so dependencies persist across container restarts.
|
||||||
|
- **Claude Code state**: Where does Claude Code store its session data? Needs to persist or be in a volume.
|
||||||
|
- **OrbStack vs Docker Desktop**: Is OrbStack required for acceptable performance, or does Docker Desktop work too?
|
||||||
|
- **Server restart**: Does `rebuild_and_restart` work inside a container (re-exec with new binary)?
|
||||||
|
|
||||||
|
## Deliverable:
|
||||||
|
A proof-of-concept Dockerfile, docker-compose.yml, and a short write-up with findings and performance benchmarks.
|
||||||
|
|
||||||
|
## Hypothesis
|
||||||
|
|
||||||
|
A single Docker container running the entire storkit stack (server + agents + toolchain) on OrbStack will provide acceptable performance for the primary use case (developing other projects) while giving us host isolation, resource limits, and a distributable product. OrbStack's VirtioFS should make bind-mounted filesystem performance close to native.
|
||||||
|
|
||||||
|
## Timebox
|
||||||
|
|
||||||
|
4 hours
|
||||||
|
|
||||||
|
## Investigation Plan
|
||||||
|
|
||||||
|
1. Audit storkit's runtime dependencies (Rust toolchain, Node.js, Claude Code CLI, cargo-nextest, git)
|
||||||
|
2. Determine where Claude Code stores session state (~/.claude)
|
||||||
|
3. Analyze how rebuild_and_restart works (exec() replacement) and whether it's container-compatible
|
||||||
|
4. Draft a multi-stage Dockerfile and docker-compose.yml
|
||||||
|
5. Document findings for each key question
|
||||||
|
6. Provide recommendation and follow-up stories
|
||||||
|
|
||||||
|
## Findings
|
||||||
|
|
||||||
|
### 1. Dockerfile: Minimal image for the full stack
|
||||||
|
|
||||||
|
**Result:** Multi-stage Dockerfile created at `docker/Dockerfile`.
|
||||||
|
|
||||||
|
The image requires these runtime components:
|
||||||
|
- **Rust 1.90+ toolchain** (~1.5 GB) — needed at runtime for `rebuild_and_restart` and agent-driven `cargo clippy`, `cargo test`, etc.
|
||||||
|
- **Node.js 22.x** (~100 MB) — needed at runtime for Claude Code CLI (npm global package)
|
||||||
|
- **Claude Code CLI** (`@anthropic-ai/claude-code`) — npm global, spawned by storkit via PTY
|
||||||
|
- **cargo-nextest** — pre-built binary, used by acceptance gates
|
||||||
|
- **git** — used extensively by agents and worktree management
|
||||||
|
- **System libs:** libssl3, ca-certificates
|
||||||
|
|
||||||
|
The build stage compiles the storkit binary with embedded frontend assets (build.rs runs `npm run build`). The runtime stage is based on `debian:bookworm-slim` but still needs Rust + Node because agents use them at runtime.
|
||||||
|
|
||||||
|
**Total estimated image size:** ~3-4 GB (dominated by the Rust toolchain). This is large but acceptable for a development tool that runs locally.
|
||||||
|
|
||||||
|
### 2. Bind mounts and filesystem performance
|
||||||
|
|
||||||
|
**OrbStack** uses Apple's VirtioFS for bind mounts, which is near-native speed. This is a significant advantage over Docker Desktop's older options:
|
||||||
|
|
||||||
|
| Runtime | Bind mount driver | Performance | Notes |
|
||||||
|
|---------|------------------|-------------|-------|
|
||||||
|
| OrbStack | VirtioFS (native) | ~95% native | Default, no config needed |
|
||||||
|
| Docker Desktop | VirtioFS | ~85-90% native | Must enable in settings (Docker Desktop 4.15+) |
|
||||||
|
| Docker Desktop | gRPC-FUSE (legacy) | ~40-60% native | Default on older versions, very slow for cargo builds |
|
||||||
|
| Docker Desktop | osxfs (deprecated) | ~30-50% native | Ancient default, unusable for Rust projects |
|
||||||
|
|
||||||
|
**For cargo builds on bind-mounted volumes:** The critical path is `target/` directory I/O. Since `target/` lives inside the bind-mounted project, large Rust projects will see a noticeable slowdown on Docker Desktop with gRPC-FUSE. OrbStack's VirtioFS makes this tolerable.
|
||||||
|
|
||||||
|
**Mitigation option:** Keep `target/` in a named Docker volume instead of on the bind mount. This gives native Linux filesystem speed for compilation artifacts while the source code remains bind-mounted. The trade-off is that `target/` won't be visible on the host, which is fine since it's a build cache.
|
||||||
|
|
||||||
|
### 3. Claude Code state persistence
|
||||||
|
|
||||||
|
Claude Code stores all state in `~/.claude/`:
|
||||||
|
- `sessions/` — conversation transcripts (used by `--resume`)
|
||||||
|
- `projects/` — per-project settings and memory
|
||||||
|
- `history.jsonl` — command history
|
||||||
|
- `session-env/` — environment snapshots
|
||||||
|
- `settings.json` — global preferences
|
||||||
|
|
||||||
|
**Solution:** Mount `~/.claude` as a named Docker volume (`claude-state`). This persists across container restarts. Session resumption (`--resume <session_id>`) will work correctly since the session files are preserved.
|
||||||
|
|
||||||
|
### 4. Networking
|
||||||
|
|
||||||
|
**Straightforward.** The container exposes port 3001 for the web UI + MCP endpoint. All chat integrations (Matrix, Slack, WhatsApp) connect outbound from the container, which works by default in Docker's bridge networking. No special configuration needed.
|
||||||
|
|
||||||
|
Port mapping: `3001:3001` in docker-compose.yml. Users access the web UI at `http://localhost:3001`.
|
||||||
|
|
||||||
|
### 5. API key handling
|
||||||
|
|
||||||
|
**Simple.** Pass `ANTHROPIC_API_KEY` as an environment variable via docker-compose.yml. The storkit server already reads it from the environment. Claude Code also reads `ANTHROPIC_API_KEY` from the environment.
|
||||||
|
|
||||||
|
### 6. Git operations on bind-mounted repos
|
||||||
|
|
||||||
|
**Works correctly.** Git operations inside the container on a bind-mounted volume are immediately visible on the host (and vice versa). The key considerations:
|
||||||
|
|
||||||
|
- **Git config:** The container runs as root, so `git config --global user.name/email` needs to be set inside the container (or mounted from host). Without this, commits have no author identity.
|
||||||
|
- **File ownership:** OrbStack maps the container's root user to the host user automatically (uid remapping). Docker Desktop does not — files created by the container may be owned by root on the host. OrbStack handles this transparently.
|
||||||
|
- **Worktrees:** `git worktree add` inside the container creates worktrees within the bind-mounted repo, which are visible on the host. This is correct behavior.
|
||||||
|
|
||||||
|
### 7. Cargo cache
|
||||||
|
|
||||||
|
**Named Docker volumes** for `/usr/local/cargo/registry` and `/usr/local/cargo/git` persist downloaded crates across container restarts. First `cargo build` downloads everything; subsequent builds use the cached crates. This is a standard Docker pattern.
|
||||||
|
|
||||||
|
### 8. OrbStack vs Docker Desktop
|
||||||
|
|
||||||
|
| Capability | OrbStack | Docker Desktop |
|
||||||
|
|-----------|----------|----------------|
|
||||||
|
| **VirtioFS (fast mounts)** | Default, always on | Must enable manually |
|
||||||
|
| **UID remapping** | Automatic (root → host user) | Manual or not available |
|
||||||
|
| **Memory usage** | ~50% less than Docker Desktop | Higher baseline overhead |
|
||||||
|
| **Startup time** | 1-2 seconds | 10-30 seconds |
|
||||||
|
| **License** | Free for personal use, paid for teams | Free for personal/small business, paid for enterprise |
|
||||||
|
| **Linux compatibility** | Full (Rosetta for x86 on ARM) | Full (QEMU for x86 on ARM) |
|
||||||
|
|
||||||
|
**Verdict:** OrbStack is strongly recommended for macOS. Docker Desktop works but requires VirtioFS to be enabled manually and has worse file ownership semantics. On Linux hosts, Docker Engine (not Desktop) is native and has none of these issues.
|
||||||
|
|
||||||
|
### 9. rebuild_and_restart inside a container
|
||||||
|
|
||||||
|
**Works with caveats.** The current implementation:
|
||||||
|
1. Runs `cargo build` from `CARGO_MANIFEST_DIR` (baked at compile time to `/app/server`)
|
||||||
|
2. Calls `exec()` to replace the process with the new binary
|
||||||
|
|
||||||
|
Inside a container, `exec()` works fine — it replaces the PID 1 process. However:
|
||||||
|
- The source tree must exist at `/app` inside the container (the path baked into the binary)
|
||||||
|
- The Rust toolchain must be available at runtime
|
||||||
|
- If the container is configured with `restart: unless-stopped`, a crash during rebuild could cause a restart loop
|
||||||
|
|
||||||
|
**The Dockerfile handles this** by copying the full source tree into `/app` in the runtime stage and including the Rust toolchain.
|
||||||
|
|
||||||
|
**Future improvement:** For the storkit-developing-itself case, mount the source tree as a volume at `/app` so code changes on the host are immediately available for rebuild. For the primary use case (developing other projects), the baked-in source is fine — the server doesn't change.
|
||||||
|
|
||||||
|
### 10. Multi-user / untrusted codebase considerations
|
||||||
|
|
||||||
|
The single-container model provides **host isolation** but no **agent-to-agent isolation**:
|
||||||
|
- All agents share the same filesystem, network, and process namespace
|
||||||
|
- A malicious codebase could interfere with other agents or the storkit server itself
|
||||||
|
- This is acceptable as a first step since the primary threat model is "storkit shouldn't wreck the host"
|
||||||
|
|
||||||
|
For true multi-tenant isolation (multiple untrusted projects), a future architecture could:
|
||||||
|
- Run one container per project (each with its own bind mount)
|
||||||
|
- Use Docker's `--read-only` with specific writable mounts
|
||||||
|
- Apply seccomp/AppArmor profiles to limit syscalls
|
||||||
|
|
||||||
|
### 11. Image distribution
|
||||||
|
|
||||||
|
The single-container approach enables simple distribution:
|
||||||
|
```
|
||||||
|
docker pull ghcr.io/crashlabs/storkit:latest
|
||||||
|
docker run -e ANTHROPIC_API_KEY=sk-ant-... -v /my/project:/workspace -p 3001:3001 storkit
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a massive UX improvement over "install Rust, install Node, install Claude Code, clone the repo, cargo build, etc."
|
||||||
|
|
||||||
|
## Recommendation
|
||||||
|
|
||||||
|
**Proceed with implementation.** The single-container Docker approach is viable and solves the stated goals:
|
||||||
|
|
||||||
|
1. **Host isolation** — achieved via standard Docker containerization
|
||||||
|
2. **Clean install/uninstall** — `docker compose up` / `docker compose down -v`
|
||||||
|
3. **Reproducible environment** — Dockerfile pins all versions
|
||||||
|
4. **Distributable product** — `docker pull` for new users
|
||||||
|
5. **Resource limits** — `deploy.resources.limits` in compose
|
||||||
|
|
||||||
|
### Follow-up stories to create:
|
||||||
|
|
||||||
|
1. **Story: Implement Docker container build and CI** — Set up automated image builds, push to registry, test that the image works end-to-end with a sample project.
|
||||||
|
|
||||||
|
2. **Story: Target directory optimization** — Move `target/` to a named volume to avoid bind mount I/O overhead for cargo builds. Benchmark the improvement.
|
||||||
|
|
||||||
|
3. **Story: Git identity in container** — Configure git user.name/email inside the container (from env vars or mounted .gitconfig).
|
||||||
|
|
||||||
|
4. **Story: Per-project container isolation** — For multi-tenant deployments, run one storkit container per project with tighter security (read-only root, seccomp, no-new-privileges).
|
||||||
|
|
||||||
|
5. **Story: Health endpoint** — Add a `/health` HTTP endpoint to the storkit server for the Docker healthcheck.
|
||||||
|
|
||||||
|
### Risks and open questions:
|
||||||
|
|
||||||
|
- **Image size (~3-4 GB):** Acceptable for a dev tool but worth optimizing later. The Rust toolchain dominates.
|
||||||
|
- **Rust toolchain at runtime:** Required for rebuild_and_restart and agent cargo commands. Cannot be eliminated without changing the architecture.
|
||||||
|
- **Claude Code CLI updates:** The CLI version is pinned at image build time. Users need to rebuild the image to get updates. Could use a volume mount for the npm global dir to allow in-place updates.
|
||||||
+1
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "Abstract agent runtime to support non-Claude-Code backends"
|
name: "Abstract agent runtime to support non-Claude-Code backends"
|
||||||
|
agent: coder-opus
|
||||||
---
|
---
|
||||||
|
|
||||||
# Refactor 343: Abstract agent runtime to support non-Claude-Code backends
|
# Refactor 343: Abstract agent runtime to support non-Claude-Code backends
|
||||||
+1
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "ChatGPT agent backend via OpenAI API"
|
name: "ChatGPT agent backend via OpenAI API"
|
||||||
|
agent: coder-opus
|
||||||
---
|
---
|
||||||
|
|
||||||
# Story 344: ChatGPT agent backend via OpenAI API
|
# Story 344: ChatGPT agent backend via OpenAI API
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
name: "Start command should say queued not error when all coders are busy"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 356: Start command should say queued not error when all coders are busy
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a ..., I want ..., so that ...
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] When all coders are busy, 'start' command responds with a short queued message instead of an error
|
||||||
|
- [ ] Message tone is neutral/positive, not a failure message
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: "Bot assign command to pre-assign a model to a story"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 357: Bot assign command to pre-assign a model to a story
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a user, I want to assign a specific model (e.g. opus) to a story before it starts, so that when a coder picks it up it uses the model I chose.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Bot recognizes `assign <number> <model>` command
|
||||||
|
- [ ] Assignment persists in the story file so it's used when the story starts
|
||||||
|
- [ ] Command appears in help output
|
||||||
|
- [ ] Works with available model names (e.g. opus, sonnet)
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: "Remove Makefile and make script/release the single entry point for releases"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 358: Remove Makefile and make script/release the single entry point for releases
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a ..., I want ..., so that ...
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Makefile is deleted
|
||||||
|
- [ ] script/release requires a version argument and prints usage if missing
|
||||||
|
- [ ] script/release still builds macOS and Linux binaries, bumps versions, generates changelog, tags, and publishes to Gitea
|
||||||
|
- [ ] No dependency on make
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
name: "Harden Docker setup for security"
|
||||||
|
retry_count: 3
|
||||||
|
blocked: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 359: Harden Docker setup for security
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a storkit operator, I want the Docker container to run with hardened security settings, so that a compromised agent or malicious codebase cannot escape the container or affect the host.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Container runs as a non-root user
|
||||||
|
- [ ] Root filesystem is read-only with only necessary paths writable (e.g. /tmp, cargo cache, claude state volumes)
|
||||||
|
- [ ] Linux capabilities dropped to minimum required (cap_drop: ALL, add back only what's needed)
|
||||||
|
- [ ] no-new-privileges flag is set
|
||||||
|
- [ ] Resource limits (CPU and memory) are configured in docker-compose.yml
|
||||||
|
- [ ] Outbound network access is restricted where possible
|
||||||
|
- [ ] ANTHROPIC_API_KEY is passed via Docker secrets or .env file, not hardcoded in compose
|
||||||
|
- [ ] Image passes a CVE scan with no critical vulnerabilities
|
||||||
|
- [ ] Port binding uses 127.0.0.1 instead of 0.0.0.0 (e.g. "127.0.0.1:3001:3001") so the web UI is not exposed on all interfaces
|
||||||
|
- [ ] Git identity is configured via explicit GIT_USER_NAME and GIT_USER_EMAIL env vars; container fails loudly on startup if either is missing (note: multi-user/distributed case where different users need different identities is out of scope and will require a different solution)
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
name: "Run storkit container under gVisor (runsc) runtime"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 360: Run storkit container under gVisor (runsc) runtime
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a storkit operator, I want the container to run under gVisor so that even if a malicious codebase escapes the container's process namespace, it cannot make raw syscalls to the host kernel.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] docker-compose.yml specifies runtime: runsc
|
||||||
|
- [ ] PTY-based agent spawning (Claude Code via PTY) works correctly under runsc
|
||||||
|
- [ ] rebuild_and_restart (exec() replacement) works correctly under runsc
|
||||||
|
- [ ] Rust compilation inside the container completes successfully under runsc
|
||||||
|
- [ ] Document host setup requirement: runsc must be installed and registered in /etc/docker/daemon.json
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: "Remove deprecated manual_qa front matter field"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 361: Remove deprecated manual_qa front matter field
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a developer, I want the deprecated manual_qa boolean field removed from the codebase, so that the front matter schema stays clean and doesn't accumulate legacy boolean flags alongside the more expressive qa: server|agent|human field that replaced it.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] manual_qa field is removed from the FrontMatter and StoryMetadata structs in story_metadata.rs
|
||||||
|
- [ ] Legacy mapping from manual_qa: true → qa: human is removed
|
||||||
|
- [ ] Any existing story files using manual_qa are migrated to qa: human
|
||||||
|
- [ ] Codebase compiles cleanly with no references to manual_qa remaining
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
name: "Bot whatsup command shows in-progress work summary"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 362: Bot whatsup command shows in-progress work summary
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a project owner in a Matrix room, I want to type "{bot_name} whatsup {story_number}" and see a full triage dump for that story, so that when something goes wrong I can immediately understand its state — blocked status, agent activity, git changes, and log tail — without hunting across multiple places or asking the bot to investigate.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] '{bot_name} whatsup {number}' finds the story in work/2_current/ by story number
|
||||||
|
- [ ] Shows the story number, name, and current pipeline stage
|
||||||
|
- [ ] Shows relevant front matter fields: blocked, agent, and any other non-empty fields
|
||||||
|
- [ ] Shows which Acceptance Criteria are checked vs unchecked
|
||||||
|
- [ ] Shows active branch and worktree path if one exists
|
||||||
|
- [ ] Shows git diff --stat of changes on the branch since branching from master
|
||||||
|
- [ ] Shows last 5 commit messages on the feature branch (not master)
|
||||||
|
- [ ] Shows the last 20 lines of the agent log for this story (if a log exists)
|
||||||
|
- [ ] Returns a friendly message if the story is not found or not currently in progress
|
||||||
|
- [ ] Registered in the command registry so it appears in help output
|
||||||
|
- [ ] Handled at bot level without LLM invocation — uses git, filesystem, and log files only
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- Interpreting or summarising log output with an LLM
|
||||||
|
- Showing logs from previous agent runs (only the current/most recent)
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
name: "MCP tool for whatsup story triage"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 363: MCP tool for whatsup story triage
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As an LLM assistant, I want to call a single MCP tool to get a full triage dump for an in-progress story, so that I can answer status questions quickly without making 8+ separate calls to piece together the picture myself.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] 'whatsup' MCP tool accepts a story_id parameter
|
||||||
|
- [ ] Returns story front matter fields (name, blocked, agent, and any other non-empty fields)
|
||||||
|
- [ ] Returns AC checklist with checked/unchecked status
|
||||||
|
- [ ] Returns active branch and worktree path if one exists
|
||||||
|
- [ ] Returns git diff --stat of changes on the feature branch since branching from master
|
||||||
|
- [ ] Returns last 5 commit messages on the feature branch
|
||||||
|
- [ ] Returns last 20 lines of the most recent agent log for the story
|
||||||
|
- [ ] Returns a clear error if the story is not found or not in work/2_current/
|
||||||
|
- [ ] Registered and discoverable via the MCP tools/list endpoint
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
name: "Surface API rate limit warnings in chat"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 365: Surface API rate limit warnings in chat
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a project owner watching the chat, I want to see rate limit warnings surfaced directly in the conversation when they appear in the agent's PTY output, so that I know immediately when an agent is being throttled without having to watch server logs.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [x] Server detects rate limit warnings in pty-debug output lines
|
||||||
|
- [x] When a rate limit warning is detected, a notification is sent to the active chat (Matrix/Slack/WhatsApp)
|
||||||
|
- [x] The notification includes which agent/story triggered the rate limit
|
||||||
|
- [x] Rate limit notifications are debounced to avoid spamming the chat with repeated warnings
|
||||||
|
|
||||||
|
## Technical Context
|
||||||
|
|
||||||
|
Claude Code emits `rate_limit_event` JSON in its streaming output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "rate_limit_event",
|
||||||
|
"rate_limit_info": {
|
||||||
|
"status": "allowed_warning",
|
||||||
|
"resetsAt": 1774443600,
|
||||||
|
"rateLimitType": "seven_day",
|
||||||
|
"utilization": 0.82,
|
||||||
|
"isUsingOverage": false,
|
||||||
|
"surpassedThreshold": 0.75
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Key fields:
|
||||||
|
- `status`: `"allowed_warning"` when approaching limit, likely `"blocked"` or similar when hard-limited
|
||||||
|
- `rateLimitType`: e.g. `"seven_day"` rolling window
|
||||||
|
- `utilization`: 0.0–1.0 fraction of limit consumed
|
||||||
|
- `resetsAt`: Unix timestamp when the window resets
|
||||||
|
- `surpassedThreshold`: the threshold that triggered the warning (e.g. 0.75 = 75%)
|
||||||
|
|
||||||
|
These events are already logged as `[pty-debug] raw line:` in the server logs. The PTY reader in `server/src/llm/providers/claude_code.rs` (line ~234) sees them but doesn't currently parse or act on them.
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
|
|
||||||
|
## Test Results
|
||||||
|
|
||||||
|
<!-- storkit-test-results: {"unit":[{"name":"rate_limit_event_json_sends_watcher_warning","status":"pass","details":"PTY reader detects rate_limit_event JSON and emits RateLimitWarning watcher event"},{"name":"rate_limit_warning_sends_notification_with_agent_and_story","status":"pass","details":"Notification listener sends chat message with agent and story info"},{"name":"rate_limit_warning_is_debounced","status":"pass","details":"Second warning within 60s window is suppressed"},{"name":"rate_limit_warnings_for_different_agents_both_notify","status":"pass","details":"Different agents are debounced independently"},{"name":"format_rate_limit_notification_includes_agent_and_story","status":"pass","details":"Notification text includes story number, name, and agent name"},{"name":"format_rate_limit_notification_falls_back_to_item_id","status":"pass","details":"Falls back to item_id when story name is unavailable"}],"integration":[]} -->
|
||||||
|
|
||||||
|
### Unit Tests (6 passed, 0 failed)
|
||||||
|
|
||||||
|
- ✅ rate_limit_event_json_sends_watcher_warning — PTY reader detects rate_limit_event JSON and emits RateLimitWarning watcher event
|
||||||
|
- ✅ rate_limit_warning_sends_notification_with_agent_and_story — Notification listener sends chat message with agent and story info
|
||||||
|
- ✅ rate_limit_warning_is_debounced — Second warning within 60s window is suppressed
|
||||||
|
- ✅ rate_limit_warnings_for_different_agents_both_notify — Different agents are debounced independently
|
||||||
|
- ✅ format_rate_limit_notification_includes_agent_and_story — Notification text includes story number, name, and agent name
|
||||||
|
- ✅ format_rate_limit_notification_falls_back_to_item_id — Falls back to item_id when story name is unavailable
|
||||||
|
|
||||||
|
### Integration Tests (0 passed, 0 failed)
|
||||||
|
|
||||||
|
*No integration tests recorded.*
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: "Bot sends shutdown message on server stop or rebuild"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 366: Bot sends shutdown message on server stop or rebuild
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a project owner in a chat room, I want the bot to send a message when the server is shutting down (via ctrl-c or rebuild_and_restart), so that I know the bot is going offline and won't wonder why it stopped responding.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Bot sends a shutdown message to active chat channels when the server receives SIGINT/SIGTERM (ctrl-c)
|
||||||
|
- [ ] Bot sends a shutdown message before rebuild_and_restart kills the current process
|
||||||
|
- [ ] Message indicates the reason (manual stop vs rebuild)
|
||||||
|
- [ ] Message is sent best-effort — shutdown is not blocked if the message fails to send
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: "Rename bot whatsup command to status"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 367: Rename bot whatsup command to status
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a project owner using the bot from a phone, I want to type "status {number}" instead of "whatsup {number}" to get a story triage dump, because "whatsup" gets autocorrected to "WhatsApp" on mobile keyboards.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] '{bot_name} status {number}' returns the same triage dump that 'whatsup' currently returns
|
||||||
|
- [ ] The 'whatsup' command is removed or aliased to 'status'
|
||||||
|
- [ ] Help output shows 'status' as the command name
|
||||||
|
- [ ] The MCP tool name (whatsup) is unaffected — this only changes the bot command
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
name: "Web UI OAuth flow for Claude authentication"
|
||||||
|
agent: "coder-opus"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 368: Web UI OAuth flow for Claude authentication
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a new user running storkit in Docker, I want to authenticate Claude through the web UI instead of running `claude login` in a terminal inside the container, so that the entire setup experience stays in the browser after `docker compose up`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Backend exposes /auth/start endpoint that generates the Claude OAuth URL with redirect_uri pointing to localhost:3001
|
||||||
|
- [ ] Backend exposes /auth/callback endpoint that receives the OAuth token and stores it where Claude Code expects it
|
||||||
|
- [ ] Backend exposes /auth/status endpoint that reports whether valid Claude credentials exist
|
||||||
|
- [ ] Frontend shows a setup screen when no Claude auth is detected on first visit
|
||||||
|
- [ ] Setup screen has a 'Connect Claude Account' button that initiates the OAuth flow
|
||||||
|
- [ ] OAuth redirect returns to the web UI which confirms success and dismisses the setup screen
|
||||||
|
- [ ] Credentials are persisted in the claude-state Docker volume so they survive container restarts
|
||||||
|
- [ ] The entire flow works without any terminal interaction after docker compose up
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
name: "CLI treats --help and --version as project paths"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 369: CLI treats --help and --version as project paths
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
When running `storkit <anything>`, the binary treats the first argument as a project path, creates a directory for it, and scaffolds `.storkit/` inside. This happens for `--help`, `--version`, `serve`, `x`, or any other string. There is no validation that the argument is an existing directory or a reasonable path before creating it.
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Run `storkit --help` or `storkit serve` or `storkit x` in any directory
|
||||||
|
2. Observe that a directory with that name is created with a full `.storkit/` scaffold inside it
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
Any argument is treated as a project path and a directory is created and scaffolded. No flags are recognised.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
- `storkit --help` prints usage info and exits
|
||||||
|
- `storkit --version` prints the version and exits
|
||||||
|
- `storkit <path>` only works if the path already exists as a directory
|
||||||
|
- If the path does not exist, storkit prints a clear error and exits non-zero
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] storkit --help prints usage information and exits with code 0
|
||||||
|
- [ ] storkit --version prints the version and exits with code 0
|
||||||
|
- [ ] storkit -h and storkit -V work as short aliases
|
||||||
|
- [ ] storkit does not create directories for any argument — the path must already exist
|
||||||
|
- [ ] If the path does not exist, storkit prints a clear error and exits non-zero
|
||||||
|
- [ ] Arguments starting with - that are not recognised produce a clear error message
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
name: "Scaffold does not create .mcp.json in project root"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 370: Scaffold does not create .mcp.json in project root
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Two related problems with project setup:
|
||||||
|
|
||||||
|
1. When the user clicks the "project setup" button in the web UI to open a new project, the scaffold does not reliably run — the `.storkit/` directory and associated files may not be created.
|
||||||
|
2. Even when the scaffold does run, it does not write `.mcp.json` to the project root. Without this file, agents spawned in worktrees cannot find the MCP server, causing `--permission-prompt-tool mcp__storkit__prompt_permission not found` errors and agent failures.
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Open the storkit web UI and use the project setup button to open a new project directory
|
||||||
|
2. Check whether the full scaffold was created (`.storkit/`, `CLAUDE.md`, `script/test`, etc.)
|
||||||
|
3. Check the project root for `.mcp.json`
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
The scaffold may not run when using the UI project setup flow. When it does run, `.mcp.json` is not created in the project root. Agents fail because MCP tools are unavailable.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
Clicking the project setup button reliably runs the full scaffold, including `.mcp.json` pointing to the server's port.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] The web UI project setup button triggers the full scaffold for new projects
|
||||||
|
- [ ] scaffold_story_kit writes .mcp.json to the project root with the server's port
|
||||||
|
- [ ] Existing .mcp.json is not overwritten if already present
|
||||||
|
- [ ] .mcp.json is included in .gitignore since the port is environment-specific
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
name: "No-arg storkit in empty directory skips scaffold"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 371: No-arg storkit in empty directory skips scaffold
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
When running `storkit` with no path argument from an empty directory (no `.storkit/`), the server starts but never calls `open_project` or the scaffold. The `find_story_kit_root` check fails to find `.storkit/`, so the fallback at main.rs:179-186 just sets `project_root = cwd` without scaffolding. This means no `.storkit/`, no `project.toml`, no `.mcp.json`, no `CLAUDE.md` — the project is non-functional.
|
||||||
|
|
||||||
|
The explicit path branch (`storkit .`) works correctly because it calls `open_project` → `ensure_project_root_with_story_kit` → `scaffold_story_kit`. The no-arg branch should do the same.
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Create a new empty directory
|
||||||
|
2. cd into it
|
||||||
|
3. Run `storkit` (no path argument)
|
||||||
|
4. Observe that no scaffold is created — `.storkit/`, `CLAUDE.md`, `.mcp.json`, etc. are all missing
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
Server starts with project_root set to cwd but no scaffold runs. The project is non-functional — no agent config, no MCP endpoint, no work pipeline directories.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
Running `storkit` with no arguments from a directory without `.storkit/` should scaffold the project the same as `storkit .` does — calling `open_project` and triggering `ensure_project_root_with_story_kit`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Running `storkit` with no args from a dir without `.storkit/` calls `open_project` and triggers the full scaffold
|
||||||
|
- [ ] The no-arg fallback path in main.rs calls `open_project(cwd)` instead of just setting project_root directly
|
||||||
|
- [ ] After `storkit` completes startup, `.storkit/project.toml`, `.mcp.json`, `CLAUDE.md`, and `script/test` all exist
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: "Scaffold auto-detects tech stack and configures script/test"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 372: Scaffold auto-detects tech stack and configures script/test
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a user setting up a new project with storkit, I want the scaffold to detect my project's tech stack and generate a working `script/test` automatically, so that agents can run tests immediately without manual configuration.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Scaffold detects Go projects (go.mod) and adds `go test ./...` to script/test
|
||||||
|
- [ ] Scaffold detects Node.js projects (package.json) and adds `npm test` to script/test
|
||||||
|
- [ ] Scaffold detects Rust projects (Cargo.toml) and adds `cargo test` to script/test
|
||||||
|
- [ ] Scaffold detects Python projects (pyproject.toml or requirements.txt) and adds `pytest` to script/test
|
||||||
|
- [ ] Scaffold handles multi-stack projects (e.g. Go + Next.js) by combining the relevant test commands
|
||||||
|
- [ ] project.toml component entries are generated to match detected tech stack
|
||||||
|
- [ ] Falls back to the generic 'No tests configured' stub if no known stack is detected
|
||||||
|
- [ ] Coder agent prompt includes instruction to configure `script/test` for the project's test framework if it still contains the generic stub
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
name: "Scaffold gitignore missing transient pipeline stage directories"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 373: Scaffold gitignore missing transient pipeline stage directories
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The `write_story_kit_gitignore` function in `server/src/io/fs.rs` does not include the transient pipeline stages (`work/2_current/`, `work/3_qa/`, `work/4_merge/`) in the `.storkit/.gitignore` entries list. These stages are not committed to git (only `1_backlog`, `5_done`, and `6_archived` are commit-worthy per spike 92), so they should be ignored for new projects.
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Scaffold a new project with storkit
|
||||||
|
2. Check `.storkit/.gitignore`
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
`.storkit/.gitignore` only contains `bot.toml`, `matrix_store/`, `matrix_device_id`, `worktrees/`, `merge_workspace/`, `coverage/`. The transient pipeline directories are missing.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
`.storkit/.gitignore` also includes `work/2_current/`, `work/3_qa/`, `work/4_merge/`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Scaffold writes work/2_current/, work/3_qa/, work/4_merge/ to .storkit/.gitignore
|
||||||
|
- [ ] Idempotent — running scaffold again does not duplicate entries
|
||||||
|
- [ ] Existing .storkit/.gitignore files get the new entries appended on next scaffold run
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: "Web UI implements all bot commands as slash commands"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 374: Web UI implements all bot commands as slash commands
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a user working in the storkit web UI, I want to type slash commands (e.g. `/status`, `/start 42`, `/cost`) in the chat input to trigger the same deterministic bot commands available in Matrix, so that I can manage my project entirely from the browser without needing a chat bot.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] /status — shows pipeline status and agent availability; /status <number> shows story triage dump
|
||||||
|
- [ ] /assign <number> <model> — pre-assign a model to a story
|
||||||
|
- [ ] /start <number> — start a coder on a story; /start <number> opus for specific model
|
||||||
|
- [ ] /show <number> — display full text of a work item
|
||||||
|
- [ ] /move <number> <stage> — move a work item to a pipeline stage
|
||||||
|
- [ ] /delete <number> — remove a work item from the pipeline
|
||||||
|
- [ ] /cost — show token spend (24h total, top stories, by agent type, all-time)
|
||||||
|
- [ ] /git — show git status (branch, uncommitted changes, ahead/behind)
|
||||||
|
- [ ] /overview <number> — show implementation summary for a merged story
|
||||||
|
- [ ] /rebuild — rebuild the server binary and restart
|
||||||
|
- [ ] /reset — clear the current Claude Code session
|
||||||
|
- [ ] /help — list all available slash commands
|
||||||
|
- [ ] Slash commands are handled at the frontend/backend level without LLM invocation
|
||||||
|
- [ ] Unrecognised slash commands show a helpful error message
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
name: "Default project.toml contains Rust-specific setup commands for non-Rust projects"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 375: Default project.toml contains Rust-specific setup commands for non-Rust projects
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
When scaffolding a new project where no tech stack is detected, the generated `project.toml` contains Rust-specific setup commands (`cargo check`) as example fallback components. This causes coder agents to try to satisfy Rust gates on non-Rust projects.
|
||||||
|
|
||||||
|
## Fix
|
||||||
|
|
||||||
|
1. In `detect_components_toml()` fallback (when no stack markers found): replace the Rust/pnpm example components with a single generic `app` component with empty `setup = []`
|
||||||
|
2. In the onboarding prompt Step 4: simplify to configure `[[component]]` entries based on what the user told the LLM in Step 2 (tech stack), rather than re-scanning the filesystem independently
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Default project.toml does not contain language-specific setup commands when that language is not detected in the project
|
||||||
|
- [ ] If go.mod is present, setup commands use Go tooling
|
||||||
|
- [ ] If package.json is present, setup commands use npm/node tooling
|
||||||
|
- [ ] If no known stack is detected, setup commands are empty or just echo a placeholder
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Create a new Go + Next.js project directory with `go.mod` and `package.json`
|
||||||
|
2. Run `storkit .` to scaffold
|
||||||
|
3. Check `.storkit/project.toml` — the component setup commands reference cargo/Rust
|
||||||
|
4. Start a coder agent — it creates a `Cargo.toml` trying to satisfy the Rust setup commands
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
The scaffolded `project.toml` has Rust-specific setup commands (`cargo check`) even for non-Rust projects. Agents try to satisfy these and create spurious files.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
The scaffolded `project.toml` should have generic or stack-appropriate setup commands. If no known stack is detected, setup commands should be empty or minimal (not Rust-specific).
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Default project.toml does not contain language-specific setup commands when that language is not detected in the project
|
||||||
|
- [ ] If go.mod is present, setup commands use Go tooling
|
||||||
|
- [ ] If package.json is present, setup commands use npm/node tooling
|
||||||
|
- [ ] If no known stack is detected, setup commands are empty or just echo a placeholder
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
name: "Rename MCP whatsup tool to status for consistency"
|
||||||
|
agent: coder-opus
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 376: Rename MCP whatsup tool to status for consistency
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a developer using storkit's MCP tools, I want the MCP tool to be called `status` instead of `whatsup`, so that the naming is consistent between the bot command (`status`), the web UI slash command (`/status`), and the MCP tool.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] MCP tool is renamed from 'whatsup' to 'status'
|
||||||
|
- [ ] MCP tool is discoverable as 'status' via tools/list
|
||||||
|
- [ ] The tool still accepts a story_id parameter and returns the same triage data
|
||||||
|
- [ ] Old 'whatsup' tool name is removed from the MCP registry
|
||||||
|
- [ ] Any internal references to the whatsup tool name are updated
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: "update_story MCP tool writes front matter values as YAML strings instead of native types"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 377: update_story MCP tool writes front matter values as YAML strings instead of native types
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The `update_story` MCP tool accepts `front_matter` as a `Map<String, String>`, so all values are written as quoted YAML strings. Fields like `retry_count` (expected `u32`) and `blocked` (expected `bool`) end up as `"0"` and `"false"` in the YAML. This causes `parse_front_matter()` to fail because serde_yaml cannot deserialize a quoted string into `u32` or `bool`. When parsing fails, the story `name` comes back as `None`, so the status command shows no title for the story.
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Call `update_story` with `front_matter: {"blocked": "false", "retry_count": "0"}`
|
||||||
|
2. Read the story file — front matter contains `blocked: "false"` and `retry_count: "0"` (quoted strings)
|
||||||
|
3. Call `get_pipeline_status` or the bot `status` command
|
||||||
|
4. The story shows with no title/name
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
Front matter values are written as quoted YAML strings. `parse_front_matter()` fails to deserialize `"false"` as `bool` and `"0"` as `u32`, returning an error. The story name is lost and the status command shows no title.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
The `update_story` tool should write `blocked` and `retry_count` as native YAML types (unquoted `false` and `0`), or `parse_front_matter()` should accept both string and native representations. The story name should always be displayed correctly in the status command.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] update_story with front_matter {"blocked": "false"} writes `blocked: false` (unquoted) in the YAML
|
||||||
|
- [ ] update_story with front_matter {"retry_count": "0"} writes `retry_count: 0` (unquoted) in the YAML
|
||||||
|
- [ ] Story name is displayed correctly in the status command after update_story modifies front matter fields
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: "Status command shows work item type (story, bug, spike, refactor) next to each item"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 378: Status command shows work item type (story, bug, spike, refactor) next to each item
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a user viewing the pipeline status, I want to see the type of each work item (story, bug, spike, refactor) so that I can quickly understand what kind of work is in progress without having to open individual files.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] The status command displays the work item type (story, bug, spike, refactor) as a label next to each item — e.g. "375 [bug] — Default project.toml contains Rust-specific setup commands"
|
||||||
|
- [ ] The type is extracted from the story_id filename convention ({id}_{type}_{slug})
|
||||||
|
- [ ] All known types are supported: story, bug, spike, refactor
|
||||||
|
- [ ] Unknown or missing types are omitted gracefully (no crash, no placeholder)
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
name: "start_agent ignores story front matter agent assignment"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 379: start_agent ignores story front matter agent assignment
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
When a model is pre-assigned to a story via the `assign` command (which writes `agent: coder-opus` to the story's YAML front matter), the MCP `start_agent` tool ignores this field. It only looks at the `agent_name` argument passed directly in the tool call. If none is passed, it auto-selects the first idle coder (usually sonnet), bypassing the user's assignment.
|
||||||
|
|
||||||
|
The auto-assign pipeline (`auto_assign.rs`) correctly reads and respects the front matter `agent` field, but the direct `tool_start_agent` path in `agent_tools.rs` does not.
|
||||||
|
|
||||||
|
Additionally, the `show` (whatsup/triage) command should display the assigned agent from the story's front matter so users can verify their assignment took effect.
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Run `assign 368 opus` — this writes `agent: coder-opus` to story 368's front matter
|
||||||
|
2. Run `start 368` (without specifying a model)
|
||||||
|
3. Observe that a sonnet coder is assigned, not coder-opus
|
||||||
|
4. Run `show 368` — the assigned agent is not displayed
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
The `start_agent` MCP tool ignores the `agent` field in the story's front matter and picks the first idle coder. The `show` command does not display the pre-assigned agent.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
When no explicit `agent_name` is passed to `start_agent`, it should read the story's front matter `agent` field and use that agent if it's available. The `show` command should display the assigned agent from front matter.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] start_agent without an explicit agent_name reads the story's front matter `agent` field and uses it if the agent is idle
|
||||||
|
- [ ] If the preferred agent from front matter is busy, start_agent either waits or falls back to auto-selection (matching auto_assign behavior)
|
||||||
|
- [ ] The show/triage command displays the assigned agent from story front matter when present
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: "Assign command restarts coder when story is already in progress"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 380: Assign command restarts coder when story is already in progress
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a user, I want `assign X opus` on a running story to stop the current coder, update the front matter, and start the newly assigned agent, so that I can switch models mid-flight without manually stopping and restarting.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] When assign is called on a story with a running coder, the current coder agent is stopped
|
||||||
|
- [ ] The story's front matter `agent` field is updated to the new agent name
|
||||||
|
- [ ] The newly assigned agent is started on the story automatically
|
||||||
|
- [ ] When assign is called on a story with no running coder, it behaves as before (just updates front matter)
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: "Bot command to delete a worktree"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 381: Bot command to delete a worktree
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a user, I want a bot command to delete a worktree so that I can clean up orphaned or unwanted worktrees without SSHing into the server.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] A new bot command (e.g. `rmtree <story_number>`) deletes the worktree for the given story
|
||||||
|
- [ ] The command stops any running agent on that story before removing the worktree
|
||||||
|
- [ ] The command returns a confirmation message on success
|
||||||
|
- [ ] The command returns a helpful error if no worktree exists for the given story
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
name: "WhatsApp transport supports Twilio API as alternative to Meta Cloud API"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 382: WhatsApp transport supports Twilio API as alternative to Meta Cloud API
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a user, I want to use Twilio's WhatsApp API instead of Meta's Cloud API directly, so that I can avoid Meta's painful developer onboarding and use Twilio's simpler signup process.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] bot.toml supports a `whatsapp_provider` field with values `meta` (default, current behavior) or `twilio`
|
||||||
|
- [ ] When provider is `twilio`, messages are sent via Twilio's REST API (`api.twilio.com`) using Account SID + Auth Token
|
||||||
|
- [ ] When provider is `twilio`, inbound webhooks parse Twilio's form-encoded format instead of Meta's JSON
|
||||||
|
- [ ] Twilio config requires `twilio_account_sid`, `twilio_auth_token`, and `twilio_whatsapp_number` in bot.toml
|
||||||
|
- [ ] All existing bot commands and LLM passthrough work identically regardless of provider
|
||||||
|
- [ ] 24-hour messaging window logic still applies (Twilio enforces this server-side too)
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
name: "Reorganize chat system into chat module with transport submodules"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Refactor 383: Reorganize chat system into chat module with transport submodules
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
- TBD
|
||||||
|
|
||||||
|
## Desired State
|
||||||
|
|
||||||
|
Currently chat-related code is scattered at the top level of `src/`: `transport.rs`, `whatsapp.rs`, `slack.rs`, plus `matrix/` as a directory module. This should be reorganized into a clean module hierarchy:
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
chat/
|
||||||
|
mod.rs # Generic chat traits, types, ChatTransport etc.
|
||||||
|
transport/
|
||||||
|
mod.rs
|
||||||
|
matrix/ # Existing matrix module moved here
|
||||||
|
whatsapp.rs # Existing whatsapp.rs moved here
|
||||||
|
slack.rs # Existing slack.rs moved here
|
||||||
|
twilio.rs # Future Twilio transport
|
||||||
|
```
|
||||||
|
|
||||||
|
The `ChatTransport` trait and shared chat types should live in `chat/mod.rs`. Each transport implementation becomes a submodule of `chat::transport`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] ChatTransport trait and shared chat types live in `chat/mod.rs`
|
||||||
|
- [ ] Matrix transport lives in `chat/transport/matrix/`
|
||||||
|
- [ ] WhatsApp transport lives in `chat/transport/whatsapp.rs`
|
||||||
|
- [ ] Slack transport lives in `chat/transport/slack.rs`
|
||||||
|
- [ ] Top-level `transport.rs`, `whatsapp.rs`, `slack.rs`, and `matrix/` are removed
|
||||||
|
- [ ] All existing tests pass without modification (or with only import path changes)
|
||||||
|
- [ ] No functional changes — pure file reorganization and re-exports
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
name: "WhatsApp markdown-to-WhatsApp formatting conversion"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 384: WhatsApp markdown-to-WhatsApp formatting conversion
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a WhatsApp user, I want bot messages to use WhatsApp-native formatting instead of raw markdown, so that headers, bold text, and links render properly.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Headers (# ## ### etc.) are converted to bold text (*Header*) in WhatsApp messages
|
||||||
|
- [ ] Markdown bold (**text**) is converted to WhatsApp bold (*text*)
|
||||||
|
- [ ] Markdown strikethrough (~~text~~) is converted to WhatsApp strikethrough (~text~)
|
||||||
|
- [ ] Markdown links [text](url) are converted to readable format: text (url)
|
||||||
|
- [ ] Code blocks and inline code are preserved as-is (already compatible)
|
||||||
|
- [ ] Matrix bot formatting is completely unaffected (conversion only applied in WhatsApp send paths)
|
||||||
|
- [ ] Existing WhatsApp chunking (4096 char limit) still works correctly after conversion
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
name: "Slack markdown-to-mrkdwn formatting conversion"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 385: Slack markdown-to-mrkdwn formatting conversion
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a Slack user, I want bot messages to use Slack-native mrkdwn formatting instead of raw markdown, so that headers, bold text, and links render properly.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Headers (# ## ### etc.) are converted to bold text (*Header*) in Slack messages
|
||||||
|
- [ ] Markdown bold (**text**) is converted to Slack bold (*text*)
|
||||||
|
- [ ] Markdown strikethrough (~~text~~) is converted to Slack strikethrough (~text~)
|
||||||
|
- [ ] Markdown links [text](url) are converted to Slack format: <url|text>
|
||||||
|
- [ ] Code blocks and inline code are preserved as-is (already compatible)
|
||||||
|
- [ ] WhatsApp and Matrix bot formatting are completely unaffected (conversion only applied in Slack send paths)
|
||||||
|
- [ ] Conversion is applied to all Slack send paths: command responses, LLM streaming, htop snapshots, delete responses, and slash command responses
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
name: "Unreleased command shows list of stories since last release"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 386: Unreleased command shows list of stories since last release
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a user, I want a bot command and web UI slash command called "unreleased" that shows a list of stories completed since the last release, so that I can see what's ready to ship.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Bot command `unreleased` returns a list of stories merged to master since the last release tag
|
||||||
|
- [ ] Web UI slash command /unreleased returns the same list
|
||||||
|
- [ ] Each entry shows story number and name
|
||||||
|
- [ ] If there are no unreleased stories, a clear message is shown
|
||||||
|
- [ ] Command is registered in the help command output
|
||||||
|
- [ ] WhatsApp, Slack, and Matrix transports all support the command via the shared command dispatcher
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
name: "Configurable base branch name in project.toml"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 387: Configurable base branch name in project.toml
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a project owner, I want to configure the main branch name in project.toml (e.g. "main", "master", "develop"), so that the system doesn't hardcode "master" and works with any branching convention.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] New optional `base_branch` setting in project.toml (e.g. base_branch = "main")
|
||||||
|
- [ ] When set, all worktree creation, merge operations, and agent prompts use the configured branch name
|
||||||
|
- [ ] When not set, falls back to the existing auto-detection logic (detect_base_branch) which reads the current git branch
|
||||||
|
- [ ] The hardcoded "master" fallback in detect_base_branch is replaced by the project.toml setting when available
|
||||||
|
- [ ] Agent prompt template {{base_branch}} resolves to the configured value
|
||||||
|
- [ ] Existing projects without the setting continue to work unchanged (backwards compatible)
|
||||||
|
- [ ] project.toml.example uses base_branch = \"main\" as the example value; the actual project.toml uses base_branch = \"master\"
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
name: "WhatsApp phone number allowlist authorization"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 389: WhatsApp phone number allowlist authorization
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a bot operator, I want to restrict which phone numbers can interact with the bot, so that only authorized users can send commands.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] New optional allowed_phones list in bot.toml for WhatsApp (similar to Matrix allowed_users)
|
||||||
|
- [ ] When configured, only messages from listed phone numbers are processed; all others are silently ignored
|
||||||
|
- [ ] When not configured (empty or absent), all phone numbers are allowed (backwards compatible)
|
||||||
|
- [ ] Unauthorized senders are logged but receive no response
|
||||||
|
- [ ] The allowlist applies to all message types: commands, LLM conversations, and async commands (htop, delete)
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
name: "WhatsApp missing async command handlers for start, rebuild, reset, rmtree, assign"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 390: WhatsApp missing async command handlers for start, rebuild, reset, rmtree, assign
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Five bot commands listed in help don't work in WhatsApp. Matrix's on_room_message pre-dispatches these via extract_*_command() functions before calling try_handle_command(), but WhatsApp's handle_incoming_message only pre-dispatches htop and delete. The missing commands have fallback handlers that return None, so they silently fall through to the LLM instead of executing.
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Send "rebuild" (or "start 386", "reset", "rmtree 386", "assign 386 opus") to the WhatsApp bot\n2. Observe the message is forwarded to the LLM instead of executing the command
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
The 5 commands (start, rebuild, reset, rmtree, assign) fall through to the LLM and generate a conversational response instead of executing the bot command.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
All commands listed in help should work in WhatsApp, matching Matrix behavior. start should spawn an agent, rebuild should rebuild the server, reset should clear the session, rmtree should remove a worktree, assign should pre-assign a model.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] start command works in WhatsApp (extract_start_command dispatch)
|
||||||
|
- [ ] rebuild command works in WhatsApp (extract_rebuild_command dispatch)
|
||||||
|
- [ ] reset command works in WhatsApp (extract_reset_command dispatch)
|
||||||
|
- [ ] rmtree command works in WhatsApp (extract_rmtree_command dispatch)
|
||||||
|
- [ ] assign command works in WhatsApp (extract_assign_command dispatch)
|
||||||
|
- [ ] Same 5 commands also work in Slack transport if similarly missing
|
||||||
|
- [ ] RETRY: Previous attempt was marked done without any code changes — the mergemaster moved the story to done but no async command handlers were actually added to whatsapp.rs. The fix must add extract_start_command, extract_rebuild_command, extract_reset_command, extract_rmtree_command, and extract_assign_command dispatch blocks to handle_incoming_message in whatsapp.rs, following the existing pattern used for htop and delete. Also check and fix Slack if similarly missing.
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
name: "strip_prefix_ci panics on multi-byte UTF-8 characters"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 391: strip_prefix_ci panics on multi-byte UTF-8 characters
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
strip_prefix_ci in commands/mod.rs slices text by byte offset using prefix.len(), which panics when the slice boundary falls inside a multi-byte UTF-8 character (e.g. right single quote U+2019, emojis). The function assumes ASCII-safe byte boundaries but real WhatsApp/Matrix messages contain Unicode.
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Send a message to the bot containing a smart quote or emoji within the first N bytes (where N = bot name length)\n2. e.g. "For now let\u2019s just deal with it" where the bot name prefix check slices at byte 12, inside the 3-byte \u2019 character
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
Thread panics: "byte index 12 is not a char boundary; it is inside \u2018\u2019\u2019 (bytes 11..14)"
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
The function should safely handle multi-byte UTF-8 without panicking. If the slice boundary isn't a char boundary, the prefix doesn't match — return None.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] strip_prefix_ci does not panic on messages containing multi-byte UTF-8 characters (smart quotes, emojis, CJK, etc.)
|
||||||
|
- [ ] Use text.get(..prefix.len()) or text.is_char_boundary() instead of direct indexing
|
||||||
|
- [ ] Add test cases for messages with emojis and smart quotes
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
name: "Extract shared transport utilities from matrix module into chat submodule"
|
||||||
|
agent: "coder-opus"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Refactor 392: Extract shared transport utilities from matrix module into chat submodule
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
- TBD
|
||||||
|
|
||||||
|
## Desired State
|
||||||
|
|
||||||
|
Several functions currently living in the matrix transport module are used by all transports (WhatsApp, Slack, Matrix). These should be pulled up into a shared location under the chat module. Candidates include: strip_prefix_ci, strip_bot_mention, try_handle_command, drain_complete_paragraphs, markdown_to_whatsapp (pattern could generalize), chunk_for_whatsapp, and the command dispatch infrastructure. A chat::util or chat::text submodule would be a natural home for string utilities like strip_prefix_ci. The command dispatch (try_handle_command, CommandDispatch, BotCommand registry) could live in chat::commands.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Shared string utilities (strip_prefix_ci, strip_bot_mention, drain_complete_paragraphs) moved to a chat::util or chat::text submodule
|
||||||
|
- [ ] Command dispatch infrastructure (try_handle_command, CommandDispatch, BotCommand, command registry) moved to chat::commands
|
||||||
|
- [ ] Per-transport formatting functions (markdown_to_whatsapp, markdown_to_slack) remain in their respective transport modules
|
||||||
|
- [ ] All transports import from the new shared location instead of reaching into matrix::
|
||||||
|
- [ ] No functional changes — purely structural refactor
|
||||||
|
- [ ] All existing tests pass and move with their code
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
name: "Pipeline stage notifications for WhatsApp and Slack transports"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 393: Pipeline stage notifications for WhatsApp and Slack transports
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a WhatsApp or Slack user, I want to receive pipeline stage transition notifications (e.g. "story moved from Current to QA") just like Matrix users do, so I can track story progress from any transport.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] WhatsApp transport spawns a notification listener at startup using the existing spawn_notification_listener infrastructure
|
||||||
|
- [ ] Slack transport spawns a notification listener at startup using the same infrastructure
|
||||||
|
- [ ] Notifications are sent to all active ambient senders/channels for the respective transport
|
||||||
|
- [ ] Stage transition notifications (story moved between pipeline stages) are delivered
|
||||||
|
- [ ] Error notifications (story failures) are delivered
|
||||||
|
- [ ] Rate limit warnings are delivered with debouncing
|
||||||
|
- [ ] Matrix notification behavior is completely unaffected
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
name: "WhatsApp and Slack permission prompt forwarding"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 394: WhatsApp and Slack permission prompt forwarding
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a WhatsApp or Slack user, I want permission requests from Claude Code to be forwarded to my chat so I can approve or deny them, rather than having them silently fail.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Permission requests are sent as messages to the WhatsApp sender with tool name and input details
|
||||||
|
- [ ] User can reply yes/y/approve or no/n/deny to approve or deny the permission
|
||||||
|
- [ ] Permission requests time out and auto-deny (fail-closed) if not answered within the configured timeout
|
||||||
|
- [ ] Slack receives the same permission forwarding treatment
|
||||||
|
- [ ] Reuses the existing permission channel infrastructure (perm_rx, PermissionForward, PermissionDecision)
|
||||||
|
- [ ] Matrix permission handling is completely unaffected
|
||||||
|
- [ ] handle_llm_message uses a tokio::select! loop (like Matrix bot.rs) to listen for both LLM output and permission requests concurrently
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: "Fix npm deprecated module warnings"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Refactor 395: Fix npm deprecated module warnings
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
- TBD
|
||||||
|
|
||||||
|
## Desired State
|
||||||
|
|
||||||
|
Address npm warnings about deprecated modules in the frontend dependencies. Update or replace deprecated packages to eliminate warnings during npm install.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] npm install runs with zero deprecation warnings
|
||||||
|
- [ ] All existing frontend tests (npm test) still pass
|
||||||
|
- [ ] npm run build succeeds without errors
|
||||||
|
- [ ] No functional regressions in the frontend
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
name: "WhatsApp bot startup announcement after restart"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 396: WhatsApp bot startup announcement after restart
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a WhatsApp user, I want the bot to announce its presence when it starts up or restarts, like it does in Matrix, so I know it's back online and ready.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Bot sends a startup message to all known WhatsApp senders (from conversation history or ambient rooms) when the server starts
|
||||||
|
- [ ] Startup message includes the bot name and indicates it is online/ready
|
||||||
|
- [ ] Slack transport gets the same startup announcement treatment
|
||||||
|
- [ ] Matrix startup announcement behavior is unaffected
|
||||||
|
- [ ] After a rebuild command, the new process sends the announcement on startup
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: "Selection screen directory picker unreadable in dark mode"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 397: Selection screen directory picker unreadable in dark mode
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The ProjectPathInput component in the selection screen uses hardcoded light-theme inline styles (white backgrounds, dark borders, dark text highlights) that don't adapt to dark mode. When the browser/OS uses dark mode, the global CSS sets text color to #f6f6f6 (white) but the dropdown keeps background: #fff — resulting in white text on a white background, making the directory picker completely unreadable.
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Run storkit under Docker (or locally) with a browser set to dark mode (prefers-color-scheme: dark).
|
||||||
|
2. Open http://localhost:3001 in the browser.
|
||||||
|
3. Click into the project path input and start typing a path to trigger the autocomplete dropdown.
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
The suggestion dropdown has white background with white/light text inherited from the dark-mode global styles. Match highlights use color: #222 which is barely visible. The close button and header bar also use light-only colors. The entire directory picker is effectively unreadable.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
The directory picker dropdown should be readable in both light and dark mode. Colors for background, text, borders, and highlights should adapt to the active color scheme.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] ProjectPathInput dropdown is readable in dark mode (prefers-color-scheme: dark)
|
||||||
|
- [ ] ProjectPathInput dropdown remains readable in light mode
|
||||||
|
- [ ] Suggestion highlight text is visible against the dropdown background in both themes
|
||||||
|
- [ ] No hardcoded light-only colors remain in ProjectPathInput inline styles
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
name: "CLI --port flag with project.toml persistence"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 399: CLI --port flag with project.toml persistence
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a developer, I want to set the server port via a --port CLI flag that persists to project.toml, so that I don't have to remember an environment variable on every run.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] `storkit --help` shows a `--port` option
|
||||||
|
- [ ] `storkit --port 4000` starts the server on port 4000
|
||||||
|
- [ ] After first run with `--port`, the port is saved to `project.toml`
|
||||||
|
- [ ] On subsequent runs without `--port`, the port from `project.toml` is used
|
||||||
|
- [ ] CLI `--port` overrides the value in `project.toml`
|
||||||
|
- [ ] Default port is 3001 when neither `--port` nor `project.toml` port is set
|
||||||
|
- [ ] `STORKIT_PORT` env var is removed — no longer read or respected
|
||||||
|
- [ ] `.storkit_port` lock file mechanism is removed (`write_port_file` / `remove_port_file`)
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- Docker compose changes (can update `STORKIT_PORT` references separately)
|
||||||
|
- Adding other CLI flags beyond `--port`
|
||||||
|
|
||||||
|
## Technical Notes
|
||||||
|
|
||||||
|
Port resolution priority: `--port` flag > `project.toml` `port` field > default 3001
|
||||||
|
|
||||||
|
The port should be written to `project.toml` on startup so subsequent runs remember it. Use the existing `config.rs` / `ProjectConfig` struct — add a `port` field.
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
name: "WhatsApp and Slack missing reset command handler"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 400: WhatsApp and Slack missing reset command handler
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The reset command has a fallback handler in chat/commands/mod.rs that returns None with a comment saying it's handled before try_handle_command. This is only true for Matrix. WhatsApp and Slack don't have pre-dispatch handling, so None causes fallthrough to LLM. This caused a real outage when stale session IDs couldn't be cleared via the bot after switching from Docker to bare-metal.
|
||||||
|
|
||||||
|
## Implementation Note
|
||||||
|
|
||||||
|
Follow the **rebuild pattern** established in story 402, with one complication: `handle_reset` in `server/src/chat/transport/matrix/reset.rs` takes a Matrix-specific `ConversationHistory` (`Arc<TokioMutex<HashMap<OwnedRoomId, RoomConversation>>>`), so it cannot be called directly from WhatsApp or Slack.
|
||||||
|
|
||||||
|
**WhatsApp session storage** (`server/src/chat/transport/whatsapp.rs`):
|
||||||
|
- Type: `WhatsAppConversationHistory = Arc<TokioMutex<HashMap<String, RoomConversation>>>` (key = sender phone number)
|
||||||
|
- Persisted to `.storkit/whatsapp_history.json` via `save_whatsapp_history`
|
||||||
|
|
||||||
|
**Slack session storage** (`server/src/chat/transport/slack.rs`):
|
||||||
|
- Type: `SlackConversationHistory = Arc<TokioMutex<HashMap<String, RoomConversation>>>` (key = channel ID)
|
||||||
|
- Persisted to `.storkit/slack_history.json` via `save_slack_history`
|
||||||
|
|
||||||
|
**Approach:**
|
||||||
|
- Use `extract_reset_command` from `server/src/chat/transport/matrix/reset.rs` to detect the command (it works transport-agnostically)
|
||||||
|
- Implement the reset inline in each transport's async message handler: clear `session_id` and `entries` for the sender/channel key, call the transport's own `save_*_history`, reply with confirmation
|
||||||
|
- Add async intercepts in `whatsapp.rs` (~line 1107, after the rebuild intercept) and `slack.rs` (~line 845, after the rebuild intercept)
|
||||||
|
- The fallback handler in `chat/commands/mod.rs` (`handle_reset_fallback`) stays as-is
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Configure bot with transport = "whatsapp" or "slack"\n2. Send "reset" to the bot\n3. Check server logs
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
Log shows "No command matched, forwarding to LLM" — reset is sent to the LLM as a conversational message instead of clearing the session.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
The bot clears the sender's session_id from conversation history and replies with confirmation like "Session cleared."
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] WhatsApp transport handles reset command: clears sender session_id and replies with confirmation
|
||||||
|
- [ ] Slack transport handles reset command: clears channel session_id and replies with confirmation
|
||||||
|
- [ ] Fallback handler in chat/commands/mod.rs no longer silently swallows the reset command
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
name: "WhatsApp and Slack missing start command handler"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 401: WhatsApp and Slack missing start command handler
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The start command has a fallback handler in chat/commands/mod.rs that returns None. Only Matrix has pre-dispatch handling for this command. On WhatsApp and Slack, the command falls through to the LLM path.
|
||||||
|
|
||||||
|
## Implementation Note
|
||||||
|
|
||||||
|
Follow the **rebuild pattern** established in story 402.
|
||||||
|
|
||||||
|
- `extract_start_command` and `handle_start` already exist in `server/src/chat/transport/matrix/start.rs`
|
||||||
|
- Add an async intercept in `server/src/chat/transport/whatsapp.rs` (see rebuild intercept ~line 1107) and `server/src/chat/transport/slack.rs` (see rebuild intercept ~line 845)
|
||||||
|
- Call `crate::chat::transport::matrix::start::extract_start_command` to detect the command, then `crate::chat::transport::matrix::start::handle_start` to execute it
|
||||||
|
- The fallback handler in `chat/commands/mod.rs` (`handle_start_fallback`) stays as-is — it exists only so `help` lists the command
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Configure bot with transport = "whatsapp" or "slack"\n2. Send "start <story_id>" to the bot\n3. Check server logs
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
Command falls through to LLM instead of starting an agent.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
The bot starts an agent for the specified story and replies with confirmation.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] WhatsApp transport handles start command: starts agent and replies with confirmation
|
||||||
|
- [ ] Slack transport handles start command: starts agent and replies with confirmation
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: "WhatsApp and Slack missing rebuild command handler"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 402: WhatsApp and Slack missing rebuild command handler
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The rebuild command has a fallback handler in chat/commands/mod.rs that returns None. Only Matrix has pre-dispatch handling for this command. On WhatsApp and Slack, the command falls through to the LLM path.
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Configure bot with transport = "whatsapp" or "slack"\n2. Send "rebuild" to the bot\n3. Check server logs
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
Command falls through to LLM instead of triggering a server rebuild.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
The bot triggers a server rebuild and replies with confirmation.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] WhatsApp transport handles rebuild command: triggers rebuild and replies with confirmation
|
||||||
|
- [ ] Slack transport handles rebuild command: triggers rebuild and replies with confirmation
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
name: "WhatsApp and Slack missing rmtree command handler"
|
||||||
|
retry_count: 2
|
||||||
|
blocked: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 403: WhatsApp and Slack missing rmtree command handler
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The rmtree command has a fallback handler in chat/commands/mod.rs that returns None. Only Matrix has pre-dispatch handling for this command. On WhatsApp and Slack, the command falls through to the LLM path.
|
||||||
|
|
||||||
|
## Implementation Note
|
||||||
|
|
||||||
|
Follow the **rebuild pattern** established in story 402.
|
||||||
|
|
||||||
|
- `extract_rmtree_command` and `handle_rmtree` already exist in `server/src/chat/transport/matrix/rmtree.rs`
|
||||||
|
- Add an async intercept in `server/src/chat/transport/whatsapp.rs` (see rebuild intercept ~line 1107) and `server/src/chat/transport/slack.rs` (see rebuild intercept ~line 845)
|
||||||
|
- Call `crate::chat::transport::matrix::rmtree::extract_rmtree_command` to detect the command, then `crate::chat::transport::matrix::rmtree::handle_rmtree` to execute it
|
||||||
|
- The fallback handler in `chat/commands/mod.rs` (`handle_rmtree_fallback`) stays as-is — it exists only so `help` lists the command
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Configure bot with transport = "whatsapp" or "slack"\n2. Send "rmtree <story_id>" to the bot\n3. Check server logs
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
Command falls through to LLM instead of removing the worktree.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
The bot removes the worktree for the specified story and replies with confirmation.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] WhatsApp transport handles rmtree command: removes worktree and replies with confirmation
|
||||||
|
- [ ] Slack transport handles rmtree command: removes worktree and replies with confirmation
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
name: "WhatsApp and Slack missing assign command handler"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug 404: WhatsApp and Slack missing assign command handler
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The assign command has a fallback handler in chat/commands/mod.rs that returns None. Only Matrix has pre-dispatch handling for this command. On WhatsApp and Slack, the command falls through to the LLM path.
|
||||||
|
|
||||||
|
## Implementation Note
|
||||||
|
|
||||||
|
Follow the **rebuild pattern** established in story 402.
|
||||||
|
|
||||||
|
- `extract_assign_command` and `handle_assign` already exist in `server/src/chat/transport/matrix/assign.rs`
|
||||||
|
- Add an async intercept in `server/src/chat/transport/whatsapp.rs` (see rebuild intercept ~line 1107) and `server/src/chat/transport/slack.rs` (see rebuild intercept ~line 845)
|
||||||
|
- Call `crate::chat::transport::matrix::assign::extract_assign_command` to detect the command, then `crate::chat::transport::matrix::assign::handle_assign` to execute it
|
||||||
|
- The fallback handler in `chat/commands/mod.rs` (`handle_assign_fallback` — note: the registry entry for `assign` currently calls `assign::handle_assign` synchronously; verify this doesn't conflict) stays as-is for `help` listing
|
||||||
|
- The fallback in `chat/commands/assign.rs` may need to return `None` instead of a real response once the async path handles it
|
||||||
|
|
||||||
|
## How to Reproduce
|
||||||
|
|
||||||
|
1. Configure bot with transport = "whatsapp" or "slack"\n2. Send "assign <story_id> <agent>" to the bot\n3. Check server logs
|
||||||
|
|
||||||
|
## Actual Result
|
||||||
|
|
||||||
|
Command falls through to LLM instead of assigning the agent.
|
||||||
|
|
||||||
|
## Expected Result
|
||||||
|
|
||||||
|
The bot assigns the specified agent to the story and replies with confirmation.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] WhatsApp transport handles assign command: assigns agent and replies with confirmation
|
||||||
|
- [ ] Slack transport handles assign command: assigns agent and replies with confirmation
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: "Auto-refresh expired OAuth token for Claude Code PTY"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 405: Auto-refresh expired OAuth token for Claude Code PTY
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a storkit user with a Claude Max subscription, I want the server to automatically refresh my expired OAuth token so that chat, Matrix, and WhatsApp integrations don't stop working when the token expires.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
### Detection
|
||||||
|
- [ ] When the Claude Code PTY returns an `authentication_failed` error, storkit detects it instead of passing the raw 401 JSON to the user
|
||||||
|
|
||||||
|
### Auto-refresh (credentials exist, refresh token valid)
|
||||||
|
- [ ] Storkit reads the OAuth refresh token from `~/.claude/.credentials.json`
|
||||||
|
- [ ] Storkit calls the Anthropic OAuth token refresh endpoint (`https://console.anthropic.com/v1/oauth/token` with `grant_type=refresh_token`) to obtain a new access token
|
||||||
|
- [ ] Storkit writes the refreshed access token (and new expiresAt) back to `~/.claude/.credentials.json`
|
||||||
|
- [ ] After a successful refresh, storkit automatically retries the original chat request
|
||||||
|
- [ ] The refresh+retry is transparent to the user — they see no error
|
||||||
|
|
||||||
|
### Full login required (no credentials, or refresh token also expired)
|
||||||
|
- [ ] If `.credentials.json` doesn't exist or the refresh call itself fails, storkit surfaces a clear error: "OAuth session expired. Please run `claude login` to re-authenticate."
|
||||||
|
- [ ] The error message is surfaced through the normal chat stream (not just server logs)
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- Implementing the full interactive `claude login` browser OAuth flow inside storkit
|
||||||
|
- Proactive token refresh before expiry (refreshing on demand when the error occurs is sufficient)
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
name: "Browser-based OAuth login flow from web UI and chat integrations"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 406: Browser-based OAuth login flow from web UI and chat integrations
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a new storkit user (or one whose refresh token has expired), I want to complete the full Claude OAuth login flow from the web UI, Matrix, or WhatsApp so that I don't need terminal access to run `claude login`.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] From the web UI, the user can initiate OAuth login — storkit generates the Anthropic authorize URL and opens it in a new tab
|
||||||
|
- [ ] After the user authenticates in the browser, the OAuth callback writes accessToken, refreshToken, and expiresAt to ~/.claude/.credentials.json
|
||||||
|
- [ ] From Matrix or WhatsApp, storkit sends the user a clickable OAuth authorize link when credentials are missing or fully expired
|
||||||
|
- [ ] After successful login, the user can immediately start chatting without restarting storkit
|
||||||
|
- [ ] If the OAuth callback fails or the user cancels, a clear error is shown
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- TBD
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user