- Thread an explicit InstallMode (Skip/Prompt/Force) through the hook
installers: startup install_builtins() is pinned to Skip and never
prompts (test-pinned on both mocked-TTY and TTY-less paths);
--install-builtins hooks runs Prompt mode across all three hook
locations (global hooks dir, roles/hooks, agents/<name>/hooks) under
one shared keep-all/replace-all sticky scope; force paths keep
unconditional overwrite semantics.
- Hoist the bundle resolve_conflict/StickyMode/ConflictAction prompt
into shared config::conflict with a NonInteractive policy: bundle
installs keep their non-TTY bail, builtin hook refreshes resolve
non-TTY conflicts to keep. Bundle UX preserved byte-for-byte,
pinned by tests.
- Extend .builtin-manifest reconcile to the resolved top-level hooks
dir (shipped set from HookAssets::iter()); malformed-manifest
fail-safe (zero deletions) preserved and test-pinned.
- Sweep removed builtin agents' hooks: agent dirs absent from the
embed with a hooks/.builtin-manifest reconcile against an empty
shipped set; empty hooks dirs are removed, user files and
manifest-less agent dirs are never touched.
- Update usage-probe-hooks.sh Scenario C to the new non-interactive
keep contract and add black-box Scenarios E-G covering role/agent
hook reconcile, top-level manifest fail-safety, and the
removed-agent sweep.
The look-don't-touch bypass was keyed on load_with_interpolation's
pre-existing bool, which mutating early paths (--authenticate, --auth-mcp,
mcp manage, vault flags) already passed as true meaning only 'skip vault
interpolation' — so they silently lost their first-run config creation.
Split the parameter: skip_interpolation keeps the historical meaning,
a distinct inspection flag alone gates the missing-config default/wizard
bypass.
inspection_only is now computed from an explicit read-only whitelist:
--sync-models is excluded (it downloads and writes models-override.yaml)
and returns to the bootstrap/strict path, keeping only its historical
interpolation-skip and vault leniency. mcp/vault inspection sub-conditions
are hoisted and shared so mutating flag combos never inherit the bypass.
Coverage: sandbox vault contract tests, a sync-models bootstrap-path pin,
a --sync-models scenario in usage-probe-inspection.sh, and a PTY wizard
regression probe (scripts/usage-probe-wizard.sh).
- Skip the builtins bootstrap, function-binary build, and tool-scope
rebuild for inspection-only runs (--info/--list-*/--mcp-list/
--list-secrets without --agent/--role): a pristine COYOTE_CONFIG_DIR
now stays byte-empty (was 269 files written before dispatch)
- Tolerate a model-less default config on the inspection surface via
AppConfig::from_config_lenient + Model::default() in bootstrap;
strict 'No available model' behavior unchanged for the REPL path and
all mutating mcp/vault flags
- --list-secrets degrades to an rc=0 informational message in sandbox
mode (mutating vault flags still fail there), pinned by test
- Add the 041.7-4 regression test: a malformed COYOTE_<NAME>_GLOBAL_TOOLS
env override logs the debug message and leaves global_tools untouched
- Adopt scripts/usage-probe-inspection.sh (10 flags: rc=0 + zero writes,
output required only for --info) and add binary-level
tests/inspection_flags.rs, incl. the agent+--info still-bootstraps pin
Black-box scenarios against a built binary with dry_run configs: hook
cadence + env scoping, runtime inertness of failing hooks, --info firing
zero agent.* events, --install-builtins hooks install/force/cold-start
semantics, and end-to-end firing of the installed log-events.sh via the
documented relative-path wiring.
Add a hidden --generate-models flag that rebuilds models.yaml from
models.dev and OpenRouter merged with the hand-owned quirks overlay,
regenerate the committed catalog with it (393 -> 1090 models), and
retire the Python updater. GitHub becomes fully quirks-owned since
upstream only tracks Copilot ids, legacy ids upstream dropped are
preserved as hand-owned quirks entries, and non-chat model families
(image/tts/audio/realtime) are excluded from generation.