Runtime Configuration (Step 0 — before any processing)
Read these files to configure domain-specific behavior:
-
ops/derivation-manifest.md— vocabulary mapping, domain context- Use
vocabulary.notesfor the notes folder name - Use
vocabulary.inboxfor the inbox folder name - Use
vocabulary.notefor the note type name in output - Use
vocabulary.topic_mapfor MOC references - Use
vocabulary.cmd_reducefor process/extract command - Use
vocabulary.cmd_reflectfor connection-finding command - Use
vocabulary.cmd_reweavefor backward-pass command - Use
vocabulary.rethinkfor rethink command name
- Use
-
ops/config.yaml— thresholds, processing preferencesself_evolution.observation_threshold(default: 10)self_evolution.tension_threshold(default: 5)
If these files don't exist, use universal defaults and generic command names.
EXECUTE NOW
INVARIANT: /next recommends, it does not execute. Present one recommendation with rationale. The user decides what to do. This prevents cognitive outsourcing where the system makes all work decisions and the user becomes a rubber stamp.
Execute these steps IN ORDER:
Step 1: Read Vocabulary
Read ops/derivation-manifest.md (or fall back to ops/derivation.md) for domain vocabulary mapping. All output must use domain-native terms. If neither file exists, use universal terms (notes, inbox, topic map, etc).
Step 2: Reconcile Maintenance Queue
Before collecting state, evaluate all maintenance conditions and reconcile the queue. This ensures maintenance tasks are current before the recommendation engine runs.
Read queue file (ops/queue/queue.json or ops/queue.yaml). If schema_version < 3, migrate:
- Add
maintenance_conditionssection with default thresholds - Add
priorityfield to existing tasks (default: "pipeline") - Set
schema_version: 3
For each condition in maintenance_conditions:
- Evaluate the condition:
| Condition | Evaluation Method |
|---|---|
| orphan_notes | For each note in {vocabulary.notes}/, count incoming [[links]]. Zero = orphan. |
| dangling_links | Extract all [[links]], verify targets exist as files. Missing = dangling. |
| inbox_pressure | Count *.md in {vocabulary.inbox}/. |
| observation_accumulation | Count status: pending in ops/observations/. |
| tension_accumulation | Count status: pending or open in ops/tensions/. |
| pipeline_stalled | Queue tasks with status: pending unchanged across sessions. |
| unprocessed_sessions | Count files in ops/sessions/ without mined: true. |
| moc_oversize | For each topic map, count linked notes. |
| stale_notes | Notes not modified in 30+ days with < 2 links. |
| low_link_density | Average link count across all notes. |
| methodology_drift | Compare config.yaml modification time vs newest ops/methodology/ note modification time. If config is newer, methodology may be stale. |
- If condition exceeds threshold AND no pending task with this condition_key exists:
Create maintenance task:
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
MAINT_MAX=$(jq '[.tasks[] | select(.id | startswith("maint-")) | .id | ltrimstr("maint-") | tonumber] | max // 0' ops/queue/queue.json)
NEXT_MAINT=$((MAINT_MAX + 1))
jq --arg id "maint-$(printf '%03d' $NEXT_MAINT)" \
--arg priority "{priority}" \
--arg key "{condition_key}" \
--arg target "{description}" \
--arg action "{recommended command}" \
--arg ts "$TIMESTAMP" \
'.tasks += [{"id": $id, "type": "maintenance", "priority": $priority, "status": "pending", "condition_key": $key, "target": $target, "action": $action, "auto_generated": true, "created": $ts}]' \
ops/queue/queue.json > tmp.json && mv tmp.json ops/queue/queue.json
- If condition is satisfied AND a pending task with this condition_key exists:
Auto-close it:
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
jq --arg key "{condition_key}" --arg ts "$TIMESTAMP" \
'(.tasks[] | select(.condition_key == $key and .status == "pending")).status = "done" |
(.tasks[] | select(.condition_key == $key and .status == "pending")).completed = $ts' \
ops/queue/queue.json > tmp.json && mv tmp.json ops/queue/queue.json
- If condition fires AND a pending task already exists:
Update the target description (specifics may have changed):
jq --arg key "{condition_key}" --arg target "{new description}" \
'(.tasks[] | select(.condition_key == $key and .status == "pending")).target = $target' \
ops/queue/queue.json > tmp.json && mv tmp.json ops/queue/queue.json
Step 3: Collect Vault State
Gather all signals. Run independent checks in parallel where possible. Record each signal even if the check returns zero — absence of signal is itself informative.
| Signal | How to Check | What to Record |
|---|---|---|
| Task stack | Read ops/tasks.md — current priorities and open items | Top items, open count, any deadlines |
| Queue state | Read ops/queue.yaml or ops/queue/queue.json — pending pipeline tasks | Total pending, by phase (create, reflect, reweave, verify), blocked phases |
| Inbox pressure | Count *.md files in {vocabulary.inbox}/, find oldest by mtime | Count per subdirectory, age of oldest item in days |
| Note count | Count *.md in {vocabulary.notes}/ | Total notes for context |
| Orphan notes | For each note, grep for [[filename]] across all files — zero hits = orphan | Count, first 5 names |
| Dangling links | Extract all [[links]] from notes/, verify each target file exists | Count, first 5 targets |
| Stale notes | Notes not modified recently AND with low link density (< 2 links) | Count |
| Goals | Read self/goals.md or ops/goals.md — current priorities, active threads | Priority list, active research directions |
| Observations | Count files with status: pending in ops/observations/ | Count |
| Tensions | Count files with status: pending or status: open in ops/tensions/ | Count |
| Methodology | Check ops/methodology/ for recent captures (files modified in last 7 days) | Count of recent, total count |
| Health | Read most recent report in ops/health/ — note timestamp and issues | Last run date, issue count, any critical issues |
| Sessions | Check ops/sessions/ for files without mined: true in frontmatter | Count of unmined sessions |
| Recent /next | Read ops/next-log.md (if exists) — last 3 recommendations | Previous suggestions to avoid repetition |
Adaptation rules:
- Directory names adapt to domain vocabulary (e.g., {vocabulary.inbox} instead of hardcoded "inbox")
- Skip checks silently for directories that do not exist — do not report "ops/sessions/ not found"
- A missing directory means that feature is not active, which is valid state
Signal collection commands:
# Inbox pressure (adapt path to vocabulary)
INBOX_COUNT=$(find {vocabulary.inbox}/ -name "*.md" -maxdepth 2 2>/dev/null | wc -l | tr -d ' ')
OLDEST_INBOX=$(find {vocabulary.inbox}/ -name "*.md" -maxdepth 2 -exec stat -f "%m %N" {} \; 2>/dev/null | sort -n | head -1)
# Note count
NOTE_COUNT=$(ls -1 {vocabulary.notes}/*.md 2>/dev/null | wc -l | tr -d ' ')
# Pending observations
OBS_COUNT=$(grep -rl '^status: pending' ops/observations/ 2>/dev/null | wc -l | tr -d ' ')
# Pending tensions
TENSION_COUNT=$(grep -rl '^status: pending\|^status: open' ops/tensions/ 2>/dev/null | wc -l | tr -d ' ')
# Unmined sessions
SESSION_COUNT=$(grep -rL '^mined: true' ops/sessions/*.md 2>/dev/null | wc -l | tr -d ' ')
Step 4: Classify by Consequence Speed
Evaluate every signal against consequence speed — how fast does inaction degrade the system?
| Speed | Signals | Threshold | Why This Priority |
|---|---|---|---|
| Session | Inbox > 5 items, orphan notes (any), dangling links (any), 10+ pending observat |