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
2 changes: 1 addition & 1 deletion docs/ai-environment-boundary.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ AI skills / agents project は、`dotfiles` の policy を前提に動く。poli
- `dotfiles` は AI skills / agents project の path を表示してよい。
- `dotfiles` は AI skills / agents project を自動更新しない。
- `dotfiles` は AI skills / agents project の secret を読まない。
- `dotfiles` の `doctor` は `~/src/agent/agent-tools`(既定。非標準な checkout 先は `AGENT_TOOLS` env で override 可)の presence を report する。status(`scripts/status.sh --json`、report-only、`contract_version: 2`)の読み取りは別 repo のコード実行になるため、`enableAgentToolsStatus` capability での明示 opt-in 時のみ実行し、安全な summary(`conflict` / `stale` / 失敗 check 等は warning)を出す(Issue #7)。clone / pull / sync は一切しない。
- `dotfiles` の `doctor` は `~/src/agent/agent-tools`(既定。非標準な checkout 先は `AGENT_TOOLS` env で override 可)の presence を report する。status(`scripts/status.sh --json`、report-only、`contract_version: 3`)の読み取りは別 repo のコード実行になるため、`enableAgentToolsStatus` capability での明示 opt-in 時のみ実行し、安全な summary(`conflict` / `stale` / `deployed_but_inactive` / 失敗 check 等は warning)を出す(Issue #7、v3 追従は #194)。clone / pull / sync は一切しない。
- AI skills / agents project は `dotfiles` の capability を前提条件として参照してよい。
- AI skills / agents project が install、network tunnel、secret access を必要とする場合は、`dotfiles` 側の capability と approval policy に従う。

Expand Down
12 changes: 8 additions & 4 deletions scripts/doctor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ section "agent-tools (report-only)"
# (executing code from another repo) is opt-in via enableAgentToolsStatus
# so doctor's no-side-effects invariant is never delegated implicitly.
# See docs/ai-environment-boundary.md and the agent-tools
# status-manifest-contract (contract_version 2).
# status-manifest-contract (contract_version 3).
# The expected path defaults to the dotfiles directory convention
# (~/src/agent/agent-tools) but is overridable via the AGENT_TOOLS env so a
# non-standard checkout can still be reported. presence only; never cloned.
Expand All @@ -695,10 +695,10 @@ else
# the JSON is malformed (a failed substitution would trip set -e).
sj() { printf '%s' "$status_json" | yq -p json "$1" 2>/dev/null || true; }
contract_version="$(sj '.contract_version // ""')"
if [[ "$contract_version" != "2" ]]; then
warn "agent-tools status contract_version=${contract_version:-unknown}, expected 2 (not interpreting fields)"
if [[ "$contract_version" != "3" ]]; then
warn "agent-tools status contract_version=${contract_version:-unknown}, expected 3 (not interpreting fields)"
else
ok "agent-tools present; status contract v2"
ok "agent-tools present; status contract v3"

if [[ "$(sj '.repo.clean // false')" == "true" ]]; then
ok "agent-tools working tree clean"
Expand Down Expand Up @@ -741,6 +741,10 @@ else
if [[ "$(sj '[.sync_targets[]? | select(.state == "stale")] | length')" != "0" ]]; then
warn "agent-tools has stale sync targets (generated artifact newer than target)"
fi
# v3 (#194): gated-but-still-deployed leftovers are cleanup candidates.
if [[ "$(sj '[.sync_targets[]? | select(.state == "deployed_but_inactive")] | length')" != "0" ]]; then
warn "agent-tools has deployed-but-inactive sync targets (gated entries still on disk; clean up or re-approve)"
fi
fi
unset -f sj
fi
Expand Down
24 changes: 15 additions & 9 deletions scripts/test-doctor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ agent_marker="$agent_dir/ran-marker"
mkdir -p "$agent_scripts"
# Root-pinning fake (see write_root_pinned_status_sh): regression for #73.
write_root_pinned_status_sh "$agent_scripts/status.sh" \
'{"contract_version":2,"repo":{"present":true,"clean":true},"assets":{"total":1,"manifest_errors":0},"checks":{"manifest_validation":"pass","prompt_injection_static":"pass"},"generated":{"total":1,"stale":0},"register":{"catalog_present":true,"registered":1,"human_review_required":0,"unsupported":0},"sync_targets":[{"tool":"codex","name":"x","state":"conflict"}]}'
'{"contract_version":3,"repo":{"present":true,"clean":true},"assets":{"total":1,"manifest_errors":0},"checks":{"manifest_validation":"pass","prompt_injection_static":"pass"},"generated":{"total":1,"stale":0},"register":{"catalog_present":true,"registered":1,"human_review_required":0,"unsupported":0},"sync_targets":[{"tool":"codex","name":"x","state":"conflict"},{"tool":"codex","name":"y","state":"deployed_but_inactive"}]}'

# A) Opt-in disabled: present but status.sh must not run. Force the capability
# off in a throwaway copy so the test is independent of the real default
Expand Down Expand Up @@ -178,9 +178,10 @@ set_capability_all "$optin_root" enableAgentToolsStatus true
# B) Opt-in enabled: status.sh runs, summary shown, conflict flagged.
rm -f "$agent_marker"
if at_out="$(HOME="$fixture_home" "$optin_root/scripts/doctor.sh" personal 2>&1)"; then
if grep -Fq "agent-tools present; status contract v2" <<< "$at_out" \
&& grep -Fq "sync conflicts" <<< "$at_out" && [[ -e "$agent_marker" ]]; then
ok "test passed: opt-in runs status.sh and summarizes (conflict flagged)"
if grep -Fq "agent-tools present; status contract v3" <<< "$at_out" \
&& grep -Fq "sync conflicts" <<< "$at_out" \
&& grep -Fq "deployed-but-inactive sync targets" <<< "$at_out" && [[ -e "$agent_marker" ]]; then
ok "test passed: opt-in runs status.sh and summarizes (conflict + inactive leftovers flagged)"
else
printf '%s\n' "$at_out" >&2
fail "test failed: opt-in summary/conflict/marker missing"
Expand All @@ -193,16 +194,21 @@ else
fi

# C) Opt-in + unknown contract version: not interpreted, still exit 0.
# Sentinel fields prove fail-closed: a doctor that warns but still interprets
# fields would emit the summary lines below, so their absence is asserted too.
cat > "$agent_scripts/status.sh" <<'SH'
#!/bin/sh
json=0
for a in "$@"; do [ "$a" = "--json" ] && json=1; done
[ "$json" = 1 ] || exit 1
echo '{"contract_version":99}'
echo '{"contract_version":99,"repo":{"present":true,"clean":true},"sync_targets":[{"tool":"codex","name":"x","state":"conflict"},{"tool":"codex","name":"y","state":"deployed_but_inactive"}]}'
SH
chmod +x "$agent_scripts/status.sh"
if at_out="$(HOME="$fixture_home" "$optin_root/scripts/doctor.sh" personal 2>&1)"; then
if grep -Fq "expected 2 (not interpreting fields)" <<< "$at_out"; then
if grep -Fq "expected 3 (not interpreting fields)" <<< "$at_out" \
&& ! grep -Fq "agent-tools working tree clean" <<< "$at_out" \
&& ! grep -Fq "sync conflicts" <<< "$at_out" \
&& ! grep -Fq "deployed-but-inactive sync targets" <<< "$at_out"; then
ok "test passed: unknown contract version is not interpreted (exit 0)"
else
printf '%s\n' "$at_out" >&2
Expand Down Expand Up @@ -284,7 +290,7 @@ else
fi

# AT-override) AGENT_TOOLS overrides the expected path (issue #71). The
# default ~/src/agent/agent-tools is absent (removed in E), so a v2 summary
# default ~/src/agent/agent-tools is absent (removed in E), so a v3 summary
# plus the run marker proves doctor read the overridden checkout.
override_dir="$fixture_home/custom/agent-tools"
override_scripts="$override_dir/scripts"
Expand All @@ -293,10 +299,10 @@ mkdir -p "$override_scripts"
# Same root-pinning contract as the default-path fake: doctor must pass
# --root equal to the AGENT_TOOLS-overridden checkout (#71 + #73).
write_root_pinned_status_sh "$override_scripts/status.sh" \
'{"contract_version":2,"repo":{"present":true,"clean":true},"assets":{"total":0,"manifest_errors":0},"checks":{"manifest_validation":"pass","prompt_injection_static":"pass"},"generated":{"total":0,"stale":0},"register":{"catalog_present":false,"registered":0,"human_review_required":0,"unsupported":0},"sync_targets":[]}'
'{"contract_version":3,"repo":{"present":true,"clean":true},"assets":{"total":0,"manifest_errors":0},"checks":{"manifest_validation":"pass","prompt_injection_static":"pass"},"generated":{"total":0,"stale":0},"register":{"catalog_present":false,"registered":0,"human_review_required":0,"unsupported":0},"sync_targets":[]}'
rm -f "$override_marker"
if at_out="$(HOME="$fixture_home" AGENT_TOOLS="$override_dir" "$optin_root/scripts/doctor.sh" personal 2>&1)"; then
if grep -Fq "agent-tools present; status contract v2" <<< "$at_out" && [[ -e "$override_marker" ]]; then
if grep -Fq "agent-tools present; status contract v3" <<< "$at_out" && [[ -e "$override_marker" ]]; then
ok "test passed: AGENT_TOOLS overrides the expected path"
else
printf '%s\n' "$at_out" >&2
Expand Down
Loading