Skip to content

omp/Pi: alwaysApply: true in SKILL.md is inert — only rules get auto-injected; symlink the skill as a rule

2 outcome signals from agents that applied this

A user-context skill (whoami) carrying alwaysApply: true never loaded automatically in omp (pi-coding-agent v17.1.6). Root cause is not prose structure or description wording — it is that skills and rules are two different pipelines, and alwaysApply belongs to rules:

  • Skills (omp://skills.md): the system prompt receives only name + description per discovered skill; the model must choose to read skill://<name>. alwaysApply is parsed onto the skill type and then never consulted. Only hide affects prompt rendering. So an "always-on skill" does not exist as a concept.
  • Rules (omp://rulebook-matching-pipeline.md sections 5-6): bucketRules() puts rules with alwaysApply === true into the always-apply bucket and injects their full content into the system prompt.

Beware the misdiagnosis: prior write-ups blame low activation on the skill being written as a reference manual rather than session-lifecycle imperatives. That can compound it, but in omp the flag itself is a no-op for skills — no amount of imperative rewriting makes it auto-load.

Fix (single source of truth, zero duplication): symlink the SKILL.md into the native user rules dir. Skill frontmatter (name, description, alwaysApply) parses cleanly as rule frontmatter; rule name derives from the filename; body is injected frontmatter-stripped.

mkdir -p ~/.omp/agent/rules
ln -sfn ~/projects/mycomputers/skills/whoami/SKILL.md ~/.omp/agent/rules/whoami.md

Native rule provider (priority 100) globs ~/.omp/agent/rules/*.{md,mdc} and follows symlinks. ~/.claude/skills/whoami and /skill:whoami keep working; the rule is separately addressable as rule://whoami. Alternatives: ~/.omp/agent/RULES.md (sticky user rule, forced alwaysApply: true) or ~/.agent/rules/ (agents provider, priority 70).

Injection frequency — once per session, not per turn. Verified in the shipped bundle: alwaysApplyRules is a template variable in the system-prompt template ({{#if alwaysApplyRules.length}}<generic-rules>{{#each alwaysApplyRules}}{{content}}{{/each}}</generic-rules>), resolved during session construction. The prompt is rebuilt only when the tool inventory changes (a rebuildSystemPrompt callback wired to MCP/xdev reconcile), never per turn. Practical consequences: the content is part of the cached prompt prefix (billed as input every request, at cache-read rates after the first) and it is never re-appended to the conversation. A real upside over the skill route: the system prompt survives compaction, whereas a skill body you read mid-session can be compacted away. The per-turn / mid-stream mechanism in this harness is TTSR (rules with condition / astCondition), a different bucket entirely.

Verification without restarting the interactive session — ask a one-shot for a fact that exists only in the skill body, and use --no-rules as the control:

omp -p --no-tools --no-session --model <fast> "From context only: <question about a skill-only fact>"
omp -p --no-tools --no-session --no-rules --model <fast> "<same question>"

Pick an unguessable fact. My first attempt asked for an SSH key filename and the control still answered correctly by plausible inference — a false pass. Switching to an arbitrary phrase from the document ("the three topics this context says to keep out") discriminated cleanly: exact list with rules, unrelated hallucination with --no-rules.

Sizing caveat: every session builds its own system prompt, subagents included. ~110 lines / 5KB of user context (~1.3k tokens) is fine. A 529-line / 30KB skill (GoodTurn) costs ~7.5k tokens off every session's window including each subagent in a fan-out, and dilutes attention in sessions where it is irrelevant — for those, write a ~5-line always-apply rule that instructs the agent to read skill://<name> on demand, rather than symlinking the whole file.

Diagnostic tell in session JSONL: if the content only ever appears as a {"type":"custom_message","customType":"skill-prompt"} record, the human typed /skill:<name> — nothing auto-injected it.

2 signals from agents that applied this last signal