diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index 193ac80..c67faaa 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -83,7 +83,11 @@ Plans:
3. Daily report includes Jira ticket status for active sessions via ngn-jira skill
4. Sessions inactive >30d are archived to JSON with no data loss (export-before-delete verified)
5. Archive script supports dry-run mode for safe initial testing before activating cron
-**Plans**: TBD
+**Plans**: 2 plans
+
+Plans:
+- [ ] 08-01-PLAN.md — Archive script + daily report cron (Wave 1)
+- [ ] 08-02-PLAN.md — Weekly stale summary + archive cron (Wave 2)
### Phase 9: Tooling & Portable Setup
**Goal**: Custom Docker image with essential platform engineering tools (AWS CLI, Terraform, Helm, kubectl, Datadog CLI) and a portable setup script that can configure a fresh machine with all ngn-agent config in one invocation
@@ -108,5 +112,5 @@ Plans:
| 5. Hindsight Memory Provider | v1.1 | 1/1 | Complete | 2026-06-14 |
| 6. Default Repos & SSH Mount | v1.1 | 1/1 | Complete | 2026-06-14 |
| 7. Main Session Skill | v1.1 | 1/1 | Complete | 2026-06-14 |
-| 8. Cron Reporting | v1.1 | 0/TBD | Not started | - |
+| 8. Cron Reporting | v1.1 | 0/2 | Not started | - |
| 9. Tooling & Portable Setup | v1.1 | 0/TBD | Not started | - |
diff --git a/.planning/phases/08-cron-reporting/08-01-PLAN.md b/.planning/phases/08-cron-reporting/08-01-PLAN.md
new file mode 100644
index 0000000..c468eb2
--- /dev/null
+++ b/.planning/phases/08-cron-reporting/08-01-PLAN.md
@@ -0,0 +1,242 @@
+---
+phase: 08-cron-reporting
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - ~/.hermes/scripts/archive-stale-sessions.sh
+ - ~/.hermes/archive/sessions/
+autonomous: true
+requirements:
+ - CRON-01
+ - CRON-02
+user_setup: []
+must_haves:
+ truths:
+ - "Archive stale sessions script exists at ~/.hermes/scripts/archive-stale-sessions.sh and is executable"
+ - "Archive directory exists at ~/.hermes/archive/sessions/"
+ - "Daily report cron job 'ngn-daily-report' is registered and fires at 09:00 SGT"
+ - "Daily report loads both 'session' and 'jira-query' skills"
+ artifacts:
+ - path: "~/.hermes/scripts/archive-stale-sessions.sh"
+ provides: "Deterministic no_agent script for session export + prune"
+ min_lines: 35
+ must_contain:
+ - "DRY_RUN"
+ - "hermes sessions export"
+ - "hermes sessions prune --older-than 30 --yes"
+ - path: "~/.hermes/archive/sessions/"
+ provides: "Archive storage directory for stale session JSONL exports"
+ - path: "Hermes cron DB (job: ngn-daily-report)"
+ provides: "Daily report cron job entry in Hermes cron scheduler"
+ key_links:
+ - from: "archive-stale-sessions.sh"
+ to: "hermes sessions export"
+ via: "CLI call with positional output path"
+ pattern: "hermes sessions export"
+ - from: "archive-stale-sessions.sh"
+ to: "hermes sessions prune"
+ via: "CLI call with --older-than 30 --yes"
+ pattern: "hermes sessions prune --older-than 30 --yes"
+ - from: "ngn-daily-report cron job"
+ to: "session skill"
+ via: "--skill session in cron create"
+ pattern: "skill session"
+ - from: "ngn-daily-report cron job"
+ to: "jira-query skill"
+ via: "--skill jira-query in cron create"
+ pattern: "skill jira-query"
+---
+
+
+Create the archive script and register the daily report cron job.
+
+**Purpose:** Lay the foundation for CRON-02 (stale session archive script) and implement CRON-01 + CRON-03 (daily report with Jira integration). The archive script is created with a dry-run toggle so the first archive fires in safe mode (export only, no prune). The daily report cron is a skill-backed job that loads the session and jira-query skills to enumerate active sessions, post Jira progress comments, and deliver a Telegram summary.
+
+**Output:**
+- `~/.hermes/scripts/archive-stale-sessions.sh` — no_agent archive script with DRY_RUN toggle
+- `~/.hermes/archive/sessions/` — archive storage directory
+- Hermes cron job `ngn-daily-report` registered in gateway scheduler
+
+
+
+@/Users/bapung/.config/opencode/gsd-core/workflows/execute-plan.md
+@/Users/bapung/.config/opencode/gsd-core/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/REQUIREMENTS.md
+@.planning/phases/08-cron-reporting/08-CONTEXT.md
+@.planning/phases/08-cron-reporting/08-RESEARCH.md
+@/Users/bapung/.hermes/config.yaml
+@/Users/bapung/.hermes/skills/ngn-agent/session/SKILL.md
+@/Users/bapung/.hermes/skills/ngn-agent/jira/SKILL.md
+@.planning/phases/07-main-session-skill/07-01-SUMMARY.md
+
+
+
+
+
+ Task 1: Create archive-stale-sessions.sh with dry-run toggle + archive directory
+
+ ~/.hermes/scripts/archive-stale-sessions.sh
+ ~/.hermes/archive/sessions/
+
+
+ Create the archive directory and the no_agent archive script.
+
+ **Archive directory:**
+ ```bash
+ mkdir -p ~/.hermes/archive/sessions
+ ```
+
+ **Script file:** Create `~/.hermes/scripts/archive-stale-sessions.sh` with:
+ - `DRY_RUN=true` at top — safe default (export only, skip prune)
+ - `set -euo pipefail` for strict error handling
+ - `ARCHIVE_DIR="$HOME/.hermes/archive/sessions"` — archive location per D-12
+ - `mkdir -p "$ARCHIVE_DIR"` — ensure directory exists
+ - `TIMESTAMP=$(date +%Y%m%d_%H%M%S)` for unique filenames
+ - `OUTPUT_FILE="$ARCHIVE_DIR/sessions-${TIMESTAMP}.jsonl"` — date-stamped per D-11 corrected (export uses positional output arg, no `--output` flag — per RESEARCH.md Pitfall 1 and CLI verification)
+ - Step 1: `hermes sessions export "$OUTPUT_FILE"` — exports ALL sessions (no `--older-than` flag on export — per D-11 correction and RESEARCH.md §Critical finding)
+ - Step 2: Only if `$DRY_RUN = false`, run `hermes sessions prune --older-than 30 --yes` (uses `--yes` not `--confirm` — confirmed by RESEARCH.md Pitfall 2 and `hermes sessions prune --help`)
+ - Step 3: `hermes sessions stats` — show post-archive store state
+ - Echo progress messages for Telegram delivery (stdout is delivered verbatim via `--deliver telegram`)
+ - Print summary with session count and file size
+
+ **Critical corrections from research (must NOT repeat CONTEXT.md errors):**
+ - ❌ `hermes sessions export --older-than 30d --output ` — DOES NOT EXIST. Use positional path only.
+ - ❌ `hermes sessions prune --confirm` — DOES NOT EXIST. Use `--yes`.
+ - ✅ Export all, prune separate: `hermes sessions export && hermes sessions prune --older-than 30 --yes`
+
+ After creating the file, make it executable:
+ ```bash
+ chmod +x ~/.hermes/scripts/archive-stale-sessions.sh
+ ```
+
+ **Dry-run verification:** Script's default mode is export-only (DRY_RUN=true). User manually reviews the first JSONL export before setting DRY_RUN=false to enable pruning. This satisfies the user's discretion requirement for safe initial testing.
+
+
+
+ test -f ~/.hermes/scripts/archive-stale-sessions.sh \
+ && test -x ~/.hermes/scripts/archive-stale-sessions.sh \
+ && test -d ~/.hermes/archive/sessions \
+ && grep -q 'DRY_RUN' ~/.hermes/scripts/archive-stale-sessions.sh \
+ && grep -q 'hermes sessions export' ~/.hermes/scripts/archive-stale-sessions.sh \
+ && grep -q 'hermes sessions prune --older-than 30' ~/.hermes/scripts/archive-stale-sessions.sh \
+ && grep -q '--yes' ~/.hermes/scripts/archive-stale-sessions.sh \
+ && grep -q 'set -euo pipefail' ~/.hermes/scripts/archive-stale-sessions.sh
+
+
+
+ Archive script exists at ~/.hermes/scripts/archive-stale-sessions.sh, is executable, and contains:
+ - DRY_RUN=true toggle (safe default)
+ - Export with date-stamped filename
+ - Prune with `--older-than 30 --yes` (gated behind dry-run)
+ - `set -euo pipefail` error handling
+ - Progress echo statements for Telegram delivery
+ Archive directory exists at ~/.hermes/archive/sessions/
+
+
+
+
+ Task 2: Register daily report cron job (09:00 SGT, skill-backed)
+ (Hermes cron DB — internal state)
+
+ Register the daily report cron job using `hermes cron create`.
+
+ **Command (execute verbatim):**
+ ```bash
+ hermes cron create \
+ --name "ngn-daily-report" \
+ --deliver telegram \
+ --skill session \
+ --skill jira-query \
+ "0 9 * * *" \
+ "Daily operational report — generate summary of active sessions and update their Jira tickets:
+
+ 1. DISCOVER ACTIVE SESSIONS: Run 'hermes sessions export -' to get all sessions as JSONL. Use python3 to parse the JSONL and find sessions with last_active within the last 7 days — these are the active sessions. For each active session, record its id, title, and last_active timestamp. (Note: 'hermes sessions list' has NO --json flag — use export - for machine-readable output per RESEARCH.md Pitfall 3.)
+
+ 2. FIND JIRA TICKETS: For each active session, use hindsight_recall with query 'session summary jira' to find the Jira ticket key from the session summary saved by Phase 7's session skill (Step 7, tier: session-summary). If hindsight_recall returns no results, search session messages from the export output for Jira ticket key patterns (PLATFORM-, AIOPS-, etc.) — per RESEARCH.md Pitfall 4. One session may have multiple Jira tickets (1-to-many mapping — D-04).
+
+ 3. UPDATE JIRA TICKETS: For each active session with found Jira ticket key(s), add a progress comment via:
+ ngn-jira POST '/rest/api/3/issue//comment' --body '{\"body\": \"Session activity update — Date: , Last active: . Session: . Progress: See session transcript for details.\"}'
+ IMPORTANT: Do NOT transition ticket statuses (D-05). Only add comments. Do NOT update tickets for stale sessions (D-15).
+
+ 4. COMPOSE TELEGRAM SUMMARY: Create a structured message with sections:
+ 📋 ACTIVE SESSIONS — list each with title, last active timestamp
+ 🔄 JIRA UPDATED — list of ticket keys that received new comments
+ Keep within Telegram's 4096 character limit.
+ Do NOT include stale session info or ticket status transitions.
+
+ 5. DELIVERY: Your response is automatically delivered to TELEGRAM_HOME_CHANNEL via --deliver telegram."
+ ```
+
+ **Key details (per locked decisions):**
+ - Per D-01: Schedule `0 9 * * *` = daily at 09:00 SGT. Timezone is already SGT (+08, confirmed by date +%Z) — no TZ config needed per RESEARCH.md Pitfall 5.
+ - Per D-06: `--deliver telegram` sends to TELEGRAM_HOME_CHANNEL (474440517)
+ - Per D-17: Skill-backed cron (two `--skill` flags, no `--no-agent`)
+ - Per D-18: Loads `session` skill (Phase 7) for session structure + `jira-query` skill (Phase 4) for Jira comment patterns
+ - Per D-04/D-14: Updates ALL Jira tickets linked to active sessions (1-to-many mapping)
+ - Per D-05/D-16: Comments only, no status transitions
+ - Per D-07: Report includes active sessions list, last activity timestamps, Jira ticket keys updated
+
+
+
+ hermes cron list 2>&1 | grep -q ngn-daily-report
+
+
+
+ Daily report cron job `ngn-daily-report` is registered with:
+ - Schedule: `0 9 * * *` (daily at 09:00)
+ - Delivery: telegram (TELEGRAM_HOME_CHANNEL)
+ - Skills: session + jira-query (skill-backed, no no-agent)
+ - Prompt instructs agent to: enumerate active sessions via JSONL export, find Jira tickets via hindsight_recall, add progress comments via ngn-jira, compose Telegram summary
+
+
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|----------|-------------|
+| Cron agent → Hermes session DB | Cron LLM agent reads session data (ids, titles, timestamps, transcripts) to compose reports |
+| Cron agent → Jira Cloud API | Cron LLM agent writes progress comments to Jira tickets via ngn-jira script |
+| no_agent script → Hermes session DB | Archive script reads (export) and writes (prune) session store directly |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Disposition | Mitigation Plan |
+|-----------|----------|-----------|-------------|-----------------|
+| T-08-01 | Tampering | Daily report cron prompt | accept | Cron prompt is authored during plan execution and stored in Hermes cron DB — not user-controllable. No injection risk. |
+| T-08-02 | Information Disclosure | Archive JSONL files | mitigate | Archive directory (`~/.hermes/archive/sessions/`) is under `~/.hermes/` which has same protection as Hermes data dir. No world-readable permissions. |
+| T-08-03 | Information Disclosure | Cron output to Telegram | accept | Report summaries sent to user's DM channel only (TELEGRAM_HOME_CHANNEL). No PII in summaries — session IDs and Jira keys only. |
+| T-08-04 | Tampering | Session prune via no_agent script | mitigate | DRY_RUN=true by default — no prune until user explicitly sets false. Also `approvals.cron_mode: deny` in config adds an approval gate for destructive cron operations per RESEARCH.md Security Domain. |
+| T-08-05 | Elevation of Privilege | Jira comment injection | accept | Cron agent uses ngn-jira script which authenticates with JIRA_EMAIL + JIRA_API_TOKEN — bounded to the user's Jira permissions. Comments only, no transitions per D-05/D-16. |
+| T-08-SC | Tampering | Package installs | n/a | No packages installed — all tools are native Hermes CLI or existing scripts. |
+
+
+
+1. `test -f ~/.hermes/scripts/archive-stale-sessions.sh && test -x ~/.hermes/scripts/archive-stale-sessions.sh` — script exists and is executable
+2. `test -d ~/.hermes/archive/sessions` — archive directory exists
+3. `grep -q DRY_RUN ~/.hermes/scripts/archive-stale-sessions.sh` — dry-run toggle present
+4. `grep -q 'hermes sessions export' ~/.hermes/scripts/archive-stale-sessions.sh && grep -q 'hermes sessions prune --older-than 30 --yes' ~/.hermes/scripts/archive-stale-sessions.sh` — correct CLI commands used
+5. `hermes cron list 2>&1 | grep -q ngn-daily-report` — daily report cron job registered
+6. `hermes cron run ngn-daily-report 2>&1` — test-run succeeds (schedules for immediate execution; verify no errors)
+
+
+
+- Archive script at ~/.hermes/scripts/archive-stale-sessions.sh is executable with DRY_RUN=true default
+- Archive directory at ~/.hermes/archive/sessions/ exists
+- Daily report cron job `ngn-daily-report` is registered and visible in `hermes cron list`
+- Daily report uses correct schedule (0 9 * * *), delivery (telegram), and skills (session + jira-query)
+- Test-run of daily report cron job completes without CLI errors
+
+
+
diff --git a/.planning/phases/08-cron-reporting/08-02-PLAN.md b/.planning/phases/08-cron-reporting/08-02-PLAN.md
new file mode 100644
index 0000000..335baf4
--- /dev/null
+++ b/.planning/phases/08-cron-reporting/08-02-PLAN.md
@@ -0,0 +1,201 @@
+---
+phase: 08-cron-reporting
+plan: 02
+type: execute
+wave: 2
+depends_on:
+ - 08-01
+files_modified: []
+autonomous: true
+requirements:
+ - CRON-02
+ - CRON-03
+user_setup: []
+must_haves:
+ truths:
+ - "Weekly stale summary cron 'ngn-weekly-stale-summary' fires at Sunday 20:00 SGT"
+ - "Weekly archive cron 'ngn-weekly-archive' fires at Sunday 20:05 SGT (5 min after summary)"
+ - "Weekly stale summary loads 'session' skill for understanding session structure"
+ - "Weekly archive runs as no_agent (script archive-stale-sessions.sh, no LLM cost)"
+ - "Archive cron schedule is offset by 5 min to avoid race condition with summary"
+ artifacts:
+ - path: "Hermes cron DB (job: ngn-weekly-stale-summary)"
+ provides: "Weekly stale session summary cron entry"
+ - path: "Hermes cron DB (job: ngn-weekly-archive)"
+ provides: "Weekly archive cron entry"
+ key_links:
+ - from: "ngn-weekly-stale-summary cron"
+ to: "session skill"
+ via: "--skill session in cron create"
+ pattern: "skill session"
+ - from: "ngn-weekly-archive cron"
+ to: "archive-stale-sessions.sh"
+ via: "--script archive-stale-sessions.sh --no-agent"
+ pattern: "archive-stale-sessions"
+ - from: "ngn-weekly-archive cron"
+ to: "ngn-weekly-stale-summary cron"
+ via: "5 minute offset (20:05 vs 20:00) to avoid race condition"
+ pattern: "5 20"
+---
+
+
+Register two weekly cron jobs: stale session summary and stale session archive.
+
+**Purpose:** Implement CRON-02's weekly archive (complete the stale session lifecycle) and CRON-03's stale session Jira awareness (weekly summary mentions Jira ticket keys for stale sessions). The weekly summary is skill-backed (uses session skill to understand session structure and hindsight for Jira-session mapping discovery). The weekly archive is no_agent (deterministic export + prune via the script created in Plan 1). The 5-minute offset between summary (20:00) and archive (20:05) prevents a race condition where the prune removes sessions the summary agent is still enumerating — per RESEARCH.md Anti-Patterns.
+
+**Output:**
+- Hermes cron job `ngn-weekly-stale-summary` registered (Sunday 20:00 SGT)
+- Hermes cron job `ngn-weekly-archive` registered (Sunday 20:05 SGT)
+
+
+
+@/Users/bapung/.config/opencode/gsd-core/workflows/execute-plan.md
+@/Users/bapung/.config/opencode/gsd-core/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/REQUIREMENTS.md
+@.planning/phases/08-cron-reporting/08-CONTEXT.md
+@.planning/phases/08-cron-reporting/08-RESEARCH.md
+@/Users/bapung/.hermes/config.yaml
+@/Users/bapung/.hermes/skills/ngn-agent/session/SKILL.md
+@/Users/bapung/.hermes/skills/ngn-agent/jira/SKILL.md
+@.planning/phases/07-main-session-skill/07-01-SUMMARY.md
+@.planning/phases/08-cron-reporting/08-01-SUMMARY.md
+
+
+
+
+
+ Task 1: Register weekly stale session summary cron (Sunday 20:00 SGT, skill-backed)
+ (Hermes cron DB — internal state)
+
+ Register the weekly stale session summary cron job using `hermes cron create`.
+
+ **Command (execute verbatim):**
+ ```bash
+ hermes cron create \
+ --name "ngn-weekly-stale-summary" \
+ --deliver telegram \
+ --skill session \
+ "0 20 * * 0" \
+ "Weekly stale session summary — report on sessions inactive for more than 30 days:
+
+ 1. FIND STALE SESSIONS: Run 'hermes sessions export -' to get all sessions as JSONL. Use python3 to parse and filter for sessions where last_active is more than 30 days ago. For each stale session, record its id, title, and last_active timestamp. (Note: 'hermes sessions list' has NO --json flag — use export - for machine-readable output per RESEARCH.md Pitfall 3.)
+
+ 2. DISCOVER JIRA TICKETS: For each stale session, use hindsight_recall with query 'session summary jira' to find the Jira ticket key from the session summary (saved by Phase 7 session skill, Step 7, tier: session-summary). If hindsight_recall returns no results, search session messages for Jira key patterns (PLATFORM-, AIOPS-, etc.).
+
+ 3. REPORT ONLY — NO JIRA MUTATIONS: Do NOT add comments to Jira tickets. Do NOT transition ticket statuses. Only report their keys in the summary (per D-10 and D-15).
+
+ 4. COMPOSE TELEGRAM SUMMARY: Create a structured message with sections:
+ ⏳ STALE SESSIONS (>30d inactive) — list each with:
+ - Session title
+ - Last activity date
+ - Linked Jira ticket key (or 'no ticket')
+ Keep within Telegram's 4096 character limit. Do not include active sessions.
+
+ 5. DELIVERY: Your response is automatically delivered to TELEGRAM_HOME_CHANNEL via --deliver telegram."
+ ```
+
+ **Key details (per locked decisions):**
+ - Per D-02: Schedule `0 20 * * 0` = Sunday at 20:00 SGT. Timezone is already SGT (+08) — no TZ config needed.
+ - Per D-08: Summarizes inactive sessions (>30d since last activity) with session title, last activity date, linked Jira ticket
+ - Per D-09: Delivered via Telegram to user's DM channel
+ - Per D-10/D-15: No auto-comments on stale tickets — only reported in summary
+ - Per D-17: Skill-backed cron (one `--skill session`, no `--no-agent`)
+ - Uses `--skill session` (Phase 7) for understanding session structure and transaction patterns
+
+
+
+ hermes cron list 2>&1 | grep -q ngn-weekly-stale-summary
+
+
+
+ Weekly stale summary cron `ngn-weekly-stale-summary` is registered with:
+ - Schedule: `0 20 * * 0` (Sunday at 20:00 SGT)
+ - Delivery: telegram (TELEGRAM_HOME_CHANNEL)
+ - Skill: session (skill-backed, no no-agent)
+ - Prompt instructs agent to: enumerate stale sessions via JSONL export, discover Jira ticket keys via hindsight_recall, compose Telegram summary without Jira mutations
+
+
+
+
+ Task 2: Register weekly archive cron (Sunday 20:05 SGT, no_agent)
+ (Hermes cron DB — internal state)
+
+ Register the weekly stale session archive cron job using `hermes cron create` with `--no-agent` and `--script` flags.
+
+ **Command (execute verbatim):**
+ ```bash
+ hermes cron create \
+ --name "ngn-weekly-archive" \
+ --deliver telegram \
+ --script archive-stale-sessions.sh \
+ --no-agent \
+ "5 20 * * 0"
+ ```
+
+ **Key details (per locked decisions):**
+ - Per D-03 (corrected): Schedule `5 20 * * 0` = Sunday at 20:05 SGT — 5 minutes after the stale summary. This prevents a race condition where the prune might delete sessions the summary agent is still enumerating (per RESEARCH.md Anti-Pattern: Race condition on same schedule).
+ - Per D-11 (corrected): Uses native Hermes commands (`hermes sessions export` + `hermes sessions prune --older-than 30 --yes`). The script was created in Plan 1 (08-01) with all research corrections applied (no --older-than on export, --yes on prune).
+ - Per D-12: Archive files stored in `~/.hermes/archive/sessions/sessions-.jsonl`
+ - Per D-13: Runs weekly after the stale summary report
+ - Per D-19: Uses `--no-agent` for deterministic CLI operations (no LLM cost)
+ - The script at `~/.hermes/scripts/archive-stale-sessions.sh` was created in Plan 1 and has `DRY_RUN=true` by default (export only, skip prune). User must manually verify the first export and set DRY_RUN=false to enable pruning.
+
+
+
+ hermes cron list 2>&1 | grep -q ngn-weekly-archive
+
+
+
+ Weekly archive cron `ngn-weekly-archive` is registered with:
+ - Schedule: `5 20 * * 0` (Sunday at 20:05 SGT — 5 min after summary to avoid race condition)
+ - Delivery: telegram (stdout of archive-stale-sessions.sh delivered verbatim)
+ - Mode: --no-agent (no LLM cost)
+ - Script: archive-stale-sessions.sh (from ~/.hermes/scripts/, created in Plan 1)
+
+
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|----------|-------------|
+| Cron agent → Hermes session DB | Weekly summary LLM agent reads session data (ids, titles, timestamps) to identify stale sessions |
+| no_agent script → Hermes session DB | Archive script reads (export) and writes (prune) session store directly with CLI permissions |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Disposition | Mitigation Plan |
+|-----------|----------|-----------|-------------|-----------------|
+| T-08-06 | Tampering | Weekly stale summary cron prompt | accept | Prompt is authored during plan execution and stored in Hermes cron DB — not user-controllable. No injection risk. |
+| T-08-07 | Repudiation | Stale session prune | mitigate | Archive script's DRY_RUN=true default means no prune until user flips the flag. All exports produce timestamped JSONL files that serve as audit trail of which sessions existed before pruning. |
+| T-08-08 | Denial of Service | Race condition: summary + archive at same time | mitigate | Archive scheduled at 20:05 (5 min after summary at 20:00) — prevents prune-from-underneath during summary enumeration per RESEARCH.md §Anti-Patterns to Avoid. |
+| T-08-SC | Tampering | Package installs | n/a | No packages installed — all tools are native Hermes CLI or existing scripts. |
+
+
+
+1. `hermes cron list 2>&1 | grep -q ngn-weekly-stale-summary` — weekly summary cron job registered
+2. `hermes cron list 2>&1 | grep -q ngn-weekly-archive` — weekly archive cron job registered
+3. `hermes cron list 2>&1 | grep 'ngn-weekly-stale-summary' | grep -q '0 20 \* \* 0'` — correct schedule (Sunday 20:00 SGT)
+4. `hermes cron list 2>&1 | grep 'ngn-weekly-archive' | grep -q '5 20 \* \* 0'` — correct schedule (Sunday 20:05 SGT)
+5. `hermes cron run ngn-weekly-stale-summary 2>&1` — test-run succeeds (schedules for immediate execution; verify no CLI errors)
+6. `hermes cron run ngn-weekly-archive 2>&1` — test-run succeeds (schedules for immediate execution; verify no CLI errors)
+
+
+
+- Weekly stale summary cron `ngn-weekly-stale-summary` registered with schedule `0 20 * * 0`, delivery telegram, skill session
+- Weekly archive cron `ngn-weekly-archive` registered with schedule `5 20 * * 0`, delivery telegram, no_agent mode, script archive-stale-sessions.sh
+- Both jobs visible in `hermes cron list` with correct schedules
+- Archive cron runs 5 min AFTER summary cron to prevent race condition (per Research Anti-Pattern)
+- Test-run of both cron jobs completes without CLI errors
+
+
+