Rule modes
A rule artifact is passive context the harness loads automatically. The rule_mode field controls when:
| Mode | Loaded when… | Required field |
|---|---|---|
always |
The agent session starts (or every turn). | none |
glob |
A file matching the glob pattern is touched in the session. | rule_globs |
auto |
The harness’s autoload heuristic decides this rule’s description matches. |
rule_description |
explicit |
The user references the rule by name. | none |
Default is always if you don’t set the field.
When to use each mode
always for project-wide guidance every session needs. Style guides, security policies, “you are a senior X engineer” framing.
type: rule
name: house-style
rule_mode: always
glob for type-specific guidance. Load the React rule only when *.tsx is involved; load the SQL rule only when *.sql is touched.
type: rule
name: react-style
rule_mode: glob
rule_globs: "src/**/*.tsx,src/**/*.ts"
auto for domain rules where the trigger is fuzzy. The harness’s autoload heuristic uses the description to score relevance against the current situation.
type: rule
name: db-migration-checks
rule_mode: auto
rule_description: "Apply when working with database migrations or schema changes"
explicit for rules so specific the user wants to invoke them deliberately. The harness materializes them but doesn’t auto-load; the user references the rule by name (slash command, @rule-name, etc.).
type: rule
name: incident-response
rule_mode: explicit
How each harness honors them
The harness adapter does the translation at materialization time. Each adapter writes the rule into the harness’s native format using the closest equivalent it has. When a harness cannot honor a mode natively, the adapter falls back to an always-loaded block. A rule that declares target_harnesses: draws an ingest lint warning for a ⚠ mode and a lint error for a ✗ mode on a named harness. Without target_harnesses:, a ✗ mode is refused at materialization when the rule loads onto that harness (§6.9).
| Mode | claude-code | claude-desktop | claude-cowork | cursor | codex | opencode | gemini | pi | hermes |
|---|---|---|---|---|---|---|---|---|---|
always |
✓ | ✗ | ⚠ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
glob |
✓ | ✗ | ⚠ | ✓ | ⚠ | ⚠ | ⚠ | ⚠ | ✓ |
auto |
⚠ | ✗ | ⚠ | ✓ | ⚠ | ⚠ | ⚠ | ⚠ | ✓ |
explicit |
⚠ | ✗ | ⚠ | ✓ | ⚠ | ⚠ | ⚠ | ⚠ | ✓ |
Legend: ✓ supported natively, ⚠ supported via fallback (ingest lint warns when a declared target_harnesses: names the harness, and materialization emits the degraded fallback), ✗ not supported (ingest lint errors when a declared target_harnesses: names the harness, and materialization onto that harness otherwise fails per §6.9). This table mirrors the rule_mode rows of the §6.7.1 capability matrix.
What each adapter writes
| Adapter | Output |
|---|---|
| claude-code | A standalone .claude/rules/<name>.md for every mode, carrying the prose with the Podium-internal fields dropped. always loads at launch and glob writes the native paths: YAML list, both native. auto and explicit fall back to a load-always file (no scoping frontmatter) and draw a lint warning, because Claude Code’s .claude/rules/ files have no description-attach or mention-only mode. |
| cursor | .cursor/rules/<name>.mdc for every mode, with the native key set per the mode: always writes alwaysApply: true, glob writes globs: <rule_globs>, auto writes description: <rule_description>, and explicit writes no auto-apply key. |
| hermes | .cursor/rules/<name>.mdc in the Cursor .mdc format for every mode. Hermes natively reads .cursor/rules/*.mdc, root AGENTS.md, and .cursorrules; it does not read .claude/rules/. |
| codex, opencode, pi | The rule body injects into root AGENTS.md between Podium-managed markers. always maps natively; glob, auto, and explicit fall back to always-loaded with a lint warning, because an injected block carries no per-file scoping. |
| gemini | The rule body injects into root GEMINI.md between Podium-managed markers, with the same always-native, non-always-fallback behavior as the AGENTS.md harnesses. |
| claude-cowork | A Cowork plugin has no native rule component, so the rule ships as a skill (plugins/<id>/skills/<name>/SKILL.md). Every mode is a fallback. |
| claude-desktop | No project-level surface, so a rule produces no Claude Desktop output. |
Authoring guidance
- Default to
alwaysfor guidance you’d want loaded every time. The cost is a few hundred tokens per session; the benefit is consistent behavior. - Use
globfor type-specific guidance that doesn’t need to be loaded for unrelated work. Sharper context, smaller working set. - Use
autocarefully. It depends on the harness’s autoload heuristic, which varies by harness and isn’t something you can test deterministically. Write therule_descriptionas a clear trigger statement rather than a summary. - Use
explicitfor rules with sharp boundaries: incident response procedures, regulatory compliance checklists, anything you’d rather have the user invoke deliberately than have the harness load opportunistically.
Lint behavior
Lint enforces the field requirements per mode:
rule_mode: globrequiresrule_globs. Missingrule_globsis an ingest error.rule_mode: autorequiresrule_description. Missingrule_descriptionis an ingest error.rule_mode: globwithrule_descriptionset: lint warning (“rule-mode ‘glob’ uses globs only; rule-description is ignored”).rule_mode: autowithrule_globsset: lint warning (“rule-mode ‘auto’ uses description only; rule-globs is ignored”).- A type other than
rulewithrule_modeset: lint warning (“rule-mode is only applicable to type: rule”).
When an artifact declares target_harnesses:, ingest lint surfaces a mismatch for any named harness whose cell for the chosen mode is ⚠ (warning) or ✗ (error). When target_harnesses: is absent, ingest stays permissive and an unsupported mode is caught at materialization onto that harness (§6.9). Authors who must use a non-portable mode can declare target_harnesses: in frontmatter to opt out of cross-harness materialization for that artifact.