Sightjack manages all runtime state under {baseDir}/.siren/.
This document describes what each directory/file does, who creates it, and how it flows through the session lifecycle.
.siren/
.gitignore # auto-managed by WriteGitIgnore / EnsureScanDir
config.yaml # project-scoped settings (Linear team/project, strictness, lang)
events/ # append-only event logs (JSONL, one file per session)
{sessionID}.jsonl # immutable event stream for a session
skills/
dmail-sendable/
SKILL.md # agent skill manifest (produces: specification, report)
dmail-readable/
SKILL.md # agent skill manifest (consumes: design-feedback, convergence)
inbox/ # incoming d-mails (feedback from downstream)
*.md
outbox/ # outgoing d-mails (specifications, reports)
*.md
archive/ # processed d-mails (permanent record)
index.jsonl # archive index (metadata of pruned/existing .md files)
*.md
insights/ # git-tracked insight ledger (per S0030)
shibito.md # Shibito revival warnings from scan
strictness.md # Per-cluster strictness estimates from scan
.run/ # ephemeral runtime data (per-session)
{sessionID}/
scan_result.json # cached ScanResult (WriteScanResult / scan --json)
classify.json # Claude Pass 1 output
cluster_*.json # Claude Pass 2 deep-scan per cluster
wave_*.json # Claude Pass 3 wave generation per cluster
waves_result.json # cached WavePlan (waves subcommand)
architect_*.json # Architect discussion response per wave
discuss_result.json # cached DiscussResult (discuss subcommand)
apply_*.json # Wave apply response per wave
apply_result.json # cached ApplyResult (apply subcommand)
scribe_*.json # Scribe ADR generation per wave
nextgen_*.json # Follow-up wave response per wave
nextgen_result.json # cached WavePlan (nextgen subcommand)
*_prompt.md # dry-run mode only
insights.lock # flock for concurrent InsightWriter access
Additionally, sightjack creates files outside .siren/:
docs/
adr/
NNNN-*.md # Architecture Decision Records (Scribe agent)
.siren/.gitignore (auto-managed by WriteGitIgnore):
events/
.run/
inbox/
outbox/
.otel.env
Individual entries are used instead of ignoring the parent .siren/ directory, so that insights/ remains git-tracked (per ADR S0030).
| Path | Git Status | Reason |
|---|---|---|
config.yaml |
Tracked | Project-level configuration (shared across clones) |
skills/ |
Ignored | Agent capability manifests (regenerated by sightjack init) |
archive/ |
Tracked | Permanent D-Mail audit trail; index.jsonl records metadata of pruned files |
insights/ |
Tracked | Semantic knowledge ledger — environment-independent (per S0030) |
.gitignore |
Tracked | Self-managed ignore rules |
events/ |
Ignored | Session-specific event logs (source of truth for state) |
.run/ |
Ignored | Ephemeral scan cache, Claude subprocess outputs, insights.lock |
inbox/ |
Ignored | Transient; consumed and archived by MonitorInbox |
outbox/ |
Ignored | Transient; courier (phonewave) picks up and delivers |
docs/adr/ |
Tracked | Immutable decision records |
Session state is derived from append-only event logs stored in .siren/events/{sessionID}.jsonl.
Each line is a JSON-encoded event with type, timestamp, session_id, sequence, and payload.
State is reconstructed by replaying all events via ProjectState(). There is no snapshot file;
the event log is the single source of truth. See ADR 0008 for design rationale.
Key functions:
LoadState(store)— replay events from a specific EventStoreLoadLatestState(baseDir)— find newest.jsonlin events/, replay to get current state
Each session creates a unique directory under .run/ containing all Claude subprocess outputs. Session ID format: session-{unixmilli}-{pid}.
| File Pattern | Claude Pass | Created By | Purpose |
|---|---|---|---|
scan_result.json |
— | WriteScanResult() |
Cached ScanResult for session resume |
classify.json |
Pass 1 | Claude subprocess | Cluster classification and issue grouping |
cluster_{NN}_{name}_c{NN}.json |
Pass 2 | Claude subprocess | Deep scan results per cluster (chunked) |
wave_{NN}_{name}.json |
Pass 3 | Claude subprocess | Generated waves per cluster |
waves_result.json |
— | waves subcommand |
Cached aggregated WavePlan for pipe replay |
architect_{name}_{waveID}.json |
— | RunArchitectDiscuss() |
Architect discussion response |
discuss_result.json |
— | discuss subcommand |
Cached DiscussResult for pipe replay |
apply_{name}_{waveID}.json |
— | RunWaveApply() |
Wave apply results (applied count, errors, ripples) |
apply_result.json |
— | apply subcommand |
Cached ApplyResult for pipe replay |
scribe_{name}_{waveID}.json |
— | RunScribeADR() |
Scribe ADR generation response |
nextgen_{name}_{waveID}.json |
— | GenerateNextWaves() |
Follow-up waves after completion |
nextgen_result.json |
— | nextgen subcommand |
Cached WavePlan for pipe replay |
*_prompt.md |
— | RunClaudeDryRun() |
Prompt files saved in --dry-run mode |
All {name} values are sanitized via sanitizeName() (scanner.go) to prevent path traversal.
The insights/ directory stores accumulated semantic knowledge from scan results. Files use YAML frontmatter with Markdown body and are git-tracked for cross-session and cross-developer persistence.
---
insight-schema-version: "1"
kind: shibito
tool: sightjack
updated_at: "2026-03-10T15:30:00+09:00"
entries: 2
---
## Insight: shibito-ENG045-ENG201
- **what**: Token circular dependency pattern re-emerged
- **why**: Issue ENG-045 was closed but pattern re-emerged in ENG-201
- **how**: Review the original fix. Consider structural prevention
- **when**: During scan, when closed issue patterns match current open issues
- **who**: sightjack scan (session-1710000000000-12345)
- **constraints**: Risk level: high
- **closed-issue-id**: ENG-045
- **current-issue-id**: ENG-201
| File | Kind | Content |
|---|---|---|
shibito.md |
shibito |
Shibito revival warnings — closed issue patterns resurfacing in current work |
strictness.md |
strictness |
Per-cluster estimated strictness levels with reasoning |
- Writes use flock-based locking via
insights.lockin.run/ - Atomic writes via temp-file + rename
- Title-based dedup ensures idempotent appends (same title = skip)
[downstream tool] sightjack [downstream tool]
| | |
| writes to inbox/ | |
|--------------------->| |
| | MonitorInbox() [fsnotify] |
| | receiveDMailIfNew() |
| | -> DrainInboxFeedback() |
| | |
| | RunConvergenceGateWithRedrain|
| | FilterConvergence() |
| | Notifier.Notify() [async] |
| | Approver.RequestApproval() |
| | re-drain if new convergence|
| | |
| | -> CollectFeedback() |
| | (accumulates for nextgen) |
| | (convergence -> notify) |
| | |
| | (wave approved) |
| | ComposeSpecification() |
| | -> outbox/ + archive/ |
| | |
| | (wave completed) |
| | ComposeReport() |
| | -> outbox/ + archive/ |
| | |
| | reads outbox/ |
| |----------------------------->|
- inbox/ -> fsnotify monitor -> archive/ (consumed by
ReceiveDMail) - convergence gate ->
RunConvergenceGateWithRedrain()runs notify + approve loop before session starts. Re-drains inbox after each approval to catch late-arriving convergence D-Mails. - specification/report -> archive/ first, then outbox/ (archive-first write order)
- D-mail format: YAML frontmatter (
name,kind,description,dmail-schema-version,issues,severity,metadata) + Markdown body - D-mail kinds:
specification,report,design-feedback,implementation-feedback,convergence,ci-result,stall-escalation - Filename pattern:
{tool}-{kind}-{sanitized-key}_{uuid8}.md(e.g.,sj-spec-auth-w1_a3f2b7c4.md,sj-report-api-w2_b7c4d8e9.md)
| File | Created By | When |
|---|---|---|
.siren/ dirs |
EnsureScanDir() |
Session startup |
.gitignore |
WriteGitIgnore() |
Session startup (via EnsureScanDir, idempotent) |
config.yaml |
runInit() |
sightjack init command (use --force to overwrite) |
events/{sessionID}.jsonl |
SessionRecorder.Record() |
Each state-changing action during a session |
skills/*/SKILL.md |
InstallSkills() |
sightjack init command (overwrites existing) |
inbox/*.md |
External tool (phonewave) | Before/during session |
outbox/*.md |
ComposeDMail() |
After wave approval or completion |
archive/*.md |
ComposeDMail() + ReceiveDMail() |
After wave approval/completion or feedback receipt |
archive/index.jsonl |
IndexWriter.Append / IndexWriter.Rebuild |
archive-prune --execute (before deletion) or archive-prune --rebuild-index |
insights/shibito.md |
WriteShibitoInsights() |
After scan, when Shibito warnings detected |
insights/strictness.md |
WriteStrictnessInsights() |
After scan, when clusters have estimated strictness |
.run/{sessionID}/* |
Claude subprocess + WriteScanResult() |
During scan/discuss/apply/scribe/nextgen |
.run/insights.lock |
InsightWriter.lock() |
During insight append (flock advisory lock) |
docs/adr/NNNN-*.md |
RunScribeADR() |
After architect modifies a wave (Scribe agent) |
.doctor_probe |
checkStateDir() |
sightjack doctor (temporary, immediately removed) |