| name | scan |
| tier | essential |
| description | Show system status report - briefs, session, blockers, git status |
| disable-model-invocation | false |
| allowed-tools | ["Read","Grep","Glob","Bash","mcp__igris-brain__igris_project_status","mcp__igris-brain__igris_brief_dashboard","mcp__igris-brain__igris_goal_list","mcp__igris-brain__igris_goal_progress","mcp__igris-brain__igris_suggestion_list","mcp__igris-brain__igris_event_log","mcp__igris-brain__igris_session_file_get","mcp__igris-brain__igris_session_file_list","mcp__igris-brain__igris_instance_list"] |
| triggers | ["SCAN","REPORT","show status","show brief status","show bug status report","show feature status report","brief overview"] |
SCAN - System Status Report
Display comprehensive status of the Igris AI system.
Arguments
$ARGUMENTS can optionally filter results:
- Empty: Full status report
P0 or P1: Filter by priority
bugs or features: Filter by type
--suggestions: Append a "Subconscious Suggestions" section (FR-106) below the regular report
Execution
1. Load Session State
Read the per-instance session file for:
- Current session mode (Active/REST MODE)
- Active briefs
- Resume point
/scan may run before this harness's /boot (the operator just opens a terminal and types SCAN), so resolve the file gracefully:
- If a
**Instance ID:** is discoverable for this harness, read ~/.igris/projects/{project}/session/instances/<instance_id>.md.
- Otherwise, call
igris_session_file_list with project=<slug> (no state filter) to enumerate all session files, and read the one with the most-recent updated_at.
- If no session file exists at all, treat the session as "no session" and render the
### Session block with Mode: None / Active Brief: None — do NOT error.
/scan is read-only and must NEVER error if no session file exists. Degrade gracefully in every branch.
1.5. Active Instances
Call igris_instance_list with status='active' to surface the live instance registry (Lock 1: the registry is the source of liveness). Render a compact table — this feeds the ### Active Instances block in §6:
### Active Instances
| Instance | Project | Current Brief | Last Active |
|----------|---------|---------------|-------------|
| a1b2c3d4 | igris-ai | FR-132 | 2026-05-18 14:22 |
| e5f6g7h8 | other-app | BR-009 | 2026-05-18 13:50 |
Show the short form of each instance_id (first 8 chars). If igris_instance_list is unavailable (older brain) or returns no rows, omit the section entirely. Do NOT block /scan.
2. Scan Briefs
Call igris_brief_dashboard with project and summary_only=true, fallback to cache glob at ~/.igris/projects/{project}/briefs/ (exclude templates):
- The dashboard returns aggregate counts by status and priority — no need to fetch individual briefs
- Apply filter if
$ARGUMENTS provided (e.g., pass status parameter for status filter)
- Build-state is read here from the canonical
igris_brief_dashboard/brief_status source — NEVER inferred from plan docs (plans are pre-build INTENT, not state; #811). Scope: this governs only the source of truth for build-state, not whether to read plans — plans stay valid input for design/intent/rationale. See docs/architecture/brief-state-source-of-truth.md.
2.5. Scan Goals (FR-110)
Call igris_goal_list with project and status='active' to list active goals for the current project. Then for each returned goal, call igris_goal_progress to compute completion. Render a compact table with text-progress bars:
### Goals (Active)
| Goal | Outcome | Deadline | Progress |
|------|---------|----------|----------|
| GL-003 "Ship v6.1" | shipped | 2026-05-01 | [########--] 7/8 |
| GL-001 "Compliance audit" | audited | 2026-05-12 | [##--------] 1/5 |
Progress bar conventions:
- 10 cells; fill ratio = round(completion_pct * 10)
- When
completion_pct is null (no serving briefs), render [----------] 0/0 with a faded/dimmed style
- Cap rendered table at 10 active goals; if more exist, append
(+N more — use /ops for full view)
If no active goals exist, omit the section entirely.
If the goal tools are unavailable (older brain), skip the section silently.
3. Check Blockers
Read ~/.igris/projects/{project}/session/BLOCKERS.md:
- Count active blockers (not in Resolved section)
- Flag critical blockers
4. Agent Roster
Discover the agent roster from the harness-agnostic canonical sources — never hand-list the agents, and never count the per-harness agent directory (it is adapter-owned, so reading it would couple the report to one harness's projection).
Two discovered sources, used together:
- Roster membership — the
## Agent roster table in ~/.igris/core/os/INDEX.md is the live set of registered agents (the same source the report has always used for the agent count). The INDEX is regenerated by gen_os_index.sh scanning each agent's frontmatter, so igris add agent / igris remove agent keep it current.
- Role + tier per agent —
~/.igris/core/agents/manifest.yaml carries a tier and a role for each agent under its agents: list. This is the canonical agent registry; render role/tier straight from it (do NOT infer or invent them).
Build the roster by reading the manifest.yaml agents: entries (each has name, tier, role) and rendering one row per agent. The os/ INDEX agent roster confirms membership/count; the manifest supplies the role + tier columns. Order rows by ascending tier, then by manifest order within a tier.
Do NOT add a "status", "state", or "invocation" column — there is no per-agent status field anywhere in the system (a disabled/enabled marker is not read by the compiler, drift engine, or delegation), so a status column would be fabricated. The roster shows only what is genuinely discoverable: agent → role → tier.
Fallbacks (degrade gracefully, in order):
- If
manifest.yaml is unreadable but the os/ INDEX agent roster exists, render a name-only roster (one column) from that roster plus the total count — omit the role/tier columns rather than invent them.
- If neither source exists, glob
~/.igris/core/agents/*.md and render a name-only roster from the filenames, or render the ### Agents count line alone.
- This section must NEVER error or block
/scan.
5. Git Status
Run: git status --short
5.5. Query Brain Stats (Optional)
If the igris-brain MCP server is available:
- Call
igris_project_status for the current project slug
- Get learning count, error count, recent metrics
If brain MCP is not available, skip this step silently. No errors, no warnings.
6. Display Report
Format as:
## System Status Report
### Session
- Mode: [Active | REST MODE | None]
- Active Brief: [ID or None]
- Resume Point: [description]
### Active Instances
[The §1.5 table — or omit if no active instances / tool unavailable]
### Briefs Inventory
| Status | Count |
|--------|-------|
| Ready | X |
| In Progress | X |
| Done | X |
| Draft | X |
[If filtered: "Showing: P0 only" or "Showing: bugs only"]
### Priority Distribution
- P0 (Critical): X briefs
- P1 (High): X briefs
- P2 (Medium): X briefs
- P3 (Low): X briefs
### Agents
X agents registered (Y skills available)
| Agent | Role | Tier |
|-------|------|------|
| architect | Implementation planning | 1 |
| forger | Code implementation | 1 |
| sentinel | Test execution | 1 |
| warden | Code review + auditing | 1 |
| mender | Error recovery | 3 |
| seeker | Codebase research | 4 |
| sage | Flutter MVVM + Actions architecture | 5 |
(The table is DISCOVERED from `core/agents/manifest.yaml` per §4 — the rows above are an illustration of the current registry, not a hand-maintained list. If the manifest is unreadable, render a name-only roster from the os/ INDEX agent roster; if neither exists, render only the count line above.)
### Blockers
[None | X active (Y critical)]
### Git Status
[Clean | X uncommitted files]
### Brain (if connected)
- Learnings: X (Y global)
- Errors cataloged: Z
- Cross-project patterns: N available
- Last brain sync: [timestamp]
### Context Docs
[The §6.8 table/nudge — or omit if the primitive is unavailable]
### Harness Drift
[The §6.7 line — or omit if the guard / manifest is absent]
### Recommendations
1. [Primary recommendation]
2. [Secondary recommendation]
6.5. Subconscious Suggestions (FR-106)
TD-102 / FR-118 / FR-191 (V7.1): This entire section is gated behind the
cognition.subconscious.enabled config flag (default false). FR-118 SHIPPED the
LLM-driven replacement: the subconscious is now a cognition instance
(digest → isolated LLM call → open-typed suggestions); the old rule engine
(stalled/gap/conflict/pattern detectors) was deleted. The section
stays silent until cognition.subconscious.enabled is flipped to true.
This section is rendered ONLY when ALL of the following are true:
cognition.subconscious.enabled is true in ~/.igris/config.json (key absent = false).
$ARGUMENTS contains the literal token --suggestions.
If either gate fails, skip this section silently — render nothing, do not
call any suggestion MCP tools.
If both gates pass and the igris-brain MCP is available:
- Call
igris_suggestion_list with:
status = 'pending'
project_slug = current project slug
limit = 1000 (handler caps at this value; >1000 pending is a degenerate state)
- Group the returned suggestions by
source_module. Post-FR-118 the
source_module is OPEN — the LLM names the kind (type_inferred=1), so the
group set is dynamic, not the fixed stalled/gap/conflict/pattern
rule modules (those still label any surviving pre-FR-118 rows). Sort the
groups by their highest-priority member, then alphabetically by kind. Within
each group, the handler already returns rows ordered by priority
(high > medium > low) then created_at DESC, so client-side iteration
preserves that order.
- Render each non-empty group as its own subsection (heading = the
source_module string, title-cased). Empty groups are omitted entirely. If
the global total is 0, render the single line No pending suggestions.
and skip every subsection.
Render template
## Subconscious Suggestions ({total} pending)
### Stalled (N)
| ID | Priority | Title | Project |
|----|----------|-------|---------|
| 12 | high | TD-005 stalled in In Progress for 35 days | igris-ai |
### Gap (N)
| ID | Priority | Title | Project |
|----|----------|-------|---------|
| 19 | medium | Project "old-app" has been quiet for 95 days | old-app |
### Conflict (N)
| ID | Priority | Title |
|----|----------|-------|
| 47 | medium | Possible contradiction: Learning #112 vs #389 |
### Pattern (N)
| ID | Priority | Title |
|----|----------|-------|
| 51 | medium | Pattern: brief activity skews toward Monday in igris-ai (60% of last 50) |
If total exceeds 1000 (the handler ceiling), append the trailing line:
(+N more — use igris_suggestion_list for full pagination).
End the section with the action hint:
Use igris_suggestion_dismiss <id> --reason "..." to silence noisy suggestions.
If igris-brain MCP is unavailable, render this single line instead:
Subconscious suggestions unavailable (brain MCP offline).
Subconscious health line (FR-118)
Independent of the gated suggestions table above, surface a single health line
for the LLM subconscious engine — when it last fired, the outcome, how many
suggestions it produced today, and the remaining daily budget. This is the
subconscious analogue of §6.6's Perception health line and is ALSO gated behind
cognition.subconscious.enabled (skip silently when the flag is absent/false —
the engine does not run, so there is nothing to report).
Query the NEW cognition.subconscious.* lifecycle namespace (the engine writes
these to event_log directly under component = 'cognition.subconscious' — the
legacy subconscious.* bus events are gone). Prefer the local-DB sqlite3 read
(same TD-080 rationale as §6.6 — the local DB is the merged superset):
sqlite3 "$HOME/.igris/memory/knowledge.db" \
"SELECT event_name, payload, created_at FROM event_log
WHERE component = 'cognition.subconscious'
ORDER BY created_at DESC LIMIT 1;"
sqlite3 "$HOME/.igris/memory/knowledge.db" \
"SELECT COALESCE(SUM(json_extract(payload,'\$.persisted')),0)
FROM event_log
WHERE component = 'cognition.subconscious'
AND event_name = 'cognition.subconscious.run_succeeded'
AND date(created_at) = date('now');"
Fallback (only when sqlite3 is absent): call igris_event_log with
component = 'cognition.subconscious', limit = 1 (it inherits the §6.6
remote-only blind spot — acceptable degradation).
Render one line under a ### Subconscious Engine heading. Map the latest event
suffix to an uppercase status (run_succeeded→SUCCEEDED,
run_failed→FAILED, run_skipped→SKIPPED, run_started→RUNNING).
budget_remaining = the run's payload.budget − payload.used_today when the
latest event is a run_skipped with reason='budget'; otherwise derive it from
the resolved cognition.subconscious.llm_daily_budget minus the count of
today's run_started rows.
### Subconscious Engine
Last run: 2026-06-24 06:00 — SUCCEEDED · suggested_today=3 · budget_remaining=2
When no cognition.subconscious.* rows exist (never run, or gate off):
### Subconscious Engine
No subconscious runs yet.
If sqlite3 is absent AND the MCP fallback also fails, omit the line entirely.
Do NOT block /scan.
6.6. Perception Engine (TD-074, TD-080)
Surface the latest detached perception extraction run so operators can see
when the LLM extractor last fired, succeeded, failed, or got skipped by the
60s min-window guard. Token budget: ~150 tokens.
Query
TD-080 fix (Gap A): read directly from the local DB via sqlite3. The
local DB is the merged superset (post any prior pull) and includes
local-only perception runs that have not yet propagated to the remote.
igris_event_log MCP routes to the remote brain — using it here would miss
this machine's unpushed runs even when the call "succeeds".
Primary query (substitute $PROJECT_SLUG):
if [[ ! "$PROJECT_SLUG" =~ ^[a-z0-9_-]+$ ]]; then
return 0
fi
sqlite3 "$HOME/.igris/memory/knowledge.db" \
"SELECT event_name, payload, created_at FROM event_log
WHERE component = 'perception' AND project_slug = '$PROJECT_SLUG'
ORDER BY created_at DESC LIMIT 1;"
Fallback (only when sqlite3 is absent on this machine — older / minimal
installs): call igris_event_log with:
component = 'perception'
project_slug = current project slug
limit = 1
The MCP handler returns rows ordered created_at DESC, matching the sqlite3
query shape. Note the fallback inherits the original blind spot: it shows
remote-only state. That's an acceptable degradation when the local read is
unavailable.
Also stat the inbox for staleness:
INBOX="$HOME/.igris/projects/$PROJECT/session/perception_inbox.jsonl"
[ -f "$INBOX" ] && wc -c < "$INBOX" || echo 0
Render
When the latest event exists, render two lines under a ### Perception Engine
heading. Format the timestamp as YYYY-MM-DD HH:MM (local), parsed from the
ISO created_at field. The status word is uppercase, mapped from the event
suffix:
perception.run_succeeded → SUCCEEDED
perception.run_failed → FAILED
perception.run_skipped → SKIPPED
perception.run_started → RUNNING (no terminal event has followed)
Detail clause depends on the status:
- SUCCEEDED →
· N candidates (from payload.candidates_count; default 0 if missing)
- FAILED →
· (reason) (parenthesized; from payload.reason; fallback (unknown reason) if blank)
- SKIPPED →
· (reason, Ns elapsed) when reason='min_window_guard', else · (reason)
- RUNNING →
· started Nm ago (may be stuck) if >5 min elapsed since the
run_started row; otherwise omit the detail clause.
Inbox clause: · inbox NKB always (round to nearest KB; show 0KB for an
empty file). Append stale when the file is non-empty AND its mtime is
more than 1 hour old.
### Perception Engine
Last run: 2026-05-01 04:22 — SUCCEEDED · 3 candidates · inbox 0KB
### Perception Engine
Last run: 2026-05-01 04:22 — FAILED (epipe_on_llm_stdin) · inbox 3.4MB stale
### Perception Engine
Last run: 2026-05-01 04:22 — SKIPPED (min_window_guard, 12s elapsed) · inbox 0KB
When no event_log rows exist for the project (older brain or never run):
### Perception Engine
No perception runs yet for this project.
If sqlite3 is absent AND the igris_event_log MCP fallback also fails,
omit the section entirely. Do NOT block /scan.
6.7. Harness Drift (FR-135, TD-021)
Surface whether the generated agent-prompt harnesses are in sync with their
canonical core/agents/*.md prompts. Read-only — this NEVER blocks /scan and
NEVER regenerates anything. Token budget: ~80 tokens.
Query
Run the drift guard wrapper read-only and capture its exit code + summary:
GUARD="$REPO_ROOT/core/scripts/cli-adapters/check_harness_drift.sh"
MANIFEST="$REPO_ROOT/harness-manifest.json"
if [ -f "$GUARD" ] && [ -f "$MANIFEST" ]; then
bash "$REPO_ROOT/scripts/validate_harness_drift.sh" 2>&1 | tail -1
fi
The wrapper checks the Igris-core agents declared in the repo-root harness manifest (content-pipeline agents are
excluded — they have no canonical in this repo until FR-136). The guard's
per-agent summary line reads N targets — M in sync, K drifted/missing.
Render
When the guard ran, render a single line under a ### Harness Drift heading
summing the per-agent results into one MATCH/DRIFTED count:
### Harness Drift
N agent targets — N in sync
When any target is out of sync (DRIFTED or MISSING), name the count and the
remedy:
### Harness Drift
N agent targets — M in sync, K drifted/missing — run `compile_harnesses.sh` to resync
If the guard script or the manifest is absent, omit the section entirely.
Do NOT block /scan, do NOT print an error.
6.8. Context Docs (FR-209)
Surface the shared project-context-doc inventory in /scan. This is a SOFT
presence nudge, not a gate; /scan reports status and remediation commands only.
Run:
igris context-docs inventory --project <slug> 2>/dev/null || true
The context-docs inventory verb owns the catalog/project/doc matching:
core/context-doc-types/*.md frontmatter (applies_when, target) × the
project profile (archetype, tech_stack) ×
~/.igris/projects/{project}/context/. /scan must not re-implement or
reinterpret that logic.
When the command returns markdown output, render it under:
### Context Docs
<inventory markdown>
If the primitive is unavailable or errors, omit the section entirely. Do not
block /scan, do not author docs automatically, and do not print a stack trace.
6.9. Janitor Engine (FR-119)
Surface a single health line for the LLM memory-hygiene engine — when it last
fired, the outcome, and the counters from its latest maintenance run. This is
the janitor analogue of §6.5's Subconscious health line and is ALSO gated behind
cognition.janitor.enabled (skip silently when the flag is absent/false — the
engine does not run, so there is nothing to report). Merge PROPOSALS themselves
render via igris_suggestion_list source_module='janitor'; this line is the
engine-health summary only.
Query the cognition.janitor.* lifecycle namespace (the engine writes these to
event_log directly under component = 'cognition.janitor') plus the latest
brain_maintenance_runs audit row for the counters. Prefer the local-DB
sqlite3 read (same TD-080 rationale as §6.5/§6.6 — the local DB is the merged
superset):
sqlite3 "$HOME/.igris/memory/knowledge.db" \
"SELECT event_name, created_at FROM event_log
WHERE component = 'cognition.janitor'
ORDER BY created_at DESC LIMIT 1;"
sqlite3 "$HOME/.igris/memory/knowledge.db" \
"SELECT status, merges_proposed, merges_applied, confidence_bumps, stale_rejected, finished_at
FROM brain_maintenance_runs ORDER BY id DESC LIMIT 1;"
Fallback (only when sqlite3 is absent): call igris_event_log with
component = 'cognition.janitor', limit = 1 (it inherits the §6.6 remote-only
blind spot — acceptable degradation).
Render one line under a ### Janitor Engine heading. Map the latest event suffix
to an uppercase status (run_succeeded→SUCCEEDED, run_failed→FAILED,
run_skipped→SKIPPED, run_started→RUNNING); append the maintenance-row
counters.
### Janitor Engine
Last run: 2026-07-02 04:00 — SUCCEEDED · merges_proposed=2 · confidence_bumps=1 · stale_rejected=3
When no cognition.janitor.* rows exist (never run, or gate off):
### Janitor Engine
No janitor runs yet.
If sqlite3 is absent AND the MCP fallback also fails, omit the line entirely.
Do NOT block /scan.