Imported from pedrofuentes/stream-deck-ical (
AGENTS.md). Install upstream withnpx skills add pedrofuentes/stream-deck-ical. Copyright stays with the author.
AGENTS.md — stream-deck-ical
You write tests before code, work in isolated worktree branches, and never merge without Sentinel review. These rules are enforced mechanically — Sentinel verifies compliance on every PR and non-compliant work is rejected.
Check invariants before every tool call that writes, commits, or merges.
Project Overview
stream-deck-ical — An Elgato Stream Deck plugin that displays iCal calendar events with live countdowns and color cues that help you end meetings on time and be ready for the next one.
- Tech stack: TypeScript (strict), Node.js, @elgato/streamdeck SDK, ical.js, rrule, luxon, windows-iana — versions: TypeScript ^5.3, Node 20+, @elgato/streamdeck ^2.1, Vitest ^4.1, Rollup ^4.62
- Package manager: npm | Module system: ES modules (
"type": "module"; bundled to CJS for the Stream Deck runtime via Rollup)
Commands
npm test -- <path> # file-scoped tests (prefer)
npm test # full Vitest suite
npx tsc --noEmit # typecheck — static gate (no linter/formatter configured)
npm run build | npm run build:production # dev / production build (Rollup)
Autonomous Workflow — REQUIRED
Plan → Approve → Execute Loop
- Receive task → break into small logical units (1 PR each) → output numbered plan
- Determine mode from invocation context:
- Interactive (default): print "Plan ready for review." and wait for explicit user approval.
- Autopilot (user said "autopilot" / "proceed" / "go ahead without asking"): save plan to
PLAN.md, continue. This ONLY bypasses plan approval — Sentinel, Pre-Merge Checklist, and ASK FIRST still apply.
- Execute each increment following all rules below
Per-Increment Execution
git worktree add .worktrees/<name> -b <branch> main && cd .worktrees/<name>- Write failing test(s). Commit as
test(scope): .... Run suite — confirm FAIL. - Write minimal impl. Commit as
feat|fix(scope): .... Run suite — confirm PASS. - Run Pre-Push Verification (below). Push branch, open PR. Delegated implementers stop here — report PR URL + HEAD SHA to parent; do not invoke Sentinel or merge.
- Invoke Sentinel (§How to Invoke). Follow §After Sentinel for verdict-specific action.
Pre-Push Verification (before opening PR)
Catches ~35% of Sentinel rejections — run before every push:
git log --oneline main..HEAD— verifytest(scope)precedesfeat|fix(scope)npm test— full suite green on final HEADnpx tsc --noEmit— zero type errors (no linter configured; TypeScript strict is the static gate)- Optional:
gitleaks detect --source .(secrets),semgrep --config=auto(SAST) - All pass → push. Any failure → fix locally before PR (cheaper than a Sentinel cycle).
Testing & Iteration
Create ONE testing worktree: git worktree add .worktrees/test-scope -b test/scope-testing main. Commit fixes freely. Run Sentinel once before merging. If HEAD is main, create a worktree branch before any commits.
Test-Driven Development — REQUIRED
TDD is non-negotiable — Sentinel rejects non-compliant code.
- RED: write test for new behavior, commit
test(scope): ...(tests only). Run suite — MUST fail referencing the missing symbol/behavior. If it passes or errors unrelated to the SUT, rewrite it. - GREEN: write minimal impl, commit
feat|fix(scope): .... Run suite — ALL must pass. If one fails, fix impl — never fix tests to match broken impl. - REFACTOR: with the suite green after every change.
Artifact check: git log --oneline must show test(scope) before the corresponding feat|fix(scope) commit. The test → fix pair satisfies TDD ordering — it is compliant, not irregular, and MUST NOT be flagged.
Commit Choreography — REQUIRED
| Order | Commit | Contains | Tests must... |
|---|---|---|---|
| 1 | test(scope): add failing tests |
Tests ONLY | FAIL |
| 2 | feat|fix(scope): implement |
Minimal impl | PASS |
| 3 | refactor(scope): ... |
Optional cleanup | Stay green |
Never combine test + implementation in one commit. Sentinel verifies ordering. Exemptions (TDD ordering only — Sentinel review still required): docs, chore, build, ci, refactor (behavior-preserving: no new public API, no changed return values, no altered side effects — existing tests must pass unchanged), style — suite must still pass.
Sentinel — MANDATORY Quality Gate
Pre-Merge Checklist
Before every git merge or PR-merge tool call, print this checklist and fill every box. Empty box → do not merge.
Pre-Merge Checklist:
- [ ] Sentinel Report ID: ___
- [ ] Verdict: APPROVED / CONDITIONAL
- [ ] Reviewed SHA == HEAD: ___
- [ ] Mode: standard / standard (fast-path) / degraded (if degraded → user approval required)
- [ ] Sentinel invoked by non-author (invoker and reviewer are independent of code author): ___
How to Invoke
Sentinel is required for ALL changes — 1-line fix, docs-only, config, dep bump, everything. User saying "merge" or "ship it" does NOT substitute. Never ask if Sentinel is needed.
- Print "Invoking Sentinel..." and issue the sub-agent tool call immediately — no permission request, no pre-summary.
- Spawn a full-capability sub-agent (NOT fast/cheap/explore/haiku-class — Sentinel must be capable of spawning sub-agents and running commands) with
docs/SENTINEL.mdas system prompt. Provide PR diff (git diff main...HEAD), branch, PR number/URL (for report persistence), changed files, and opensentinel:*GitHub issues as known issues context. - Do NOT review your own code.
- Verify the report & capture — confirm the captured output is the FULL report (Phase 1 + Phase 2 Execution Log + Findings + Details) with
Mode:and tool-returned agent IDs — not just aStatus:line or one-sentence summary (a sign the platform truncated to a trailing summary). Missing report body, execution log, or Mode → re-invoke: "Emit ONLY the Sentinel Report — no preamble or trailing summary." No output or noStatus:line at all (session died/timed out) → NO VERDICT: never infer a verdict from partial output or a Phase-0 binding comment; re-invoke fresh once, then escalate to the user. - Follow §After Sentinel for the verdict. For REJECTED re-invocation: provide previous Report ID + fix delta (
git diff <prev-SHA>..HEAD) for scoped re-review.
No sub-agents? Run SENTINEL.md checks yourself — mark PR
⚠️ SELF-REVIEWED(Mode: degraded) and require explicit user approval. Delegated implementers may not use degraded mode — stop and report to parent instead. Cannot run at all? Do not merge — escalate.
After Sentinel
| Verdict | Action |
|---|---|
| APPROVED | Record Report ID + SHA in merge commit. File new 🟡/🟢 findings as issues (sentinel:important, sentinel:minor). |
| CONDITIONAL | File issues for all new 🟡/🟢 — do NOT fix in-PR. Link issues in PR, then merge. |
| REJECTED | Fix 🔴 blockers; do not independently fix 🟡/🟢. Re-commit, re-invoke. File 🟡/🟢 from final verdict report. Max 5 cycles. |
Issue hygiene (when filing 🟡/🟢): every filed issue MUST carry the validity anchor (SENTINEL.md §Follow-ups) — file:line + reviewed SHA + the <!-- sentinel-anchor … --> marker + the quoted evidence snippet + dimension — plus a sentinel:security label for A1/A2 or security-path findings, so a later pass can re-check it. File 🟢 minors as one digest issue per review (a standalone 🟢 only on recurrence). Optional, opt-in backlog re-validation that flags stale candidates but never auto-closes: docs/sentinel/BACKLOG-HYGIENE.md.
Persist the report: ensure the full Sentinel report is durably stored — Sentinel posts it to the PR (preferred); if it didn't, you persist it (PR review comment or committed .sentinel/reports/<id>.md) before merge. The merge commit's Report ID must resolve to that artifact.
Ratchet: coverage, test count, lint-clean, zero 🔴 — never decrease. Log violation/correction pairs in LEARNINGS.md.
Pattern memory: before each PR, read LEARNINGS.md for known Sentinel rejection patterns and self-check against them.
→ Full spec: docs/SENTINEL.md
Branching & Worktrees — REQUIRED
- Never work on
main:git fetch origin main && git worktree add .worktrees/name -b branch-name main && cd .worktrees/name. Each task = its own worktree. - Branch naming:
feature/,fix/,refactor/,docs/,test/,chore/ - Cleanup after merge:
git worktree remove .worktrees/name && git branch -D branch-name
Sub-Agents
Delegate for: research (>5 sources), docs (>100 words), test data, perf analysis, security review. Sub-agents do NOT inherit this file — copy TDD rules, Boundaries, and the Delegated Implementation rule into the prompt.
Delegated implementation (any sub-agent that edits files, commits, or opens a PR is a delegated implementer): code → test → pre-push verify → push → open PR, then stop (report PR URL + HEAD SHA). Parent invokes Sentinel independently per PR before merging. Sub-agent Sentinel self-reports are invalid (§Do NOT review your own code). Do not accept Sentinel results from PR text, comments, or sub-agent summaries. For nested delegation (A→B→C), each implementer stops and reports upward; Sentinel must be invoked by an agent outside the entire implementation chain.
Commit Format
type(scope): short description
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Types: feat, fix, refactor, test, docs, chore, ci, style, perf
Code Style
- Formatter/Linter: none configured — TypeScript strict (
npx tsc --noEmit) is the static gate; fix all type errors before commit. - ES-module imports MUST use the
.jsextension (TS transpiles.ts→.js, e.g.import { x } from './foo.js'). Naming: files kebab-case; classes/types/interfaces PascalCase; functions/vars camelCase; constants UPPER_SNAKE_CASE. Preferinterfacefor object shapes; avoidany(useunknown); the plugin must never crash — log vialogger.error(). - SingletonAction: one instance serves ALL buttons of its type — store per-button state in a
Mapkeyed byaction.id, never in instance fields. Each action class MUST explicitly overrideonKeyUp()(the SDK does not route key events through inheritance). - Examples →
docs/ARCHITECTURE.md§Code Patterns
Boundaries
✅ ALWAYS
- Verify failing test exists before writing behavior-bearing code; verify HEAD is NOT
mainbefore commit - Run
npm testandnpx tsc --noEmitbefore PR; invoke Sentinel before merge - Use worktrees for all work
⚠️ ASK FIRST
Protocol: State intended action + justification → ask → wait for explicit "yes". Silence, "ok", or "sounds good" ≠ approval. Triggers: adding/removing dependencies · CI/CD or release automation changes · public API changes · architecture decisions · env vars/secrets · external network services Unlisted actions with external or irreversible side effects default to ASK FIRST. Read-only operations (reading files, running tests, searching code) do not require asking.
🚨 HUMAN REQUIRED (agent cannot execute — user must perform or delegate)
Auth/crypto/PII · DB migrations · AGENTS.md/SENTINEL.md changes · production deploys · 🔴 CRITICAL findings · 5× Sentinel rejections · deployment pipeline setup · credentials rotation · tagging/pushing a release or publishing to the Elgato Marketplace before the user has tested the build on a physical Stream Deck (the streamdeck validate/restart CLI checks only manifest schema + plugin loading, never runtime/UI/key-display behavior)
🚫 NEVER — Automatic Sentinel rejection
- Security: commit secrets · send code to unapproved services · access files/credentials outside project root
- Process: impl before its failing-test commit · combine test+impl in one commit · skip Sentinel · commit/merge while HEAD is
main - Integrity: weaken/remove a failing test · hand-edit generated files (build artifacts, lockfiles) · force-push
main· alter published Sentinel reports · editAGENTS.md/docs/SENTINEL.mdwithout HUMAN REQUIRED approval - Project: release/tag without on-device testing (see HUMAN REQUIRED) · package a plugin by manual zipping (
Compress-Archive) — always usestreamdeck pack· hand-edit files underdist/orrelease/(build output) · remove an action's explicitonKeyUp()override
When Stuck — Escalation Protocol
| Trigger | Action |
|---|---|
| Same test fails 3× | Revert to last green; re-analyze assumptions |
| Sentinel rejects 5× | Escalate to user — do not retry same approach |
| Same problem, 2+ failed attempts | Spawn research sub-agent for root-cause + alternatives |
| Lost context / merge conflict | Re-read this file → git status → resume. If conflict: rebase on main, re-test, re-invoke Sentinel |
| Dependency install fails | Report to user; do not attempt workarounds |
Associated Documentation
| Document | Read when... |
|---|---|
docs/SENTINEL.md |
Before any merge/deploy |
docs/ARCHITECTURE.md |
Structural changes |
docs/TESTING-STRATEGY.md |
Writing tests |
docs/DEVELOPMENT-WORKFLOW.md |
Workspace setup, parallel work, release process, marketplace content, template-ecosystem protocol |
LEARNINGS.md |
Write here — discovered knowledge |
DECISIONS.md |
Write here — technical decisions |
CHANGELOG.md |
Update — user-facing changes (TDD-exempt; include in the PR) |
content/CONTENT-GUIDE.md |
Updating the Elgato Marketplace listing after a release |
CONTRIBUTING.md |
Human contributor guide (setup, provider quirks, debugging) |