fix: bound observer counters and apply first-tick thresholds

This commit is contained in:
haelyra
2026-10-11 02:16:56 -04:00
parent 9d7e5a5bc9
commit 401faee091
3 changed files with 56 additions and 22 deletions
@@ -10,7 +10,7 @@
"id": "hand-seeded",
"source": "manual-curation-pending-regeneration"
},
"registryDigest": "55e9ce5724329ea6315394ed8969fc23d820f1163d82a9f3e1baef304e7ca30f",
"registryDigest": "3a5aa65956a41c77902b6e3287d8352f04a06e04343decec804d0a5e93e538f5",
"schemaVersion": 1,
"triggers": {
"skill:agent-runtime-gateway-smoke-test": [
+20 -21
View File
@@ -586,22 +586,21 @@ touch "$ACTIVITY_FILE" 2>/dev/null || true
should_signal=0
_clv2_bump_signal_counter() {
counter=0
if [ -f "$SIGNAL_COUNTER_FILE" ]; then
counter=$(cat "$SIGNAL_COUNTER_FILE" 2>/dev/null || echo 0)
# Guard against a corrupt counter file: a non-integer value would abort the
# hook under `set -e` at the arithmetic below.
case "$counter" in
''|*[!0-9]*) counter=0 ;;
esac
counter=$((counter + 1))
if [ "$counter" -ge "$SIGNAL_EVERY_N" ]; then
should_signal=1
counter=0
fi
echo "$counter" > "$SIGNAL_COUNTER_FILE"
else
echo "1" > "$SIGNAL_COUNTER_FILE"
counter=$(head -c 20 "$SIGNAL_COUNTER_FILE" 2>/dev/null || echo 0)
fi
# Bound parsing and avoid shell arithmetic overflow on corrupt local state.
case "$counter" in
''|*[!0-9]*) counter=0 ;;
esac
if [ "${#counter}" -gt 18 ]; then counter=0; fi
counter=$((10#$counter + 1))
if [ "$counter" -ge "$SIGNAL_EVERY_N" ]; then
should_signal=1
counter=0
fi
echo "$counter" > "$SIGNAL_COUNTER_FILE"
}
if command -v flock >/dev/null 2>&1 && exec 8>"$SIGNAL_COUNTER_LOCK" 2>/dev/null; then
@@ -655,14 +654,14 @@ try:
time.sleep(0.02)
try:
with open(sys.argv[2], encoding="ascii") as source:
value = source.read().strip()
counter = int(value) if value and value.isascii() and value.isdigit() else 0
counter += 1
signal = int(counter >= int(sys.argv[3]))
if signal:
counter = 0
value = source.read(20).strip()
except FileNotFoundError:
counter, signal = 1, 0
value = ""
counter = int(value) if 0 < len(value) <= 18 and value.isascii() and value.isdigit() else 0
counter += 1
signal = int(counter >= int(sys.argv[3]))
if signal:
counter = 0
with open(sys.argv[2], "w", encoding="ascii") as target:
target.write(str(counter) + "\n")
print(signal)
@@ -269,6 +269,30 @@ async function runStrandedLock() {
}
}
async function runCounterBoundary(initial, threshold, expected, forcePython) {
const { testDir, projectDir, testObserve } = buildSandbox();
try {
if (initial !== null) {
fs.writeFileSync(path.join(projectDir, '.observer-signal-counter'), initial);
}
const extraEnv = { ECC_OBSERVER_SIGNAL_EVERY_N: String(threshold) };
if (forcePython || !hasFlock) {
const bashEnv = path.join(testDir, 'no-flock.sh');
const fixture = forcePython
? 'command() { if [ "$1" = "-v" ] && [ "$2" = "flock" ]; then return 1; fi; builtin command "$@"; }\n'
// Exercise the real shell counter on macOS with an inherited-fd OS
// lock fixture. Native GNU flock remains a Linux platform check.
: `flock() { python3 -c 'import fcntl,sys; fcntl.flock(int(sys.argv[-1]), fcntl.LOCK_UN if sys.argv[1] == "-u" else fcntl.LOCK_EX)' "$@"; }\n`;
fs.writeFileSync(bashEnv, fixture);
extraEnv.BASH_ENV = bashEnv;
}
await runObserve(testObserve, projectDir, extraEnv);
assert.strictEqual(readCounter(projectDir), expected);
} finally {
cleanupDir(testDir);
}
}
async function runKilledOwner() {
const { testDir, projectDir, testObserve } = buildSandbox();
let owner;
@@ -305,6 +329,17 @@ async function runKilledOwner() {
await asyncTest('concurrent invocations never lose a counter increment', runConcurrent);
await asyncTest('stranded mkdir lock cannot delay or freeze later observations', runStrandedLock);
await asyncTest('busy lock skips safely and a killed owner releases it automatically', runKilledOwner);
for (const forcePython of [false, true]) {
const label = forcePython ? 'Python fallback' : hasFlock ? 'flock' : 'shell counter with OS-lock fixture';
await asyncTest(`${label} applies threshold one to a missing first counter`,
() => runCounterBoundary(null, 1, 0, forcePython));
await asyncTest(`${label} resets a malformed counter before incrementing`,
() => runCounterBoundary('invalid\n', 2, 1, forcePython));
await asyncTest(`${label} resets an oversized counter before incrementing`,
() => runCounterBoundary('9'.repeat(5000) + '\n', 2, 1, forcePython));
await asyncTest(`${label} parses leading-zero counters as decimal`,
() => runCounterBoundary('08\n', 20, 9, forcePython));
}
} else {
console.log(' - skipping shell-execution tests (requires non-Windows + python3)');
}