Skip to content

AI-1630: onboarding docs for scheduled tasks - #1108

Merged
kanat merged 6 commits into
acceleratefrom
docs/AI-1630-scheduled-tasks-onboarding
Oct 21, 2026
Merged

kanat merged 6 commits into
acceleratefrom
docs/AI-1630-scheduled-tasks-onboarding

Conversation

@kanat

@kanat kanat commented Oct 21, 2026

Copy link
Copy Markdown

Why

Scheduled tasks shipped in #1050 (AI-1516) and #1098 (AI-1602). A new developer has one feature doc that had drifted from the code and no package guide or skill. AI-1630 asks for one way in. Docs only: no Go, SQL or generated file changes.

How it works

Three files, no overlap:

.factory/features/scheduled-tasks.md      what and why: behaviour, decisions, open tickets
        │ links
        ├─> acceleration/internal/scheduling/AGENTS.md   how the code works
        │      code map: tool / REST -> store -> Scheduler -> delivery -> channels
        │      terms table, rules each with the test that enforces it, test commands
        └─> .claude/skills/scheduled-tasks/SKILL.md      how to use and debug it
               turn it on, schedule locally / on staging, "nothing arrived?" checklist

Repo-root AGENTS.md gets one line that points to the skill, as it does for connectors.

Claims fixed in the feature doc

47 claims checked against origin/accelerate 52dee65. 2 were wrong, 4 were incomplete, and 1 cannot be checked from code. The full claim-by-claim table is in the author ledger.

Claim Old text New text Evidence
Peer hand-off "handed over the peer forwarder and answered on acceptance" the node holding the session walks the task's channels and answers with the outcome internal/api/scheduled_delivery.go:536
Unanswered call "the next channel is tried (or the occurrence retried)" not_answered is final: the next channel is tried, the occurrence is not retried (S5) scheduled_delivery.go:836, :843, :245
Who gets the tools "the caller is identified" a verified user or a backend acting for one, plus a store and a config; guests and anonymous callers get none internal/session/schedule_tools.go:106, connector_tools.go:569
Zone order tool, session, users.timezone, default the same, but REST create has no session step internal/api/scheduled_tasks.go:219
users.timezone write "except for an anonymous caller" also not for an incognito session internal/api/session_create.go:76
Worker "Every router runs the worker" every router with a store and sessions cmd/router/main.go:1264
Pipecat/Retell/… timers stated as fact marked unverified no source in repo

Added because the code does it and the doc said nothing:

Added Evidence
AI-1602: a paused one-shot resumed late writes one missed run (task_paused) and completes it. This holds for the API and the tool. The tool names the time in the task's zone internal/scheduling/scheduler.go:129, schedule_tools.go:435
S4: a pause or edit writes a held run as missed. A config delete does not do this yet (AI-1600) internal/store/scheduled_tasks.go:270 vs :938
S5: a call more than 15 min late is missed. not_answered and already_called are final scheduled_delivery.go:65, :733
S18: a repeat that Describe cannot say is refused. A stored one still runs internal/scheduling/describe.go:34
S20: 25 active tasks per user per config, or max_tasks_per_user (1–100) scheduling.go:72, configs.go:2030, store/scheduled_tasks.go:194
Second migration 20261105130000_scheduled_tasks_wake.sql (wake index) store/scheduled_tasks.go:873
last_run_at is the settle time (max(finished_at), UTC). A run's scheduled_for is the due time in the task's zone. Includes a worked example store/scheduled_tasks.go:845, api/scheduled_tasks.go:712
Lease 1 min, renewal 10 s, retries after 30 s / 2 min / 10 min, 16 slots, 3 lost workers fail the run, 5 failed occurrences fail the task scheduler.go:19,22,25,57,62, store/scheduled_tasks.go:59
Not done: the 28 open tickets by id and title (AI-1527..1532, AI-1555..1567, AI-1592..1600), read from Linear on 2026-10-21 Linear

Checks

  • Every test named in internal/scheduling/AGENTS.md exists under the suite it is listed with (grep of func (s *<Suite>) <Test>().
  • go test ./internal/scheduling/ and the five internal/api unit tests named in "Run the tests" pass. go test -tags integration -list compiles and lists all five suites.
  • Unverified: the integration suites were not run (docs only). The local and staging walkthroughs in the skill were checked against the code and the JS SDK's hosted headers, not run live.

Code findings, not fixed here

  • A config delete leaves a held run running and keeps its lease_until (store/scheduled_tasks.go:938). This is tracked in AI-1600.
  • Resuming a failed one-shot that has no occurrence left differs by surface. The API runs the config and limit checks, then completes the task (api/scheduled_tasks.go:508). The tool refuses with schedule_in_past (schedule_tools.go:378).

🤖 Generated with Claude Code

Check the feature doc claim by claim against the code, add the scheduling
package's AGENTS.md (code map, terms, rules with the test behind each, how
to run the suites) and a scheduled-tasks skill (turn it on, schedule one
locally or on staging, what to check when nothing arrived).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the docs label Oct 21, 2026

@kanat kanat left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 17ed773cd merged onto origin/accelerate 63e674ae7 (base moved; #1106 touched configs.go). CI: labeler pass, CodeRabbit skipped (no Go job for docs).

What I ran:

  • I checked the claims: 74 in the feature doc (26 refs, 18 constants), 42 in AGENTS.md, and the skill.
  • The 74 test names exist under their suites, with the right build tags.
  • Linear: all 28 listed tickets are open and their titles match. None in the ranges is missing.
  • I ran every "Run the tests" command verbatim on my own _test DB: SchedulerSuite ok (153 s), ScheduledTasksSuite ok (86 s), DrainSuite ok, Store+Migration timed out at 600 s (R1.8), and that rerun with a longer timeout passed. The two unit commands pass.
  • I ran the local walkthrough on the docker router: config create OK; respond fails (R1.1); REST create OK ("Every weekday at 09:00 (Europe/Amsterdam)"); step 4 fails (R1.2). With both fixed by hand, the tool-made one-shot fired on time: delivered to conversation, task completed, last_run_at UTC, scheduled_for +02:00.
  • I did not run the staging section (not allowed). I checked the JWT recipe offline with a dummy secret: the HS256 signature matches and the claims are {server,iat,exp}. Sourcing .env prints nothing.

[Should fix] R1.10 .factory/features/scheduled-tasks.md:164 (unchanged line, so not inline): "devices naming their own zone" is listed as not done. But createSession is x-client-accessible and takes timezone, and the router remembers it as the user's zone (api/schemas.go:288, api/session_create.go:76). Remove it, or name what is still missing.

Nits (for tickets, not this PR):

  • R1.11: feature doc :135-140 and AGENTS.md :102: the pause/edit settle writes every running scheduled run missed, in flight or held. Only an edit that leaves a one-shot active keeps its claim (store/scheduled_tasks.go:294,316-347).
  • R1.12: feature doc :57: "a pause, edit or delete takes the claim" contradicts that exception.
  • R1.13: feature doc :81: CheckMove also refuses a move when no run is pending or the current run is due (scheduling.go:59-68).
  • R1.14: feature doc :107 and :165 say "tools don't take deliver_to/quiet_hours" twice.
  • R1.15: feature doc :52-66 and S4/S5 repeat the AGENTS.md Rules and constants: mechanism inside the what/why file.
  • R1.16: style. The paragraphs at :20-37, :52-66 and :93-108 chain 4-8 claims per sentence (STE). "run" is both the record and the action ("run by hand"). :79 "anybody else's answer 404".
  • R1.17: AGENTS.md :54: missed also covers a held run settled by a pause or edit.
  • R1.18: AGENTS.md :60: the prompt's late= counts from ScheduledFor, not from the hold's end (scheduled_delivery.go:470, AI-1597).
  • R1.19: AGENTS.md :83, feature doc :61 and skill :156: maxScheduledTaskRunAttempts counts claims, including the take-up after a hold, not only dead workers.
  • R1.20: AGENTS.md :65: says three suites, but the Rules also cite the Migration, Drain and DataMove suites. :157 TestDataMoveSuite exists in both store and api (this one is internal/store), and no command runs it.
  • R1.21: AGENTS.md :190: AI-1566 is a panic on a fresh DB, not a flake, and step 1 tells readers to use a fresh DB.
  • R1.22: AGENTS.md :57: an end equal to the start also wraps (quiet.go:42).
  • R1.23: AGENTS.md :138 and :146: TestACallersOwnScheduleTaskKeepsTheName tests name precedence. The WhatsApp and opt-out tests prove fallback and refusal, not "written once".
  • R1.24: AGENTS.md :4: "the two pure parts" names one group.
  • R1.25: skill log table: it lacks could not record the session a scheduled task opened (scheduled_delivery.go:690), could not give back a scheduled task's call (:745), ...rang into (:786), could not end a scheduled task's call (:793), could not record a scheduled task run by hand (scheduled_tasks.go:578) and could not read when a scheduled task last ran (:637). grep -i 'scheduled task' also matches asynq's Failed to forward scheduled tasks noise.
  • R1.26: skill :57-59: the commands use X-Customer-Id, so they work in proxy mode only, not in api_key mode.
  • R1.27: skill :131: ?api_key= on a URL that already has ?state=active must be &api_key=.
  • R1.28: skill :124: openssl -hmac "$STREAM_API_SECRET" puts the secret in argv, where ps shows it.

Author's code finding 2 (resume of a failed one-shot differs between the API and the tool): ticket, out of scope for docs.

Root AGENTS.md +5 lines fit the Connectors/Security pattern.

Verdict: request changes, for the R1.1 blocker (the copyable respond command fails) and R1.2-R1.10.

U=(-H 'X-Stream-User-Id: alice')
SID=$(curl -s "$R/v1/agents/sessions" "${H[@]}" "${U[@]}" \
-d '{"agent": "reminders", "timezone": "Europe/Amsterdam"}' | jq -r .id)
curl -s "$R/v1/agents/sessions/$SID/respond" "${H[@]}" "${U[@]}" \

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Blocker] R1.1 This respond call fails as written. I ran it on the local router (proxy mode) and got 400 personal conversations require a command ID (internal/session/session.go:507): a text session with a named user needs a durable command. With "request_id": "remind-1" it returns 200, a receipt with state, and the model calls schedule_task. Fix: add request_id to the body, and say how to read the reply (GET /v1/agents/sessions/$SID/commands/remind-1).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2826f16: respond now sends "request_id": "remind-1", and the next line reads GET .../commands/remind-1. That endpoint answers only state (thinking, then completed), not the reply text, so the skill says to read the reply in the conversation. Ran the extracted step 2-4 commands on the local docker router (proxy mode): config 201, respond receipt state thinking then completed, task created.

4. Watch it. Fire it now without moving the schedule, then read its runs:

```bash
curl -s "$R/v1/agents/scheduled-tasks?state=active" "${H[@]}" "${U[@]}" | jq '.items[] | {id, title, next_run_at, last_run_at}'

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Should fix] R1.2 $TASK is never set, so POST .../scheduled-tasks//run returns 400 validation_failed (path.id) and the runs jq fails with Cannot iterate over null. Fix: capture it from the create in step 3, e.g. TASK=$(curl ... | jq -r .id). With TASK set, I checked that the run answers 202 and becomes delivered to conversation.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2826f16: step 3 captures TASK=$(curl ... | jq -r .id) from the REST create; for a tool-made task it says to take the id from the step 4 list. Ran as written: run answered 202 trigger manual, then the run row read delivered, delivered_to conversation.

Comment thread .factory/features/scheduled-tasks.md Outdated
the zone differs, except for an anonymous caller or an incognito session
(`internal/api/session_create.go:76`). Each user may hold 25 active tasks per agent config
(`scheduling.MaxActivePerUser`, `scheduling.go:72`), or the config's
`scheduling.max_tasks_per_user` (1 to 100, `internal/api/configs.go:2030`); the count is

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Should fix] R1.3 The configs.go refs went stale when #1106 merged into the base: :2030 is now :2105 and :298 (line 48) is now :308. The same applies in SKILL.md: :2028 → :2103 (line 34), :298 → :308 (line 141) and :299 → :309 (line 191). Fix: merge the base and re-check every configs.go ref.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2826f16: merged origin/accelerate (04c60fe) and re-checked every file:line ref against the merged tree. configs.go refs are now :2105 and :308 in the feature doc, and :2103, :308, :309 in the skill. The only referenced file the merge changed is configs.go.

Comment thread .factory/features/scheduled-tasks.md Outdated
- **A paused one-shot resumed after its time is closed, not run (AI-1602).** Resume over
the API or the `update_scheduled_task` `resume` action writes one `missed` run with
error `task_paused` and completes the task (`scheduling.SleptThrough`,
`scheduler.go:129`). A second resume writes no second run: the occurrence's unique key

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Should fix] R1.4 A second resume does not just write no second run. The first resume completes the task, so the second resume gets 409 a completed task cannot be resumed (internal/api/scheduled_tasks.go:490), and the tool refuses it too (schedule_tools.go:365). TestResumingALateOneShotTwiceRecordsItsOccurrenceOnce asserts the 409. ON CONFLICT DO NOTHING covers only two resumes that race. Fix: say the second resume is refused (409).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2826f16: S-AI-1602 now says a second resume is refused with 409 (api/scheduled_tasks.go:490; the tool refuses it at schedule_tools.go:365). The unique key only meets two resumes that race.

Comment thread .factory/features/scheduled-tasks.md Outdated
delete does not settle a held run yet (AI-1600).
- **A call more than 15 minutes late is `missed`, and an unanswered call is not retried
(S5).** The call is not placed (`scheduledCallGrace`, `scheduled_delivery.go:65`).
Nobody picking up within the 90 s ring is `not_answered`, final: the next channel in

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Should fix] R1.5 "the occurrence is not retried" is wrong when a later channel in deliver_to fails with a reason that is not final. The chain is final only when every channel's failure is final (scheduled_delivery.go:244). Otherwise the occurrence is retried and the phone step answers already_called (:734). Fix: say the call is not placed again. The occurrence is retried only when another channel's failure is not final.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2826f16: S5 says the call is not placed again, the next channel is still tried, and the occurrence is retried only when another channel fails with a non-final reason (scheduled_delivery.go:245); the phone step then answers already_called (:734). I cite :245 for the final = final && ... line; it is at :245 on the merged tree, not :244.

| run | The record of one occurrence that came due, or of one run by hand: `running`, `delivered`, `failed` or `missed`, with `error` a reason code. Its `prompt` is the instructions as they were at the claim | `store.ScheduledTaskRun`, table `scheduled_task_runs`, unique per (`customer_id`, `task_id`, `scheduled_for`) for `trigger = schedule` | run `scheduled_for` 2026-10-26T09:00:00+01:00, `state` `delivered`, `delivered_to` `conversation` |
| claim, lease | A worker's hold on a task: `claim_id` fences every later write of that worker, `lease_until` says when another worker may take it. Lease 1 min, renewed every 10 s | `ClaimDueScheduledTasks`, `RenewScheduledTaskLease`, `scheduledLease`, `renewLeaseEvery` (`scheduler.go:19`, `:22`) | A pause clears `claim_id`; the next renewal finds no row and stops the delivery |
| held | An occurrence that came due in quiet hours: the claim stays, the lease moves to the hold's end, the run stays `running` with `postponed_until` | `Postponed`, `PostponeScheduledTask`, `ScheduledTaskRun.PostponedUntil` | Due 23:30 with quiet hours 22:00–07:00: held until 07:00, delivered then |
| missed | A run not delivered because it was too late or the task slept through it. Not a failure: the failure streak is left as it is | `ScheduledTaskRunMissed`, `Missed`, `Scheduler.missed`, `SleptThrough` | A daily task the router could not reach for 3 h: the two oldest occurrences are `missed`, the latest is delivered |

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Should fix] R1.6 This example does not hold. A daily task with the router down for 3 h has one occurrence. It is 3 h late, past repeatGrace (1 h), so it is missed and nothing is delivered (TestARecurringOccurrenceTwoHoursLateIsMissed). Skip-then-deliver needs more than a day of downtime (TestARecurringTaskSkipsOccurrencesOlderThanAnHour: one missed, the latest delivered). Fix: use, e.g., an hourly-free example: "a daily task the router could not reach for 25 h: yesterday's occurrence is missed, today's (59 min old) is delivered".

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2826f16: the example is now a daily task the router could not reach for 25 h (yesterday missed, today 59 min old delivered), with the 3 h case stated as one occurrence past the 1 h grace, so missed and nothing delivered. Tests: TestARecurringTaskSkipsOccurrencesOlderThanAnHour, TestARecurringOccurrenceTwoHoursLateIsMissed.

so `testing.yaml` wins over `ROUTER_POSTGRES_DSN` (`TestTheTestingFileWinsOverTheEnvironment`):

1. Point `testing.yaml` at a database of your own whose name ends in `_test`, such as
`model_router_test_<slug>_test`. Never commit that edit. `StoreSuite` drops the schema of

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Should fix] R1.7 StoreSuite does not drop the schema of the database it gets. It drops testenv.Database(dsn, "store") (internal/store/store_test.go:96). My run created model_router_store_test_ai1630r_test, next to ..._stmig_... and ..._drain_.... Fix: say each suite derives its own _test database from that DSN, and StoreSuite drops the schema of its own.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2826f16: step 1 says some suites derive a database of their own with testenv.Database (model_router_test_<slug>_test becomes model_router_store_test_<slug>_test), and StoreSuite drops the schema of its own (store_test.go:96), never yours. My run created model_router_store_test_ai1630f_test.

cd acceleration
grep dsn internal/config/testing.yaml # your own *_test database
go test -tags integration -p 1 -count=1 -run TestSchedulerSuite ./internal/scheduling/
go test -tags integration -p 1 -count=1 -run 'TestStoreSuite|TestScheduledTasksMigrationSuite' ./internal/store/

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Should fix] R1.8 This command can fail as written. It runs all of StoreSuite (516 tests, not only scheduling), and the default go test timeout is 10 min. My run at load ~40 hit it: FAIL internal/store 600.410s. A rerun with -timeout 40m passed in 458 s at load ~20. Fix: add -timeout 30m, or narrow -run to the scheduled-task tests.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2826f16: the store command now has -timeout 40m, and the text says it runs all of StoreSuite (more than 500 tests). Ran the command as written: ok internal/store 717.183s (12 min, over the 10 min default). I did not add TestDataMoveSuite to it: on this head five other DataMoveSuite tests (change capture) fail on my DB, unrelated to scheduling. The scheduled-task one has its own command and passes.

Comment thread .claude/skills/scheduled-tasks/SKILL.md Outdated

| Code | Meaning | Retried |
| --- | --- | --- |
| `delivery_failed` | Any other failure, or its workers died three times | yes, unless the workers died |

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Should fix] R1.9 delivery_failed is also final, not retried, once a call was placed: when the call's session cannot open (scheduled_delivery.go:783), or when the task cannot be sent as the call's first turn (:857). Both use undeliveredFinally. Fix the Retried cell, e.g. "yes, unless its workers died or a call was already placed".

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2826f16: the Retried cell reads: yes, unless it was claimed three times or a call was already placed (scheduled_delivery.go:783, :857). The cause cell now says the attempt cap counts claims.

kanat and others added 3 commits October 21, 2026 14:16
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@kanat kanat left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delta review of 2826f161d (merge 04c60feb3 + 2 fix commits), scratch-merged onto origin/accelerate 51b7dc6b7 with no conflicts. Verdict: GO.

Prior findings (all fixed, checked on the merged tree):

  • R1.1 fixed. I extracted the skill's step 2-4 bash blocks with sed and ran them against the local docker router (proxy mode). respond with request_id: remind-1 returns a receipt, and GET .../commands/remind-1 goes thinking then completed. Without it the router still answers 400 (session.go:507).
  • R1.2 fixed. TASK=$(... | jq -r .id) is set. POST .../run answers 202 trigger manual, and the run reads delivered, delivered_to conversation.
  • R1.3 fixed: configs.go:2103/2105/308/309 match the code.
  • R1.4-R1.10 fixed. The refs (scheduled_tasks.go:490, schedule_tools.go:365, scheduled_delivery.go:245/734/783/843/857, store_test.go:96, schemas.go:288) and the 25 h example (scheduler_test.go, TestARecurringTaskSkipsOccurrencesOlderThanAnHour) are right.
  • R1.11-R1.28 fixed. SaveScheduledTask (store/scheduled_tasks.go:270-347) matches S4. CheckMove (scheduling.go:59-68) and QuietEnd match. All 27 log-line refs are right. The node JWT snippet, run with a dummy secret, matches an independent Python HMAC: signature true, claims {server,iat,exp}, no secret in argv. All 74 test names exist.

Suites, run as written on my own fresh DB, -p 1:

  • scheduling unit, SchedulerSuite, ScheduledTasksSuite, DrainSuite: ok.
  • Store and Migration with -timeout 40m: ok, 430 s.
  • TestDataMoveSuite/TestScheduledTasksMoveWithTheirRuns as documented: ok. (Whole TestDataMoveSuite on the same merged tree: ok, 24 s. The fixer's 5 failures do not reproduce.)

New findings, both Nits and not blocking:

  • .claude/skills/scheduled-tasks/SKILL.md:52 and :218 cite manager.go:333. On the current base (#1110 added an import and lines) it is manager.go:340. This is base drift: the fixer was correct against 63e674ae7. Fix it in place when the base is merged before the PR merge. No other cited file changed between 63e674ae7 and 51b7dc6b7.
  • .factory/features/scheduled-tasks.md:123 cites channels/service.go:202 for Notify, but that line is whatsappWindow. This is old text and not blocking.

CI: labeler pass, CodeRabbit skipped. Staging was not touched.

kanat and others added 2 commits October 21, 2026 15:06
@kanat
kanat marked this pull request as ready for review October 21, 2026 19:07
@kanat
kanat merged commit 0e562c6 into accelerate Oct 21, 2026
2 checks passed
@kanat
kanat deleted the docs/AI-1630-scheduled-tasks-onboarding branch October 21, 2026 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant