Stabilize Leo evidence sync without gateway restart
This commit is contained in:
parent
833e27b5c4
commit
1e9de6d864
6 changed files with 99 additions and 5 deletions
|
|
@ -131,7 +131,7 @@ if [ "$OLD_SHA" = "none" ] || git diff --name-only "$OLD_SHA" "$NEW_SHA" -- syst
|
|||
fi
|
||||
fi
|
||||
|
||||
# Restart services only if Python files changed
|
||||
# Restart services only when changed files affect a running process.
|
||||
RESTART=""
|
||||
add_restart() {
|
||||
case " $RESTART " in
|
||||
|
|
@ -164,7 +164,8 @@ if [ "$OLD_SHA" != "none" ]; then
|
|||
if git diff --name-only "$OLD_SHA" "$NEW_SHA" -- diagnostics/ 2>/dev/null | grep -q '\.py$'; then
|
||||
add_restart teleo-diagnostics
|
||||
fi
|
||||
if git diff --name-only "$OLD_SHA" "$NEW_SHA" -- hermes-agent/leoclean-bin/ hermes-agent/leoclean-skills/vps/ 2>/dev/null | grep -q .; then
|
||||
if git diff --name-only "$OLD_SHA" "$NEW_SHA" -- hermes-agent/leoclean-bin/ hermes-agent/leoclean-skills/vps/ 2>/dev/null \
|
||||
| bash deploy/leoclean-gateway-restart-required.sh; then
|
||||
add_restart_if_unit_active leoclean-gateway
|
||||
fi
|
||||
else
|
||||
|
|
@ -216,7 +217,7 @@ if [ -n "$RESTART" ]; then
|
|||
exit 1
|
||||
fi
|
||||
else
|
||||
log "No Python changes — services not restarted"
|
||||
log "No runtime changes - services not restarted"
|
||||
fi
|
||||
|
||||
echo "$NEW_SHA" > "$STAMP_FILE"
|
||||
|
|
|
|||
20
deploy/leoclean-gateway-restart-required.sh
Normal file
20
deploy/leoclean-gateway-restart-required.sh
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Read changed repo paths from stdin. Markdown skills are loaded from the synced
|
||||
# profile per turn; runtime code changes still require a gateway restart.
|
||||
restart_required=1
|
||||
while IFS= read -r path || [ -n "$path" ]; do
|
||||
case "$path" in
|
||||
hermes-agent/leoclean-bin/*)
|
||||
restart_required=0
|
||||
;;
|
||||
hermes-agent/leoclean-skills/vps/*.md|hermes-agent/leoclean-skills/vps/*/*.md)
|
||||
;;
|
||||
hermes-agent/leoclean-skills/vps/*)
|
||||
restart_required=0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
exit "$restart_required"
|
||||
|
|
@ -152,6 +152,22 @@ and copy either (a) a full UUID plus observed `status` and `applied_at`, or (b)
|
|||
exact observed counts for the relevant `claims`, `sources`, `claim_edges`,
|
||||
`claim_evidence`, and `kb_proposals` tables. Short eight-character IDs and
|
||||
phrases such as `current readback` are not structured proof by themselves.
|
||||
Copy exactly one of these formats, replacing every all-caps token with a value
|
||||
from the current tool call:
|
||||
|
||||
```text
|
||||
DB readback: proposal: `PROPOSAL_UUID_36_CHARS`; status: `OBSERVED_STATUS`; applied_at: `OBSERVED_TIMESTAMP_OR_NONE`.
|
||||
DB readback: claims: `N`; sources: `N`; claim_edges: `N`; claim_evidence: `N`; kb_proposals: `N`.
|
||||
```
|
||||
|
||||
Never shorten a UUID: it must contain all 36 characters and four hyphens. Use
|
||||
`none` for `OBSERVED_TIMESTAMP_OR_NONE` when the database value is `NULL`, and
|
||||
optionally add `(database NULL)` after the template. Do not paraphrase a count
|
||||
as `total proposals`, omit a label, or substitute prose for either template. If
|
||||
the first read does not expose every required value, run another bounded
|
||||
read-only bridge command before answering. Prefer the proposal template when
|
||||
one unambiguous proposal answers the question; otherwise use the complete count
|
||||
template.
|
||||
Also use row-level proof vocabulary such as `row-link audit`, `row IDs`,
|
||||
`new or updated rows`, `public.*`, and `postflight proof` where relevant. End
|
||||
with exactly one final line beginning
|
||||
|
|
|
|||
|
|
@ -145,6 +145,22 @@ and copy either (a) a full UUID plus observed `status` and `applied_at`, or (b)
|
|||
exact observed counts for the relevant `claims`, `sources`, `claim_edges`,
|
||||
`claim_evidence`, and `kb_proposals` tables. Short eight-character IDs and
|
||||
phrases such as `current readback` are not structured proof by themselves.
|
||||
Copy exactly one of these formats, replacing every all-caps token with a value
|
||||
from the current tool call:
|
||||
|
||||
```text
|
||||
DB readback: proposal: `PROPOSAL_UUID_36_CHARS`; status: `OBSERVED_STATUS`; applied_at: `OBSERVED_TIMESTAMP_OR_NONE`.
|
||||
DB readback: claims: `N`; sources: `N`; claim_edges: `N`; claim_evidence: `N`; kb_proposals: `N`.
|
||||
```
|
||||
|
||||
Never shorten a UUID: it must contain all 36 characters and four hyphens. Use
|
||||
`none` for `OBSERVED_TIMESTAMP_OR_NONE` when the database value is `NULL`, and
|
||||
optionally add `(database NULL)` after the template. Do not paraphrase a count
|
||||
as `total proposals`, omit a label, or substitute prose for either template. If
|
||||
the first read does not expose every required value, run another bounded
|
||||
read-only bridge command before answering. Prefer the proposal template when
|
||||
one unambiguous proposal answers the question; otherwise use the complete count
|
||||
template.
|
||||
Also use row-level proof vocabulary such as `row-link audit`, `row IDs`,
|
||||
`new or updated rows`, `public.*`, and `postflight proof` where relevant. End
|
||||
with exactly one final line beginning
|
||||
|
|
|
|||
|
|
@ -32,6 +32,12 @@ def test_gcp_kb_skill_uses_cloudsql_bridge_not_vps_docker() -> None:
|
|||
assert "DB readback:" in text
|
||||
assert "full UUID plus observed `status` and `applied_at`" in squashed
|
||||
assert "Short eight-character IDs" in text
|
||||
assert "PROPOSAL_UUID_36_CHARS" in text
|
||||
assert "OBSERVED_STATUS" in text
|
||||
assert "OBSERVED_TIMESTAMP_OR_NONE" in text
|
||||
assert "claims: `N`; sources: `N`; claim_edges: `N`; claim_evidence: `N`; kb_proposals: `N`" in text
|
||||
assert "Never shorten a UUID" in text
|
||||
assert "Do not paraphrase a count" in text
|
||||
assert "canonical identity requires DB rows plus" in squashed
|
||||
assert "not canonical Postgres" in text
|
||||
assert "not the source of truth" in text
|
||||
|
|
@ -77,6 +83,12 @@ def test_vps_kb_skill_keeps_vps_scope_explicit() -> None:
|
|||
assert "DB readback:" in text
|
||||
assert "full UUID plus observed `status` and `applied_at`" in squashed
|
||||
assert "Short eight-character IDs" in text
|
||||
assert "PROPOSAL_UUID_36_CHARS" in text
|
||||
assert "OBSERVED_STATUS" in text
|
||||
assert "OBSERVED_TIMESTAMP_OR_NONE" in text
|
||||
assert "claims: `N`; sources: `N`; claim_edges: `N`; claim_evidence: `N`; kb_proposals: `N`" in text
|
||||
assert "Never shorten a UUID" in text
|
||||
assert "Do not paraphrase a count" in text
|
||||
assert "canonical identity requires DB rows plus" in squashed
|
||||
assert "not canonical Postgres" in text
|
||||
assert "not the source of truth" in text
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
|
|
@ -80,7 +81,19 @@ def test_deploy_scripts_install_systemd_units_and_enable_agent_healthcheck():
|
|||
assert "teleo-agent-healthcheck.timer" in manual_deploy
|
||||
|
||||
|
||||
def test_deploy_scripts_sync_leoclean_skills_and_restart_gateway_when_active():
|
||||
def _leoclean_gateway_restart_required(*changed_paths: str) -> bool:
|
||||
result = subprocess.run(
|
||||
["bash", str(REPO_ROOT / "deploy" / "leoclean-gateway-restart-required.sh")],
|
||||
input="\n".join(changed_paths),
|
||||
capture_output=True,
|
||||
check=False,
|
||||
text=True,
|
||||
)
|
||||
assert result.returncode in {0, 1}, result.stderr
|
||||
return result.returncode == 0
|
||||
|
||||
|
||||
def test_deploy_scripts_sync_leoclean_skills_and_restart_gateway_for_runtime_changes():
|
||||
auto_deploy = (REPO_ROOT / "deploy" / "auto-deploy.sh").read_text()
|
||||
manual_deploy = (REPO_ROOT / "deploy" / "deploy.sh").read_text()
|
||||
sudoers = (REPO_ROOT / "deploy" / "sudoers" / "teleo-auto-deploy").read_text()
|
||||
|
|
@ -89,7 +102,7 @@ def test_deploy_scripts_sync_leoclean_skills_and_restart_gateway_when_active():
|
|||
assert 'LEOCLEAN_SKILLS_DIR="/home/teleo/.hermes/profiles/leoclean/skills"' in auto_deploy
|
||||
assert 'hermes-agent/leoclean-bin/' in auto_deploy
|
||||
assert 'hermes-agent/leoclean-skills/vps/' in auto_deploy
|
||||
assert 'git diff --name-only "$OLD_SHA" "$NEW_SHA" -- hermes-agent/leoclean-bin/ hermes-agent/leoclean-skills/vps/' in auto_deploy
|
||||
assert 'deploy/leoclean-gateway-restart-required.sh' in auto_deploy
|
||||
assert 'add_restart_if_unit_active leoclean-gateway' in auto_deploy
|
||||
assert 'sudo systemctl restart "$svc"' in auto_deploy
|
||||
assert "sudo systemctl restart $RESTART" not in auto_deploy
|
||||
|
|
@ -102,6 +115,22 @@ def test_deploy_scripts_sync_leoclean_skills_and_restart_gateway_when_active():
|
|||
assert 'systemctl is-active --quiet leoclean-gateway.service' in manual_deploy
|
||||
|
||||
|
||||
def test_auto_deploy_hot_syncs_leoclean_markdown_without_gateway_restart():
|
||||
assert not _leoclean_gateway_restart_required(
|
||||
"hermes-agent/leoclean-skills/vps/teleo-kb-bridge/SKILL.md"
|
||||
)
|
||||
assert not _leoclean_gateway_restart_required(
|
||||
"hermes-agent/leoclean-skills/vps/live-leo-telegram/SKILL.md"
|
||||
)
|
||||
|
||||
|
||||
def test_auto_deploy_restarts_gateway_for_leoclean_runtime_changes():
|
||||
assert _leoclean_gateway_restart_required("hermes-agent/leoclean-bin/kb_tool.py")
|
||||
assert _leoclean_gateway_restart_required(
|
||||
"hermes-agent/leoclean-skills/vps/teleo-kb-bridge/runtime_helper.py"
|
||||
)
|
||||
|
||||
|
||||
def test_auto_deploy_sudoers_installer_is_root_only_and_visudo_checked():
|
||||
installer = (REPO_ROOT / "deploy" / "install-auto-deploy-sudoers.sh").read_text()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue