fix(fleet): enforce exact comms authority
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -61,25 +61,33 @@ MOSAIC_HOME="$T5" MOSAIC_INSTALL_MODE=bogus MOSAIC_SYNC_ONLY=1 bash "$INSTALL" >
|
||||
chk "F5 failure: invalid mode rejected (nonzero exit)" "[ $rc -ne 0 ]"
|
||||
chk "F5 failure: SOUL + credentials intact" "grep -q orig '$T5/SOUL.md' && grep -q keepme '$T5/credentials/c.json'"
|
||||
|
||||
# F6 — keep-mode re-seed (the `mosaic update` path) MUST NOT wipe user fleet data.
|
||||
# Regression for the roster-loss bug: fleet/ was not in PRESERVE_PATHS.
|
||||
# F6 — keep-mode re-seed (the `mosaic update` path) MUST preserve only the
|
||||
# exact user-owned roster paths while refreshing framework-owned schema/examples.
|
||||
T6=$(mktemp -d); mkdir -p "$T6/fleet/examples" "$T6/fleet/run" "$T6/fleet/agents"
|
||||
printf '# persona\n' > "$T6/SOUL.md" # makes it a recognized existing install (→ keep mode)
|
||||
printf 'version: 1\nagents:\n - name: coder0\n' > "$T6/fleet/roster.yaml"
|
||||
printf 'version: 1\nagents:\n - name: custom\n' > "$T6/fleet/my-fleet.yaml"
|
||||
printf '{"version":1,"agents":[{"name":"json-user"}]}\n' > "$T6/fleet/roster.json"
|
||||
printf 'version: 1\nagents:\n - name: not-active-roster\n' > "$T6/fleet/my-fleet.yaml"
|
||||
printf 'ts=x\n' > "$T6/fleet/run/coder0.hb"
|
||||
printf 'MOSAIC_AGENT_NAME=coder0\n' > "$T6/fleet/agents/coder0.env"
|
||||
printf '# stale preset\n' > "$T6/fleet/examples/general.yaml"
|
||||
printf '{"stale":true}\n' > "$T6/fleet/roster.schema.json"
|
||||
E6=$(mktemp -d)
|
||||
cp "$T6/fleet/roster.yaml" "$E6/roster-yaml.expected"
|
||||
cp "$T6/fleet/roster.json" "$E6/roster-json.expected"
|
||||
cp "$T6/fleet/run/coder0.hb" "$E6/run.expected"
|
||||
cp "$T6/fleet/agents/coder0.env" "$E6/agent.expected"
|
||||
echo 3 > "$T6/.framework-version"
|
||||
run "$T6" keep
|
||||
chk "F6 reseed: user roster.yaml SURVIVES keep-mode sync" "grep -q coder0 '$T6/fleet/roster.yaml'"
|
||||
chk "F6 reseed: other user fleet/*.yaml survives (glob)" "[ -f '$T6/fleet/my-fleet.yaml' ]"
|
||||
chk "F6 reseed: per-agent env (fleet/agents) survives" "[ -f '$T6/fleet/agents/coder0.env' ]"
|
||||
chk "F6 reseed: heartbeat run dir (fleet/run) survives" "[ -f '$T6/fleet/run/coder0.hb' ]"
|
||||
chk "F6 reseed: framework examples ARE refreshed (not preserved stale)" "grep -q orchestrator '$T6/fleet/examples/general.yaml'"
|
||||
chk "F6 reseed: framework roster.schema.json seeded" "[ -f '$T6/fleet/roster.schema.json' ]"
|
||||
chk "F6 reseed: exact roster.yaml bytes survive keep-mode sync" "cmp -s '$T6/fleet/roster.yaml' '$E6/roster-yaml.expected'"
|
||||
chk "F6 reseed: exact roster.json bytes survive keep-mode sync" "cmp -s '$T6/fleet/roster.json' '$E6/roster-json.expected'"
|
||||
chk "F6 reseed: unrelated fleet YAML is not preserved" "[ ! -f '$T6/fleet/my-fleet.yaml' ]"
|
||||
chk "F6 reseed: per-agent env bytes survive" "cmp -s '$T6/fleet/agents/coder0.env' '$E6/agent.expected'"
|
||||
chk "F6 reseed: heartbeat bytes survive" "cmp -s '$T6/fleet/run/coder0.hb' '$E6/run.expected'"
|
||||
chk "F6 reseed: framework examples are refreshed" "grep -q orchestrator '$T6/fleet/examples/general.yaml'"
|
||||
chk "F6 reseed: framework roster schema is refreshed" "cmp -s '$T6/fleet/roster.schema.json' '$FW/fleet/roster.schema.json'"
|
||||
|
||||
rm -rf "$T1" "$T2" "$T3" "$T4" "$T5" "$T6"
|
||||
rm -rf "$T1" "$T2" "$T3" "$T4" "$T5" "$T6" "$E6"
|
||||
echo
|
||||
echo "RESULT: $pass passed, $fail failed"
|
||||
[ "$fail" -eq 0 ]
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Regression checks for connector-kind-conditional fleet roster schema."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from jsonschema import Draft202012Validator
|
||||
|
||||
schema_path = Path(__file__).resolve().parents[3] / "fleet" / "roster.schema.json"
|
||||
schema = json.loads(schema_path.read_text(encoding="utf-8"))
|
||||
validator = Draft202012Validator(schema)
|
||||
base = {
|
||||
"version": 1,
|
||||
"transport": "tmux",
|
||||
"agents": [{"name": "orchestrator", "runtime": "pi"}],
|
||||
}
|
||||
|
||||
valid = [
|
||||
{"kind": "tmux"},
|
||||
{"kind": "discord", "discord": {"channel_id": "123"}},
|
||||
{
|
||||
"kind": "matrix",
|
||||
"matrix": {
|
||||
"homeserver_url": "https://matrix.example",
|
||||
"user_id": "@mosaic:example",
|
||||
"room_id": "!room:example",
|
||||
},
|
||||
},
|
||||
]
|
||||
invalid = [
|
||||
{"kind": "tmux", "discord": {"channel_id": "123"}},
|
||||
{"kind": "tmux", "matrix": {}},
|
||||
{"kind": "discord"},
|
||||
{"kind": "discord", "matrix": {}},
|
||||
{
|
||||
"kind": "discord",
|
||||
"discord": {"channel_id": "123"},
|
||||
"matrix": {
|
||||
"homeserver_url": "https://matrix.example",
|
||||
"user_id": "@mosaic:example",
|
||||
"room_id": "!room:example",
|
||||
},
|
||||
},
|
||||
{"kind": "matrix"},
|
||||
{"kind": "matrix", "discord": {"channel_id": "123"}},
|
||||
{"kind": "discord", "discord": {"channel_id": ""}},
|
||||
{"kind": "discord", "discord": {"channel_id": " "}},
|
||||
{
|
||||
"kind": "matrix",
|
||||
"matrix": {
|
||||
"homeserver_url": "",
|
||||
"user_id": "@mosaic:example",
|
||||
"room_id": "!room:example",
|
||||
},
|
||||
},
|
||||
{
|
||||
"kind": "matrix",
|
||||
"matrix": {
|
||||
"homeserver_url": "\t",
|
||||
"user_id": "@mosaic:example",
|
||||
"room_id": "!room:example",
|
||||
},
|
||||
},
|
||||
{
|
||||
"kind": "matrix",
|
||||
"matrix": {
|
||||
"homeserver_url": "https://matrix.example",
|
||||
"user_id": "",
|
||||
"room_id": "!room:example",
|
||||
},
|
||||
},
|
||||
{
|
||||
"kind": "matrix",
|
||||
"matrix": {
|
||||
"homeserver_url": "https://matrix.example",
|
||||
"user_id": " ",
|
||||
"room_id": "!room:example",
|
||||
},
|
||||
},
|
||||
{
|
||||
"kind": "matrix",
|
||||
"matrix": {
|
||||
"homeserver_url": "https://matrix.example",
|
||||
"user_id": "@mosaic:example",
|
||||
"room_id": "",
|
||||
},
|
||||
},
|
||||
{
|
||||
"kind": "matrix",
|
||||
"matrix": {
|
||||
"homeserver_url": "https://matrix.example",
|
||||
"user_id": "@mosaic:example",
|
||||
"room_id": "\n",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
for connector in valid:
|
||||
errors = list(validator.iter_errors({**base, "connector": connector}))
|
||||
if errors:
|
||||
print(f"expected valid connector {connector}: {errors}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
|
||||
for connector in invalid:
|
||||
if not list(validator.iter_errors({**base, "connector": connector})):
|
||||
print(f"expected invalid connector: {connector}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
|
||||
print("connector schema regression: PASS")
|
||||
Reference in New Issue
Block a user