From 63230edf4d773e4c4ed64bf89a3461d439e4e762 Mon Sep 17 00:00:00 2001 From: Bagas Purwa Sentika Date: Mon, 15 Jun 2026 22:42:04 +0800 Subject: [PATCH] docs(08): resolve open questions in RESEARCH.md --- .../phases/08-cron-reporting/08-RESEARCH.md | 603 ++++++++++++++++++ 1 file changed, 603 insertions(+) create mode 100644 .planning/phases/08-cron-reporting/08-RESEARCH.md diff --git a/.planning/phases/08-cron-reporting/08-RESEARCH.md b/.planning/phases/08-cron-reporting/08-RESEARCH.md new file mode 100644 index 0000000..f14f194 --- /dev/null +++ b/.planning/phases/08-cron-reporting/08-RESEARCH.md @@ -0,0 +1,603 @@ +# Phase 8: Cron Reporting - Research + +**Researched:** 2026-06-15 +**Domain:** Hermes cron scheduling, session lifecycle management, Telegram delivery, Jira integration via cron +**Confidence:** HIGH + +## Summary + +Phase 8 implements daily operational reporting and weekly stale session lifecycle management using Hermes' built-in cron and session management capabilities. Three cron jobs are needed: a daily report (skill-backed), a weekly stale summary (skill-backed), and a weekly archive (no_agent). All use `hermes cron create` with appropriate flags. + +**Critical finding:** The CONTEXT.md assumptions about `hermes sessions export` flags are incorrect — `--older-than` and `--output` do not exist on the export command. The export takes a positional output path and supports only `--source` and `--session-id` filters. The archive script must compensate by exporting all sessions and relying on the separate `prune --older-than 30` step to remove old ones. + +**Primary recommendation:** Two skill-backed cron jobs (daily report 09:00 SGT, weekly summary Sunday 20:00 SGT) plus one no_agent script cron (archive Sunday 20:05 SGT). The Jira-session mapping is not a direct DB field — the skill-backed cron agents use `hindsight_recall` or `session_search` to discover ticket keys from session summaries stored by Phase 7's session skill. + + +## User Constraints (from CONTEXT.md) + +### Locked Decisions +- **D-01:** Daily report runs at 09:00 SGT (UTC+8) — Hermes cron job using `hermes cron create` +- **D-02:** Weekly stale session summary runs Sunday 20:00 SGT (UTC+8) — separate cron job +- **D-03:** Weekly stale session archive runs Sunday 20:00 SGT (UTC+8) — same cron job as summary (archive after summary) +- **D-04:** Daily report updates all Jira tickets linked to active sessions with last activity and progress +- **D-05:** Do NOT update Jira ticket statuses — only add comments with session progress/state +- **D-06:** Daily report delivered via Telegram to user's DM channel +- **D-07:** Report includes: list of active sessions, last activity timestamp, Jira ticket keys updated +- **D-08:** Weekly report summarizes all inactive sessions (>30d since last activity) +- **D-09:** Delivered via Telegram to user's DM channel +- **D-10:** No auto-comments on stale tickets — only reported in the summary +- **D-11:** Use native Hermes commands for archive (export + prune) — no custom scripts for core logic +- **D-12:** Archive location: `~/.hermes/archive/sessions/` +- **D-13:** Run weekly Sunday 20:00 SGT, after the stale summary report +- **D-14:** Active sessions with linked Jira tickets get daily progress comments +- **D-15:** Stale sessions with Jira tickets are mentioned in the weekly summary only +- **D-16:** Ticket status transitions only happen when user explicitly asks +- **D-17:** Both cron jobs use Hermes skill-backed cron +- **D-18:** The daily report skill references the existing session skill and ngn-jira skill +- **D-19:** The stale archive step uses `no_agent: true` mode + +### the agent's Discretion +- Report formatting: Telegram message structure (sections, bullet points) — planner can design based on Telegram's 4096-char limit +- Cron job naming: Standard naming convention for the two cron jobs +- Dry-run phase: First archive run should be manual (export then review JSONL before enabling prune) + +### Deferred Ideas (OUT OF SCOPE) +- Auto-close tickets for archived sessions — user wants manual control +- Teams gateway delivery — only Telegram for now +- Custom archive dashboard — JSONL files are searchable via grep/FTS5 + + + +## Phase Requirements + +| ID | Description | Research Support | +|----|-------------|------------------| +| CRON-01 | Daily session summary report delivered via Telegram at 09:00 | `hermes cron create --deliver telegram --skill session --skill jira-query "0 9 * * *"` — verified working. Daily report prompt guides agent to list active sessions, query Jira, compose Telegram message. | +| CRON-02 | Stale session auto-archive (30d inactivity) — export to JSON, delete from live DB | `hermes sessions export ` + `hermes sessions prune --older-than 30 --yes`. Export lacks `--older-than` filter, so archive script exports all sessions. Prune correctly filters by age. Verified working. | +| CRON-03 | Daily report includes Jira ticket status via ngn-jira skill | `ngn-jira` script available at `~/.hermes/scripts/ngn-jira`. Jira-session mapping lives in hindsight memory (session summaries) and session message content, not as a direct DB field. | + + +## Architectural Responsibility Map + +| Capability | Primary Tier | Secondary Tier | Rationale | +|------------|-------------|----------------|-----------| +| Daily report scheduling | Cron scheduler (Hermes gateway) | — | `hermes cron create` registers the job; Hermes gateway process fires it on schedule | +| Active session discovery | Cron agent (LLM) | Session DB (SQLite) | Skill-backed cron agent runs `hermes sessions list` or `hermes sessions export -` to enumerate sessions and determine active/stale via `last_active` timestamps | +| Jira comment posting | Cron agent (LLM) | `ngn-jira` script | Agent crafts `ngn-jira POST '/rest/api/3/issue//comment'` for each active session with a Jira link | +| Telegram message delivery | Hermes cron delivery system | — | `--deliver telegram` sends cron agent's response to TELEGRAM_HOME_CHANNEL (474440517) | +| Stale session archive | no_agent script (bash) | Hermes CLI | `--no-agent` mode runs `~/.hermes/scripts/archive-stale-sessions.sh` which calls `hermes sessions export` + `hermes sessions prune` directly | +| Jira-session mapping lookup | Hindsight memory / session_search | Session message content | No direct `jira_key` field in session DB; mapping is discovered through hindsight recall of session summaries or searching session messages | + +## Standard Stack + +### Core +| Library/Tool | Version | Purpose | Why Standard | +|-------------|---------|---------|--------------| +| `hermes cron create` | v0.16.0 | Register scheduled cron jobs | Built-in — no external scheduler needed | +| `hermes sessions export` | v0.16.0 | Export sessions to JSONL backup | Native Hermes command, writes full session data (messages included) | +| `hermes sessions prune` | v0.16.0 | Delete old sessions from live DB | Native Hermes command with `--older-than` and `--yes` flags | +| `hermes sessions list` | v0.16.0 | List recent sessions with last active times | Native Hermes command, human-readable table output | +| `ngn-jira` script | — | Jira Cloud API wrapper (GET/POST/PUT/DELETE) | Already exists in `~/.hermes/scripts/`, used by Phase 7 session skill | + +### Supporting +| Library/Tool | Version | Purpose | When to Use | +|-------------|---------|---------|-------------| +| `session` skill (Phase 7) | v1.0.0 | Session lifecycle definitions and hindsight session summaries | Daily and weekly cron jobs load this to understand session structure and Jira mapping | +| `jira-query` skill (Phase 4) | v1.0.0 | Jira API query patterns | Daily report cron job loads this for Jira comment operations | +| `bash` (no_agent script) | system | Deterministic archive operations without LLM cost | Archive step only — pure CLI, no LLM involvement | +| `jq` (optional) | system | Filter JSONL for precise archive content | Optional — only needed if archive should contain only stale sessions (avoids dependency) | +| `date` command | system | Date-stamped archive filenames | `$(date +%Y%m%d)` for `sessions-20260615.jsonl` naming | + +### Alternatives Considered +| Instead of | Could Use | Tradeoff | +|------------|-----------|----------| +| Skill-backed cron | `--script` + `--no-agent` for daily report | Need LLM for composing natural-language reports and determining active vs stale from context | +| Separate cron jobs | Single cron with complex script | Cleaner separation of concerns — daily vs weekly have different scopes | +| Custom Jira field for session-ID | Hindsight + session_search lookup | Adding custom Jira fields requires admin access and changes existing workflow | + +**Installation:** +```bash +# No new packages needed — all tools are native Hermes CLI or existing scripts +# Create archive directory: +mkdir -p ~/.hermes/archive/sessions +``` + +**Version verification:** +```bash +hermes --version +# Hermes Agent v0.16.0 (2026.6.5) · upstream 78c11d99 + +# Verify ngn-jira script exists: +file ~/.hermes/scripts/ngn-jira +# Bourne-Again shell script, ASCII text + +# Verify hermes sessions commands available: +hermes sessions --help +hermes cron --help +``` + +## Package Legitimacy Audit + +> No external packages are installed in this phase. All dependencies are native Hermes CLI commands and existing scripts. + +| Package | Registry | Age | Downloads | Source Repo | Verdict | Disposition | +|---------|----------|-----|-----------|-------------|---------|-------------| +| (none) | — | — | — | — | — | No packages to audit | + +**Packages removed due to [SLOP] verdict:** none +**Packages flagged as suspicious [SUS]:** none + +## Architecture Patterns + +### System Architecture Diagram + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ HERMES GATEWAY PROCESS │ +│ (cron scheduler — checks every minute, fires due jobs) │ +└──────┬────────────────────────────────┬───────────────────────────┘ + │ │ + ▼ ▼ +┌────────────────┐ ┌──────────────────────────┐ +│ DAILY REPORT │ │ WEEKLY SUNDAY 20:00 SGT │ +│ 09:00 SGT │ │ │ +│ skill-backed │ ├─────────────────────┬────┤ +│ with session + │ │ Weekly Stale │ │ +│ jira-query │ │ Summary │ │ +│ skills │ │ (skill-backed) │ │ +└────────┬───────┘ └──────────┬────────────┘ │ + │ │ │ + ▼ ▼ ▼ +┌─────────────────┐ ┌────────────────┐ ┌─────────────────┐ +│ Agent loads │ │ Agent queries │ │ no_agent script │ +│ skills, runs: │ │ sessions list, │ │ runs: │ +│ │ │ finds stale >30d│ │ │ +│ 1. hermes │ │ identifies Jira │ │ 1. hermes │ +│ sessions list │ │ tickets (via │ │ sessions │ +│ → find active │ │ hindsight) │ │ export │ +│ sessions │ │ composes stale │ │ → archive │ +│ │ │ summary report │ │ │ +│ 2. hindsight │ │ sends Telegram │ │ 2. hermes │ +│ _recall → │ │ DM │ │ sessions │ +│ find Jira keys│ └────────────────┘ │ prune │ +│ for each │ │ --older-than │ +│ active session│ │ 30 --yes │ +│ │ └────────┬────────┘ +│ 3. ngn-jira POST │ │ +│ → add progress │ ▼ +│ comment on │ ┌─────────────────┐ +│ each Jira │ │ ~/.hermes/archive│ +│ ticket │ │ /sessions/ │ +│ │ │ sessions- │ +│ 4. sends │ │ 20260615.jsonl │ +│ Telegram DM │ └─────────────────┘ +│ summary │ +└─────────────────┘ + + TELEGRAM_HOME_CHANNEL (474440517) + ▲ ▲ + │ │ + Daily Report Weekly Summary + │ │ + ┌─────┴──────────────┴──────┐ + │ USER's TELEGRAM DM │ + └────────────────────────────┘ +``` + +### Recommended Project Structure + +No new project files needed — all configuration is in `~/.hermes/`: + +``` +~/.hermes/ +├── config.yaml # Already configured: TELEGRAM_HOME_CHANNEL, cron section +├── scripts/ +│ ├── ngn-jira # Existing — Jira Cloud API wrapper +│ ├── ngn-confluence # Existing +│ ├── ngn-bitbucket # Existing +│ ├── session-init.sh # Existing +│ └── archive-stale-sessions.sh # NEW — no_agent archive script +├── cron/ +│ └── output/ # Cron output logs (auto-managed by Hermes) +└── archive/ + └── sessions/ # NEW — stale session JSONL archives + └── sessions-20260615.jsonl # Named by date +``` + +### Pattern 1: Skill-Backed Cron Job Registration + +**What:** Register a cron job that loads one or more skills and instructs an LLM agent to perform a task on a schedule. + +**When to use:** Any recurring task that requires natural language composition, decision-making, or tool orchestration. + +**Example:** +```bash +# Register daily report (skill-backed) +hermes cron create \ + --name "ngn-daily-report" \ + --deliver telegram \ + --skill session \ + --skill jira-query \ + "0 9 * * *" \ + "Daily operational report: list all active sessions with their last activity timestamps using hermes sessions list, for each active session find its linked Jira ticket via hindsight_recall or message search, then add a progress comment via ngn-jira POST /rest/api/3/issue//comment, and finally compose a Telegram summary of all updated tickets and active sessions." + +# Register weekly stale summary (skill-backed) +hermes cron create \ + --name "ngn-weekly-stale-summary" \ + --deliver telegram \ + --skill session \ + "0 20 * * 0" \ + "Weekly stale session summary: run hermes sessions list to find all sessions, identify sessions where last activity is more than 30 days ago (stale), for each stale session find its linked Jira ticket via hindsight_recall, compose a summary report with session title, last activity date, and linked Jira ticket. Do NOT add Jira comments — only report in the summary. Send via Telegram." +``` + +**Verification:** +```bash +# List registered cron jobs +hermes cron list + +# Test-run a cron job +hermes cron run ngn-daily-report + +# Check cron scheduler status +hermes cron status +``` + +### Pattern 2: no_agent Deterministic Cron Script + +**What:** Register a cron job that runs a shell script without LLM involvement, delivering its stdout verbatim to the target. + +**When to use:** Deterministic CLI operations that don't need AI reasoning (backup, cleanup, export). + +**Example script (`~/.hermes/scripts/archive-stale-sessions.sh`):** +```bash +#!/bin/bash +# Archive stale sessions (inactive >30 days) and prune from live DB +set -euo pipefail + +ARCHIVE_DIR="$HOME/.hermes/archive/sessions" +mkdir -p "$ARCHIVE_DIR" +OUTPUT_FILE="$ARCHIVE_DIR/sessions-$(date +%Y%m%d).jsonl" + +echo "=== Stale Session Archive: $(date) ===" +echo "" + +echo "1. Exporting all sessions to $OUTPUT_FILE ..." +hermes sessions export "$OUTPUT_FILE" +export_exit=$? +echo " Export exit code: $export_exit" +echo " File size: $(wc -c < "$OUTPUT_FILE") bytes" +echo "" + +echo "2. Pruning sessions older than 30 days from live DB ..." +hermes sessions prune --older-than 30 --yes +prune_exit=$? +echo " Prune exit code: $prune_exit" +echo "" + +echo "3. Session store stats after archive:" +hermes sessions stats +echo "" + +if [ $export_exit -eq 0 ] && [ $prune_exit -eq 0 ]; then + echo "Archive completed successfully." +else + echo "Archive completed with warnings (see exit codes above)." +fi +``` + +**Registration:** +```bash +# Register weekly archive (no_agent, 5 min after stale summary to avoid race) +hermes cron create \ + --name "ngn-weekly-archive" \ + --deliver telegram \ + --script archive-stale-sessions.sh \ + --no-agent \ + "5 20 * * 0" +``` + +### Anti-Patterns to Avoid +- **Race condition on same schedule:** If weekly summary and archive run at exactly the same time, the prune might remove sessions while the summary is still being composed. Offset by at least 5 minutes (e.g., 20:00 summary, 20:05 archive). +- **Custom cron daemon:** Hermes already has a full cron implementation — no need for system cron, launchd, or external schedulers. +- **Storing Jira key as session metadata:** The session DB has no `jira_key` field. Attempting to add one would require modifying Hermes internals. Use hindsight memory for the mapping (as designed by Phase 7). + +## Don't Hand-Roll + +| Problem | Don't Build | Use Instead | Why | +|---------|-------------|-------------|-----| +| Scheduling recurring tasks | Custom cron/launchd/service | `hermes cron create` | Hermes gateway manages scheduling, delivery, retries, and logging natively | +| Session backup/export | Custom SQLite query tool | `hermes sessions export` | Native JSONL export includes complete session data (messages, tokens, costs) | +| Session cleanup | Custom deletion script | `hermes sessions prune` | Handles DB vacuuming and FTS index maintenance after deletion | +| Telegram message delivery | Custom Telegram bot | `--deliver telegram` | Uses existing TELEGRAM_HOME_CHANNEL config and channel_directory | +| Jira API integration | Custom HTTP calls | `ngn-jira` script | Existing script handles auth, base URL, error handling | + +**Key insight:** Every tool needed for this phase already exists in the Hermes ecosystem. The phase is about composing them through cron job registration, not building new components. The only new file is the `archive-stale-sessions.sh` script. + +## Common Pitfalls + +### Pitfall 1: Export lacks `--older-than` filter +**What goes wrong:** CONTEXT.md specifies `hermes sessions export --older-than 30d --output ` but this flag does not exist. +**Why it happens:** The export command only supports `--source` and `--session-id` filters. +**How to avoid:** The no_agent archive script exports ALL sessions to the archive file, then the separate `hermes sessions prune --older-than 30 --yes` step removes only old ones from the live DB. This is safe — the archive is a snapshot and extra data in it is harmless. +**Warning signs:** `hermes: error: unrecognized arguments: --older-than` + +### Pitfall 2: Prune uses `--yes` not `--confirm` +**What goes wrong:** CONTEXT.md specifies `hermes sessions prune --older-than 30d --confirm` but the correct flag is `--yes` (or `-y`). +**How to avoid:** Use `hermes sessions prune --older-than 30 --yes` in the no_agent script. Without `--yes`, the command prompts for confirmation and blocks in no_agent mode. +**Warning signs:** Cron job hangs waiting for confirmation input. + +### Pitfall 3: `hermes sessions list` does NOT support `--json` +**What goes wrong:** The agent expects JSON output to parse session data programmatically. +**How to avoid:** For the skill-backed cron, the agent runs `hermes sessions list` and parses the human-readable table. Alternatively, use `hermes sessions export -` which outputs JSONL and can be parsed. The export includes all session metadata including `last_active`, `source`, `title`, `message_count`. +**Warning signs:** `hermes: error: unrecognized arguments: --json` + +### Pitfall 4: Jira-session mapping not in session DB +**What goes wrong:** The cron agent cannot find which Jira tickets belong to which sessions by querying the session DB. +**Why it happens:** Hermes session DB has no `jira_key` field. The mapping is recorded only in hindsight memory (session summaries saved by Phase 7's session skill) and in session message content. +**How to avoid:** The skill-backed cron agents should use `hindsight_recall` with query "session summary" to find Jira ticket keys, or use `session_search` tool to search session transcripts for ticket key patterns like `PLATFORM-123`. +**Warning signs:** Empty Jira update section in daily report. + +### Pitfall 5: Cron schedule interpretation (UTC vs local) +**What goes wrong:** Cron expression `0 9 * * *` might be interpreted as UTC if the system timezone is not configured. +**Why it happens:** Hermes cron uses system timezone by default. With `timezone: ''` in config, it falls back to system default. +**How to avoid:** System already in SGT (UTC+8, confirmed via `date +%Z` → `+08`). The created test cron showed "Next run: 2026-06-16T09:00:00+08:00" confirming correct timezone handling. No TZ configuration needed. +**Warning signs:** Cron fires at wrong hour (e.g., 01:00 UTC instead of 09:00 SGT). + +### Pitfall 6: Cron job naming — must use unique name +**What goes wrong:** Hermes cron jobs are identified by name or ID. Using duplicate names may cause confusion. +**How to avoid:** Use descriptive unique names: `ngn-daily-report`, `ngn-weekly-stale-summary`, `ngn-weekly-archive`. +**Warning signs:** `hermes cron list` shows unexpected jobs. + +## Code Examples + +### Command 1: Create the daily report cron job +```bash +# Daily at 09:00 SGT — report on active sessions, update Jira tickets +hermes cron create \ + --name "ngn-daily-report" \ + --deliver telegram \ + --skill session \ + --skill jira-query \ + "0 9 * * *" \ + "Daily operational report: run hermes sessions list to find all active sessions (updated today or yesterday), for each active session search for its linked Jira ticket key using hindsight_recall with query 'session summary jira' or message search, then add a progress comment to each Jira ticket via ngn-jira POST /rest/api/3/issue//comment with the last activity timestamp and session summary, and finally compose a Telegram summary listing: active sessions with last activity, Jira ticket keys updated, and session titles." +``` +**Verified:** `hermes cron create --deliver telegram` successfully creates jobs with correct timezone handling. [VERIFIED: Hermes CLI v0.16.0] + +### Command 2: Create the weekly stale summary cron job +```bash +# Sunday 20:00 SGT — stale session summary (no Jira comments) +hermes cron create \ + --name "ngn-weekly-stale-summary" \ + --deliver telegram \ + --skill session \ + "0 20 * * 0" \ + "Weekly stale session summary: run hermes sessions list to find all sessions where last activity is more than 30 days ago. For each stale session, find the linked Jira ticket key via hindsight_recall if available. Compose a Telegram summary listing: session title, last activity date, linked Jira ticket. Do NOT add comments to Jira tickets — only report in the summary." +``` +**Verified:** Cron schedule `0 20 * * 0` = Sunday at 20:00. [VERIFIED: conventional cron expression] + +### Command 3: Create the weekly archive cron job (no_agent) +```bash +# Create archive directory first +mkdir -p ~/.hermes/archive/sessions + +# Register no_agent archive (5 min after summary to avoid race condition) +hermes cron create \ + --name "ngn-weekly-archive" \ + --deliver telegram \ + --script archive-stale-sessions.sh \ + --no-agent \ + "5 20 * * 0" +``` +**Verified:** `--no-agent` flag skips LLM entirely. Script must be under `~/.hermes/scripts/`. Stdout delivered verbatim. [VERIFIED: Hermes CLI v0.16.0] + +### Command 4: Create the archive script +**File:** `~/.hermes/scripts/archive-stale-sessions.sh` +```bash +#!/bin/bash +# Archive stale sessions (inactive >30 days) and prune from live DB +# This script runs weekly via hermes cron with --no-agent +# Stdout is delivered to Telegram via --deliver telegram +set -euo pipefail + +ARCHIVE_DIR="$HOME/.hermes/archive/sessions" +mkdir -p "$ARCHIVE_DIR" +TIMESTAMP=$(date +%Y%m%d_%H%M%S) +OUTPUT_FILE="$ARCHIVE_DIR/sessions-${TIMESTAMP}.jsonl" + +echo "=== Stale Session Archive ===" +echo "Started: $(date)" +echo "" + +echo "[1/3] Exporting session store..." +hermes sessions export "$OUTPUT_FILE" +echo " -> $(wc -l < "$OUTPUT_FILE") sessions exported" +echo " -> Size: $(du -h "$OUTPUT_FILE" | cut -f1)" +echo "" + +echo "[2/3] Pruning sessions older than 30 days..." +hermes sessions prune --older-than 30 --yes +echo " Done." +echo "" + +echo "[3/3] Post-archive stats:" +hermes sessions stats +echo "" + +echo "✓ Archive complete." +``` +**Verified:** `--older-than 30` takes integer days (not `30d`), `--yes` skips interactive confirmation. [VERIFIED: Hermes CLI v0.16.0] + +### Command 5: List, test-run, and manage cron jobs +```bash +# List all cron jobs +hermes cron list + +# Test-run a specific job (fires immediately) +hermes cron run ngn-daily-report + +# Check if gateway/cron scheduler is running +hermes cron status +# → "Gateway is running — cron jobs will fire automatically" + +# Remove a cron job (if needed) +hermes cron remove ngn-daily-report + +# Edit an existing cron job (add/remove skills, change schedule) +hermes cron edit ngn-daily-report --add-skill jira-query +hermes cron edit ngn-daily-report --schedule "30 9 * * *" +``` +**Verified:** All commands tested against Hermes CLI v0.16.0. [VERIFIED: Hermes CLI v0.16.0] + +### Command 6: Session management operations +```bash +# List all sessions +hermes sessions list + +# Export all sessions to stdout (JSONL format) +hermes sessions export - + +# Export to file +hermes sessions export /tmp/my-sessions.jsonl + +# Show session store statistics +hermes sessions stats + +# Filter sessions by source +hermes sessions list --source telegram +hermes sessions export telegram_sessions.jsonl --source telegram + +# Prune old sessions (with confirmation prompt) +hermes sessions prune --older-than 30 +# Without confirmation (for no_agent scripts): +hermes sessions prune --older-than 30 --yes +``` +**Verified:** All commands tested. Export uses positional output arg, not `--output`. List does not support `--json`. Prune uses `--yes` not `--confirm`. [VERIFIED: Hermes CLI v0.16.0] + +### Command 7: Post-archive verification (dry-run procedure) +```bash +# Step 1: Manual export (dry-run — inspect before enabling prune) +hermes sessions export ~/.hermes/archive/sessions/sessions-$(date +%Y%m%d)-dryrun.jsonl + +# Step 2: Review the exported JSONL +wc -l ~/.hermes/archive/sessions/sessions-*-dryrun.jsonl +# Check for sensitive data, verify format + +# Step 3: Check which sessions would be pruned (no --dry-run flag on prune) +# Use export + filter to preview: +hermes sessions export - | python3 -c " +import sys, json, time +now = time.time() +for line in sys.stdin: + if line.strip(): + s = json.loads(line) + age_days = (now - s['last_active']) / 86400 + if age_days > 30: + print(f'WOULD PRUNE: {s[\"id\"]} — last active {age_days:.0f}d ago') +" + +# Step 4: Only after review, enable prune in the cron script +``` + +## State of the Art + +| Old Approach | Current Approach | When Changed | Impact | +|--------------|------------------|--------------|--------| +| Manual session management | Automated cron-driven lifecycle | Phase 8 | Sessions auto-archived after 30d inactivity, no manual cleanup needed | +| No reporting | Daily Telegram summaries with Jira progress | Phase 8 | Regular operational visibility without user having to ask | +| Jira tickets created but not tracked | Daily progress comments on linked tickets | Phase 8 | Jira tickets reflect real session activity automatically | + +**Deprecated/outdated:** +- No deprecated approaches — this phase introduces new capabilities not previously available in the system. + +## Assumptions Log + +| # | Claim | Section | Risk if Wrong | +|---|-------|---------|---------------| +| A1 | `hindsight_recall` tool is available in Hermes cron agent context | Architecture Patterns — Jira-session mapping | Low — agent can fall back to `session_search` or export + grep for ticket key patterns | +| A2 | Phase 7 session skill successfully saved session summaries to hindsight with Jira ticket keys | Architecture Patterns | Medium — if session skill was not active for older sessions, those sessions have no hindsight record; daily report would miss their Jira links | +| A3 | 1-to-many session→Jira mapping (one session can update multiple Jira tickets) | Requirements — CRON-03 | Low — depends on how Phase 7 session skill records Jira tickets; the agent can update multiple tickets if it finds multiple keys | +| A4 | All sessions in DB have `ended_at` set (not just active sessions) | Patterns — archive script | Low — prune behavior depends on this; if active sessions have `ended_at: null`, prune may not delete them (which is correct) | + +## Open Questions (RESOLVED) + +1. **Does `hermes sessions prune --older-than 30` only delete sessions where `ended_at` is not null?** — RESOLVED: Plan 08-01 archive script uses `hermes sessions export` first (captures ALL sessions to JSONL), then runs `prune --older-than 30 --yes`. If prune only affects ended sessions, active-but-idle sessions are safely preserved. The export captures everything regardless, so no data loss either way. + +2. **Does `session_search` tool work in cron agent context?** — RESOLVED: Plan 08-01 daily report cron prompt instructs agent to use `hermes sessions export -` (JSONL to stdout) as the primary data source, with `hindsight_recall` for Jira-session mapping. No dependency on `session_search` availability in cron context. + +3. **How does the agent determine "active" vs "stale"?** — RESOLVED: The daily report prompt tells the agent to use `hermes sessions export -` and parse the JSONL output (via python3 in Docker) to filter sessions by `last_active` timestamp. Stale = `last_active` > 30 days. Active = `last_active` < 24h. + +## Environment Availability + +| Dependency | Required By | Available | Version | Fallback | +|------------|------------|-----------|---------|----------| +| `hermes` CLI | All cron jobs | ✓ | v0.16.0 | — | +| Hermes gateway | Cron scheduler | ✓ | Running (PID 3178) | — | +| `ngn-jira` script | Daily report Jira comments | ✓ | ~/.hermes/scripts/ngn-jira | — | +| `bash` | Archive script | ✓ | system | — | +| `date` command | Archive filename timestamp | ✓ | system | — | +| `mkdir` | Archive directory creation | ✓ | system | — | +| `jq` (optional) | JSONL filtering | ✗ | — | Use `python3 -c` or grep instead | +| `python3` | JSONL parsing (fallback) | ✓ | system | — | + +**Missing dependencies with no fallback:** None — all required tools are available. + +**Missing dependencies with fallback:** `jq` — the archive script and cron agents can use `python3 -c` for JSONL parsing instead. + +## Security Domain + +> `security_enforcement` not explicitly disabled; include security considerations. + +### Applicable ASVS Categories + +| ASVS Category | Applies | Standard Control | +|---------------|---------|-----------------| +| V2 Authentication | no | — | +| V3 Session Management | no | — | +| V4 Access Control | no | — | +| V5 Input Validation | no | — | +| V6 Cryptography | no | — | + +### Known Threat Patterns for Hermes Cron + +| Pattern | STRIDE | Standard Mitigation | +|---------|--------|---------------------| +| Cron job prompt injection | Tampering | Prompt is authored by the implementer and stored in Hermes config; not user-controllable | +| Archive file writes to home dir | Tampering | Script uses `$HOME/.hermes/archive/sessions/` — within protected Hermes data directory | +| Jira API token exposure via cron output | Information Disclosure | Cron output delivered to Telegram DM only (not logs). `wrap_response: true` in config may wrap output, reducing inline token leak risk | +| Unauthorized session deletion | Tampering | `approvals.cron_mode: deny` — destructive operations require manual approval in gateway; no_agent script's `prune --yes` should be reviewed for safety | + +### Specific Security Notes + +- **Cron output delivery:** `--deliver telegram` sends to TELEGRAM_HOME_CHANNEL (user's DM). Output is not stored in logs (cron output directory is empty by default). +- **Archive data sensitivity:** JSONL export includes full session transcripts — may contain AWS account IDs, project names, Jira ticket details. Archive files should have the same protection as the Hermes data directory (`~/.hermes/`). + +## Sources + +### Primary (HIGH confidence) +- **Hermes CLI v0.16.0 (local)** — All cron and session commands tested directly via terminal + - `hermes cron create --help` — full syntax with all flags + - `hermes sessions export --help` — confirmed no `--older-than` flag + - `hermes sessions prune --help` — confirmed `--older-than` and `--yes` flags + - `hermes sessions list --help` — confirmed table output only, no `--json` + - `hermes cron edit --help` — confirmed `--add-skill`, `--remove-skill`, `--agent`, `--no-agent` flags +- **Session JSONL export** — Full session schema inspected: `id`, `source`, `title`, `last_active`, `archived`, `message_count`, `messages`, `model`, token/cost data +- **System timezone** — Confirmed UTC+8 (SGT): `date +%Z` → `+08` +- **Cron job creation test** — Created test job with `--deliver telegram`, confirmed "Next run: 2026-06-16T09:00:00+08:00" + +### Secondary (MEDIUM confidence) +- **CONTEXT.md** — Locked decisions from user discussion (D-01 through D-19, discretion areas) +- **Phase 7 session skill** (`~/.hermes/skills/ngn-agent/session/SKILL.md`) — Jira ticket creation pattern, hindsight summary format +- **Phase 4 jira-query skill** (`~/.hermes/skills/ngn-agent/jira/SKILL.md`) — Jira API call patterns +- **`~/.hermes/config.yaml`** — `TELEGRAM_HOME_CHANNEL`, `cron:` section, `timezone:''` + +### Tertiary (LOW confidence) — None used + +## Metadata + +**Confidence breakdown:** +- Standard stack: HIGH — All tools verified directly against Hermes CLI +- Architecture: HIGH — Patterns confirmed by user decisions and system capabilities +- Pitfalls: HIGH — Each pitfall verified against actual CLI behavior vs CONTEXT.md assumptions + +**Research date:** 2026-06-15 +**Valid until:** 2026-07-15 (Hermes is actively developed; CLI flags may change in future releases)