main · last commit
13 days ago ·
7g0stsfu
ah-xuc Stage 2: webhooks, Task Spec, full buckets, Telegram, srht push, watchdog
epic
Past Stand
bd reopen ah-xuc
| Created by | Eugene Blikh |
| Owner | bigbes@gmail.com |
| Created | 2026-07-12T23:36:28Z |
| Updated | 2026-07-13T08:38:18Z |
| Closed | 2026-07-13T08:38:18Z |
Per SPEC SS14.2: Vikunja webhook receiver (HMAC) as reconciler poke; Task Spec YAML frontmatter (role/model/skills/limits) + label type defaults + validation -> Triage bounce; buckets Triage/Blocked/Question; Telegram notifier (honor HTTPS_PROXY; TG blocked from lab network); push agent/task-N branch to git.srht.bigb.es for review + link in In Review comment; watchdog on stale events.jsonl; pi --skill materialization from /srv/skills.
Stage 2 design decisions (coordinator, 2026-07-13): 1. Webhook: POST /webhooks/vikunja on the SAME loopback mux; exposure to Vikunja is infra (ah-k23, tunnel/reverse-proxy). Hex HMAC-SHA256 of the raw body in X-Vikunja-Signature, constant-time compare; secret vikunja.webhook_secret; unset secret = route not registered. Any verified event = Poke(); payload untrusted beyond event_name logging. Poll loop remains the source of truth (Vikunja delivers webhooks once, no retries). 2. Buckets: triage/blocked/question are PARKED buckets — mapped and title-validated, never claim sources, never kill triggers, detached runtime supervision only. Triage is the bounce target for spec validation failures (not Failed — the human fixes and re-drags). Blocked/Question earn real semantics in Stage 4. domain.CanonicalBuckets() = States() + the three; buckets are a superset of states; config requires all nine keys. 3. Task Spec: YAML frontmatter (--- delimited) at the head of the description. Fields: role, model, skills, timeout. Merge precedence: config defaults < type:<name> label preset (config task_types) < frontmatter. Multiple type:* labels or an unknown type = validation error. Unknown frontmatter fields = warnings appended to the claim comment, never a bounce. Prompt renders over the frontmatter-stripped body. 4. Skills: names are safe slugs; resolved to <skills_dir>/<name>; SKILL.md must exist with a non-empty frontmatter description (pi refuses skills without one). pi argv keeps --no-skills --no-extensions and adds explicit --skill <abs> per skill — deterministic set. 5. Per-run timeout: effective value (spec/type override or config default) persisted on the run row (migration v2, runs.timeout_seconds, 0 = config default) so restarts enforce the right deadline. 6. Watchdog: stalled = Running and now - max(events.jsonl mtime, StartedAt) > stall_timeout (default 10m, 0 disables, else >= 1m). Kill + finalize OutcomeStalled/exit -1. Neither-state observation stays deadline-only. 7. Telegram: optional config block {token, chat_id, api_base}; default-transport proxy semantics honor HTTPS_PROXY; notify is best-effort after move+comment on finalize (success/failed/timeout/stalled) and spec bounce; token redacted from errors/logs. Unconfigured = existing slog no-op. 8. Publish: Runner.Publish(ctx, taskID) -> PublishInfo{RemoteURL, Branch, WebURL}; plain git push of agent/task-N to per-repo review_remote (never force); zero info + nil err = not configured; failure degrades to a push-failed line in the In Review comment; re-push on refinalize is idempotent. review_url template with {branch} builds the human link. 9. vikunja.web_url (default: url minus /api/v1) builds human task links for comments and notifications. 10. Scope cuts: NO worker pool / max_parallel in Stage 2 (research mentions it; epic and SPEC roadmap do not). No repo selection via spec (multi-repo is Stage 5). No new external deps. Infra prerequisites tracked in ah-k23: three new board columns (Triage/Blocked/Question), Vikunja webhook target+secret, srht repos + SSH key for the daemon user, TG bot token + proxy path.
ah-xuc.1
— Stage 2 foundation: domain buckets/outcome, ports contracts, config surface
P2
closed
@Eugene Blikh
ah-xuc.10
— cmd/agenthubd: wire webhook secret and Telegram notifier
P2
closed
@Eugene Blikh
ah-xuc.11
— e2e: Stage 2 flows — webhook poke, Triage bounce, skills, publish, watchdog, Telegram
P2
closed
@Eugene Blikh
ah-xuc.12
— docs: sync SPEC with Stage 2 behavior
P2
closed
@Eugene Blikh
ah-xuc.2
— internal/spec: Task Spec frontmatter parse, type-label defaults, validation
P2
closed
@Eugene Blikh
ah-xuc.3
— vikunja: fetch task labels into BoardTask.Labels
P2
closed
@Eugene Blikh
ah-xuc.4
— store: migration v2 — per-run timeout column
P2
closed
@Eugene Blikh
ah-xuc.5
— runner: explicit --skill arguments and Publish to the review remote
P2
closed
@Eugene Blikh
ah-xuc.6
— telegram: ports.Notifier implementation (Bot API through HTTPS_PROXY)
P2
closed
@Eugene Blikh
ah-xuc.7
— httpapi: Vikunja webhook receiver with HMAC verification
P2
closed
@Eugene Blikh
ah-xuc.8
— reconcile: spec-driven claim, Triage bounce, parked buckets, per-run timeout
P2
closed
@Eugene Blikh
ah-xuc.9
— reconcile: publish review branch, watchdog on stale events, Telegram notifications
P2
closed
@Eugene Blikh
No outgoing dependencies.
ah-xuc.10
— cmd/agenthubd: wire webhook secret and Telegram notifier
parent-child
ah-xuc.5
— runner: explicit --skill arguments and Publish to the review remote
parent-child
ah-xuc.2
— internal/spec: Task Spec frontmatter parse, type-label defaults, validation
parent-child
ah-xuc.7
— httpapi: Vikunja webhook receiver with HMAC verification
parent-child
ah-xuc.8
— reconcile: spec-driven claim, Triage bounce, parked buckets, per-run timeout
parent-child
ah-xuc.4
— store: migration v2 — per-run timeout column
parent-child
ah-xuc.1
— Stage 2 foundation: domain buckets/outcome, ports contracts, config surface
parent-child
ah-xuc.12
— docs: sync SPEC with Stage 2 behavior
parent-child
ah-xuc.6
— telegram: ports.Notifier implementation (Bot API through HTTPS_PROXY)
parent-child
ah-xuc.11
— e2e: Stage 2 flows — webhook poke, Triage bounce, skills, publish, watchdog, Telegram
parent-child
ah-xuc.9
— reconcile: publish review branch, watchdog on stale events, Telegram notifications
parent-child
ah-xuc.3
— vikunja: fetch task labels into BoardTask.Labels
parent-child
ah-xuc.10
— cmd/agenthubd: wire webhook secret and Telegram notifier
parent-child
closed
ah-xuc.11
— e2e: Stage 2 flows — webhook poke, Triage bounce, skills, publish, watchdog, Telegram
blocks
closed
ah-xuc.12
— docs: sync SPEC with Stage 2 behavior
blocks
closed
ah-xuc.5
— runner: explicit --skill arguments and Publish to the review remote
parent-child
closed
ah-xuc.2
— internal/spec: Task Spec frontmatter parse, type-label defaults, validation
parent-child
closed
ah-xuc.8
— reconcile: spec-driven claim, Triage bounce, parked buckets, per-run timeout
blocks
closed
ah-xuc.9
— reconcile: publish review branch, watchdog on stale events, Telegram notifications
blocks
closed
ah-xuc.7
— httpapi: Vikunja webhook receiver with HMAC verification
parent-child
closed
ah-xuc.4
— store: migration v2 — per-run timeout column
parent-child
closed
ah-xuc.1
— Stage 2 foundation: domain buckets/outcome, ports contracts, config surface
parent-child
closed
ah-xuc.3
— vikunja: fetch task labels into BoardTask.Labels
blocks
closed
ah-xuc.6
— telegram: ports.Notifier implementation (Bot API through HTTPS_PROXY)
blocks
closed
| id | ah-xuc |
| content_hash | 19aa9e472e32b714a672302d4a5b9431d5154fa20c3d0f98ef188f68d40ea1b7 |
| title | Stage 2: webhooks, Task Spec, full buckets, Telegram, srht push, watchdog |
| description | Per SPEC SS14.2: Vikunja webhook receiver (HMAC) as reconciler poke; Task Spec YAML frontmatter (role/model/skills/limits) + label type defaults + validation -> Triage bounce; buckets Triage/Blocked/Question; Telegram notifier (honor HTTPS_PROXY; TG blocked from lab network); push agent/task-N branch to git.srht.bigb.es for review + link in In Review comment; watchdog on stale events.jsonl; pi --skill materialization from /srv/skills. |
| design | Stage 2 design decisions (coordinator, 2026-07-13): 1. Webhook: POST /webhooks/vikunja on the SAME loopback mux; exposure to Vikunja is infra (ah-k23, tunnel/reverse-proxy). Hex HMAC-SHA256 of the raw body in X-Vikunja-Signature, constant-time compare; secret vikunja.webhook_secret; unset secret = route not registered. Any verified event = Poke(); payload untrusted beyond event_name logging. Poll loop remains the source of truth (Vikunja delivers webhooks once, no retries). 2. Buckets: triage/blocked/question are PARKED buckets — mapped and title-validated, never claim sources, never kill triggers, detached runtime supervision only. Triage is the bounce target for spec validation failures (not Failed — the human fixes and re-drags). Blocked/Question earn real semantics in Stage 4. domain.CanonicalBuckets() = States() + the three; buckets are a superset of states; config requires all nine keys. 3. Task Spec: YAML frontmatter (--- delimited) at the head of the description. Fields: role, model, skills, timeout. Merge precedence: config defaults < type:<name> label preset (config task_types) < frontmatter. Multiple type:* labels or an unknown type = validation error. Unknown frontmatter fields = warnings appended to the claim comment, never a bounce. Prompt renders over the frontmatter-stripped body. 4. Skills: names are safe slugs; resolved to <skills_dir>/<name>; SKILL.md must exist with a non-empty frontmatter description (pi refuses skills without one). pi argv keeps --no-skills --no-extensions and adds explicit --skill <abs> per skill — deterministic set. 5. Per-run timeout: effective value (spec/type override or config default) persisted on the run row (migration v2, runs.timeout_seconds, 0 = config default) so restarts enforce the right deadline. 6. Watchdog: stalled = Running and now - max(events.jsonl mtime, StartedAt) > stall_timeout (default 10m, 0 disables, else >= 1m). Kill + finalize OutcomeStalled/exit -1. Neither-state observation stays deadline-only. 7. Telegram: optional config block {token, chat_id, api_base}; default-transport proxy semantics honor HTTPS_PROXY; notify is best-effort after move+comment on finalize (success/failed/timeout/stalled) and spec bounce; token redacted from errors/logs. Unconfigured = existing slog no-op. 8. Publish: Runner.Publish(ctx, taskID) -> PublishInfo{RemoteURL, Branch, WebURL}; plain git push of agent/task-N to per-repo review_remote (never force); zero info + nil err = not configured; failure degrades to a push-failed line in the In Review comment; re-push on refinalize is idempotent. review_url template with {branch} builds the human link. 9. vikunja.web_url (default: url minus /api/v1) builds human task links for comments and notifications. 10. Scope cuts: NO worker pool / max_parallel in Stage 2 (research mentions it; epic and SPEC roadmap do not). No repo selection via spec (multi-repo is Stage 5). No new external deps. Infra prerequisites tracked in ah-k23: three new board columns (Triage/Blocked/Question), Vikunja webhook target+secret, srht repos + SSH key for the daemon user, TG bot token + proxy path. |
| acceptance_criteria | |
| notes | |
| status | closed |
| priority | 3 |
| issue_type | epic |
| assignee | NULL |
| estimated_minutes | NULL |
| created_at | 2026-07-12T23:36:28Z |
| created_by | Eugene Blikh |
| owner | bigbes@gmail.com |
| updated_at | 2026-07-13T08:38:18Z |
| closed_at | 2026-07-13T08:38:18Z |
| closed_by_session | |
| external_ref | NULL |
| spec_id | |
| compaction_level | 0 |
| compacted_at | NULL |
| compacted_at_commit | NULL |
| original_size | NULL |
| sender | |
| ephemeral | 0 |
| wisp_type | |
| pinned | 0 |
| is_template | 0 |
| mol_type | |
| work_type | |
| source_system | |
| metadata | �{} |
| source_repo | |
| close_reason | Stage 2 delivered: 21 commits b3ee57d..HEAD (webhook HMAC poke, Task Spec frontmatter + type-label presets + Triage bounce, parked buckets Triage/Blocked/Question, per-run timeout with store v2, stall watchdog, Telegram notifier via HTTPS_PROXY, review-branch publish to srht remotes with bounded push, e2e flows, SPEC synced to Stages 1-2). Every wave gated by hostile review + empirical validation; final epic-wide gate 11/11. Runtime prerequisites (board columns, webhook target, srht repos, TG bot) remain in ah-k23. |
| event_kind | |
| actor | |
| target | |
| payload | |
| await_type | |
| await_id | |
| timeout_ns | 0 |
| waiters | |
| hook_bead | |
| role_bead | |
| agent_state | |
| last_activity | NULL |
| role_type | |
| rig | |
| due_at | NULL |
| defer_until | NULL |
| no_history | 0 |
| started_at | NULL |
| is_blocked | 0 |
| id | 12e9e9c8-2748-58a3-ba44-9ca7ef884ac7 |
| issue_id | ah-xuc.10 |
| type | parent-child |
| created_at | 2026-07-13T08:16:17Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | 23b27664-16c6-5611-b0d7-eb405e062295 |
| issue_id | ah-xuc.5 |
| type | parent-child |
| created_at | 2026-07-13T08:15:26Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | 252a0719-62cd-54d5-8db4-9309eb609a4e |
| issue_id | ah-xuc.2 |
| type | parent-child |
| created_at | 2026-07-13T08:15:07Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | 2f0edb7d-9d7d-5865-b263-93db7cce7609 |
| issue_id | ah-xuc.7 |
| type | parent-child |
| created_at | 2026-07-13T08:15:43Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | 3fba1fe9-f3a2-5360-8b0a-f344c7e79be6 |
| issue_id | ah-xuc.8 |
| type | parent-child |
| created_at | 2026-07-13T08:15:59Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | 5669575f-fde5-5b23-900f-c355ce60fe1b |
| issue_id | ah-xuc.4 |
| type | parent-child |
| created_at | 2026-07-13T08:15:21Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | 63d4cba5-bfba-51a5-b90c-6a0050f54b13 |
| issue_id | ah-xuc.1 |
| type | parent-child |
| created_at | 2026-07-13T08:14:51Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | a15afbfe-2443-533e-be9d-c7e27fbc77e2 |
| issue_id | ah-xuc.12 |
| type | parent-child |
| created_at | 2026-07-13T08:16:35Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | a40dca03-2215-5737-bc0b-af57df910a5f |
| issue_id | ah-xuc.6 |
| type | parent-child |
| created_at | 2026-07-13T08:15:38Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | c444dec4-d3cd-580b-a8b6-a8d8ebc2d660 |
| issue_id | ah-xuc.11 |
| type | parent-child |
| created_at | 2026-07-13T08:16:24Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | d8b95e75-79d8-555c-941d-5259c652680e |
| issue_id | ah-xuc.9 |
| type | parent-child |
| created_at | 2026-07-13T08:16:06Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | d8fdc3c7-ff08-5a0f-8663-42c0e5ff9cc0 |
| issue_id | ah-xuc.3 |
| type | parent-child |
| created_at | 2026-07-13T08:15:10Z |
| created_by | Eugene Blikh |
| metadata | �{} |
| thread_id | |
| depends_on_issue_id | ah-xuc |
| depends_on_wisp_id | NULL |
| depends_on_external | NULL |
| id | 019f58b0-7816-7c6b-a36d-bbbd7f5b5eb4 |
| issue_id | ah-xuc |
| event_type | created |
| actor | Eugene Blikh |
| old_value | |
| new_value | |
| comment | NULL |
| created_at | 2026-07-13T02:36:27Z |
| id | 019f59e8-8043-7459-b96e-7d73392d0d2f |
| issue_id | ah-xuc |
| event_type | updated |
| actor | Eugene Blikh |
| old_value | {"id":"ah-xuc","title":"Stage 2: webhooks, Task Spec, full buckets, Telegram, srht push, watchdog","description":"Per SPEC SS14.2: Vikunja webhook receiver (HMAC) as reconciler poke; Task Spec YAML frontmatter (role/model/skills/limits) + label type defaults + validation -\u003e Triage bounce; buckets Triage/Blocked/Question; Telegram notifier (honor HTTPS_PROXY; TG blocked from lab network); push agent/task-N branch to git.srht.bigb.es for review + link in In Review comment; watchdog on stale events.jsonl; pi --skill materialization from /srv/skills.","status":"open","priority":3,"issue_type":"epic","owner":"bigbes@gmail.com","created_at":"2026-07-12T23:36:28Z","created_by":"Eugene Blikh","updated_at":"2026-07-12T23:36:28Z"} |
| new_value | {"design":"Stage 2 design decisions (coordinator, 2026-07-13):\n1. Webhook: POST /webhooks/vikunja on the SAME loopback mux; exposure to Vikunja is infra (ah-k23, tunnel/reverse-proxy). Hex HMAC-SHA256 of the raw body in X-Vikunja-Signature, constant-time compare; secret vikunja.webhook_secret; unset secret = route not registered. Any verified event = Poke(); payload untrusted beyond event_name logging. Poll loop remains the source of truth (Vikunja delivers webhooks once, no retries).\n2. Buckets: triage/blocked/question are PARKED buckets — mapped and title-validated, never claim sources, never kill triggers, detached runtime supervision only. Triage is the bounce target for spec validation failures (not Failed — the human fixes and re-drags). Blocked/Question earn real semantics in Stage 4. domain.CanonicalBuckets() = States() + the three; buckets are a superset of states; config requires all nine keys.\n3. Task Spec: YAML frontmatter (--- delimited) at the head of the description. Fields: role, model, skills, timeout. Merge precedence: config defaults \u003c type:\u003cname\u003e label preset (config task_types) \u003c frontmatter. Multiple type:* labels or an unknown type = validation error. Unknown frontmatter fields = warnings appended to the claim comment, never a bounce. Prompt renders over the frontmatter-stripped body.\n4. Skills: names are safe slugs; resolved to \u003cskills_dir\u003e/\u003cname\u003e; SKILL.md must exist with a non-empty frontmatter description (pi refuses skills without one). pi argv keeps --no-skills --no-extensions and adds explicit --skill \u003cabs\u003e per skill — deterministic set.\n5. Per-run timeout: effective value (spec/type override or config default) persisted on the run row (migration v2, runs.timeout_seconds, 0 = config default) so restarts enforce the right deadline.\n6. Watchdog: stalled = Running and now - max(events.jsonl mtime, StartedAt) \u003e stall_timeout (default 10m, 0 disables, else \u003e= 1m). Kill + finalize OutcomeStalled/exit -1. Neither-state observation stays deadline-only.\n7. Telegram: optional config block {token, chat_id, api_base}; default-transport proxy semantics honor HTTPS_PROXY; notify is best-effort after move+comment on finalize (success/failed/timeout/stalled) and spec bounce; token redacted from errors/logs. Unconfigured = existing slog no-op.\n8. Publish: Runner.Publish(ctx, taskID) -\u003e PublishInfo{RemoteURL, Branch, WebURL}; plain git push of agent/task-N to per-repo review_remote (never force); zero info + nil err = not configured; failure degrades to a push-failed line in the In Review comment; re-push on refinalize is idempotent. review_url template with {branch} builds the human link.\n9. vikunja.web_url (default: url minus /api/v1) builds human task links for comments and notifications.\n10. Scope cuts: NO worker pool / max_parallel in Stage 2 (research mentions it; epic and SPEC roadmap do not). No repo selection via spec (multi-repo is Stage 5). No new external deps.\nInfra prerequisites tracked in ah-k23: three new board columns (Triage/Blocked/Question), Vikunja webhook target+secret, srht repos + SSH key for the daemon user, TG bot token + proxy path."} |
| comment | NULL |
| created_at | 2026-07-13T08:17:17Z |
| id | 019f5aa0-88ab-7036-9543-c212c67ea38d |
| issue_id | ah-xuc |
| event_type | closed |
| actor | Eugene Blikh |
| old_value | |
| new_value | Stage 2 delivered: 21 commits b3ee57d..HEAD (webhook HMAC poke, Task Spec frontmatter + type-label presets + Triage bounce, parked buckets Triage/Blocked/Question, per-run timeout with store v2, stall watchdog, Telegram notifier via HTTPS_PROXY, review-branch publish to srht remotes with bounded push, e2e flows, SPEC synced to Stages 1-2). Every wave gated by hostile review + empirical validation; final epic-wide gate 11/11. Runtime prerequisites (board columns, webhook target, srht repos, TG bot) remain in ah-k23. |
| comment | NULL |
| created_at | 2026-07-13T11:38:18Z |
| id | 019f7cda-91af-7cc1-bdac-89704908c309 |
| issue_id | ah-xuc |
| event_type | label_added |
| actor | Eugene Blikh |
| old_value | NULL |
| new_value | NULL |
| comment | Added label: milestone:stage-2 |
| created_at | 2026-07-20T03:08:46Z |
| id | 019f7cdc-0b0c-769b-8590-79393129165d |
| issue_id | ah-xuc |
| event_type | label_removed |
| actor | Eugene Blikh |
| old_value | NULL |
| new_value | NULL |
| comment | Removed label: milestone:stage-2 |
| created_at | 2026-07-20T03:10:23Z |
No comments.
Close reason