mirror of
https://github.com/boshu2/agentops.git
synced 2026-09-14 15:08:13 +08:00
36dbf77293
* docs(discovery): plan agents control plane hardening
* feat(agents): harden operator control plane
* Release v2.39.0
* fix(tests): align next-work schema test with widened type enum
Commit ed35c47f added `docs` and `chore` to the script's VALID_TYPES,
but tests/scripts/check-next-work-schema-rows.bats:42 still used
`type:"docs"` as its supposed-to-be-rejected example, so the test
now passes through the validator — `bats-tests` has been red on
main since.
Switch the rejected-type example to `finding`, which is still
outside the enum and is also the canonical example cited in the
script's own header comment ("type=finding ... caught at push time").
Local: 11/11 in tests/scripts/check-next-work-schema-rows.bats pass.
* docs: capture finding generator postmortem (#157)
* fix(rpi): sort verdicts deterministically in context and status helpers (#158)
BuildPhaseContext and JoinVerdicts iterated the verdicts map directly,
producing non-deterministic output across runs. Sort keys before
rendering so prompt injection and status logs are reproducible.
Closes council finding W-7 (context-orchestration-leverage batch). The
related buildHandoffContext path was already fixed to delegate to
FormatVerdicts (which sorts); this completes the remaining helpers.
- cli/internal/rpi/status.go: sort keys in JoinVerdicts
- cli/internal/rpi/phased_context.go: sort keys in BuildPhaseContext
- tests: replace "ordering not deterministic" waivers with exact-order
asserts and add a 50-run stability check
https://claude.ai/code/session_01Qw4bZvXUNuLs8Rp1j1homB
Co-authored-by: Claude <noreply@anthropic.com>
* fix(codex): audit noisy hook injections (#159)
* docs: add competitive radar (#160)
* fix(merge): drop unused AgentsDoctorError ref and prune allowlist entries without production refs
---------
Co-authored-by: Claude <noreply@anthropic.com>
121 lines
4.2 KiB
Bash
Executable File
121 lines
4.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# check-agents-hash-snapshot.sh — content-hash gate for ~/.agents/ mutation
|
|
# during test runs.
|
|
#
|
|
# Complements scripts/check-home-isolation.sh which only checks for the
|
|
# t.Setenv("HOME", ...) pattern statically via grep. This gate catches any
|
|
# mutation of the protected ~/.agents/ subtrees, including:
|
|
# - Tests that write to ~/.agents/ without HOME isolation
|
|
# - Tests that write then use os.Chtimes to hide the mtime (the
|
|
# mtime-bypass attack; demonstrated in cli/internal/overnight/recovery_test.go:285
|
|
# for legitimate recovery fixture setup, but the same API can hide
|
|
# test-side poisoning)
|
|
#
|
|
# Usage:
|
|
# SNAP=$(./scripts/check-agents-hash-snapshot.sh capture)
|
|
# go test ./...
|
|
# ./scripts/check-agents-hash-snapshot.sh diff "$SNAP"
|
|
#
|
|
# Scoped to the subtrees Dream and flywheel tests are most likely to
|
|
# corrupt: learnings, patterns, findings, rpi/next-work.jsonl.
|
|
|
|
# shasum presence check — skip gracefully on minimal containers
|
|
if ! command -v shasum >/dev/null 2>&1; then
|
|
echo "WARN: shasum not available; ~/.agents hash gate skipped" >&2
|
|
exit 0
|
|
fi
|
|
|
|
AGENTS_HUB="${AGENTS_HUB_OVERRIDE:-$HOME/.agents}"
|
|
SCOPES=(learnings patterns findings rpi/next-work.jsonl)
|
|
|
|
# --ignore-untracked: exclude git-untracked files from the scoped subtrees
|
|
# so local scratch files (docs/blog/, *_test.txt, etc.) do not trip the gate.
|
|
# Env override: HASH_GATE_IGNORE_UNTRACKED=1 is equivalent to passing the flag.
|
|
# The pre-push wrapper also honors HASH_GATE_TIMEOUT_SECONDS for bounded local
|
|
# runtime when shared ~/.agents state is busy.
|
|
IGNORE_UNTRACKED="${HASH_GATE_IGNORE_UNTRACKED:-0}"
|
|
|
|
args=()
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--ignore-untracked) IGNORE_UNTRACKED=1 ;;
|
|
*) args+=("$arg") ;;
|
|
esac
|
|
done
|
|
set -- "${args[@]+"${args[@]}"}"
|
|
|
|
# is_untracked <path> — returns 0 if the path exists inside a git repo and is
|
|
# untracked per `git status`. Returns 1 otherwise (tracked, ignored, or not in
|
|
# a repo). Safe to call with absolute paths; resolves the repo from the path's
|
|
# parent directory.
|
|
is_untracked() {
|
|
local p="$1"
|
|
[[ "$IGNORE_UNTRACKED" == "1" ]] || return 1
|
|
command -v git >/dev/null 2>&1 || return 1
|
|
local dir
|
|
dir="$(dirname "$p")"
|
|
[[ -d "$dir" ]] || return 1
|
|
# --error-unmatch is noisy; use ls-files with --error-unmatch exit code.
|
|
if ( cd "$dir" && git rev-parse --is-inside-work-tree >/dev/null 2>&1 ); then
|
|
if ( cd "$dir" && git ls-files --error-unmatch -- "$p" >/dev/null 2>&1 ); then
|
|
return 1 # tracked
|
|
fi
|
|
return 0 # inside repo, not tracked
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
snapshot() {
|
|
for scope in "${SCOPES[@]}"; do
|
|
local target="$AGENTS_HUB/$scope"
|
|
if [[ -f "$target" ]]; then
|
|
if is_untracked "$target"; then
|
|
continue
|
|
fi
|
|
shasum -a 256 "$target" 2>/dev/null || true
|
|
elif [[ -d "$target" ]]; then
|
|
while IFS= read -r -d '' f; do
|
|
if is_untracked "$f"; then
|
|
continue
|
|
fi
|
|
shasum -a 256 "$f" 2>/dev/null || true
|
|
done < <(find "$target" -type f -print0 2>/dev/null) \
|
|
| sort
|
|
fi
|
|
done
|
|
}
|
|
|
|
cmd="${1:-}"
|
|
case "$cmd" in
|
|
capture)
|
|
out="${AGENTOPS_HASH_SNAPSHOT:-$(mktemp -t agents-snap.XXXXXX)}"
|
|
snapshot > "$out"
|
|
echo "$out"
|
|
;;
|
|
diff)
|
|
snap_file="${2:-}"
|
|
if [[ -z "$snap_file" || ! -f "$snap_file" ]]; then
|
|
echo "ERROR: diff requires a valid snapshot file path" >&2
|
|
exit 2
|
|
fi
|
|
current=$(mktemp -t agents-snap.XXXXXX)
|
|
snapshot > "$current"
|
|
if ! diff -q "$snap_file" "$current" >/dev/null 2>&1; then
|
|
echo "FAIL: ~/.agents mutation detected during tests" >&2
|
|
echo "--- snapshot before (path: $snap_file)" >&2
|
|
echo "+++ snapshot after" >&2
|
|
diff "$snap_file" "$current" | head -40 >&2
|
|
rm -f "$current"
|
|
exit 1
|
|
fi
|
|
rm -f "$current"
|
|
;;
|
|
*)
|
|
echo "usage: $0 [--ignore-untracked] {capture|diff <snapshot-file>}" >&2
|
|
echo " HASH_GATE_IGNORE_UNTRACKED=1 $0 capture # env form" >&2
|
|
exit 2
|
|
;;
|
|
esac
|