Skip to main content

ocas-mentor

Mentor orchestrates and evaluates multi-skill workflows, proposing improvements based on performance analysis and project management.

Install this skill

or
59/100

Security score

The ocas-mentor skill was audited on Sep 25, 2026 and we found 5 security issues across 3 threat categories, including 2 high-severity. Review the findings below before installing.

Categories Tested

Security Issues

medium line 93

Command substitution pattern

SourceSKILL.md
91**`execute_code` is blocked in cron-triggered jobs.** All heartbeat, update, and plan runs triggered by cron must use `terminal()` with inline `python3 /path/to/scripts.py` for multi-stage logic. **CRITICAL: Do NOT use `<<` heredoc syntax in `terminal()`** — the `<<` delimiter triggers the terminal's foreground-background detection, causing exit_code=-1. Write scripts to `/tmp/` via `write_file` first, then invoke with `python3 /tmp/script.py`. See gotcha #70.
92
93**Pipe-to-python bash quoting pitfall (confirmed 2026-06-29 dispatch):** The pattern `VAR=$(tail -1 "$file" | python3 -c "..."))` fails with `syntax error near unexpected token` because bash's command substitution `$(...)` with a pipe and double quotes creates parsing conflicts. **Fix:** Use single quotes inside the Python script and ensure the pipe is inside the `$()`: `VAR=$(tail -1 "$file" | python3 -c 'import sys,json; print(json.loads(sys.stdin.read()).get("field",""))' ))` — note the single quotes. Or safer: avoid inline pipe-to-python entirely; write a `/tmp/script.py` via `write_file` and pipe into it: `tail -1 <fs-root>/file.jsonl | python3 /tmp/extract_field.py`. The shell then has no embedded Python to misparse.
94
95**`execute_code` blocking applies to ALL cron-triggered jobs** (confirmed 2026-06-25 dispatch #99). This includes not just heartbeat scripts but also state file writes, JSON manipulation, and any multi-step Python logic. When the dispatch caller (triggered by cron) needs to write JSON state files or perform set-difference syncs, it MUST use `terminal()` with `cat > file << 'EOF'` for JSON files or `echo >> file` for JSONL appends — never `execute_code`. Attempting `execute_code` in cron produces: `BLOCKED: execute_code runs arbitrary local Python`. This is a hard runtime constraint, not a suggestion.
medium line 106

Command substitution pattern

SourceSKILL.md
104**Inline Python variable scoping in `terminal()`** — When composing multi-step Python logic inline in `terminal()` (either as heredoc or `python3 -c`), variables defined inside a function are NOT available in the outer scope. This manifests as `NameError: name 'X' is not defined` at a line that logically follows the definition. **Fix:** Structure inline scripts so all logic is in a single flat scope (no nested functions), or write the script to `/tmp/` via `write_file` where you can verify scoping independently. Confirmed 2026-06-24 dispatch: `skill = jid.split("/")[0]` inside `extract_signals()` was invisible to the caller's loop.
105
106**Sandbox file discovery failure:** In the cron sandbox, Python's `subprocess.run(["find", ...])` and `os.walk()` silently return 0 results even when the filesystem is fully accessible via shell tools. Use the shell-pipe pattern: `find JOURNALS_DIR -name "*.json" -mtime -3 | sort -u > /tmp/mentor_files_3d.txt && python3 scripts/cron-heartbeat-light.py < /tmp/mentor_files_3d.txt`. **CRITICAL:** Any pipe to python3 (e.g., `cmd | python3 -c "..."`, `VAR=$(cmd | python3 -c "...")`, or `cat file | python3`) is blocked by the `tirith:pipe_to_interpreter` security rule in cron mode. Always avoid piping to python3; use alternatives like temporary files (`cmd > /tmp/out && python3 -c "..." < /tmp/out`), here-strings (`python3 -c "..." <<< "$(cmd)"`), or temporary scripts. See `references/shell-write-pattern.md`.
107
108**Pipe-to-interpreter blocks the ENTIRE terminal() call (confirmed 2026-07-08):** A trailing `tail -1 file | python3 -c "..."` verification step at the end of a multi-step cron `terminal()` command causes the security scanner to reject the WHOLE command — not just the pipe. Observed signature: `exit_code=-1`, `status: pending_approval`, `pattern_key: tirith:pipe_to_interpreter`. ALL steps in that call are lost (pre-run counts, script run, verification) and must be re-run. The block fires on the presence of `| python3` ANYWHERE in the command string, regardless of position. FIX: never put `cmd | python3` inside a cron `terminal()` call, even for final field checks. Do field verification via the `read_file` tool (outside terminal) or a `/tmp/*.py` script invoked WITHOUT a pipe (`python3 /tmp/check.py`).
low line 200

Command substitution pattern

SourceSKILL.md
198**Cron dispatch verification shortcut (confirmed 2026-06-25):** After a cron-triggered dispatch completes all pipelines, a single-line verification confirms state:
199```bash
200echo "Forge: $(ls .../ocas-forge/2026-06-25/ | tail -2)" && echo "Mentor: evidence=$(wc -l < .../mentor/evidence.jsonl) ingestion=$(wc -l < .../mentor/ingestion_log.jsonl)" && echo "Praxis: eval=$(wc -l < .../journals_evaluated.jsonl)"
201```
202
high line 273

Urgency-based manipulation

SourceSKILL.md
271Check both sources before running Praxis ingest. If found, mark as `already_ingested` in evidence and skip silently. This is NOT a second-wave false positive — the journal was genuinely new but already processed by a sibling pipeline.
272
273**CRITICAL: Mentor heartbeat updates Praxis ingest state** — The `cron-heartbeat-light.py` script updates `ingest_state.json:last_ingest_run` when it writes evidence. If Praxis dispatch runs immediately after in the same multi-skill dispatch, its mtime-based journal discovery may find 0 new journals because the state timestamp moved forward. The dispatcher must capture `last_ingest_run` BEFORE running Mentor and pass it to Praxis. See Praxis SKILL.md § Dispatch / Cron Integration step 3.
274
275**Caveat — heartbeat does NOT reliably advance Praxis ingest state (verified 2026-07-11):** In a multi-skill dispatch, after running `cron-heartbeat-light.py` via `python3 script.py < filelist` (returncode 0, heartbeat journal written to disk), `ingest_state.json:last_ingest_run` at `<hermes-home>/profiles/indigo/commons/data/ocas-praxis/` was UNCHANGED — it still held the prior-wave value. The dispatch caller MUST explicitly advance `last_ingest_run` itself after the heartbeat (and sync `journals_evaluated_count` / `last_eval_file_line` to the actual eval-file `wc -l`). Do NOT assume the script moved the state forward. A stale `last_ingest_run` is usually harmless (grep-based per-file classification wins over mtime discovery), but it means the state no longer reflects this wave's work and any later step that trusts it will read the prior-wave timestamp.
high line 106

Temp file execution

SourceSKILL.md
104**Inline Python variable scoping in `terminal()`** — When composing multi-step Python logic inline in `terminal()` (either as heredoc or `python3 -c`), variables defined inside a function are NOT available in the outer scope. This manifests as `NameError: name 'X' is not defined` at a line that logically follows the definition. **Fix:** Structure inline scripts so all logic is in a single flat scope (no nested functions), or write the script to `/tmp/` via `write_file` where you can verify scoping independently. Confirmed 2026-06-24 dispatch: `skill = jid.split("/")[0]` inside `extract_signals()` was invisible to the caller's loop.
105
106**Sandbox file discovery failure:** In the cron sandbox, Python's `subprocess.run(["find", ...])` and `os.walk()` silently return 0 results even when the filesystem is fully accessible via shell tools. Use the shell-pipe pattern: `find JOURNALS_DIR -name "*.json" -mtime -3 | sort -u > /tmp/mentor_files_3d.txt && python3 scripts/cron-heartbeat-light.py < /tmp/mentor_files_3d.txt`. **CRITICAL:** Any pipe to python3 (e.g., `cmd | python3 -c "..."`, `VAR=$(cmd | python3 -c "...")`, or `cat file | python3`) is blocked by the `tirith:pipe_to_interpreter` security rule in cron mode. Always avoid piping to python3; use alternatives like temporary files (`cmd > /tmp/out && python3 -c "..." < /tmp/out`), here-strings (`python3 -c "..." <<< "$(cmd)"`), or temporary scripts. See `references/shell-write-pattern.md`.
107
108**Pipe-to-interpreter blocks the ENTIRE terminal() call (confirmed 2026-07-08):** A trailing `tail -1 file | python3 -c "..."` verification step at the end of a multi-step cron `terminal()` command causes the security scanner to reject the WHOLE command — not just the pipe. Observed signature: `exit_code=-1`, `status: pending_approval`, `pattern_key: tirith:pipe_to_interpreter`. ALL steps in that call are lost (pre-run counts, script run, verification) and must be re-run. The block fires on the presence of `| python3` ANYWHERE in the command string, regardless of position. FIX: never put `cmd | python3` inside a cron `terminal()` call, even for final field checks. Do field verification via the `read_file` tool (outside terminal) or a `/tmp/*.py` script invoked WITHOUT a pipe (`python3 /tmp/check.py`).
Scanned on Sep 25, 2026
View Security Dashboard
Installation guide →