Skip to content

feat(claude-config): audit the model-configuration settings the harness accepts but declines to honor - #1923

Merged
kyle-sexton merged 2 commits into
mainfrom
docs/roster-r18-model-config
Aug 4, 2026
Merged

feat(claude-config): audit the model-configuration settings the harness accepts but declines to honor#1923
kyle-sexton merged 2 commits into
mainfrom
docs/roster-r18-model-config

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

Summary

Doc-alignment roster row 18 — the priority head's final row: Model configuration (code.claude.com/docs/en/model-config), the corpus's most-cited harness page, first formally captured here (82,975 B, MD5 5c9e06c5…) with a semantic-stability proof: normalized token streams of the live page and the frozen 2026-07-31 snapshot are identical (10,222 tokens each, control-probed) — retroactively confirming seven earlier rows' model-config-dependent work ran against an unchanged page.

claude-config 0.21.7 / criteria 1.15.0:

  • The audit skill advertised settings auditing yet had zero coverage of the model-configuration keys. New category H (Model and effort settings) — four rows, each a value the harness accepts into a settings file and then declines to honor: effortLevel: max/ultracode (not accepted there), fallbackModel beyond three after dedup (with the raw-maxItems:3 schema test reported separately — the two tests genuinely diverge, verified empirically), a specific entry disabling its family wildcard (including the Mantle-ID and custom-model-option arms), and enforceAvailableModels against an unset/empty list (rated error per the skill's own enforcement-bypass rubric). Visibility stated per row from the page's own text (row 3's substitution notice; row 2's documented silence). Category H is wired into the skill's phase flow: "Eight categories", a Category H section, and a MANDATORY Phase 3.3 live model-config fetch gating every H finding. settings.local.json routed through the safe-read path.
  • criteria I17's carve-out justification corrected (1.15.0): it claimed the schema makes effortLevel: max "unreachable" in settings — false (JSON Schema is advisory; the harness reads a violating file). The directive is unchanged; the justification now states the true reason and points at the sibling that owns the file-level check, making the two halves of one plugin cohere.
  • Loop-lane alias stamp refreshed in place (prescribed by that convention's own no-drift rule; claim re-verified against today's bytes). CLOSE-2 re-confirmed discharged. The roster's named component (dotfiles pin audit) declined — machine-scope, not this repo's to own.

Recorded upstream: schemastore's effortLevel description omits Opus 5/Sonnet 5 from its effort-support list — stale against the live page; their fix, flagged because this PR now cites that schema.

Test plan

  • markdownlint 0 errors; changelog parity all modes; instruction-scan.test.sh 46/46; conflict-scan.test.sh 41/41; scripted quote fidelity (6 literals exact, 3 near-miss control probes absent); schema constraints script-verified including the no-uniqueItems fact the divergence rests on.
  • Two full independent passes converged: the producer's reviewer (found the blocking category-letter collision that made the section unreachable, plus 8 accuracy/coherence defects — all fixed) and an orchestrator-commissioned Fable verifier (upstream fidelity, the stability-proof reproduction, IA-10-boundary and quote-vs-cite adjudications, then a delta re-verify of the fix round: every item PASS, empty defect list, both its residual observations non-blocking).
  • Producer self-caught one defect pre-review (a sorted dedup misidentifying dead chain entries) and corrected one of its own premises when tested (A–F never quote upstream; the quote call stands on the consumer-repo ground instead, disclosed as a deliberate departure).

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_019gaVX25Txd6GXdiu9HEH3X

…reached

The `audit` skill advertises settings-file correctness against current official
docs, and categories A-G reach schema, permissions, MCP servers, hooks, plugins,
environment variables, and the skill-listing budget. None reached the
model-configuration keys, so `effortLevel`, `fallbackModel`, `availableModels`,
and `enforceAvailableModels` went unaudited inside the skill's own stated scope.

Category H adds four rows sharing one shape: each names a value the harness
accepts into the file and then does not apply as its author expects.
`effortLevel: max` is not the level that persists; a fourth `fallbackModel`
entry risks being ignored; a specific model listed beside its own family
wildcard narrows the allowlist to that one version; `enforceAvailableModels`
without a non-empty `availableModels` does nothing at all. Phase 2's
enumeration, `validation-categories.md`, and a mandatory Phase 3 model-config
fetch move with it, so the skill's own flow reaches the category.

Three rows are `warning`; the `enforceAvailableModels` pairing is `error`,
because this skill's own severity guide rates an enforcement bypass that way and
that is what the finding is - an administrator who set the key believes the
Default option is constrained when it is not. That row reports the pairing the
page states, not the placement: both keys belong in the highest-precedence
managed source, and no file this skill reads can decide whether they are there.

How loudly each surfaces differs, so the rows say so individually rather than
the section claiming a blanket silence - false for the allowlist row, where a
narrowed alias shows a substitution notice naming both models. Two rows also
have an authoring-time path: the declared schema constrains `effortLevel` by
`enum` and `fallbackModel` by `maxItems`. They stay because the schema is
advisory and the harness reads a file that violates it, and where the two
authorities disagree they are reported separately - `maxItems` caps raw array
length while the page caps after deduplication, so a four-entry chain holding
one duplicate fails the schema and satisfies the harness.

Rows were admitted on one test - the key must appear in a file this skill
actually opens. That excluded the managed-source placement rule, which no local
file can decide. `modelOverrides` key validation is excluded separately and
stated as a note: validating a key means resolving it against Models overview,
whose own restatement deferral governs new consumers of its facts.

The separate frontmatter-`effort` lint stays deferred and untouched. Category H
differs from it on three counts - a different key, a different file, and a host
that already exists - which is what that deferral permits.

The rows restate the session-only effort semantics rather than citing this
repo's philosophy doc, because this checklist ships into consumer repositories
where that doc is absent and the citation would resolve to nothing. They also
quote the page verbatim where A-F restate their sources inline; that departure
is deliberate, and the Phase 3.3 fetch is what keeps the quotes honest.

Also fixes a false claim the new category exposed: `I17`'s `effortLevel: max`
carve-out justified itself with the settings schema making the value
"unreachable there". The schema is advisory, the value is writable, and the
harness reads a file that violates it - which is why category H checks for it.
The carve-out is unchanged and still correct; it now rests on the
editor-catches-it reason and points at the category owning the file-level check.

The loop-lane convention's model-alias claim carried a 2026-07-23 verification
stamp predating Opus 5 GA and v2.1.219. Re-verified against today's bytes and
holding, so the date refreshes in place per that convention's own rule that a
no-drift firing takes no changelog entry and no bump.

Verified against https://code.claude.com/docs/en/model-config fetched as raw
markdown, 82,975 B, MD5 5c9e06c53a3dcd6defe81b0590923a93, and against
https://json.schemastore.org/claude-code-settings.json. Every quoted literal
and both schema constraints matched by script, with near-miss control probes.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@claude

claude Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 5m 17s —— View job


Review: PR #1923 — audit the model-configuration settings the harness accepts but declines to honor

Scope reviewed: all 7 changed files (docs/conventions/loop-lane/README.md, plugins/claude-config/.claude-plugin/plugin.json, plugins/claude-config/CHANGELOG.md, plugins/claude-config/skills/audit-instructions/reference/criteria.md, plugins/claude-config/skills/audit/SKILL.md, plugins/claude-config/skills/audit/context/validation-categories.md, plugins/claude-config/skills/audit/reference/audit-checklist.md). Security review is left to the dedicated security lane per REVIEW.md.

Fresh-docs check: Since this PR's new Category H rows lean heavily on exact upstream wording, I independently fetched code.claude.com/docs/en/model-config and cross-checked every quoted/paraphrased claim (the effortLevel "not accepted here" quote, the fallback-chain "capped at three ... after duplicate removal" quote, the wildcard-disabling behavior including the Mantle ID and ANTHROPIC_CUSTOM_MODEL_OPTION arms, and the enforceAvailableModels-with-empty-availableModels no-op behavior). All of them match the live page verbatim or in substance — no fabricated or stale claims found.

Cross-checked version/changelog consistency: plugin.json 0.21.6→0.21.7 matches the new ## [0.21.7] CHANGELOG.md entry; criteria.md frontmatter 1.14.0→1.15.0 is consistent with the I17 fix described in that changelog entry. SKILL.md's Phase 3.3→3.4 renumbering for "Permission syntax verification" is correctly threaded through (no stale 3.3 references left pointing at the old section). Category count ("Eight categories" / "A–H") is consistent across SKILL.md, validation-categories.md, and audit-checklist.md.

Findings

1. (Moderate) Category H's settings.local.json verification path is broken — the script it points to doesn't emit the values being checked.

audit-checklist.md L125-128 says: for settings.local.json, "follow this skill's safe-read rule and go through check-structure.sh rather than dumping values." But check-structure.sh's local branch only emits Env keys / Deny count / Ask count / Allow count / Plugin keys — it never surfaces effortLevel, fallbackModel, availableModels, or enforceAvailableModels. As written, an auditor following Category H's own instructions for settings.local.json gets zero signal on any of the four keys the category exists to check, so Category H silently doesn't cover that file despite the checklist implying it does.

It's also worth noting these four values aren't secrets (unlike the env block or permission rules check-structure.sh is protecting), so routing them through the counts-only safe-read path isn't actually necessary — a direct jq '.effortLevel, .fallbackModel, .availableModels, .enforceAvailableModels' settings.local.json, the same approach used for settings.json and ~/.claude/settings.json two sentences earlier in this same paragraph, would work and wouldn't require touching the script at all.

Fix this →

2. (Nit) Garbled sentence in the Category H intro.

audit-checklist.md L118-119:

"the declared schema section A checks for constrains effortLevel by enum and fallbackModel by maxItems"

reads as two verbs back-to-back ("checks for constrains") — a word/clause got dropped. The parallel sentence in validation-categories.md L129 ("the declared settings schema constrains effortLevel by enum...") states the same fact cleanly, so this looks like a leftover from editing rather than an intentional phrasing. Suggest something like "the declared schema — the one Section A checks for — constrains effortLevel by enum and fallbackModel by maxItems". Given this repo's own emphasis on exact wording (quote-fidelity scripting, etc.), worth a follow-up polish pass even though it doesn't change any check's behavior.

Fix this →

Not flagged, but noted

  • Could not independently run instruction-scan.test.sh / conflict-scan.test.sh / markdownlint in this sandbox (Bash tool required interactive approval for non-git commands that wasn't available here) — I was unable to independently verify the PR's "46/46" / "41/41" / "0 errors" test-plan claims and am relying on the PR description for those.
  • The fallbackModel dedup jq recipe in the new H row (reduce .[] as $m ([]; if index($m) then . else . + [$m] end)) is correct — it preserves first-seen order, unlike unique, which the row's own prose correctly flags as wrong for this use.
  • docs/conventions/loop-lane/README.md's date-only bump (2026-07-23 → 2026-08-04) and the plugin.json version bump are both mechanical and consistent with the changelog.

@claude

claude Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 1m 13s —— View job


Security Review — PR #1923

  • Fetch diff and read changed files
  • Check for injection surfaces, workflow permission changes, unsafe handling
  • Post findings

No security issues found.

All 7 changed files are documentation/prose and metadata:

  • docs/conventions/loop-lane/README.md — a single date-string update in a verification citation.
  • plugins/claude-config/.claude-plugin/plugin.json — semver bump only (0.21.60.21.7).
  • plugins/claude-config/CHANGELOG.md — new changelog entries (prose).
  • plugins/claude-config/skills/audit-instructions/reference/criteria.md, skills/audit/SKILL.md, skills/audit/context/validation-categories.md, skills/audit/reference/audit-checklist.md — Markdown skill instructions adding a new "Category H" audit checklist for model/effort settings (effortLevel, fallbackModel, availableModels, enforceAvailableModels).

None of this touches .github/workflows/*, contains executable code, alters tool permissions:/allowedTools, introduces shell/run: blocks, handles secrets or tokens, or adds network egress. The jq snippets included in audit-checklist.md are illustrative documentation text for a human/agent to run manually during a future audit — they are not embedded in any automated workflow or hook in this diff, so there's no injection surface via untrusted input reaching them. No GitHub Actions patterns (pull_request_target, workflow_run, script injection via github context) are present since no workflow files changed.

This PR is a low-risk, docs/skill-content-only change with no security-relevant surface.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 66a85aab78

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/claude-config/skills/audit/reference/audit-checklist.md Outdated
Comment thread plugins/claude-config/skills/audit/reference/audit-checklist.md Outdated
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

Claude has reviewed this PR 2 times. The lane skips further automatic reviews after 5; deleting this comment resets the count.

…te the enforce check

Two defects in the category H that 66a85aa added.

The safe-read route could not surface the keys. Category H reads
`settings.json` and `~/.claude/settings.json` directly, but the safe-read rule
routes `settings.local.json` through `check-structure.sh`, and that helper
emitted only environment, permission, and plugin counts. A category H key
living only in the local file was therefore invisible, and its defect silently
missed - the exact failure the category exists to catch.

Counts could not have closed the gap: the allowlist wildcard rule turns on which
family each entry names, and the fallback cap turns on entry order. So the
helper now reports the four keys by value for that file. They are configuration
identifiers - level names, model names, a boolean - not credentials, so the
env-value and secret-field guard is untouched, and new assertions prove env
values and env key names still never appear in the output. `unset` and
`(empty list)` are reported distinctly because they are different findings.
Existing output lines are unchanged and the new ones appended; the script's own
suite went from 10 checks to 24, all passing, shellcheck clean.

The enforcement-bypass row fired on the wrong condition. It keyed on
`enforceAvailableModels` being present, so `{"enforceAvailableModels": false}`
with no allowlist - someone turning enforcement off deliberately - would have
been reported as an `error`. It now requires the flag to be `true` AND the list
unset or empty, gating on the value rather than the key. Severity stays `error`
for the gated case, which is a real bypass: the administrator believes the
Default option is constrained when it is not.

Checklist and `validation-categories.md` both name what the helper emits, so the
local-file route is documented where an auditor will look rather than implied.

Quote fidelity re-run against the same capture, unchanged: all six literals
matched, all three near-miss controls absent.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@claude

claude Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 4m 30s —— View job


Review: PR #1923 (updated — re-review after synchronize)

  • Gather context (PR diff, prior review comments, custom-instructions scope)
  • Review new commits since last review (check-structure.sh / check-structure.test.sh changes)
  • Check whether prior findings (safe-read path gap, enforcement-gating gap) were addressed
  • Review remaining changed files for correctness / CLAUDE.md alignment
  • Post final review

Scope reviewed: the diff since the prior review, i.e. commit 686a9fe ("fix(claude-config): make category H reach settings.local.json, and gate the enforce check"), across CHANGELOG.md, validation-categories.md, audit-checklist.md, check-structure.sh, check-structure.test.sh. Security review is left to the dedicated security lane per REVIEW.md (already posted separately, no issues).

Prior findings — status

Both my earlier Moderate finding and the two Codex P2 comments described the same underlying gap, and both are now fixed:

  1. Safe-read path couldn't surface category H keys (Moderate, fixed). check-structure.sh's local branch now emits Effort level, Fallback chain (raw + post-dedup counts), Fallback entries (in order, undeduped), Available models, and Enforce available models by value, distinguishing unset from (empty list). The env-value/secret guard is untouched — env and permissions stay counts. audit-checklist.md L125-132 and validation-categories.md now document what the helper emits, so this is discoverable where an auditor would look rather than only implied.

  2. Enforcement-bypass row fired on key presence instead of value (Moderate, fixed). audit-checklist.md L139 now reads enforceAvailableModels: true is paired with a non-empty availableModels, with the jq recipe gated on select(.enforceAvailableModels == true). An explicit false (enforcement deliberately disabled) is correctly no longer flagged as an error.

Traced the new jq carefully for edge cases (fallbackModel/availableModels present as null, absent keys, empty arrays) — null | length is 0 in jq, so the (empty list) branch is taken safely in all these cases; no crash paths found.

Test coverage: new Case 5 (values present, effort/fallback/allowlist/enforce all surfaced, env still counted not dumped, no secret or env-key-name leak) and Case 6 (absent-vs-empty distinction) both use the file's existing assert_exit/assert_contains/assert_not_contains helpers correctly. Verified the commit message's "10 checks before → 24 after" arithmetic by hand: it holds specifically in an environment where Case 4's chmod 000 unreadable-file simulation is skipped (e.g. a CI container running as root, where [[ -r ... ]] still reports readable) — worth knowing if that count is ever re-verified in a non-root shell, where it'd read 15→29 instead. Not a defect, just a caveat on a specific number if anyone re-checks it later.

Still open (non-blocking)

  • (Nit, unresolved) audit-checklist.md L118-119 still has the garbled sentence flagged in the prior review: "the declared schema section A checks for constrains effortLevel by enum..." (two verbs back to back). This fix commit didn't touch it — reasonable, since it's a pure wording nit and the commit was scoped to the two functional gaps above. Fix this →

Not flagged, but noted

  • Could not execute check-structure.test.sh, shellcheck, or markdownlint in this sandbox (non-git Bash commands required interactive approval unavailable here) — same limitation as the prior review run. Verified the new jq logic and test assertions by manual trace instead of execution.
  • Confirmed Category H's table intentionally omits Category G (skill-listing budget — its checks are procedural and documented as living in SKILL.md, per audit-checklist.md L5), so the A–H header sequence with no G-row table is correct, not a gap.
  • CHANGELOG.md's [0.21.7] entry was amended in place by the fix commit rather than double-bumping the version — correct, since 0.21.7 was still unreleased when the fix landed.

Both defects raised in the original review (and matched independently by Codex) are resolved correctly, with test coverage added for the new behavior. Only the pre-existing wording nit remains open.

@claude

claude Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 2m 16s —— View job


Security Review — PR #1923 (re-run on synchronize)

  • Fetch diff and read changed files (focus on new script changes)
  • Check for injection surfaces, workflow permission changes, unsafe handling
  • Post findings

No security issues found.

This job re-ran because the PR was updated with a new commit (686a9fe) since the prior security pass. That commit touches:

  • plugins/claude-config/skills/audit/scripts/check-structure.sh — adds four new fields (Effort level, Fallback chain, Fallback entries, Available models, Enforce available models) to the local branch's jq output for settings.local.json.
  • plugins/claude-config/skills/audit/scripts/check-structure.test.sh — two new fixture-driven test cases covering the above.
  • plugins/claude-config/skills/audit/reference/audit-checklist.md, skills/audit/context/validation-categories.md, CHANGELOG.md — prose only.

Focused on the two script files as the only ones with executable logic:

  • The new jq filter (check-structure.sh L104-115) reads settings.local.json via jq -r and prints the results with printf. All string interpolation (\(...)) happens inside jq's own expression language, not shell — arbitrary values in fallbackModel/availableModels (even ones containing $(), backticks, or ") can't reach shell evaluation this way, since they're never captured into a shell variable that's later eval'd or interpolated into a command string. Output goes straight to stdout.
  • The existing secret-safety property is preserved and now test-verified: env and permission entries stay counts-only (L105-108), and the new test fixture explicitly asserts a planted TOKEN/sk--prefixed value never leaks into the script's output (check-structure.test.sh L118-137).
  • PROJECT_ROOT resolution and the read-permission/JSON-validity guards (L38-49, L70-76) are unchanged by this commit.
  • No workflow files, permissions: blocks, or run: steps are touched; nothing in this diff executes untrusted GitHub Actions context data.

The two Codex P2 comments from the prior round (settings.local.json coverage gap, and gating the enforcement check on enforceAvailableModels == true) were both addressed by this commit — those are correctness fixes, not security findings, and are consistent with what's now in the script/checklist.

@kyle-sexton
kyle-sexton merged commit 81680cb into main Aug 4, 2026
32 checks passed
@kyle-sexton
kyle-sexton deleted the docs/roster-r18-model-config branch August 4, 2026 11:38
kyle-sexton added a commit that referenced this pull request Aug 4, 2026
…and its fences (#1924)

## Summary

Doc-alignment roster row 241: **Choosing a Claude model and effort level
in Claude Code** (claude.com blog) — the post the owner's model-routing
lane and two main-tree surfaces cite for the model-vs-effort dial
discrimination. First custody baseline taken (article-body-only hash, so
site chrome can't fake drift). The roster's premise was corrected en
route: the post was *not* "digested nowhere" — planning's session-config
already carried a faithful two-knob digest; what it lacked was custody
and the post's own qualifiers.

**planning 0.27.3** — the "Two orthogonal knobs" section gains:

- Its citation, led by the strongest ground available: model-config's
own delegation sentence ("For guidance on which model and effort level
fit different kinds of work, see [the post] on the blog") — the
reference page points *at* the post; the narrowed negative is the
supporting ground: no reference page states the try-versus-know
**diagnostic** (choosing-a-model *orders* the levers — "Tuning effort is
often a better lever than switching models" — and the effort page pairs
effort against prompting; ordering a lever is not diagnosing which
failure you have).
- The post's **context-first prior step** neither knob had: on a wrong
answer, examine the provided context before touching a dial — sharpened
here because the step is this skill's own product (the Brief *is* the
context fix).
- Two missing fences: the discriminator is "a starting point, not a hard
rule" (the post's figure caption, disclosed as such), and raising effort
is sharpest below the model's default.
- The ambiguity signal: ambiguity the rounds could not retire argues up;
a Brief precise enough to execute from argues down.

Both pre-existing main-tree citations verified holding against the live
post. Owner-facing findings routed to the dotfiles ledger (not this
repo's to edit): the CLAUDE.md lane's dropped "clearly tried" (making
the two branches overlap), the Fable-reservation grounds contradiction,
the missing context-first step — with one earlier finding partially
withdrawn after the verifier surfaced the choosing-a-model source.

## Test plan

- Docs-only; markdownlint 0 errors; check-skill PASS identical to base;
scripted quote fidelity across four live sources (post, effort,
model-config, choosing-a-model) — after the producer caught and fixed
its own circular self-match, all spans verbatim with one disclosed
editorial elision.
- Independent fresh-context Fable verifier (rationale withheld, 8 binary
criteria incl. an adversarial test of the load-bearing negative): 7/8
PASS; its one FAIL (the negative stated too broadly) fixed with its
prescribed wording, the fix cross-verified verbatim against both live
pages before amending.

## Related

- No linked issue.
- Doc-alignment loop, roster row 241. Predecessors: #1908#1920, #1922,
#1923.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_019gaVX25Txd6GXdiu9HEH3X

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant