Commit Graph
1408 Commits
Author SHA1 Message Date
github-actions[bot] f106b101ab chore: refresh generated model catalog [skip ci] 2026-09-19 12:31:07 +00:00
github-actions[bot] 77322698e4 chore: refresh generated model catalog [skip ci] 2026-09-19 00:50:24 +00:00
Dark-Alex-17 e237845827 feat: Improved comment etiquette for the sisyphus suite 2026-09-18 12:31:48 -06:00
Dark-Alex-17 4571ed647e feat: Improved PR hygiene for commenting and posting review summaries on PRs created by the architect and Sisyphus 2026-09-18 12:27:37 -06:00
Dark-Alex-17 0b55f7c735 test: update agent suite tests to work with new code review suite changes 2026-09-18 11:42:54 -06:00
Dark-Alex-17 461245a57c fix: force code review lanes to be additive only, not opt-in, and bump the verifier run cap 2026-09-18 10:44:04 -06:00
github-actions[bot] dcf2d99fa9 chore: refresh generated model catalog [skip ci] 2026-09-18 12:32:28 +00:00
github-actions[bot] d524328d86 chore: refresh generated model catalog [skip ci] 2026-09-18 00:51:35 +00:00
Dark-Alex-17 e002bd6727 feat: Suppor mid-turn auto compressions so sufficiently large histories don't create deadlocks for max input tokens 2026-09-17 17:12:18 -06:00
Dark-Alex-17 56ed4664cd Merge branch 'fix/proper-round-pairs'
CI / All (ubuntu-latest) (push) Failing after 30s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-09-17 16:33:31 -06:00
Dark-Alex-17 3ee78a46df fix: improved caching by simply keeping groupings of tool calls by terminating a round on each tool merge into the bucket, rather than waiting for LLM narration to end a round. 2026-09-17 16:32:21 -06:00
Alex Clarke dc6c176b4f Merge pull request #31 from Dark-Alex-17/feat/PLAN-hooks-v2
PLAN-hooks-v2: hook system follow-ups and v2 features
2026-09-17 15:26:41 -06:00
Dark-Alex-17 00de9d40d1 Merge remote-tracking branch 'origin/main' into feat/PLAN-hooks-v2 2026-09-17 14:55:31 -06:00
Dark-Alex-17 08dfbac6f6 style: applied uniform style across the new rag and mcp hook implementation 2026-09-17 14:39:33 -06:00
Dark-Alex-17 74e7bc54eb feat(TASK-061): assert COYOTE_ROLE on all session/turn events; fix two stale comments
Cover the remaining role-carrying events with test_sink assertions:
session.started (ctx-held role), session.resumed and session.ended
(session-held role), turn.interrupted and turn.failed. Correct the
mcp_factory race comment (the loser fires a second plain connected and
overwrites the winner's map entry, not a reconnect) and the role_extras
doc (derived roles fall through to the session-held role).
2026-09-17 14:00:07 -06:00
Dark-Alex-17 dc2f26d0e6 feat(TASK-061): fix reconnect-flag race and completed-after-save hook ordering
- McpFactory::acquire now reads the live probe and the reconnect flag
  under one lock section so a racing first connect cannot be
  misreported as COYOTE_MCP_RECONNECT=true; the residual window across
  the spawn await (no singleflight) is documented. try_get_active,
  now unused, is removed.
- All three RAG sync funnels (init, init_with_config,
  refresh_document_paths) fire rag.sync.completed only after a
  successful save(), matching the documented contract; a save error
  routes through rag.sync.failed with COYOTE_ERROR before propagating.
- New funnel-level tests pin init_with_config's started-then-completed
  ordering, the failed event on a save error, and the failed event on
  the sync Err arm that a user abort surfaces through.
2026-09-17 13:43:16 -06:00
Dark-Alex-17 4e53e143bb fix: use ssh to clone private git repos in sandboxes
CI / All (ubuntu-latest) (push) Failing after 30s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-09-17 13:29:45 -06:00
Dark-Alex-17 166a3807f8 fix: Route git clones through the sbx proxy so the proxy can inject credentials 2026-09-17 13:16:47 -06:00
Dark-Alex-17 04b983459b feat(TASK-061): add rag.sync.* and mcp.server.* hook events plus COYOTE_ROLE
- Register five new events (26 -> 31): rag.sync.started/completed/failed
  and mcp.server.connected/failed, whitelist-gated for agent global hooks
  like every non-escalation event
- Bracket every real RAG document sync with rag.sync.* via pre-resolved
  RagSyncHooks snapshots carried into the build funnels; cache hits, pure
  loads, and attaches fire nothing, and a user abort mid-sync counts as a
  failure
- Fire mcp.server.connected only on real spawns in McpFactory::acquire
  (reusing a live server is silent), marking respawns of a previously
  live key with COYOTE_MCP_RECONNECT; mcp.server.failed carries
  COYOTE_ERROR and COYOTE_MCP_AUTH_REQUIRED on auth failures. Boot-time
  registry connects deliberately fire nothing
- Carry COYOTE_ROLE on session.* and turn.* events, resolved at fire
  time so mid-session role switches and temp roles are reflected
2026-09-17 13:16:05 -06:00
Dark-Alex-17 fcdcb303a3 style: Cleaned up unnecessary path prefixes, imports, stray scripts, and some documentation 2026-09-17 12:00:16 -06:00
Dark-Alex-17 5bd1174ddd fix: isolate the adversary review-stack lock for parallel tests and non-POSIX platforms
The advisory flock in run_checks.py uses one system-wide lock path
(~/.cache/coyote/review-stack.lock). Concurrent runs of the timeout/deadline
tests contend on it: the losers sleep in the bounded retry loop, the total
deadline (computed before lock acquisition) expires, and commands record
'skipped: deadline' instead of the expected timeout/kill behavior.

- run_checks.py: ADVERSARY_STACK_LOCK_PATH env override relocates the lock
  file (isolation seam, documented alongside the other env seams); every
  other lock semantic (default path, bounded wait, proceed-unlocked warning,
  fd held for process lifetime, skip when no commands) is unchanged.
- run_checks.py: guard the POSIX-only fcntl import — windows-latest CI has
  python3 and runs these tests, so the unconditional import crashed the
  script there; on non-POSIX the lock is now skipped with a stderr WARN,
  matching the script's documented non-POSIX limitations.
- tests: the shared adversary_script_command helper points every spawned
  script at its own throwaway lock path, isolating the run_checks tests from
  each other and from any real review running on the same machine.
- tests: adversary_run_checks_stack_lock_path_is_env_overridable pins the
  override (lock file appears at the overridden path across two sequential
  runs; no lock file is created when no commands are declared).
2026-09-17 10:49:35 -06:00
Dark-Alex-17 bfa058130a merge: absorb main (concurrent-build file locks) into feat/PLAN-hooks-v2 2026-09-17 10:34:23 -06:00
Dark-Alex-17 1c73b82812 feat: create file locks on concurrent builds and scaffolding verification/tests to prevent race conditions and conflicts while reviewing 2026-09-17 10:34:00 -06:00
Dark-Alex-17 9e811c088f merge: absorb main (model catalog refresh) into feat/PLAN-hooks-v2 2026-09-17 10:17:38 -06:00
github-actions[bot] 02b9986807 chore: refresh generated model catalog [skip ci] 2026-09-17 12:35:37 +00:00
Dark-Alex-17 ad9b319149 test(TASK-050): give each macro-bracketing marker its own file
cmd opens '>>' redirection targets write-exclusive, so when the STARTED
and terminal hook children appended to one shared log concurrently, the
loser of the open race hit a sharing violation and silently dropped its
marker line -- a permanent loss that surfaced as an intermittent
Windows-CI-only failure of the macro-bracketing suite (the engine's exit
drain was verified sound: spawn intent is registered synchronously at
fire time and covers macro-context fires). One file per event makes
writer overlap impossible while keeping every exact-count assertion
byte-identical.
2026-09-17 01:00:48 -06:00
Dark-Alex-17 611900057c test(TASK-050): wait on every expected marker before asserting hook counts
Hook processes are fire-and-forget: the binary's exit drain acknowledges
spawns only, so the STARTED writer can land after the terminal one. The
macro-bracketing tests waited only for the terminal marker and then
asserted STARTED's count immediately, flaking on Windows CI and loaded
Linux runners. The bounded wait now covers every expected marker before
the exact-count and absence assertions run; the probe script's scenario K
comment is corrected to state the spawn-ack-only contract it already
waits around.
2026-09-17 00:33:37 -06:00
Dark-Alex-17 ec66494e8f docs(TASK-052): add hooks feature bullet to the README quick links 2026-09-17 00:16:08 -06:00
Dark-Alex-17 3d37ed237a test(TASK-050): build the macro-bracketing hook commands in cmd's dialect on windows
The fixture's hook commands were sh-only: single-quoted redirect targets
are literal under cmd /C, so the marker log never received content and
all six tests timed out on windows-latest. Build the marker command
per-platform — parenthesized `(echo MARKER)>> "log"` on Windows to
avoid cmd's trailing-space echo gotcha — and move the interpolated
paths into single-quoted YAML scalars so Windows backslashes are not
read as double-quoted-scalar escapes. Also replace the cleanup call
that a wait timeout skipped with a drop guard, so failed runs no
longer leak temp dirs.
2026-09-16 23:51:18 -06:00
Dark-Alex-17 83978617bc fix: bootstrap the async entry point on a 16 MiB thread to survive Windows
Windows links the process main thread with a ~1 MiB stack (unix defaults
to 8 MiB). Coyote's root async future is a compiler-generated state
machine whose debug-build poll frames blow through 1 MiB the moment
block_on polls it, so every invocation of the binary on Windows died at
startup with STATUS_STACK_OVERFLOW (0xC00000FD) before doing any work.

Replace #[tokio::main] with an explicit bootstrap: main() spawns a
dedicated thread (named "main", 16 MiB stack) that builds the same
multi-thread enable_all runtime and block_on's the async body, then joins
and propagates the Result unchanged; panics are re-raised on the real
main thread via resume_unwind without re-invoking the panic hook, so
panic output and exit codes stay identical. Runtime worker threads get
the same 16 MiB headroom since spawned tasks embed the same class of
giant state machine.

The bootstrap is unconditional rather than cfg(windows)-gated: the unix
arrangement is behaviorally identical (same runtime flavor and driver
set — tokio's ctrl_c signal driver is thread-agnostic), and one shared
code path removes the entire small-main-stack class everywhere instead
of special-casing one OS. Verified on Linux with the full test suite
(inspection_flags 12/12) and the hooks usage probe end-to-end, including
the terminal ctrl-c -> agent.interrupted scenario.
2026-09-16 23:38:28 -06:00
Dark-Alex-17 bf10aeb74d test(TASK-047): split the ntfs short-name guard test by platform reality
On NTFS the fixture self-collides: .builtin-manifest auto-generates the
8.3 short name BUILTI~1, so writing that fixture file opens the manifest
through its alias and clobbers it -- the exact threat the guard models.
Gate the fixture-based test to unix, and add a windows counterpart that
proves the collision where 8.3 generation is enabled and asserts the
filter invariant (alias-shaped entries are neither removal candidates
nor claimed) either way.
2026-09-16 23:24:18 -06:00
Dark-Alex-17 c2e30c12a6 fix(TASK-046): honor the --mcp-list inspection contract without a vault
Pure --mcp-list never reads secrets (only --mcp-add does), yet the mcp
action path always ran strict Vault::init, so a virgin environment with
no password file exited 1 instead of printing the empty listing. Route
the mcp_inspect path through the established Vault::init_lenient
constructor; get/remove/add stay on the strict init.

Make tests/inspection_flags.rs hermetic against host state: every
spawned binary gets a fresh fake HOME (and USERPROFILE) so a real
~/.coyote_password can't mask virgin-environment regressions, scrubs
IS_SANDBOX/COYOTE_PROVIDER/COYOTE_PLATFORM outside the sandbox-mode
tests, and pins CWD so workspace mcp.json discovery can't leak in.
New tests pin both sides of the boundary: the lenient list path (empty
and configured, never bootstrapping a password file) and the strict
vault gate for get/remove/add.
2026-09-16 23:09:59 -06:00
Dark-Alex-17 879cf8e097 merge: absorb main (PR #29 merged, PR #30 responses-api, catalog refresh) into feat/PLAN-hooks-v2 2026-09-16 22:34:23 -06:00
Dark-Alex-17 22078b1ddc test(TASK-051): cover windows hook dispatch, payload posture, and install semantics
Add a #[cfg(windows)] dispatch test module exercising the real cmd /C
raw_arg path: env delivery through a verbatim command line, fire()
detachment via a gate-file batch loop, payload-file lifecycle with the
per-user temp-dir posture assertion, backslashed COYOTE_HOOKS_DIR
install override, per-scope cwd resolution with an end-to-end %CD%
check, and exec-bit-free install. Hoist wait_for to shared test scope
and move the two OS-agnostic dispatch tests (spawn-failure payload
cleanup, debug-only error paths) into an ungated dispatch_portable
module so both cfg lanes share them instead of duplicating.
2026-09-16 22:32:42 -06:00
Dark-Alex-17 f7d0f93fa1 fix(TASK-050): flag invalid whitelist globs that extend no event name
The unknown-entry diagnostic only inspected gate entries prefixed by the
current event's full name, so a partial glob like 'tool.st*' — which
extends no event name at all — was silently inert on every event.
Entries containing '*' now validate against the wildcard grammar
(*, *.*, *.<name>, <event>.* over a real event) and anything outside it
gets the same debug-level diagnostic; exact entries keep the existing
event-prefix path unchanged.
2026-09-16 21:54:25 -06:00
Dark-Alex-17 4b2428b334 test(TASK-050): probe wildcard whitelist grammar and macro-without-agent inertness
Scenario K pins the global_hooks wildcard forms (exact, <event>.*, *.<name>, *.*) and partial-glob inertness against a real binary; Scenario L proves --macro without --agent fires zero agent.* events.
2026-09-16 21:42:15 -06:00
Dark-Alex-17 dee89d4ca3 feat(TASK-050): bracket --macro runs like --execute and close the isolated-macro leak
The --macro dispatch arm now opens the top-level agent bracket before
macro_execute and closes it signal-aware afterwards, draining pending
hooks like the --execute arm; both bracket calls no-op without a loaded
agent, so agent-less macro runs are unaffected. The isolated arm of
macro_execute closes the bracket an in-macro .agent step opens on its
fresh context, on the step-failure path too. Binary-level tests pin
agent.started plus exactly one terminal event for the --agent flag and
for in-macro .agent steps in both isolation modes, on success and
failure.
2026-09-16 21:23:04 -06:00
Dark-Alex-17 90f7631b35 feat(TASK-050): add wildcard grammar to the global-hooks whitelist
Whitelist entries now admit hooks via <event>.* (every hook of that
event), *.<name> (that hook name on every event), and * / *.*
(everything), alongside the unchanged exact <event>.<name> form.
Partial globs like tool.st* are not wildcards: they admit nothing and
still land in the unknown-entry debug diagnostics, while <event>.*
entries are exempt from them. Escalation events keep resolving ungated
regardless of whitelist content.
2026-09-16 21:23:04 -06:00
Dark-Alex-17 6ae093a482 fix(TASK-049): cover role and agent scopes in escalation bypass test; keep spawn-failure logging at debug 2026-09-16 20:58:24 -06:00
Dark-Alex-17 c73d5317a0 feat(TASK-049): harden hook dispatch against NUL env values and nested-supervisor latch misses
- strip interior NUL bytes from hook env values in fire_resolved so
  agent-authored text (e.g. escalation questions) cannot suppress a hook
  via a failed spawn; raise the spawn-failure log to warn!
- make Supervisor::has_active_tasks recurse into child supervisors,
  mirroring cancel_recursive, so a prompt ctrl-c that cancels only a
  grandchild still latches as an interruption
- document the signal-first vs error-first classification asymmetry at
  top_level_agent_finished / SpawnResultHooks::fire and the deliberate
  REPL teardown cancel; pin ctrlc-with-no-error => agent.interrupted
- probe Scenario J: pty-driven ctrl-c during a live headless --agent run
  fires exactly one agent.interrupted with no COYOTE_AGENT_ERROR
2026-09-16 20:31:36 -06:00
Dark-Alex-17 a3b71a6889 feat(TASK-049): classify idle-prompt ctrl-c + ctrl-d as a completed exit
Ctrl-C at the reedline prompt no longer latches the abort signal
unconditionally: latch_prompt_interrupt only marks an interruption when
the supervisor had live agents/jobs to cancel (new
Supervisor::has_active_tasks), so the advertised exit path (ctrl-c at an
idle prompt, then ctrl-d) fires agent.completed at teardown instead of
agent.interrupted. Mid-turn ctrl-c still latches directly via
wait_user_interrupt/poll_abort_signal, preserving the
interrupted-then-exit classification.

Also: probe-lane tests for escalation.answered single-fire,
escalation question env truncation, agent.failed inverse gate, and
hooks probe Scenario I (session.started vs session.resumed); comment
notes on escalation.answered semantics and the agent__cancel blind
spot.
2026-09-16 19:55:52 -06:00
Dark-Alex-17 739c1be8bb feat(TASK-049): add escalation.raised/answered, session.resumed, and agent.interrupted hook events
- escalation.raised fires in the child ctx after queue submit; escalation.answered
  fires in the parent ctx from the taken request, so both agree on origin envs
- escalation.* resolves global hooks ungated: the per-agent global_hooks whitelist
  never applies to these root-level events
- session.resumed fires on the use_session resume leg, inverse of session.started
- agent.interrupted replaces agent.completed/failed when the abort signal was
  ctrl-c'd: top-level bracket (now abort-aware), spawned-subagent result arm
  (agent__cancel / REPL ctrl-c), and graph agent nodes via the session signal;
  exit_agent and the shell-exit path stay non-interrupted by passing no signal
- document the deliberate non-fire paths (direct prompts, escalation timeout and
  parent-drop, graph approval nodes)
2026-09-16 19:30:59 -06:00
Dark-Alex-17 1744d2ba52 test(TASK-048): cover canonicalized hooks-dir override warning and payload orphan unlink on spawn failure
Two conformance gaps were tests-only:

- install_builtin_hooks warning: a #[serial] test proves the canonicalized
  compare stays quiet when COYOTE_HOOKS_DIR merely respells the default
  (via a '..' hop and, on unix, a symlink), and a second test proves a
  genuine override still warns on a fresh install where the default hooks
  dir does not exist (fallback compare).
- run_hook payload cleanup: a dispatch test fires a hook with a payload
  and an unspawnable cwd, anchors on the spawn-failure debug log (and the
  absence of a payload-write failure) so the assertion cannot pass
  vacuously, then polls with a bounded deadline until the payload file is
  unlinked.

Both tests were verified red-capable: reverting the canonicalization or
skipping the unlink on the error branch makes them fail.
2026-09-16 19:05:17 -06:00
github-actions[bot] 6804f198bc chore: refresh generated model catalog [skip ci] 2026-09-17 00:50:55 +00:00
Dark-Alex-17 7ace1731c6 fix(TASK-048): cover agent and repo scripts in eol=lf rules; probe Scenario H for installed bundled scripts
Extends the .gitattributes eol=lf guard to assets/agents/** (33 embedded
shell scripts share the CRLF failure mode the hooks rules fix) and
scripts/*.sh. Adopts the probe-authored Scenario H: black-box checks of
the installed log-events.sh (XDG default, 0600, UTC header, secret
filter, symlink refusal) and notify.sh (control-byte-stripped fallback),
plus non-executable .builtin-manifest.
2026-09-16 18:48:15 -06:00
Dark-Alex-17 89a9398c28 feat(TASK-048): install polish, hooks-dir mixin scan, script and probe hardening
- one shared exec-bit predicate (Language-recognized extensions => 0755) used by
  every hook/agent installer; non-script files in hooks dirs stay non-executable
- mixin discovery scans the global hooks dir and each agent's hooks dir;
  .builtin-manifest is never scanned
- NTFS 8.3 short-name aliases rejected as builtin-manifest entries
- hook-script install announcements restricted to real hook locations
- COYOTE_HOOKS_DIR override warning compares canonicalized paths
- embedded assets decode via checked from_utf8 instead of from_utf8_unchecked
- log-events.sh defaults under ${XDG_STATE_HOME:-$HOME/.local/state}/coyote/hooks.log
  keeping 0600/symlink-refusal/secret-filter hardening; notify.sh tty fallback strips
  control bytes; both use UTC ISO-8601 timestamps
- payload files orphaned by a write failure are unlinked; stale coyote-hook-*.json
  payloads swept best-effort at startup
- sweep_removed_agent_hooks never follows symlinked agent dirs
- usage-probe-hooks.sh replaces bare sleeps with a bounded poll, re-verifies 0755 on
  restored scripts, and covers roles/agent hook locations
- TestConfigDirGuard hoisted into crate::testing
- .gitattributes pins LF line endings for bundled scripts

hooks is now a reserved bundle name.
2026-09-16 18:30:02 -06:00
Dark-Alex-17 e7bd75947c feat(TASK-047): three-state hook install mode, top-level manifest reconcile, removed-agent sweep
- 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.
2026-09-16 17:59:09 -06:00
Dark-Alex-17 d89d24aff4 fix(TASK-046): keep inspection leniency off mutating and writing flags
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).
2026-09-16 16:48:57 -06:00
Alex Clarke f59421844a Merge pull request #30 from Dark-Alex-17/feat/PLAN-responses-api
PLAN-responses-api: OpenAI Responses API as the default wire format
2026-09-16 15:51:37 -06:00
Dark-Alex-17 3dc84f5aef fix: revert manual changelog update, as it should only be updated by CI/CD 2026-09-16 15:33:35 -06:00