[Bug]: quota gate authorizes automatic turns for a Goal whose declared active state file is missing
Maintainer antworten meist innerhalb von 1 Tag
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 3/5
- Geschätzter Aufwand
- 1-2 Tage
- Anfängerfreundlichkeit
- 75/100
Rechercherichtung
Start with goal_status_health_ok in loopx/control_plane/quota/decision_summary.py, then trace how per-goal findings are represented in the status contract and compare with docs/status-data-contract.md so the deliberately shadowed source-registry findings remain non-blocking. Reproduce with the issue's bootstrap, state-file deletion, status, and quota should-run commands. Done when a missing-state finding makes quota fail closed with that finding as the reason, while the shadowed findings still do not block.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Preflight
- I searched existing issues and discussions for this behavior.
- This report is not an unpatched security vulnerability.
- The report and attachments contain no credentials or private/internal material.
Issue origin
Observed or reproduced in a real environment
LoopX version or commit
a54c030ee79ea9270fed4ca94f9592f32a19232a (current main, LoopX 2.0.0a1)
Host or runtime surface
Not host-specific
LoopX area
Control plane (goals, todos, quota, scheduler, registry, runtime)
Problem
The quota gate authorizes an automatic turn for a Goal whose declared active state file is missing.
loopx status already knows this state. It emits a global-registry finding
kind=state_file_missing, severity=action with the recommended action
reconnect / preview loopx retire-global-goal, and lifts it into the attention
queue. But the same status also projects the Goal as
connected_without_run with recommended_action=run the first read-only adapter tick, and the quota gate follows that lane:
loopx quota should-run --goal-id <id> returns status_health_ok=true,
decision=run, should_run=true, normal_delivery_allowed=true,
interaction_contract.agent_channel.must_attempt=true, and a required_reads
entry that runs cat against the missing ACTIVE_GOAL_STATE.md.
loopx/control_plane/quota/decision_summary.py::goal_status_health_ok is the
guard that is supposed to keep this fail closed. Today it fails only when
global_registry.ok is False or the status contract reports an error for the
Goal; it never reads the per-goal state_file_missing finding. The finding is
not one of the source-registry provenance shadows that
docs/status-data-contract.md deliberately collapses into the live quota item,
so nothing suppresses it.
Who is harmed and how much. Any owner of an automatic host loop (Codex App
heartbeat, Claude Code /loop, generic host schedulers) whose Goal outlives its
active state file: a renamed or moved project, a removed agent worktree, a
deleted scratch checkout, or an interrupted state write. The machine-wide
registry intentionally keeps such Goals — that is why
loopx retire-global-goal and the state_file_missing finding exist — so the
condition is a supported operational state, not corruption. Every automation
cadence then authorizes and obligates a model turn against a Goal with no
readable state. At the documented initial App automation cadence
(app_automation_initial_rrule = FREQ=MINUTELY;INTERVAL=30 in
docs/quota-allocation.md) that is up to 48 authorized turns per day for one
stale entry. The turn cannot advance the Goal and cannot clear the finding,
and the same should_run=true decision is returned next time, so the loop keeps
spending turns until a human notices and retires the entry. The documented
repair action (reconnect or retire) is never selected because the authoritative
gate says run. The defect is host-independent; the quota decision is the same
on Linux, macOS and Windows.
Minimal reproduction
From a source checkout of a54c030ee79ea9270fed4ca94f9592f32a19232a on any
platform; <tmp> is a fresh directory:
loopx bootstrap --project <tmp> --goal-id state-gone --objective "reproduce missing state"
rm <tmp>/.loopx/goals/state-gone/ACTIVE_GOAL_STATE.md
loopx --format json status
# finding: kind=state_file_missing, severity=action,
# recommended_action="reconnect ... or preview loopx retire-global-goal"
loopx --format json quota should-run --goal-id state-gone --runtime-profile generic_cli
Observed:
{"status_health_ok": true, "decision": "run", "should_run": true,
"normal_delivery_allowed": true, "state": "eligible",
"recommended_action": "run the first read-only adapter tick and save a compact run record"}
with interaction_contract.agent_channel.must_attempt=true and
required_reads[0].command pointing at the deleted
.loopx/goals/state-gone/ACTIVE_GOAL_STATE.md. Deleting the whole project
directory instead of only the state file produces the same decision.
Expected behavior
The quota gate refuses automatic compute for a Goal whose declared active state
file is missing, matching the status finding and its recommended repair action:
status_health_ok=false, should_run=false, normal_delivery_allowed=false,
with the missing-state finding named as the reason. A host automation then
surfaces the repair/retire action instead of spending a turn on an unreadable
Goal.
Actual behavior
should_run=true with normal delivery allowed and an unreadable required read.
The same status payload simultaneously recommends retiring the Goal, so the
two control-plane surfaces contradict each other and the quota gate wins.
Sanitized diagnostics
$ loopx --format json status | jq '.global_registry.findings[]
| select(.goal_id=="state-gone")'
{"kind":"state_file_missing","severity":"action",
"message":"`state-gone` active state file is missing",
"recommended_action":"preview `loopx retire-global-goal --goal-id state-gone`; ...",
"goal_id":"state-gone"}
$ loopx --format json quota should-run --goal-id state-gone --runtime-profile generic_cli \
| jq '{status_health_ok, decision, should_run, normal_delivery_allowed, state}'
{"status_health_ok":true,"decision":"run","should_run":true,
"normal_delivery_allowed":true,"state":"eligible"}
Additional context
The narrowest owner is goal_status_health_ok in
loopx/control_plane/quota/decision_summary.py: it already gates quota on
status/contract health, so consulting the per-goal state_file_missing finding
there keeps the decision fail closed without reinterpreting the attention queue.
source_registry_missing / stale_source_registry are deliberately shadowed
by docs/status-data-contract.md and should not be made blockers.
- Vorherrschende Sprache
- Python
- Sterne
- 6.2k
- Forks
- 596
- Ø Merge
- 6 Std. 44 Min.
- Gemergte PRs (30 T.)
- 915
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Hat eine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus loopx-project/loopx
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
loopx-project/loopx#6842 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
loopx-project/loopx#6108 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 65/100
loopx-project/loopx#5688 · 2 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 60/100
loopx-project/loopx#5632 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 65/100
loopx-project/loopx#5443 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
Alle Issues in loopx-project/loopx
Ähnliche Issues
-
idxmax/idxmin raise ValueError when a single partition is all-NAEvtl. vergeben @Jason10191019999 hat das heute übernommen. Offenneeds triage
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
Maintainer antworten meist innerhalb von 3 Tagen
-
bug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 64/100
-
[BUG]: Windows GUI package of v1.17.0-rc3 has no GTK DLLsEvtl. vergeben @andrut-ds hat das heute übernommen. Offenbug CI/CD Pactus-GUI Windows
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 82/100
pactus-project/pactus#2428 ·
Maintainer antworten meist innerhalb von 1 Tag
-
DiskDataset.reshard() can hang with a zero or negative shard sizeEvtl. vergeben @Programmer-Develops hat das heute übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
Maintainer antworten meist innerhalb von 19 Tagen
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 66/100
GNS3/gns3-server#3017 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag