diff --git a/README.md b/README.md index 12d1fef..18300b8 100644 --- a/README.md +++ b/README.md @@ -276,7 +276,7 @@ operator-entrypoints/wp-coding-agents-setup/setup.md | `--local` | Local machine mode. Skips server infrastructure. | | `--existing` | Add to an existing WordPress install. | | `--wp-path ` | WordPress root path. Implies `--existing`. | -| `--agent-slug ` | Override the Data Machine agent slug. | +| `--agent-slug ` | Override the Data Machine agent slug used by runtime instruction projections. Persisted for subsequent upgrades. | | `--kimaki-unit ` | Target a Kimaki systemd instance, such as `kimaki-example.service`. | | `--kimaki-data-dir ` | Override that Kimaki instance's state directory. | | `--kimaki-lock-port ` | Override that Kimaki instance's lock port. | diff --git a/lib/desired-state-reconciler.sh b/lib/desired-state-reconciler.sh index 3dbb607..12b7a25 100644 --- a/lib/desired-state-reconciler.sh +++ b/lib/desired-state-reconciler.sh @@ -31,6 +31,7 @@ installation_profile_value() { studio) printf '%s' "$INSTALLATION_PROFILE_STUDIO" ;; source_mode) printf '%s' "$INSTALLATION_PROFILE_SOURCE_MODE" ;; runtime) printf '%s' "$INSTALLATION_PROFILE_RUNTIME" ;; + agent_slug) printf '%s' "$INSTALLATION_PROFILE_AGENT_SLUG" ;; install_chat) printf '%s' "$INSTALLATION_PROFILE_INSTALL_CHAT" ;; chat_bridge) printf '%s' "$INSTALLATION_PROFILE_CHAT_BRIDGE" ;; homeboy_mode) printf '%s' "$INSTALLATION_PROFILE_HOMEBOY_MODE" ;; @@ -56,6 +57,7 @@ installation_profile_normalize() { INSTALLATION_PROFILE_STUDIO="${IS_STUDIO:-false}" INSTALLATION_PROFILE_SOURCE_MODE="${SOURCE_MODE:-workspace}" INSTALLATION_PROFILE_RUNTIME="${RUNTIME:-}" + INSTALLATION_PROFILE_AGENT_SLUG="${AGENT_SLUG:-}" INSTALLATION_PROFILE_INSTALL_CHAT="${INSTALL_CHAT:-true}" if [ "$INSTALLATION_PROFILE_INSTALL_CHAT" = true ]; then INSTALLATION_PROFILE_CHAT_BRIDGE="${CHAT_BRIDGE:-}" @@ -125,7 +127,7 @@ installation_profile_write() { local key umask 077 : > "$tmp" - for key in operation site_path local_mode external_wordpress studio source_mode runtime install_chat chat_bridge homeboy_mode workspace_repositories workspace_repository_clones components plugin_candidates; do + for key in operation site_path local_mode external_wordpress studio source_mode runtime agent_slug install_chat chat_bridge homeboy_mode workspace_repositories workspace_repository_clones components plugin_candidates; do printf '%s=%s\n' "$key" "$(installation_profile_value "$key")" >> "$tmp" done mv "$tmp" "$file" @@ -146,6 +148,7 @@ installation_profile_load() { case "$key" in source_mode) [ "${SOURCE_MODE_EXPLICIT:-false}" = true ] || SOURCE_MODE="$value" ;; runtime) [ -n "${RUNTIME:-}" ] || RUNTIME="$value" ;; + agent_slug) [ -n "${AGENT_SLUG:-}" ] || AGENT_SLUG="$value" ;; install_chat) case "$value" in true|false) INSTALL_CHAT="$value" ;; diff --git a/lib/runtime-guidance-desired-state.sh b/lib/runtime-guidance-desired-state.sh index 44b219b..06e164b 100644 --- a/lib/runtime-guidance-desired-state.sh +++ b/lib/runtime-guidance-desired-state.sh @@ -127,6 +127,26 @@ runtime_guidance_desired_state_apply_instructions() { runtime_guidance_desired_state_apply_function runtime_generate_instructions } +# Keep an existing managed Codex projection aligned with canonical AGENTS.md +# even when another runtime is primary. The managed marker is explicit +# authority; user-owned AGENTS.override.md files remain untouched. +runtime_guidance_sync_managed_codex_projection() { + local projection="$SITE_PATH/AGENTS.override.md" + [ -f "$projection" ] || return 0 + grep -Fq '' "$projection" || return 0 + + local selected_runtime_file="${RUNTIME_FILE:-}" + # shellcheck disable=SC1090 + source "$SCRIPT_DIR/runtimes/codex.sh" + runtime_discover_dm_paths || return $? + _codex_sync_override || return $? + + if [ "${RUNTIME:-}" != codex ] && [ -n "$selected_runtime_file" ] && [ -f "$selected_runtime_file" ]; then + # shellcheck disable=SC1090 + source "$selected_runtime_file" + fi +} + # apply and verify — use the shared plan executor so partial-record evidence # and future per-record verification stay consistent with other adapters. runtime_guidance_desired_state_apply() { diff --git a/tests/installation-profile.sh b/tests/installation-profile.sh index ac0342d..0290fcf 100644 --- a/tests/installation-profile.sh +++ b/tests/installation-profile.sh @@ -19,6 +19,7 @@ EXTERNAL_WORDPRESS=false IS_STUDIO=true SOURCE_MODE=workspace RUNTIME=opencode +AGENT_SLUG=builder CHAT_BRIDGE=kimaki HOMEBOY_MODE=enabled WORKSPACE_REPOSITORIES="$WORKSPACE" @@ -55,6 +56,7 @@ fi SOURCE_MODE="" RUNTIME="" +AGENT_SLUG="" CHAT_BRIDGE="" HOMEBOY_MODE=auto WORKSPACE_REPOSITORIES="" @@ -63,6 +65,7 @@ SOURCE_MODE_EXPLICIT=false installation_profile_load test "$SOURCE_MODE" = workspace test "$RUNTIME" = opencode +test "$AGENT_SLUG" = builder test "$CHAT_BRIDGE" = kimaki test "$HOMEBOY_MODE" = enabled test "$WORKSPACE_REPOSITORIES" = "$WORKSPACE" @@ -98,8 +101,10 @@ test "$INSTALL_CHAT" = true # Explicit command-line intent remains authoritative over persisted defaults. RUNTIME=codex +AGENT_SLUG=reviewer installation_profile_load test "$RUNTIME" = codex +test "$AGENT_SLUG" = reviewer # Disabled optional components round-trip as intent rather than being inferred # from whatever bridge binaries happen to be present during upgrade. diff --git a/tests/runtime-guidance-desired-state.sh b/tests/runtime-guidance-desired-state.sh index b98da3c..47f1208 100644 --- a/tests/runtime-guidance-desired-state.sh +++ b/tests/runtime-guidance-desired-state.sh @@ -92,4 +92,29 @@ reconciler_apply_plan assert_eq "$(<"$TRACE")" $'install\npaths\ncontext\nconfig\nhooks\ninstructions\nmcp' "runtime state survives its ordered component step" rm -f "$TRACE" +# Canonical composition refreshes managed secondary-runtime projections while +# preserving user-owned files and the selected runtime adapter. +TRACE="$(mktemp)" +SITE_PATH="$(mktemp -d)" +SCRIPT_DIR="$(mktemp -d)" +mkdir "$SCRIPT_DIR/runtimes" +printf '%s\n' \ + 'runtime_discover_dm_paths() { printf "paths\\n" >> "$TRACE"; }' \ + '_codex_sync_override() { printf "codex\\n" >> "$TRACE"; }' \ + > "$SCRIPT_DIR/runtimes/codex.sh" +printf '%s\n' \ + 'runtime_discover_dm_paths() { printf "selected\\n" >> "$TRACE"; }' \ + > "$SCRIPT_DIR/runtimes/opencode.sh" +RUNTIME=opencode +RUNTIME_FILE="$SCRIPT_DIR/runtimes/opencode.sh" +printf '\n' > "$SITE_PATH/AGENTS.override.md" +runtime_guidance_sync_managed_codex_projection +assert_eq "$(<"$TRACE")" $'paths\ncodex' "managed Codex projection refreshes after canonical composition" +runtime_discover_dm_paths +assert_eq "$(<"$TRACE")" $'paths\ncodex\nselected' "selected runtime adapter is restored after projection refresh" +printf '# user owned\n' > "$SITE_PATH/AGENTS.override.md" +runtime_guidance_sync_managed_codex_projection +assert_eq "$(<"$TRACE")" $'paths\ncodex\nselected' "user-owned Codex projection remains untouched" +rm -rf "$SITE_PATH" "$SCRIPT_DIR" "$TRACE" + echo "OK: runtime and guidance desired-state adapter" diff --git a/upgrade.sh b/upgrade.sh index 046179b..e528c4d 100755 --- a/upgrade.sh +++ b/upgrade.sh @@ -131,6 +131,7 @@ MULTISITE=false MULTISITE_TYPE="subdirectory" MODE="existing" RUNTIME="" +AGENT_SLUG="${AGENT_SLUG:-}" DETECTED_RUNTIMES=() IS_STUDIO=false CHAT_BRIDGE="" @@ -1475,6 +1476,9 @@ if _run_filter_active reconciliation; then fi sync_skills regenerate_agents_md +if _run_filter_active agents-md; then + runtime_guidance_sync_managed_codex_projection +fi sync_claude_code_runtime sync_runtime_signature sync_runtime_instructions