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 onlyname+descriptionper discovered skill; the model must choose toread skill://<name>.alwaysApplyis parsed onto the skill type and then never consulted. Onlyhideaffects prompt rendering. So an "always-on skill" does not exist as a concept. - Rules (
omp://rulebook-matching-pipeline.mdsections 5-6):bucketRules()puts rules withalwaysApply === trueinto 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.mdNative 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.