Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ Legacy Intelligence Sync history remains in its [archive](https://github.com/ain

## [0.19.0]

### Added

- Agents and skills accept a tool-neutral `effort:` — `low`, `medium`, `high`, `xhigh`, `max` or `ultra`. Claude Code agents and skills receive it as `effort:` (`ultra` becomes `max`), Codex agents as `model_reasoning_effort`, and the shared `.agents/skills/` tree keeps it as written; Copilot, Cursor, OpenCode, Antigravity and Pi receive no effort key (decision 0015).
- An `effort:` off that scale prints a `WARNING:` line naming the source file and the value, also under `sync --compact` and `init`, renders as absent and leaves sync successful; an empty `effort:` is absent without a warning.

### Changed

- A tier no longer sets a reasoning effort in any tool. Codex agents without `effort:` no longer get a tier-derived `model_reasoning_effort` (`xhigh` for `frontier`, `high` for `heavy` or no tier, `medium` for `standard`, `low` for `light`), so Codex's own setting applies and `frontier` and `heavy` render identical Codex agents.

### Breaking

- [ ] Every Codex agent that should keep a reasoning effort states it as `effort:` in its source; after sync, `.codex/agents/*.toml` carries `model_reasoning_effort` only for agents whose source sets a valid `effort:`.

### Fixed

- `status --check` no longer reports `.gitignore` patterns as missing when the file has CRLF line endings on Linux and macOS; it decides presence the way the `.gitignore` writer does, so `intelligence init` clears the finding (#45).
Expand Down
1 change: 1 addition & 0 deletions cli/commands/init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ manifest="$root/intelligence.yaml"
echo "# its stack, and the constraints to respect"
echo "# $content_dir/agents/<name>.md a persona (frontmatter: name, description,"
echo "# tier: frontier|heavy|standard|light,"
echo "# effort: low|medium|high|xhigh|max|ultra,"
echo "# access: full|readonly)"
echo "# $content_dir/skills/<name>/SKILL.md a procedure invoked as /<name>"
echo "# '.intelligence/' is CLI-managed (packages + engine content) — never edit it."
Expand Down
131 changes: 123 additions & 8 deletions cli/tests/e2e-lifecycle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -667,9 +667,10 @@ chk grep -Fq ".agents/rules/big.md renders $override_chars;" "$OUT/agr-override-
echo "== every built-in adapter renders every model tier =="
# The expected model comes from the engine's own default table, so a model
# bump does not touch this test; what it pins is that each adapter emits the
# tier's resolved model, never an empty one, and Codex's effort ladder.
# tier's resolved model, never an empty one, and that a tier never sets a
# reasoning effort (decision 0015).
TIERS="$OUT/model-tiers"
mkdir -p "$TIERS/intelligence/agents"
mkdir -p "$TIERS/intelligence/agents" "$TIERS/intelligence/skills"
git -C "$TIERS" init --quiet
cat > "$TIERS/intelligence.yaml" <<EOF
project:
Expand All @@ -682,6 +683,7 @@ sources:
agents:
- "intelligence/agents"
skills:
- "intelligence/skills"

targets:
agents: { enabled: true, output: "AGENTS.md" }
Expand Down Expand Up @@ -712,14 +714,20 @@ for tier in frontier heavy standard light; do
chk grep -Fqx "model: \"$(default_model opencode "$tier")\"" "$TIERS/.opencode/agents/$tier-agent.md"
chk grep -Fqx "model: \"$(default_model antigravity "$tier")\"" "$TIERS/.agents/agents/$tier-agent.md"
done
chk grep -Fqx 'model_reasoning_effort = "xhigh"' "$TIERS/.codex/agents/frontier-agent.toml"
chk grep -Fqx 'model_reasoning_effort = "high"' "$TIERS/.codex/agents/heavy-agent.toml"
chk grep -Fqx 'model_reasoning_effort = "medium"' "$TIERS/.codex/agents/standard-agent.toml"
chk grep -Fqx 'model_reasoning_effort = "low"' "$TIERS/.codex/agents/light-agent.toml"
# No tier resolves to heavy, and its effort follows the model it resolved to.
# No tier resolves to heavy.
chk grep -Fqx "model = \"$(default_model codex heavy)\"" "$TIERS/.codex/agents/untiered-agent.toml"
chk grep -Fqx 'model_reasoning_effort = "high"' "$TIERS/.codex/agents/untiered-agent.toml"
chk grep -Fqx "model: $(default_model claude heavy)" "$TIERS/.claude/agents/untiered-agent.md"
# A tier selects the model only: without `effort:` no tool receives an effort,
# so the tool's own setting applies — and frontier and heavy, which share a
# Codex model, render the same agent.
for agent in frontier-agent heavy-agent standard-agent light-agent untiered-agent; do
chknot grep -q 'model_reasoning_effort' "$TIERS/.codex/agents/$agent.toml"
chknot grep -q '^effort:' "$TIERS/.claude/agents/$agent.md"
done
eq() { [ "$1" = "$2" ]; }
# same_but_identity <a> <b> — identical once name and description are set aside.
same_but_identity() { eq "$(grep -Ev '^(name|description) = ' "$1")" "$(grep -Ev '^(name|description) = ' "$2")"; }
chk same_but_identity "$TIERS/.codex/agents/frontier-agent.toml" "$TIERS/.codex/agents/heavy-agent.toml"
# A tier nothing resolves still renders, but never silently: one warning per
# tool and tier, however many agents carry it. A custom tier the manifest
# overrides resolves quietly for that tool.
Expand All @@ -746,6 +754,113 @@ chknot grep -Fq "tier 'heavy'" "$OUT/tiers-unknown.txt"
chk grep -q "^WARNING: no claude model for tier 'haevy'" "$OUT/tiers-compact.txt"
chk grep -q "^WARNING: no claude model for tier 'haevy'" "$OUT/tiers-replay.txt"

echo "== effort is written once and rendered where a tool has a field =="
# Decision 0015: the neutral level reaches Claude Code (the nearest lower level
# it has) and Codex; the shared skills tree keeps it as written; every other
# tool gets no effort key. An off-scale value warns, names its source and is
# rendered as absent without failing the sync; an empty one is absent, silently.
# fm_effort_keys <file> — the frontmatter lines whose key names any effort
# (`effort`, `reasoningEffort`, ...); a body line is the author's text and stays.
fm_effort_keys() {
awk 'NR == 1 && $0 != "---" { exit } /^---$/ { if (++n == 2) exit; next }
index($0, ":") && tolower(substr($0, 1, index($0, ":") - 1)) ~ /effort/' "$1"
}
no_effort_key() { [ -z "$(fm_effort_keys "$1")" ]; }
tiers_agent() { # <name> <frontmatter line>...
local name="$1"
shift
{ printf -- '---\nname: %s\ndescription: "Agent %s"\n' "$name" "$name"
[ "$#" -eq 0 ] || printf '%s\n' "$@"
printf -- '---\n\n# Agent\n\neffort: a body line keeps its text\n'; } > "$TIERS/intelligence/agents/$name.md"
}
tiers_skill() { # <name> <frontmatter line>...
local name="$1"
shift
mkdir -p "$TIERS/intelligence/skills/$name"
{ printf -- '---\nname: %s\ndescription: "Skill %s"\n' "$name" "$name"
[ "$#" -eq 0 ] || printf '%s\n' "$@"
printf -- '---\n\n# Skill\n\neffort: a body line keeps its text\n'; } > "$TIERS/intelligence/skills/$name/SKILL.md"
}
tiers_agent effort-xhigh 'tier: light' 'effort: xhigh'
tiers_agent effort-ultra 'effort: ultra'
tiers_agent effort-quoted 'effort: "low"'
tiers_agent effort-typo 'effort: hiigh'
tiers_agent effort-case 'effort: High'
tiers_agent effort-bare 'effort:'
tiers_agent effort-empty 'effort: ""'
# Only the first `effort:` counts, as for every other frontmatter key.
tiers_agent effort-twice 'effort: medium' 'effort: ultra'
printf -- '---\r\nname: effort-crlf\r\ndescription: "CRLF"\r\neffort: max\r\n---\r\n\r\n# Agent\r\n' \
> "$TIERS/intelligence/agents/effort-crlf.md"
tiers_skill skill-ultra 'effort: ultra'
tiers_skill skill-quoted 'effort: "high"'
tiers_skill skill-typo 'effort: hiigh'
tiers_skill skill-bare 'effort:'
tiers_skill skill-empty 'effort: ""'
tiers_skill skill-plain
RC=0
(cd "$TIERS" && IS_SUPPRESS_CLI_NOTE=1 bash "$CLI" sync > "$OUT/effort-sync.txt" 2>&1) || RC=$?
[ "$RC" -eq 0 ] || { echo "FAIL: effort sync exited $RC"; cat "$OUT/effort-sync.txt"; fail=1; }
claude_effort() { chk grep -Fqx "effort: $2" "$TIERS/.claude/agents/$1.md"; }
codex_effort() { chk grep -Fqx "model_reasoning_effort = \"$2\"" "$TIERS/.codex/agents/$1.toml"; }
claude_effort effort-xhigh xhigh; codex_effort effort-xhigh xhigh
claude_effort effort-ultra max; codex_effort effort-ultra ultra
claude_effort effort-quoted low; codex_effort effort-quoted low
claude_effort effort-twice medium; codex_effort effort-twice medium
claude_effort effort-crlf max; codex_effort effort-crlf max
# Effort leaves the model alone.
chk grep -Fqx "model: $(default_model claude light)" "$TIERS/.claude/agents/effort-xhigh.md"
chk grep -Fqx "model = \"$(default_model codex light)\"" "$TIERS/.codex/agents/effort-xhigh.toml"
chk eq "$(fm_effort_keys "$TIERS/.claude/agents/effort-twice.md")" "effort: medium"
for agent in effort-typo effort-case effort-bare effort-empty; do
chk no_effort_key "$TIERS/.claude/agents/$agent.md"
chknot grep -q 'model_reasoning_effort' "$TIERS/.codex/agents/$agent.toml"
done
# Tools without a per-agent effort field never receive the key.
for agent in effort-xhigh effort-ultra effort-quoted effort-typo effort-twice effort-crlf; do
for rendered in ".github/agents/$agent.agent.md" ".opencode/agents/$agent.md" \
".agents/agents/$agent.md" ".cursor/agents/$agent.md"; do
chk no_effort_key "$TIERS/$rendered"
done
done
# The frontmatter is rewritten, never the body that documents it.
chk grep -Fqx 'effort: a body line keeps its text' "$TIERS/.claude/agents/effort-typo.md"
chk grep -Fqx 'effort: a body line keeps its text' "$TIERS/.cursor/agents/effort-xhigh.md"
# Skills: Claude's level in .claude, the neutral value as written in the shared
# tree, nothing where the tool has no field, and nothing for an off-scale or
# empty value anywhere.
chk grep -Fqx 'effort: max' "$TIERS/.claude/skills/skill-ultra/SKILL.md"
chk grep -Fqx 'effort: ultra' "$TIERS/.agents/skills/skill-ultra/SKILL.md"
chk grep -Fqx 'effort: "high"' "$TIERS/.claude/skills/skill-quoted/SKILL.md"
chk grep -Fqx 'effort: "high"' "$TIERS/.agents/skills/skill-quoted/SKILL.md"
for skill in skill-ultra skill-quoted skill-typo skill-bare skill-empty skill-plain; do
for tree in .github/skills .cursor/skills; do
chk no_effort_key "$TIERS/$tree/$skill/SKILL.md"
done
done
for skill in skill-typo skill-bare skill-empty skill-plain; do
for tree in .claude/skills .agents/skills; do
chk no_effort_key "$TIERS/$tree/$skill/SKILL.md"
chk grep -Fqx 'effort: a body line keeps its text' "$TIERS/$tree/$skill/SKILL.md"
done
done
# One warning per off-scale source, naming it and the value; none for empty.
for source in agents/effort-typo.md:hiigh agents/effort-case.md:High skills/skill-typo/SKILL.md:hiigh; do
chk eq "$(grep -c "^WARNING: .*intelligence/${source%%:*}:[0-9]* effort \"${source##*:}\" is not one of low, medium, high, xhigh, max, ultra" "$OUT/effort-sync.txt")" 1
done
chknot grep -Eq 'WARN(ING)?: .*(effort-bare|effort-empty|skill-bare|skill-empty|effort-quoted|effort-twice|effort-crlf|skill-quoted)' "$OUT/effort-sync.txt"
chk eq "$(grep -Ec 'WARN(ING)?: .*effort "' "$OUT/effort-sync.txt")" 3
# `sync --compact`, which `init` runs, keeps only unindented WARNING: lines, so
# the typo reaches it: on a full render, and on the unchanged run that replays it.
(cd "$TIERS" && IS_SUPPRESS_CLI_NOTE=1 bash "$CLI" sync --compact --force > "$OUT/effort-compact.txt" 2>&1) \
|| { echo "FAIL: effort compact sync"; cat "$OUT/effort-compact.txt"; fail=1; }
(cd "$TIERS" && IS_SUPPRESS_CLI_NOTE=1 bash "$CLI" sync --compact > "$OUT/effort-replay.txt" 2>&1) \
|| { echo "FAIL: effort compact replay"; cat "$OUT/effort-replay.txt"; fail=1; }
for report in effort-compact effort-replay; do
chk grep -q '^WARNING: .*intelligence/agents/effort-typo.md:[0-9]* effort "hiigh" is not one of' "$OUT/$report.txt"
chk grep -q '^WARNING: .*intelligence/skills/skill-typo/SKILL.md:[0-9]* effort "hiigh" is not one of' "$OUT/$report.txt"
done

echo "== skills only the owner invokes get Codex's policy at sync =="
# The source states the intent once; Codex's own file is the engine's to write,
# for project skills as much as package ones, and an author's own file is kept.
Expand Down
81 changes: 81 additions & 0 deletions cli/tests/unit-engine.sh
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,87 @@ for ide in claude cursor copilot codex antigravity opencode; do
done
done

echo "== effort: an independent expected matrix =="
# map_effort_var is the one mapping every adapter and the skill-copy pass
# render through (decision 0015), so this matrix pins the table itself.
# Columns follow the neutral scale; `-` means the tool receives no effort.
chk eq "$IS_EFFORT_LEVELS" "low medium high xhigh max ultra"
while read -r tool low medium high xhigh max ultra; do
row="$tool:"
for level in $IS_EFFORT_LEVELS; do
map_effort_var "$tool" "$level"
row+=" ${IS_EFFORT:--}"
done
chk eq "$row" "$tool: $low $medium $high $xhigh $max $ultra"
done <<'EOF'
claude low medium high xhigh max max
codex low medium high xhigh max ultra
open low medium high xhigh max ultra
copilot - - - - - -
cursor - - - - - -
opencode - - - - - -
antigravity - - - - - -
pi - - - - - -
EOF
# Off the scale is matched exactly, case and quotes included, and is absent.
for tool in claude codex open; do
for value in "" hiigh High HIGH " high" "high " '"high"' "low medium" ultra-high; do
map_effort_var "$tool" "$value"
chk eq "$tool [$value] -> [$IS_EFFORT]" "$tool [$value] -> []"
done
done
chk eq "$(map_effort claude ultra)" "max"
# The awk passes receive map_effort_var's own answers, never a second table.
effort_map_var claude
chk eq "$IS_EFFORT_MAP" $'low=low\nmedium=medium\nhigh=high\nxhigh=xhigh\nmax=max\nultra=max\n'
effort_map_var copilot
chk eq "$IS_EFFORT_MAP" ""

echo "== lint_frontmatter_files: an off-scale effort warns once, an empty one never =="
L="$OUT/lint-effort"
mkdir -p "$L"
printf '%s\n' '---' 'effort: hiigh' 'effort: high' '---' 'effort: a body line' > "$L/typo.md"
printf '%s\n' '---' 'effort: High' '---' > "$L/case.md"
printf '%s\n' '---' 'effort: high' 'effort: hiigh' '---' > "$L/second.md"
printf '%s\n' '---' 'effort:' '---' > "$L/bare.md"
printf '%s\n' '---' 'effort: ""' '---' > "$L/empty.md"
printf '%s\n' '---' 'effort: "xhigh"' '---' > "$L/quoted.md"
printf '%s\r\n' '---' 'effort: ultra' '---' > "$L/crlf.md"
printf '%s\n' 'effort: hiigh' > "$L/nofm.md"
warnings="$(lint_frontmatter_files "$L"/*.md 2>&1 >/dev/null)"
chk eq "$(printf '%s\n' "$warnings" | grep -c 'effort')" 2
chk eq "$(printf '%s\n' "$warnings" | grep -c "^WARNING: $L/typo.md:2 effort \"hiigh\" is not one of low, medium, high, xhigh, max, ultra")" 1
chk eq "$(printf '%s\n' "$warnings" | grep -c "^WARNING: $L/case.md:2 effort \"High\" is not one of")" 1

echo "== copy_skill_bundle_dirs_for: effort rendered for the tree's tool =="
K="$OUT/skill-effort"
mkdir -p "$K/src/up" "$K/src/typo" "$K/src/quoted" "$K/src/none/references"
printf '%s\n' '---' 'name: up' 'effort: ultra' 'effort: low' '---' 'effort: a body line' > "$K/src/up/SKILL.md"
printf '%s\n' '---' 'name: typo' 'effort: hiigh' '---' > "$K/src/typo/SKILL.md"
printf '%s\n' '---' 'name: quoted' "effort: 'high'" '---' > "$K/src/quoted/SKILL.md"
printf '%s\n' '---' 'name: none' '---' > "$K/src/none/SKILL.md"
printf '%s\n' '---' 'effort: ultra' '---' > "$K/src/none/references/notes.md"
skill_fm() { awk '/^---$/ { if (++n == 2) exit; next } /^effort:/' "$1"; }
for tool in claude open copilot; do
copy_skill_bundle_dirs_for "$tool" "$K/$tool" "$K/src/up" "$K/src/typo" "$K/src/quoted" "$K/src/none"
chk eq "$(skill_fm "$K/$tool/typo/SKILL.md")" ""
chk eq "$(skill_fm "$K/$tool/none/SKILL.md")" ""
# Only the top-level SKILL.md is a skill's frontmatter; the body and the
# bundled references are the author's text.
chk eq "$(tail -n 1 "$K/$tool/up/SKILL.md")" "effort: a body line"
chk eq "$(skill_fm "$K/$tool/none/references/notes.md")" "effort: ultra"
done
chk eq "$(skill_fm "$K/claude/up/SKILL.md")" "effort: max"
chk eq "$(skill_fm "$K/claude/quoted/SKILL.md")" "effort: 'high'"
chk eq "$(skill_fm "$K/open/up/SKILL.md")" "effort: ultra"
chk eq "$(skill_fm "$K/open/quoted/SKILL.md")" "effort: 'high'"
chk eq "$(skill_fm "$K/copilot/up/SKILL.md")" ""
chk eq "$(skill_fm "$K/copilot/quoted/SKILL.md")" ""
# The plain form renders for the shared open-standard tree.
copy_skill_bundle_dirs "$K/plain" "$K/src/up" "$K/src/typo"
chk eq "$(skill_fm "$K/plain/up/SKILL.md")" "effort: ultra"
chk eq "$(skill_fm "$K/plain/typo/SKILL.md")" ""

echo "== count_matching_files: what find | wc -l reports per directory =="
C="$OUT/counts"
mkdir -p "$C/rules/nested" "$C/skills/one/refs" "$C/skills/two" "$C/agents" "$C/empty"
Expand Down
1 change: 1 addition & 0 deletions decisions/0013-add-a-frontier-model-tier.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

Date: 2026-10-07
Status: accepted
Superseded by 0015 in part: point 2's separation of `frontier` from `heavy` by Codex reasoning effort, and point 3's effort clause. A tier now selects the model only.

## Context

Expand Down
Loading
Loading