[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6GXS2840SJ61285SZ43EJC3":3,"skill-same:01M6GXS2840SJ61285SZ43EJC3":83},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":55,"commitSha":56,"skillDirectory":57,"forksCount":58,"repositoryHtmlUrl":59,"repositoryHomepageUrl":60,"ownerHtmlUrl":61,"ownerBlogUrl":60,"ownerDescription":62,"updatedAt":63,"install":64,"resources":76},"01M6GXS2840SJ61285SZ43EJC3","watch",null,"File sentinel that monitors the working directory for changes and marker comments, then auto-triggers appropriate skills. Poll-based via git diff against the last scan commit. Writes intake items for batch processing and routes marker actions through \u002Fdo. Use for automatic reactions to file changes; do NOT use for one-off inspection or tasks needing human judgment per file.","SethGammon\u002FCitadel","SethGammon","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F5523585?v=4","01M6GXJ90FD2CE9QF5GQERWN06","Seth Gammon","01M6GXJ94VXYGK5KAW14EYE901",930,"MIT","https:\u002F\u002Fgithub.com\u002FSethGammon\u002FCitadel\u002Fblob\u002Fmain\u002Fskills\u002Fwatch\u002FSKILL.md",[],[5,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"stop","processing","false","reactions","judgment","human","scan","do","files","directory","auto","diff","sentinel","start","actions","batch","git","tasks","commit","2026-11-10 10:12:30",false,"---\nname: watch\nlicense: MIT\ndescription: >-\n  File sentinel that monitors the working directory for changes and marker\n  comments, then auto-triggers appropriate skills. Poll-based via git diff\n  against the last scan commit. Writes intake items for batch processing and\n  routes marker actions through \u002Fdo. Use for automatic reactions to file\n  changes; do NOT use for one-off inspection or tasks needing human judgment\n  per file.\nuser-invocable: true\nauto-trigger: false\ntrigger_keywords:\n  - watch\n  - watch files\n  - watch changes\n  - file sentinel\n  - monitor files\n  - watch start\n  - watch stop\n  - watch scan\n  - marker comments\n  - \"@citadel\"\nlast-updated: 2026-03-29\n---\n\n# \u002Fwatch -- File Sentinel\n\nUse when the user wants automatic reactions to file changes or marker comments (`@citadel:`).\nDo NOT use for one-off file inspection or tasks that need human judgment per file.\n\n## Orientation\n\n**Use when:** running a file sentinel that triggers a skill or command automatically when watched paths change.\n**Don't use when:** monitoring CI status on a PR (use \u002Fpr-watch); running a one-shot verification check (use \u002Fverify).\n\n## Default execution path (READ FIRST)\n\n**`\u002Fwatch start` does NOT call `CronCreate` by default.** Only pass `--remote`\nto use Anthropic's routine system, and only after explicit user confirmation.\n`CronCreate` counts against the **15 routine runs \u002F 24h** cap — at the default\n5-minute interval a watch exhausts the quota in under an hour.\n\n### Default flow — `\u002Fwatch start` (no `--remote` flag)\n1. Do Steps 1 and 2 below (check existing watch, determine baseline commit).\n2. **Skip Step 3** — do NOT call `CronCreate`. Leave `cronId: null` in state.\n3. Write the state file (Step 4) with `status: \"watching\"`.\n4. Output:\n   ```\n   Watch state created: .planning\u002Fwatch-state.json\n     Baseline: {commit hash, first 7 chars}\n\n   To start real-time watching, run in a separate terminal:\n     npm run watch:local\n\n   For cloud-persistent polling (machine off, user away):\n     \u002Fwatch start --remote     (uses CronCreate, counts against 15\u002Fday cap)\n   ```\n\n### Opt-in routine flow — `\u002Fwatch start --remote`\nOnly when `--remote` is explicitly passed:\n1. Confirm: \"This will use `CronCreate`, which counts against your 15 routine\n   runs \u002F 24h quota. At a 5-minute interval this exhausts the quota in under\n   an hour. Continue? (y\u002FN)\"\n2. On confirmation, run the full Step 1–5 protocol including `CronCreate`.\n\n## Commands\n\n| Command | Behavior |\n|---|---|\n| `\u002Fwatch start` | Default: create state, prompt user to run `npm run watch:local` |\n| `\u002Fwatch start --remote` | Use `CronCreate` polling (counts against 15\u002Fday quota — requires confirmation) |\n| `\u002Fwatch start --interval {N}m` | Set poll interval for `--remote` mode (default: 5m) |\n| `\u002Fwatch stop` | Stop watching, tear down cron |\n| `\u002Fwatch status` | Show watch state, last scan time, pending actions |\n| `\u002Fwatch scan` | Run a single scan now (manual trigger) |\n\n## Protocol\n\n### \u002Fwatch start\n\n#### Step 1: Check for existing watch\n\n1. Read `.planning\u002Fwatch-state.json` if it exists\n2. If `status` is `\"watching\"`:\n   - Show current state: last scan time, interval, pending actions count\n   - Ask: \"A watch is already active. Stop it and start a new one?\"\n   - If yes: run `\u002Fwatch stop` first, then continue\n   - If no: abort\n\n#### Step 2: Determine baseline commit\n\n1. Run `git rev-parse HEAD` to get the current commit hash\n2. If not a git repo: fall back to timestamp-based detection (store current\n   time as `lastScanTime`, skip commit-based diffing)\n3. Store this as `lastScanCommit`\n\n#### Step 3: Create poll schedule (--remote only)\n\n```\nCronCreate:\n  interval: \"{N}m\"  (default: 5m)\n  command: \"\u002Fwatch scan\"\n```\n\nSave the cron ID in the state file.\n\n#### Step 4: Write state file\n\nWrite `.planning\u002Fwatch-state.json`:\n\n```json\n{\n  \"status\": \"watching\",\n  \"lastScanCommit\": \"abc1234\",\n  \"lastScanTime\": null,\n  \"interval\": \"5m\",\n  \"cronId\": \"{id from step 3 or null}\",\n  \"pendingActions\": {},\n  \"processedMarkers\": {},\n  \"stats\": {\n    \"scansRun\": 0,\n    \"markersFound\": 0,\n    \"intakeItemsCreated\": 0,\n    \"skillsDispatched\": 0\n  }\n}\n```\n\n#### Step 5: Confirm (--remote only)\n\n```\nWatch started.\n  Interval:  every {N}m\n  Baseline:  {commit hash, first 7 chars}\n  State:     .planning\u002Fwatch-state.json\n```\n\n---\n\n### \u002Fwatch stop\n\n1. Read `.planning\u002Fwatch-state.json`. If missing or not `\"watching\"`: \"No watch is active.\"\n2. `CronDelete: {cronId}` — if cronId is missing or deletion fails, continue.\n3. Update state: `\"status\": \"stopped\", \"cronId\": null`. Preserve all other fields.\n4. Output:\n   ```\n   Watch stopped.\n     Scans completed:      {stats.scansRun}\n     Markers found:        {stats.markersFound}\n     Intake items created: {stats.intakeItemsCreated}\n     Skills dispatched:    {stats.skillsDispatched}\n   ```\n\n---\n\n### \u002Fwatch status\n\n1. If `.planning\u002Fwatch-state.json` missing: \"No watch configured. Use `\u002Fwatch start` to begin.\"\n2. Output state fields: status, lastScanTime, lastScanCommit, interval, pendingActions.length, stats.\n3. If `pendingActions` non-empty, list each: `[{action}] {file}:{line} -- {description}`\n\n---\n\n### \u002Fwatch scan\n\n#### Step 1: Load state\n\n1. Read `.planning\u002Fwatch-state.json`\n2. If missing: create default state with `lastScanCommit` from `git rev-parse HEAD` and `status: \"watching\"`.\n\n#### Step 2: Detect changed files\n\n**Git mode (primary):**\n1. `git diff --name-only {lastScanCommit} HEAD` (committed changes)\n2. `git diff --name-only` (unstaged) and `git diff --name-only --cached` (staged)\n3. Merge and deduplicate all three lists\n\n**Fallback mode (no git):**\n1. `find . -newer {timestamp_file} -type f`\n2. Exclude `node_modules\u002F`, `.git\u002F`, `.planning\u002F`, `dist\u002F`, `build\u002F`\n\nIf no files changed: update `lastScanTime` and `stats.scansRun`, exit early.\n\n#### Step 3: Scan for marker comments\n\nSearch changed files for:\n\n| Pattern | Languages |\n|---|---|\n| `\u002F\u002F @citadel: {action} {description}` | JS, TS, Go, Rust, C, Java |\n| `# @citadel: {action} {description}` | Python, Shell, YAML, Ruby |\n| `\u002F* @citadel: {action} {description} *\u002F` | CSS, multi-line C-style |\n| `\u003C!-- @citadel: {action} {description} -->` | HTML, Markdown |\n\n**Action-to-skill mapping:**\n\n| Action | Skill |\n|---|---|\n| `review` | `\u002Freview` |\n| `test` | `\u002Ftest-gen` |\n| `fix` | `\u002Fsystematic-debugging` |\n| `document` | `\u002Fdoc-gen` |\n| `refactor` | `\u002Frefactor` |\n| `todo` | intake item |\n\nUnknown actions become intake items with the action preserved as metadata.\n\n**Deduplication:** Every marker gets a stable identity hash: sha256 over `{file path}`, `{action}`, and the normalized marker text (trimmed, internal whitespace collapsed), joined with NUL separators and truncated to 16 hex chars. Line numbers are excluded, so the hash survives line shifts. `processedMarkers` is a map keyed by this hash with `{file, action, firstSeen, lastSeen}`; `pendingActions` uses the same hash keys. Both maps are pruned past 500 entries by dropping the oldest `lastSeen`. Markers whose hash is already in `processedMarkers` are skipped.\n\n#### Step 4: Classify unmarked changes\n\n| File pattern | Auto-action |\n|---|---|\n| `*.test.*`, `*.spec.*`, `__tests__\u002F*` | Queue: \"run tests\" intake item |\n| `*.md` in `docs\u002F` or project root | Queue: \"doc staleness check\" intake item |\n| `src\u002F**\u002F*.ts`, `src\u002F**\u002F*.tsx` | Queue: \"changed source\" intake item |\n| `package.json`, `tsconfig.json` | Queue: \"config change\" intake item (high priority) |\n\n#### Step 5: Dispatch markers\n\nFor each new marker:\n1. `\u002Fdo {action} in {file} at line {line}: {description}`\n2. Log dispatch, add to `processedMarkers`, increment `stats.skillsDispatched`\n\n**Batch limit:** Dispatch at most 5 per scan. Queue overflow in `pendingActions`.\n\n#### Step 6: Write intake items\n\nFilename: `watch-{action}-{file slug}-{epoch ms}.md` in `.planning\u002Fintake\u002F`\n\n```markdown\n---\ntitle: \"{action} {file}:{line}\"\nstatus: pending\npriority: normal\ntarget: {file path}\nsource: watch\nmarker_hash: {16-char marker hash}\n---\n\nMarker comment found at {file}:{line}:\n`{raw marker line}`\n\n{description, if any}\n```\n\nBefore writing, skip the item if the marker hash is already in `processedMarkers`, or if any existing `.planning\u002Fintake\u002F*.md` carries a matching `marker_hash` in its frontmatter. Every new intake item records its `marker_hash` so future scans (and concurrent processes) can detect it.\n\n#### Step 7: Update state\n\n- `lastScanCommit`: `git rev-parse HEAD`\n- `lastScanTime`: current ISO timestamp\n- Increment `stats.scansRun`, `stats.markersFound`\n- Update `pendingActions` and `processedMarkers`\n\n#### Step 8: Report\n\nManual scan:\n```\nScan complete.\n  Files changed:      {N}\n  Markers found:      {new} ({total} total)\n  Actions dispatched: {N} (batch limit: 5)\n  Intake items:       {N} written to .planning\u002Fintake\u002F\n  Pending actions:    {N}\n```\n\nCron poll: silent.\n\n---\n\n## Integration Points\n\n- **Intake pipeline:** Writes to `.planning\u002Fintake\u002F` for `\u002Fautopilot`.\n- **Intent router:** Routes markers through `\u002Fdo` — never invokes skills directly.\n- **Daemon:** `\u002Fdaemon` can start a watch alongside a campaign.\n\n---\n\n## Fringe Cases\n\n**`.planning\u002F` missing:** Create on first scan.\n**Not a git repo:** Fall back to timestamp detection; warn once.\n**No files changed:** Update stats, exit silently.\n**Unknown action:** Treat as intake item, preserve raw action.\n**Deleted file:** Skip marker scanning; write intake item noting deletion.\n**Large diff (100+ files):** Cap at 50 per scan, queue rest.\n**Binary files:** Skip during marker scanning.\n**Corrupted state:** Reset to defaults, preserve `processedMarkers` if readable.\n**CronCreate not available:** Warn and suggest manual `\u002Fwatch scan`.\n**Scan overlap:** Scans serialize through a lock directory (`.planning\u002Fwatch-state.json.lock`, acquired via atomic `mkdir` with ~10 retries at 100ms). Each scan records `scanStartedAt` and `scanPid` in state under the lock, and holds the lock across the state read-modify-write and intake writes. A scan that cannot acquire the lock and finds another scan started under 60 seconds ago logs a skip notice and exits cleanly. A lock older than 30 seconds (by mtime) is treated as stale and removed.\n**Marker removed:** Stale `processedMarkers` entries age out via the 500-entry oldest-`lastSeen` prune.\n\n---\n\n## Contextual Gates\n\n**Disclosure:** \"Starting file watch on [paths]. Triggers [skill] on change. Stop with Ctrl+C.\"\n**Reversibility:** amber — runs sentinel that triggers other skills on file change; triggered skills may modify files; stop with Ctrl+C and run `\u002Fwatch stop`\n**Trust gates:**\n- Any: start watch and view scan reports\n- Familiar (5+ sessions): triggered skills run autonomously on file change; novices should use with caution and review dispatched actions\n\n## Quality Gates\n\n- Scan completes in under 10 seconds for repos up to 100K lines\n- No duplicate intake items for the same file and classification\n- No re-dispatched already-processed markers\n- Batch limit of 5 dispatches per scan enforced\n- State file updated atomically at end of scan\n- Works on Windows, macOS, Linux (Node.js fs + git CLI)\n- CronCreate failure does not leave watch in inconsistent state\n\n## Exit Protocol\n\n- **`\u002Fwatch start`:** Output confirmation block. No HANDOFF.\n- **`\u002Fwatch stop`:** Output stop summary with lifetime stats.\n- **`\u002Fwatch scan` (manual):** Output scan report with counts.\n- **`\u002Fwatch scan` (cron):** Silent.\n- **`\u002Fwatch status`:** Output current state.\n- **On error:** Clear message with fix. Never leave cron running if state is inconsistent.\n","# \u002Fwatch -- File Sentinel\n\nUse when the user wants automatic reactions to file changes or marker comments (`@citadel:`).\nDo NOT use for one-off file inspection or tasks that need human judgment per file.\n\n## Orientation\n\n**Use when:** running a file sentinel that triggers a skill or command automatically when watched paths change.\n**Don't use when:** monitoring CI status on a PR (use \u002Fpr-watch); running a one-shot verification check (use \u002Fverify).\n\n## Default execution path (READ FIRST)\n\n**`\u002Fwatch start` does NOT call `CronCreate` by default.** Only pass `--remote`\nto use Anthropic's routine system, and only after explicit user confirmation.\n`CronCreate` counts against the **15 routine runs \u002F 24h** cap — at the default\n5-minute interval a watch exhausts the quota in under an hour.\n\n### Default flow — `\u002Fwatch start` (no `--remote` flag)\n1. Do Steps 1 and 2 below (check existing watch, determine baseline commit).\n2. **Skip Step 3** — do NOT call `CronCreate`. Leave `cronId: null` in state.\n3. Write the state file (Step 4) with `status: \"watching\"`.\n4. Output:\n   ```\n   Watch state created: .planning\u002Fwatch-state.json\n     Baseline: {commit hash, first 7 chars}\n\n   To start real-time watching, run in a separate terminal:\n     npm run watch:local\n\n   For cloud-persistent polling (machine off, user away):\n     \u002Fwatch start --remote     (uses CronCreate, counts against 15\u002Fday cap)\n   ```\n\n### Opt-in routine flow — `\u002Fwatch start --remote`\nOnly when `--remote` is explicitly passed:\n1. Confirm: \"This will use `CronCreate`, which counts against your 15 routine\n   runs \u002F 24h quota. At a 5-minute interval this exhausts the quota in under\n   an hour. Continue? (y\u002FN)\"\n2. On confirmation, run the full Step 1–5 protocol including `CronCreate`.\n\n## Commands\n\n| Command | Behavior |\n|---|---|\n| `\u002Fwatch start` | Default: create state, prompt user to run `npm run watch:local` |\n| `\u002Fwatch start --remote` | Use `CronCreate` polling (counts against 15\u002Fday quota — requires confirmation) |\n| `\u002Fwatch start --interval {N}m` | Set poll interval for `--remote` mode (default: 5m) |\n| `\u002Fwatch stop` | Stop watching, tear down cron |\n| `\u002Fwatch status` | Show watch state, last scan time, pending actions |\n| `\u002Fwatch scan` | Run a single scan now (manual trigger) |\n\n## Protocol\n\n### \u002Fwatch start\n\n#### Step 1: Check for existing watch\n\n1. Read `.planning\u002Fwatch-state.json` if it exists\n2. If `status` is `\"watching\"`:\n   - Show current state: last scan time, interval, pending actions count\n   - Ask: \"A watch is already active. Stop it and start a new one?\"\n   - If yes: run `\u002Fwatch stop` first, then continue\n   - If no: abort\n\n#### Step 2: Determine baseline commit\n\n1. Run `git rev-parse HEAD` to get the current commit hash\n2. If not a git repo: fall back to timestamp-based detection (store current\n   time as `lastScanTime`, skip commit-based diffing)\n3. Store this as `lastScanCommit`\n\n#### Step 3: Create poll schedule (--remote only)\n\n```\nCronCreate:\n  interval: \"{N}m\"  (default: 5m)\n  command: \"\u002Fwatch scan\"\n```\n\nSave the cron ID in the state file.\n\n#### Step 4: Write state file\n\nWrite `.planning\u002Fwatch-state.json`:\n\n```json\n{\n  \"status\": \"watching\",\n  \"lastScanCommit\": \"abc1234\",\n  \"lastScanTime\": null,\n  \"interval\": \"5m\",\n  \"cronId\": \"{id from step 3 or null}\",\n  \"pendingActions\": {},\n  \"processedMarkers\": {},\n  \"stats\": {\n    \"scansRun\": 0,\n    \"markersFound\": 0,\n    \"intakeItemsCreated\": 0,\n    \"skillsDispatched\": 0\n  }\n}\n```\n\n#### Step 5: Confirm (--remote only)\n\n```\nWatch started.\n  Interval:  every {N}m\n  Baseline:  {commit hash, first 7 chars}\n  State:     .planning\u002Fwatch-state.json\n```\n\n---\n\n### \u002Fwatch stop\n\n1. Read `.planning\u002Fwatch-state.json`. If missing or not `\"watching\"`: \"No watch is active.\"\n2. `CronDelete: {cronId}` — if cronId is missing or deletion fails, continue.\n3. Update state: `\"status\": \"stopped\", \"cronId\": null`. Preserve all other fields.\n4. Output:\n   ```\n   Watch stopped.\n     Scans completed:      {stats.scansRun}\n     Markers found:        {stats.markersFound}\n     Intake items created: {stats.intakeItemsCreated}\n     Skills dispatched:    {stats.skillsDispatched}\n   ```\n\n---\n\n### \u002Fwatch status\n\n1. If `.planning\u002Fwatch-state.json` missing: \"No watch configured. Use `\u002Fwatch start` to begin.\"\n2. Output state fields: status, lastScanTime, lastScanCommit, interval, pendingActions.length, stats.\n3. If `pendingActions` non-empty, list each: `[{action}] {file}:{line} -- {description}`\n\n---\n\n### \u002Fwatch scan\n\n#### Step 1: Load state\n\n1. Read `.planning\u002Fwatch-state.json`\n2. If missing: create default state with `lastScanCommit` from `git rev-parse HEAD` and `status: \"watching\"`.\n\n#### Step 2: Detect changed files\n\n**Git mode (primary):**\n1. `git diff --name-only {lastScanCommit} HEAD` (committed changes)\n2. `git diff --name-only` (unstaged) and `git diff --name-only --cached` (staged)\n3. Merge and deduplicate all three lists\n\n**Fallback mode (no git):**\n1. `find . -newer {timestamp_file} -type f`\n2. Exclude `node_modules\u002F`, `.git\u002F`, `.planning\u002F`, `dist\u002F`, `build\u002F`\n\nIf no files changed: update `lastScanTime` and `stats.scansRun`, exit early.\n\n#### Step 3: Scan for marker comments\n\nSearch changed files for:\n\n| Pattern | Languages |\n|---|---|\n| `\u002F\u002F @citadel: {action} {description}` | JS, TS, Go, Rust, C, Java |\n| `# @citadel: {action} {description}` | Python, Shell, YAML, Ruby |\n| `\u002F* @citadel: {action} {description} *\u002F` | CSS, multi-line C-style |\n| `\u003C!-- @citadel: {action} {description} -->` | HTML, Markdown |\n\n**Action-to-skill mapping:**\n\n| Action | Skill |\n|---|---|\n| `review` | `\u002Freview` |\n| `test` | `\u002Ftest-gen` |\n| `fix` | `\u002Fsystematic-debugging` |\n| `document` | `\u002Fdoc-gen` |\n| `refactor` | `\u002Frefactor` |\n| `todo` | intake item |\n\nUnknown actions become intake items with the action preserved as metadata.\n\n**Deduplication:** Every marker gets a stable identity hash: sha256 over `{file path}`, `{action}`, and the normalized marker text (trimmed, internal whitespace collapsed), joined with NUL separators and truncated to 16 hex chars. Line numbers are excluded, so the hash survives line shifts. `processedMarkers` is a map keyed by this hash with `{file, action, firstSeen, lastSeen}`; `pendingActions` uses the same hash keys. Both maps are pruned past 500 entries by dropping the oldest `lastSeen`. Markers whose hash is already in `processedMarkers` are skipped.\n\n#### Step 4: Classify unmarked changes\n\n| File pattern | Auto-action |\n|---|---|\n| `*.test.*`, `*.spec.*`, `__tests__\u002F*` | Queue: \"run tests\" intake item |\n| `*.md` in `docs\u002F` or project root | Queue: \"doc staleness check\" intake item |\n| `src\u002F**\u002F*.ts`, `src\u002F**\u002F*.tsx` | Queue: \"changed source\" intake item |\n| `package.json`, `tsconfig.json` | Queue: \"config change\" intake item (high priority) |\n\n#### Step 5: Dispatch markers\n\nFor each new marker:\n1. `\u002Fdo {action} in {file} at line {line}: {description}`\n2. Log dispatch, add to `processedMarkers`, increment `stats.skillsDispatched`\n\n**Batch limit:** Dispatch at most 5 per scan. Queue overflow in `pendingActions`.\n\n#### Step 6: Write intake items\n\nFilename: `watch-{action}-{file slug}-{epoch ms}.md` in `.planning\u002Fintake\u002F`\n\n```markdown\n---\ntitle: \"{action} {file}:{line}\"\nstatus: pending\npriority: normal\ntarget: {file path}\nsource: watch\nmarker_hash: {16-char marker hash}\n---\n\nMarker comment found at {file}:{line}:\n`{raw marker line}`\n\n{description, if any}\n```\n\nBefore writing, skip the item if the marker hash is already in `processedMarkers`, or if any existing `.planning\u002Fintake\u002F*.md` carries a matching `marker_hash` in its frontmatter. Every new intake item records its `marker_hash` so future scans (and concurrent processes) can detect it.\n\n#### Step 7: Update state\n\n- `lastScanCommit`: `git rev-parse HEAD`\n- `lastScanTime`: current ISO timestamp\n- Increment `stats.scansRun`, `stats.markersFound`\n- Update `pendingActions` and `processedMarkers`\n\n#### Step 8: Report\n\nManual scan:\n```\nScan complete.\n  Files changed:      {N}\n  Markers found:      {new} ({total} total)\n  Actions dispatched: {N} (batch limit: 5)\n  Intake items:       {N} written to .planning\u002Fintake\u002F\n  Pending actions:    {N}\n```\n\nCron poll: silent.\n\n---\n\n## Integration Points\n\n- **Intake pipeline:** Writes to `.planning\u002Fintake\u002F` for `\u002Fautopilot`.\n- **Intent router:** Routes markers through `\u002Fdo` — never invokes skills directly.\n- **Daemon:** `\u002Fdaemon` can start a watch alongside a campaign.\n\n---\n\n## Fringe Cases\n\n**`.planning\u002F` missing:** Create on first scan.\n**Not a git repo:** Fall back to timestamp detection; warn once.\n**No files changed:** Update stats, exit silently.\n**Unknown action:** Treat as intake item, preserve raw action.\n**Deleted file:** Skip marker scanning; write intake item noting deletion.\n**Large diff (100+ files):** Cap at 50 per scan, queue rest.\n**Binary files:** Skip during marker scanning.\n**Corrupted state:** Reset to defaults, preserve `processedMarkers` if readable.\n**CronCreate not available:** Warn and suggest manual `\u002Fwatch scan`.\n**Scan overlap:** Scans serialize through a lock directory (`.planning\u002Fwatch-state.json.lock`, acquired via atomic `mkdir` with ~10 retries at 100ms). Each scan records `scanStartedAt` and `scanPid` in state under the lock, and holds the lock across the state read-modify-write and intake writes. A scan that cannot acquire the lock and finds another scan started under 60 seconds ago logs a skip notice and exits cleanly. A lock older than 30 seconds (by mtime) is treated as stale and removed.\n**Marker removed:** Stale `processedMarkers` entries age out via the 500-entry oldest-`lastSeen` prune.\n\n---\n\n## Contextual Gates\n\n**Disclosure:** \"Starting file watch on [paths]. Triggers [skill] on change. Stop with Ctrl+C.\"\n**Reversibility:** amber — runs sentinel that triggers other skills on file change; triggered skills may modify files; stop with Ctrl+C and run `\u002Fwatch stop`\n**Trust gates:**\n- Any: start watch and view scan reports\n- Familiar (5+ sessions): triggered skills run autonomously on file change; novices should use with caution and review dispatched actions\n\n## Quality Gates\n\n- Scan completes in under 10 seconds for repos up to 100K lines\n- No duplicate intake items for the same file and classification\n- No re-dispatched already-processed markers\n- Batch limit of 5 dispatches per scan enforced\n- State file updated atomically at end of scan\n- Works on Windows, macOS, Linux (Node.js fs + git CLI)\n- CronCreate failure does not leave watch in inconsistent state\n\n## Exit Protocol\n\n- **`\u002Fwatch start`:** Output confirmation block. No HANDOFF.\n- **`\u002Fwatch stop`:** Output stop summary with lifetime stats.\n- **`\u002Fwatch scan` (manual):** Output scan report with counts.\n- **`\u002Fwatch scan` (cron):** Silent.\n- **`\u002Fwatch status`:** Output current state.\n- **On error:** Clear message with fix. Never leave cron running if state is inconsistent.\n",{"name":5,"license":15,"description":7,"auto-trigger":39,"last-updated":43,"user-invocable":44,"trigger_keywords":45},1774742400000,true,[5,46,47,48,49,50,51,52,53,54],"watch files","watch changes","file sentinel","monitor files","watch start","watch stop","watch scan","marker comments","@citadel","main","e1a3d45697a0f323aaedcbea0ab6a0387b5fe722","skills\u002Fwatch",84,"https:\u002F\u002Fgithub.com\u002FSethGammon\u002FCitadel","https:\u002F\u002Fsethgammon.github.io\u002FCitadel\u002F","https:\u002F\u002Fgithub.com\u002FSethGammon","Building Citadel, the repo-local operating layer for Claude Code and Codex. Persistent work, routing, verification, and coordinated agent fleets.","2026-11-13 02:05:13",{"source":8,"skillName":5,"commands":65,"prompt":75,"zipAvailable":44},[66,69,72],{"tool":67,"command":68},"npx","npx skills add SethGammon\u002FCitadel --skill watch",{"tool":70,"command":71},"bunx","bunx skills add SethGammon\u002FCitadel --skill watch",{"tool":73,"command":74},"pnpm","pnpm dlx skills add SethGammon\u002FCitadel --skill watch","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FSethGammon\u002FCitadel\u002Fblob\u002Fmain\u002Fskills\u002Fwatch\u002FSKILL.md",[77],{"path":78,"resourceType":79,"resourceTypeDesc":80,"sizeBytes":81,"sourceHtmlUrl":82},"__benchmarks__\u002Fmarker-dedup-two-scans.md","REFERENCE","参考文档",2175,"https:\u002F\u002Fgithub.com\u002FSethGammon\u002FCitadel\u002Fblob\u002Fmain\u002Fskills\u002Fwatch\u002F__benchmarks__\u002Fmarker-dedup-two-scans.md",[84,109,137,164,183,214],{"skillNo":85,"name":86,"nameZh":6,"description":87,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":88,"categories":89,"keywords":90,"publishedAt":38},"01M6GXSBKE9A2HQWF8QRAZ4WJ5","workspace","Multi-repo campaign coordinator. Same lifecycle as fleet -- scope claims, discovery relay, wave-based execution -- but the unit of work is a repo, not a file. Coordinates campaigns across repositories with shared context.","https:\u002F\u002Fgithub.com\u002FSethGammon\u002FCitadel\u002Fblob\u002Fmain\u002Fskills\u002Fworkspace\u002FSKILL.md",[],[86,91,92,93,94,95,96,97,98,99,100,101,102,21,103,104,105,106,107,108],"repos","snowflake","multi-repo","work","context","campaign","coordinate","but","across","campaigns","shared","as","scope","add","unit","coordinator","fleet","claims",{"skillNo":110,"name":111,"nameZh":6,"description":112,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":113,"categories":114,"keywords":120,"publishedAt":38},"01M6GXS873AD1Y6RTM6GV47F3E","wiki","Markdown-first knowledge base where the LLM acts as librarian. Ingests raw sources, compiles and interlinks topic files, self-maintains an index. No vector DB or embeddings required -- uses LLM-native navigation over structured markdown up to ~400K words.","https:\u002F\u002Fgithub.com\u002FSethGammon\u002FCitadel\u002Fblob\u002Fmain\u002Fskills\u002Fwiki\u002FSKILL.md",[115],{"categoryNo":116,"categoryName":117,"taxonomyType":118,"taxonomyTypeDesc":119},"01JBSEEDCAT000000000000021","资料检索与研究","SCENARIO","使用场景",[111,121,122,123,124,125,126,127,128,129,27,130,131,132,133,134,102,21,135,136],"vector","embeddings","librarian","markdown","knowledge","karpathy","knowledge management","karpathy wiki","up","maintain","self","knowledge base","project","db","sources","native",{"skillNo":138,"name":139,"nameZh":6,"description":140,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":141,"categories":142,"keywords":146,"publishedAt":38},"01M6GXRXHYJ5RD9RZP81S2VFSW","verify","Self-test the Citadel hook pipeline from within a live session. Exercises real tool calls (Write, Edit, Bash, Read) and checks that hooks fired, telemetry accumulated, and no errors occurred. Reports HOOK HEALTH: PASS or HOOK HEALTH: FAIL with a per-hook breakdown.","https:\u002F\u002Fgithub.com\u002FSethGammon\u002FCitadel\u002Fblob\u002Fmain\u002Fskills\u002Fverify\u002FSKILL.md",[143],{"categoryNo":144,"categoryName":145,"taxonomyType":118,"taxonomyTypeDesc":119},"01JBSEEDCAT000000000000008","测试与质量",[139,147,148,149,150,151,152,153,154,155,156,157,158,131,159,21,160,161,162,163],"citadel","telemetry","harness","pipeline","Bash","tool","read","write","edit","session","hooks","reports","live","hook","errors","calls","true",{"skillNo":165,"name":166,"nameZh":6,"description":167,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":168,"categories":169,"keywords":170,"publishedAt":38},"01M6GXRS9MCJFMQZ917H2XH3AC","unharness","Safely leave Citadel using the active adoption receipt. Produces a no-write, reviewable plan, preserves a portable archive, removes only exact owned material, and reports modified or externally registered surfaces as retained or unknown. Legacy installs must be imported before exact leave is claimed.","https:\u002F\u002Fgithub.com\u002FSethGammon\u002FCitadel\u002Fblob\u002Fmain\u002Fskills\u002Funharness\u002FSKILL.md",[],[166,171,147,149,172,173,174,175,176,129,158,177,178,179,180,181,102,21,182,163],"uninstall","adoption","plan","archive","remove","only","Legacy","surfaces","clean","imported","active","material",{"skillNo":184,"name":185,"nameZh":6,"description":186,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":187,"categories":188,"keywords":197,"publishedAt":38},"01M6GXRMEJ2VHD43R9FNPD4910","triage","GitHub issue and PR investigator. Pulls open issues\u002FPRs, classifies them, searches the codebase for root cause or reviews contributed code, proposes fixes with file:line references, and optionally implements fixes. Use for investigating GitHub issues and reviewing PRs; do NOT use for general code review unrelated to GitHub issues.","https:\u002F\u002Fgithub.com\u002FSethGammon\u002FCitadel\u002Fblob\u002Fmain\u002Fskills\u002Ftriage\u002FSKILL.md",[189,192],{"categoryNo":190,"categoryName":191,"taxonomyType":118,"taxonomyTypeDesc":119},"01JBSEEDCAT000000000000007","代码审查",{"categoryNo":193,"categoryName":194,"taxonomyType":195,"taxonomyTypeDesc":196},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[185,198,199,200,201,202,203,204,205,206,207,208,209,210,26,21,211,212,213,163],"code review","review","review pr","pr","code","github issues","issues","open","use","unrelated","issue","general","references","line","investigator","reviews",{"skillNo":215,"name":216,"nameZh":6,"description":217,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":218,"categories":219,"keywords":221,"publishedAt":38},"01M6GXRG4QPGKHVCJPRCEWBW2S","test-gen","Generate and verify tests — happy path, edge cases, error paths — using the project's own framework and patterns","https:\u002F\u002Fgithub.com\u002FSethGammon\u002FCitadel\u002Fblob\u002Fmain\u002Fskills\u002Ftest-gen\u002FSKILL.md",[220],{"categoryNo":144,"categoryName":145,"taxonomyType":118,"taxonomyTypeDesc":119},[222,223,224,225,226,227,139,154,228,229,133,230,231,104,232,163,233],"test","test gen","write tests","generate","framework","edge","generate tests","patterns","tests","paths","error","path"]