mirror of
https://github.com/tiennm99/claude-code-routine-trigger-worker.git
synced 2026-05-25 03:36:10 +00:00
docs: CHANGELOG v0.1.0 + planning artifacts
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.1.0] - 2026-05-09
|
||||
|
||||
### Added
|
||||
- Cloudflare Workers `scheduled` handler that POSTs to the Claude Code routine `/fire` endpoint.
|
||||
- Default 5×daily cron at UTC+7 (00:07 / 05:13 / 10:19 / 15:23 / 20:37) — scattered minutes for shared-infra hygiene.
|
||||
- Token-substitution template with `{ISO}`, `{LocalTime}`, `{Cron}` (default: `Scheduled trigger at {LocalTime}`).
|
||||
- IANA timezone support via `TZ` env var (default: `UTC`).
|
||||
- Structured JSON logs for fire success / non-2xx / network failure.
|
||||
- Vitest test suite covering template substitution, fire paths, header correctness, token redaction.
|
||||
- GitHub Actions CI: `npm test` + `wrangler deploy --dry-run` on push and PR.
|
||||
- Apache-2.0 license.
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
phase: 1
|
||||
title: Repo scaffold
|
||||
status: completed
|
||||
priority: P1
|
||||
effort: 1h
|
||||
dependencies: []
|
||||
---
|
||||
|
||||
# Phase 1: Repo scaffold
|
||||
|
||||
## Overview
|
||||
Greenfield repo init. Apache-2.0 license, README skeleton, JS + wrangler toolchain, gitignore. No transpile step — Workers runtime executes ES modules natively.
|
||||
|
||||
## Requirements
|
||||
- **Functional:** `npm install` succeeds; `wrangler deploy --dry-run` validates an empty `worker.js` handler.
|
||||
- **Non-functional:** No secrets in repo. Pin wrangler major version. kebab-case file names.
|
||||
|
||||
## Architecture
|
||||
Flat layout — single-purpose worker, no need for src/ subdirs (KISS).
|
||||
|
||||
```
|
||||
claude-code-routine-trigger-worker/
|
||||
├── .github/workflows/ci.yml # phase 4
|
||||
├── .gitignore
|
||||
├── LICENSE # Apache-2.0
|
||||
├── README.md # phase 5
|
||||
├── package.json
|
||||
├── wrangler.toml # phase 3
|
||||
├── worker.js # phase 2
|
||||
└── worker.test.js # phase 4
|
||||
```
|
||||
|
||||
## Related Code Files
|
||||
- Create: `LICENSE` (Apache-2.0 verbatim)
|
||||
- Create: `.gitignore` (node_modules, .dev.vars, .wrangler, dist, .env*)
|
||||
- Create: `package.json` (name, version 0.0.0, `"type": "module"`, scripts: test, deploy, dev)
|
||||
- Create: `README.md` (one-liner placeholder; full content in phase 5)
|
||||
|
||||
## Implementation Steps
|
||||
1. `cd /config/workspace/tiennm99/claude-code-routine-trigger-worker && git init -b main`
|
||||
2. Write `LICENSE` (copy Apache-2.0 from sibling `claude-code-routine-cron/LICENSE`).
|
||||
3. Write `.gitignore`:
|
||||
```
|
||||
node_modules/
|
||||
dist/
|
||||
.wrangler/
|
||||
.dev.vars
|
||||
.env
|
||||
.env.*
|
||||
*.log
|
||||
```
|
||||
4. Write `package.json` with `"type": "module"`, devDeps: `wrangler` (^4), `vitest` (phase 4), `@cloudflare/vitest-pool-workers` (phase 4).
|
||||
Scripts:
|
||||
- `dev`: `wrangler dev`
|
||||
- `deploy`: `wrangler deploy`
|
||||
- `test`: `vitest run`
|
||||
5. Run `npm install` — confirm lockfile generated.
|
||||
6. Stub `README.md` with one-liner: `> Cloudflare Workers cron port of claude-code-routine-cron — fires Claude Code routines on a schedule.`
|
||||
7. First commit: `chore: init repo scaffold`.
|
||||
|
||||
## Success Criteria
|
||||
- [ ] `npm install` exits 0
|
||||
- [ ] `wrangler deploy --dry-run` validates empty `worker.js` exporting `export default {}`
|
||||
- [ ] `git status` clean after first commit
|
||||
- [ ] LICENSE file matches Apache-2.0 exactly
|
||||
- [ ] `package.json` has `"type": "module"`
|
||||
|
||||
## Risk Assessment
|
||||
- **Wrangler version drift:** pin to current major (e.g. `^4.0.0`); document upgrade path in README.
|
||||
- **Node version:** wrangler 4 needs Node ≥18; document in README prereqs.
|
||||
- **No type safety:** mitigate by JSDoc type hints in `worker.js` for `Env` and handler signatures (cheap, optional, no toolchain).
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
---
|
||||
phase: 2
|
||||
title: Worker handler + fire client
|
||||
status: completed
|
||||
priority: P1
|
||||
effort: 2-3h
|
||||
dependencies:
|
||||
- 1
|
||||
---
|
||||
|
||||
# Phase 2: Worker handler + fire client
|
||||
|
||||
## Overview
|
||||
Implement the `scheduled` handler in plain JS with JSDoc type annotations: render text template, POST to `/fire`, log structured JSON. Single `worker.js` file. No `fetch` handler (per locked decision: scheduled-only).
|
||||
|
||||
## Requirements
|
||||
- **Functional:**
|
||||
- On cron tick, POSTs to `ROUTINE_FIRE_URL` with bearer token, beta header, JSON body.
|
||||
- Logs `claude_code_session_url` on 2xx; logs status + body on non-2xx; never throws (would crash CF retry).
|
||||
- Renders `TEXT_TEMPLATE` with `{LocalTime}`, `{Cron}`, `{ISO}` placeholders.
|
||||
- No retry (each `/fire` = new session).
|
||||
- **Non-functional:**
|
||||
- Token never logged.
|
||||
- Use `console.log(JSON.stringify({...}))` for structured logs (CF picks these up).
|
||||
- JSDoc `@typedef Env` for environment bindings + `@param`/`@returns` on all exports.
|
||||
- Total file ≤ 200 lines (split if exceeds).
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
ScheduledEvent (cron, scheduledTime)
|
||||
│
|
||||
▼
|
||||
fireRoutine(env, controller)
|
||||
├── renderText(template, vars) # simple {Tok} substitution
|
||||
├── fetch(URL, POST, headers, body)
|
||||
└── log(json) # success or error
|
||||
```
|
||||
|
||||
### Mapping from Go daemon
|
||||
| Go (claude-code-routine-cron) | JavaScript (this worker) |
|
||||
|---|---|
|
||||
| `Config.FireURL` env | `env.ROUTINE_FIRE_URL` secret |
|
||||
| `Config.Token` env | `env.ROUTINE_FIRE_TOKEN` secret |
|
||||
| `Config.Schedules` env | `wrangler.toml` `[triggers].crons` (literal) |
|
||||
| `Config.Location` (TZ) | `env.TZ` plain var, default `UTC` |
|
||||
| `Config.Template` (Go text/template) | `env.TEXT_TEMPLATE` plain string + `{Tok}` substitution |
|
||||
| `FireClient.Fire()` | `fireRoutine()` function |
|
||||
| `cron.New()` scheduler | CF Workers scheduled handler (built-in) |
|
||||
| `slog` JSON logs | `console.log(JSON.stringify(...))` |
|
||||
|
||||
### JSDoc typedefs
|
||||
```js
|
||||
/**
|
||||
* @typedef {object} Env
|
||||
* @property {string} ROUTINE_FIRE_URL - secret: Anthropic /fire endpoint
|
||||
* @property {string} ROUTINE_FIRE_TOKEN - secret: per-routine bearer token
|
||||
* @property {string} [TEXT_TEMPLATE] - plain var: prompt template, supports {LocalTime} {Cron} {ISO}
|
||||
* @property {string} [TZ] - plain var: IANA tz, default 'UTC'
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} FireResponse
|
||||
* @property {string} type
|
||||
* @property {string} claude_code_session_id
|
||||
* @property {string} claude_code_session_url
|
||||
*/
|
||||
```
|
||||
|
||||
### Headers (verbatim from siblings)
|
||||
```
|
||||
Authorization: Bearer ${token}
|
||||
anthropic-version: 2023-06-01
|
||||
anthropic-beta: experimental-cc-routine-2026-04-01
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### Body
|
||||
```json
|
||||
{ "text": "<rendered template>" }
|
||||
```
|
||||
|
||||
### Template substitution (KISS — not Go text/template)
|
||||
| Token | Value |
|
||||
|---------------|-------------------------------------------------------|
|
||||
| `{ISO}` | `new Date(scheduledTime).toISOString()` |
|
||||
| `{LocalTime}` | formatted via `Intl.DateTimeFormat(env.TZ ?? 'UTC')` |
|
||||
| `{Cron}` | `controller.cron` |
|
||||
|
||||
Default template: `Scheduled trigger at {LocalTime}` — same default as Go daemon.
|
||||
|
||||
## Related Code Files
|
||||
- Create: `worker.js` (export default `{ scheduled }`, JSDoc-annotated)
|
||||
- (Optional split if >200 lines) `fire-client.js`, `template-renderer.js`
|
||||
|
||||
## Implementation Steps
|
||||
1. Top of `worker.js`: write JSDoc `@typedef Env` + `@typedef FireResponse` blocks.
|
||||
2. Implement `renderText(template, vars)` with JSDoc:
|
||||
```js
|
||||
/**
|
||||
* @param {string} template
|
||||
* @param {Record<string, string>} vars
|
||||
* @returns {string}
|
||||
*/
|
||||
```
|
||||
Replaces `{Key}` tokens, leaves unknown tokens intact.
|
||||
3. Implement `formatLocalTime(date, tz)` using `Intl.DateTimeFormat`. JSDoc: `@param {Date} date`, `@param {string} tz`, `@returns {string}`.
|
||||
4. Implement `fireRoutine(env, controller)` with JSDoc `@param {Env} env`, `@param {ScheduledController} controller`, `@returns {Promise<void>}`:
|
||||
- Build `text` via `renderText`.
|
||||
- `fetch(env.ROUTINE_FIRE_URL, { method: 'POST', headers, body: JSON.stringify({text}) })`.
|
||||
- On `!response.ok`: `console.log(JSON.stringify({level:'error', cron, status, body}))`.
|
||||
- On `ok`: parse JSON, log `{level:'info', cron, session_url, session_id}`.
|
||||
- Wrap in try/catch — log network errors, don't rethrow.
|
||||
5. Export default with JSDoc `@type {ExportedHandler<Env>}`:
|
||||
```js
|
||||
/** @type {ExportedHandler<Env>} */
|
||||
export default {
|
||||
async scheduled(controller, env, ctx) {
|
||||
ctx.waitUntil(fireRoutine(env, controller));
|
||||
},
|
||||
};
|
||||
```
|
||||
6. `npx tsc --noEmit --allowJs --checkJs worker.js --target es2022 --module esnext --moduleResolution bundler --types @cloudflare/workers-types` (one-shot smoke check; not added to scripts to avoid TS dependency creep). Optional — skip if undesired.
|
||||
7. Local smoke: `wrangler dev --test-scheduled`, then `curl "http://localhost:8787/__scheduled?cron=*+*+*+*+*"` — confirm log line.
|
||||
|
||||
## Success Criteria
|
||||
- [ ] `worker.js` ≤ 200 lines (split if needed)
|
||||
- [ ] All exported functions have JSDoc with `@param` + `@returns`
|
||||
- [ ] `wrangler deploy --dry-run` validates module
|
||||
- [ ] Token never appears in logged output (manual grep on `wrangler dev` output)
|
||||
- [ ] Local `__scheduled` test produces structured JSON log
|
||||
|
||||
## Risk Assessment
|
||||
- **`Intl.DateTimeFormat` TZ support on Workers runtime:** supported since 2023; verify with smoke test.
|
||||
- **`ctx.waitUntil` vs awaiting in `scheduled`:** `waitUntil` ensures fire completes even if handler returns; preferred per CF docs.
|
||||
- **No compile-time type errors:** JSDoc + editor LSP catches most; runtime tests (phase 4) catch the rest.
|
||||
- **Template injection:** `text` is sent only to Anthropic API; no XSS / SQL surface — safe.
|
||||
|
||||
## Security Considerations
|
||||
- Token comes only from `env.ROUTINE_FIRE_TOKEN` (CF Secret), never bundled.
|
||||
- `console.log` body must not include `Authorization` header value.
|
||||
- TLS to `api.anthropic.com` is automatic (Workers `fetch` validates certs).
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
---
|
||||
phase: 3
|
||||
title: wrangler config + secrets
|
||||
status: completed
|
||||
priority: P1
|
||||
effort: 1h
|
||||
dependencies:
|
||||
- 1
|
||||
- 2
|
||||
---
|
||||
|
||||
# Phase 3: wrangler config + secrets
|
||||
|
||||
## Overview
|
||||
Author `wrangler.toml` with literal `[triggers].crons`, define plain vars vs secrets, document deployment flow. CF Workers cron triggers fire from `wrangler.toml` only — same constraint as GH Actions.
|
||||
|
||||
## Requirements
|
||||
- **Functional:**
|
||||
- `wrangler deploy` succeeds with valid `wrangler.toml`.
|
||||
- At least one cron expression in `[triggers]` (default 5×daily mirroring sibling repos).
|
||||
- Secrets uploaded out-of-band (not in `wrangler.toml`).
|
||||
- **Non-functional:**
|
||||
- All comments above each block explain the field.
|
||||
- Default crons use scattered minutes (avoid minute `0`) per `claude-code-routine-trigger` README warning — though CF cron is more reliable than GH, scattering is still good hygiene.
|
||||
|
||||
## Architecture
|
||||
|
||||
### `wrangler.toml` shape
|
||||
```toml
|
||||
name = "claude-code-routine-trigger-worker"
|
||||
main = "worker.js"
|
||||
compatibility_date = "2026-05-09"
|
||||
compatibility_flags = ["nodejs_compat"] # only if Intl needs polyfill — verify in phase 2
|
||||
|
||||
# Plain vars (visible in dashboard, fine for non-secrets)
|
||||
[vars]
|
||||
TEXT_TEMPLATE = "Scheduled trigger at {LocalTime}"
|
||||
TZ = "Asia/Ho_Chi_Minh"
|
||||
|
||||
# Cron triggers — LITERAL, cannot read from env/secrets/vars
|
||||
[triggers]
|
||||
crons = [
|
||||
"7 17 * * *", # 00:07 UTC+7
|
||||
"13 22 * * *", # 05:13 UTC+7
|
||||
"19 3 * * *", # 10:19 UTC+7
|
||||
"23 8 * * *", # 15:23 UTC+7
|
||||
"37 13 * * *", # 20:37 UTC+7
|
||||
]
|
||||
|
||||
# Observability — opt-in, free tier supports basic logs
|
||||
[observability]
|
||||
enabled = true
|
||||
```
|
||||
|
||||
### Secrets (uploaded via `wrangler secret put`)
|
||||
| Name | Source |
|
||||
|----------------------|-------------------------------------|
|
||||
| `ROUTINE_FIRE_URL` | Anthropic routine editor → API trigger |
|
||||
| `ROUTINE_FIRE_TOKEN` | Anthropic routine editor (shown once) |
|
||||
|
||||
### Local dev: `.dev.vars`
|
||||
```
|
||||
ROUTINE_FIRE_URL=https://api.anthropic.com/v1/claude_code/routines/trig_.../fire
|
||||
ROUTINE_FIRE_TOKEN=sk-ant-oat01-...
|
||||
```
|
||||
Gitignored. Used by `wrangler dev` only.
|
||||
|
||||
### `wrangler.toml.example`
|
||||
Committed copy with placeholder values for documentation; real `wrangler.toml` ships with author's defaults but secrets are NEVER literal.
|
||||
|
||||
## Related Code Files
|
||||
- Create: `wrangler.toml`
|
||||
- Create: `wrangler.toml.example` (or document in README — KISS, prefer README)
|
||||
- Create: `.dev.vars.example` with placeholder secret names + dummy values
|
||||
|
||||
## Implementation Steps
|
||||
1. Write `wrangler.toml` per architecture block above.
|
||||
- Pick `compatibility_date` = today (2026-05-09).
|
||||
- Decide on `nodejs_compat` flag — only enable if phase 2 needs it (`Intl` is built-in, likely **don't need it**).
|
||||
2. Write `.dev.vars.example`:
|
||||
```
|
||||
ROUTINE_FIRE_URL=https://api.anthropic.com/v1/claude_code/routines/trig_REPLACE/fire
|
||||
ROUTINE_FIRE_TOKEN=sk-ant-oat01-REPLACE
|
||||
```
|
||||
3. Verify `.gitignore` includes `.dev.vars` (added in phase 1).
|
||||
4. Local validation: `wrangler dev` — must boot without error.
|
||||
5. Document secret-upload flow in phase 5 README:
|
||||
```bash
|
||||
echo -n 'https://...' | wrangler secret put ROUTINE_FIRE_URL
|
||||
echo -n 'sk-ant-oat01-...' | wrangler secret put ROUTINE_FIRE_TOKEN
|
||||
```
|
||||
6. Commit: `feat: wrangler config with default 5x-daily crons`.
|
||||
|
||||
## Success Criteria
|
||||
- [ ] `wrangler dev` starts cleanly with `.dev.vars` populated
|
||||
- [ ] `wrangler deploy --dry-run` validates `wrangler.toml` without errors
|
||||
- [ ] All 5 default crons match sibling repos' cadence (UTC+7 daily 00/05/10/15/20)
|
||||
- [ ] No secret values in any committed file
|
||||
- [ ] `.dev.vars` confirmed in `.gitignore`
|
||||
|
||||
## Risk Assessment
|
||||
- **CF cron expression syntax:** CF Workers cron supports standard 5-field; `*/N` and ranges work. Avoid `?` (Quartz-only).
|
||||
- **Minute-`0` GH issue does not apply to CF** but scattering minutes is still recommended (free tier shared infra benefits).
|
||||
- **Free tier limit:** 5 cron triggers per worker. Default config uses exactly 5 — at limit. Document that adding a 6th requires another worker.
|
||||
|
||||
## Security Considerations
|
||||
- Secrets never in `wrangler.toml` (only `[vars]` for non-secrets).
|
||||
- `.dev.vars` is gitignored.
|
||||
- `wrangler secret put` accepts stdin, avoiding shell history leak (use `echo -n ... | wrangler secret put`).
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
phase: 4
|
||||
title: Tests + CI
|
||||
status: completed
|
||||
priority: P2
|
||||
effort: 2h
|
||||
dependencies:
|
||||
- 2
|
||||
- 3
|
||||
---
|
||||
|
||||
# Phase 4: Tests + CI
|
||||
|
||||
## Overview
|
||||
Vitest with `@cloudflare/vitest-pool-workers` for in-Workers-runtime tests. Cover template rendering, fire client behavior (success / non-2xx / network error), token redaction. GitHub Actions CI on push.
|
||||
|
||||
## Requirements
|
||||
- **Functional:**
|
||||
- Tests cover: template substitution, missing-token rendering, fire success path, non-2xx path, fetch throws path.
|
||||
- CI runs `npm test` on push to `main` and PRs.
|
||||
- **Non-functional:**
|
||||
- Coverage report (text summary in CI logs).
|
||||
- Tests use mocked `fetch` — no real network calls (don't burn user's Anthropic quota).
|
||||
- Tests written in plain JS with JSDoc, matching production code style.
|
||||
- Race-free is N/A on Workers; instead assert `console.log` output never contains token.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Test layout
|
||||
```
|
||||
worker.test.js # all tests in one file initially (KISS)
|
||||
```
|
||||
Split if exceeds 200 lines.
|
||||
|
||||
### Test cases (mirror Go daemon's test coverage)
|
||||
| # | Case | Assertion |
|
||||
|---|------|-----------|
|
||||
| 1 | `renderText` substitutes `{LocalTime}`, `{Cron}`, `{ISO}` | output equals expected string |
|
||||
| 2 | `renderText` leaves unknown `{Foo}` tokens intact | output contains `{Foo}` |
|
||||
| 3 | `fireRoutine` 2xx response logs `session_url` | console captured contains `session_url` |
|
||||
| 4 | `fireRoutine` 401 response logs `level:error` | captured log has `level:'error'`, `status:401` |
|
||||
| 5 | `fireRoutine` `fetch` rejects (network) logs error, doesn't throw | no thrown exception, error log present |
|
||||
| 6 | Token redaction | for cases 3, 4, 5: stringified logs do NOT contain token value |
|
||||
| 7 | Headers correctness | mocked `fetch` receives `Authorization: Bearer ${token}`, `anthropic-version`, `anthropic-beta` |
|
||||
| 8 | Body shape | request body parses as `{ "text": <rendered> }` |
|
||||
| 9 | Default template when `TEXT_TEMPLATE` unset | output starts with `Scheduled trigger at ` |
|
||||
| 10 | TZ defaults to UTC when `env.TZ` unset | local time formatting uses UTC |
|
||||
|
||||
### CI workflow
|
||||
`.github/workflows/ci.yml` — single job, Node 20, runs test + wrangler dry-run as smoke gate.
|
||||
|
||||
## Related Code Files
|
||||
- Create: `worker.test.js`
|
||||
- Create: `vitest.config.js` (uses `@cloudflare/vitest-pool-workers`)
|
||||
- Create: `.github/workflows/ci.yml`
|
||||
|
||||
## Implementation Steps
|
||||
1. Install: `npm i -D vitest @cloudflare/vitest-pool-workers`.
|
||||
2. Write `vitest.config.js`:
|
||||
```js
|
||||
import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config';
|
||||
export default defineWorkersConfig({
|
||||
test: { poolOptions: { workers: { wrangler: { configPath: './wrangler.toml' } } } },
|
||||
});
|
||||
```
|
||||
3. Write `worker.test.js` covering all 10 cases. Use `vi.fn()` mock for `globalThis.fetch`. JSDoc-annotate test helper functions for IDE assist.
|
||||
4. Run `npm test` — all green.
|
||||
5. Write `.github/workflows/ci.yml`:
|
||||
```yaml
|
||||
name: ci
|
||||
on:
|
||||
push: { branches: [main] }
|
||||
pull_request:
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: '20', cache: 'npm' }
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
- run: npx wrangler deploy --dry-run
|
||||
```
|
||||
6. Push to GitHub; verify green CI.
|
||||
|
||||
## Success Criteria
|
||||
- [ ] All 10 test cases pass locally
|
||||
- [ ] `npm test` exits 0 in CI
|
||||
- [ ] `wrangler deploy --dry-run` exits 0 in CI
|
||||
- [ ] No real HTTP traffic generated by tests (verify by mock spy)
|
||||
- [ ] Coverage ≥ 80% (track via vitest `--coverage`; not enforced as blocking gate)
|
||||
|
||||
## Risk Assessment
|
||||
- **Workers test pool quirks:** `@cloudflare/vitest-pool-workers` requires wrangler config; ensure `wrangler.toml` validates first.
|
||||
- **Mocking `fetch` inside Workers runtime:** verify pattern works — fall back to dependency-injected fetch if not.
|
||||
- **Beta header pinning in tests:** assert exact value to catch accidental edits.
|
||||
|
||||
## Security Considerations
|
||||
- Test fixtures use placeholder token `sk-ant-oat01-test-only` — never a real value.
|
||||
- Test asserts logs **don't contain** the token (regression guard).
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
phase: 5
|
||||
title: Docs + release
|
||||
status: completed
|
||||
priority: P2
|
||||
effort: 1.5h
|
||||
dependencies:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
---
|
||||
|
||||
# Phase 5: Docs + release
|
||||
|
||||
## Overview
|
||||
Full README mirroring sibling repos' tone, comparison-table back-link in both siblings, v0.1.0 GitHub release, public deployment of the author's own instance.
|
||||
|
||||
## Requirements
|
||||
- **Functional:**
|
||||
- README covers: why-this-vs-siblings, quickstart, env vars, secret upload, customize schedule, beta header policy, security, license.
|
||||
- GitHub release `v0.1.0` tagged.
|
||||
- Both sibling READMEs updated with a row pointing to this repo.
|
||||
- **Non-functional:**
|
||||
- Same prose voice as siblings (`> [!TIP]` / `> [!WARNING]` callouts, terse).
|
||||
- Code blocks copy-pasteable.
|
||||
- All claims about CF cron precision sourced (link CF docs).
|
||||
|
||||
## Architecture
|
||||
|
||||
### README sections (in order)
|
||||
1. **Header + tagline** — one-liner.
|
||||
2. **Why this vs siblings** — three-column comparison table.
|
||||
3. **Quickstart** — `wrangler deploy` + 2 secret-puts + done.
|
||||
4. **Environment variables** — table (name, required, default, notes).
|
||||
5. **Multiple schedules** — `wrangler.toml` `[triggers].crons` array.
|
||||
6. **Templates** — `{LocalTime}`, `{Cron}`, `{ISO}`.
|
||||
7. **Customize schedule** — edit `wrangler.toml`, `wrangler deploy`.
|
||||
8. **Local development** — `.dev.vars` + `wrangler dev --test-scheduled`.
|
||||
9. **Secret rotation** — `wrangler secret put` overwrites.
|
||||
10. **CF Workers free tier** — link, note 5-cron-per-worker limit.
|
||||
11. **Beta header** — pinned value, bump policy.
|
||||
12. **Operational notes** — log access (`wrangler tail`), no idempotency.
|
||||
13. **License** — Apache-2.0.
|
||||
|
||||
### Sibling repo updates
|
||||
- Edit `claude-code-routine-trigger/README.md` — add row in the "no longer used by author" callout listing `trigger-worker` as another option.
|
||||
- Edit `claude-code-routine-cron/README.md` — extend "Why this vs `claude-code-routine-trigger`" table to a 3-way comparison including `trigger-worker`.
|
||||
|
||||
## Related Code Files
|
||||
- Modify: `README.md` (this repo)
|
||||
- Modify (out of repo, requires PR/commit): `/config/workspace/tiennm99/claude-code-routine-trigger/README.md`
|
||||
- Modify (out of repo): `/config/workspace/tiennm99/claude-code-routine-cron/README.md`
|
||||
- Create: `CHANGELOG.md` (Keep-A-Changelog format)
|
||||
- Optional: `.github/release.yml` for release-notes automation
|
||||
|
||||
## Implementation Steps
|
||||
1. Draft README per architecture sections — use sibling READMEs verbatim where applicable (Apache-2.0 license clauses, beta header note, security note about per-routine token scope).
|
||||
2. Write `CHANGELOG.md` with `## [0.1.0] - 2026-MM-DD` initial entry listing features.
|
||||
3. Local proof: deploy author's instance via `wrangler deploy`, attach a non-prod routine, observe one cron tick, capture `session_url` for README screenshot.
|
||||
4. Tag and release:
|
||||
```bash
|
||||
git tag v0.1.0
|
||||
git push origin v0.1.0
|
||||
gh release create v0.1.0 --generate-notes
|
||||
```
|
||||
5. Open PRs (or direct commits if author owns) to update sibling READMEs with the comparison row.
|
||||
6. Final commit: `docs: README + v0.1.0 release notes`.
|
||||
|
||||
## Success Criteria
|
||||
- [ ] README renders cleanly on GitHub
|
||||
- [ ] All code blocks in README are runnable (copy-paste tested)
|
||||
- [ ] Comparison table accurately reflects all 3 options
|
||||
- [ ] Both sibling READMEs reference this repo
|
||||
- [ ] `v0.1.0` tag pushed and GitHub release published
|
||||
- [ ] Author's own deployment fired at least one successful cron tick (proof-of-life)
|
||||
|
||||
## Risk Assessment
|
||||
- **Beta header drift:** if Anthropic ships new dated beta between phase 2 and release, bump in code + CHANGELOG before tagging.
|
||||
- **Sibling repo edits cause merge conflicts:** branch + PR if user has unmerged work; otherwise direct commit.
|
||||
- **CF dashboard URL format changes:** don't hardcode dashboard URLs in README; use `wrangler tail` (CLI) which is stable.
|
||||
|
||||
## Security Considerations
|
||||
- README must NOT include real `ROUTINE_FIRE_URL` or token — only `sk-ant-oat01-...` placeholder format.
|
||||
- Document that pushing to a public fork preserves `.dev.vars` exclusion (gitignore line).
|
||||
|
||||
## Next Steps (post v0.1.0)
|
||||
- v0.2: optional `WEBHOOK_URL` to mirror fire output to Slack/Discord (low priority — most users use `wrangler tail`).
|
||||
- v0.3: `wrangler.toml.example` with multi-routine deployment helper script.
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
title: claude-code-routine-trigger-worker — CF Workers cron port
|
||||
description: >-
|
||||
Cloudflare Workers port of claude-code-routine-cron: scheduled handler fires
|
||||
Claude Code routine /fire endpoint, zero infra, free tier
|
||||
status: completed
|
||||
completed: 2026-05-09
|
||||
priority: P2
|
||||
created: 2026-05-09T00:00:00.000Z
|
||||
target_repo: /config/workspace/tiennm99/claude-code-routine-trigger-worker
|
||||
reference_repos:
|
||||
- /config/workspace/tiennm99/claude-code-routine-trigger
|
||||
- /config/workspace/tiennm99/claude-code-routine-cron
|
||||
license: Apache-2.0
|
||||
blockedBy: []
|
||||
blocks: []
|
||||
---
|
||||
|
||||
# claude-code-routine-trigger-worker — CF Workers cron port
|
||||
|
||||
## Problem
|
||||
Two existing siblings cover the routine-fire space, both with trade-offs:
|
||||
- `claude-code-routine-trigger` (GH Actions): free, but cron unreliable (30 min – 2h delays, occasional drops at minute `0`).
|
||||
- `claude-code-routine-cron` (Go daemon, Docker): sub-second precision, but requires self-hosted infra.
|
||||
|
||||
Gap: a free, no-infra option with reliable timing.
|
||||
|
||||
## Goal
|
||||
Cloudflare Workers port: scheduled handler POSTs to the Claude Code routine `/fire` endpoint on a fixed cron. Free tier (CF Workers cron triggers cost $0 within free limits). Single TypeScript file, deploy via `wrangler deploy`.
|
||||
|
||||
## Locked Decisions
|
||||
| # | Decision | Choice |
|
||||
|---|----------|--------|
|
||||
| 1 | Repo name | Completed |
|
||||
| 2 | Scope | Completed |
|
||||
| 3 | Cron config | Completed |
|
||||
| 4 | Manual HTTP fire | Completed |
|
||||
| 5 | Language | Completed |
|
||||
| 6 | Secret management | `wrangler secret put` — `ROUTINE_FIRE_URL`, `ROUTINE_FIRE_TOKEN` |
|
||||
| 7 | Optional config | `TEXT_TEMPLATE` (env var, plain string with placeholders), `TZ` (env var) |
|
||||
| 8 | License | Apache-2.0 (matches siblings) |
|
||||
|
||||
## Non-goals
|
||||
- Manual `/fire` HTTP endpoint (KISS — scheduled only)
|
||||
- Multiple routines per worker (deploy N workers for N routines)
|
||||
- Retry on failure (each POST = new session; retries multiply sessions)
|
||||
- Durable Objects / KV / D1 (stateless)
|
||||
- Custom log shipping (CF dashboard `wrangler tail` is enough)
|
||||
- Go-style `text/template` (use simple `{token}` substitution — KISS)
|
||||
|
||||
## Comparison Matrix (post-implementation)
|
||||
| | trigger (GH Actions) | cron (Go daemon) | **trigger-worker (this)** |
|
||||
| ---------------- | -------------------- | ------------------------ | ------------------------- |
|
||||
| Runs on | GitHub runners | self-hosted Docker | Cloudflare edge |
|
||||
| Cost | free (GH minutes) | minimal (own infra) | free (CF free tier) |
|
||||
| Cron precision | ±30 min – 2 h | sub-second | ±15 sec (CF SLA) |
|
||||
| Setup | fork + 2 secrets | env vars + Docker | wrangler deploy + secrets |
|
||||
| Audit trail | Actions runs page | container stdout | CF dashboard / `tail` |
|
||||
|
||||
## Phases
|
||||
|
||||
| Phase | Name | Status |
|
||||
|-------|------|--------|
|
||||
| 1 | [Repo scaffold](./phase-01-repo-scaffold.md) | Completed |
|
||||
| 2 | [Worker handler + fire client](./phase-02-worker-handler-fire-client.md) | Completed |
|
||||
| 3 | [wrangler config + secrets](./phase-03-wrangler-config-secrets.md) | Completed |
|
||||
| 4 | [Tests + CI](./phase-04-tests-ci.md) | Completed |
|
||||
| 5 | [Docs + release](./phase-05-docs-release.md) | Completed |
|
||||
|
||||
## Dependencies
|
||||
None. Greenfield repo. Reference repos read-only.
|
||||
|
||||
## Risk Register
|
||||
| Risk | Severity | Mitigation |
|
||||
|---|---|---|
|
||||
| CF Workers cron precision in practice | Low | Documented ±15 sec — adequate for routine triggering |
|
||||
| Wrangler config drift with new CF features | Low | Pin wrangler major version in `package.json` |
|
||||
| Beta header (`experimental-cc-routine-2026-04-01`) churn | Medium | Constant in code; bump via release; siblings have same exposure |
|
||||
| Free tier cron limit (5 unique cron expressions per worker) | Low | One-routine-per-worker model means single-digit crons typical |
|
||||
| Secrets accidentally committed | High | `.dev.vars` gitignored; secrets only via `wrangler secret put` in CI |
|
||||
|
||||
## Success Criteria
|
||||
- [ ] `wrangler deploy` ships worker; first cron tick fires `/fire` and logs `claude_code_session_url`.
|
||||
- [ ] `wrangler tail` shows JSON-structured logs for each fire.
|
||||
- [ ] All CF Workers cron expressions in `wrangler.toml` validated by `wrangler dev` locally.
|
||||
- [ ] CI runs typecheck + tests on push.
|
||||
- [ ] README documents quickstart, env vars, secret rotation, beta header policy.
|
||||
- [ ] Release `v0.1.0` published with `wrangler.toml` example.
|
||||
|
||||
## References
|
||||
- Sibling Go daemon: `/config/workspace/tiennm99/claude-code-routine-cron`
|
||||
- Sibling GH Actions: `/config/workspace/tiennm99/claude-code-routine-trigger`
|
||||
- Anthropic `/fire` API: https://platform.claude.com/docs/en/api/claude-code/routines-fire
|
||||
- CF Workers cron: https://developers.cloudflare.com/workers/configuration/cron-triggers/
|
||||
Reference in New Issue
Block a user