Skip to content

doctor tells you to run keel autonomy on to clear the kill switch; that command cannot clear it #693

Description

@eaitbrahim

doctor's kill-switch finding tells the operator to run a command that cannot clear the kill switch.

Finding(
    "rail.kill_switch",
    HALTED,
    "kill switch engaged",
    "every entry is vetoed; this is a correct state, not a fault",
    "keel autonomy on",          # <- does not do this
)

autonomy_on calls repo.set_autonomous(True, ...) and nothing else. Its own docstring says so in terms:

It does NOT let the agent clear a safety halt: releasing the kill-switch or a drawdown breaker always needs a human, whatever this is set to.

The command that clears it is keel resume (trading.disengage_kill_switchrepo.set_state("kill_switch", False)).

How it plays out

Observed while bootstrapping the equities profile on 2026-09-02. A fresh database defaults the kill switch to engaged, so the first cycle prints skipped: kill_switch. Following doctor's advice:

$ keel ... autonomy on
About to turn autonomy ON.  Type "yes" to confirm: yes
autonomy ON, with NO expiry

$ keel ... agent
[1788361666] skipped: kill_switch      <- unchanged

The operator has now typed a confirmation for a dangerous capability — unattended order placement — and is still halted, with no indication that the two are different gates. Arming autonomy while still halted is strictly worse than either state alone: it grants the risky half and withholds the harmless one.

Why the wording matters more than usual here

These are deliberately separate controls and the separation is load-bearing: who gets asked versus whether the agent runs at all. A fix line that conflates them teaches the operator they are one thing, which is the opposite of what the design says. keel kill / keel resume and keel autonomy on / off are two pairs on purpose.

Acceptance

  • rail.kill_switch's fix line names keel resume.
  • Every doctor fix line names a command that can actually produce the state it describes — pinned by a test, because this is the second doc/behaviour drift found by running the documented path (see the rail-count drift, fix(docs): the rail count drifted to five different answers in one commit #687).
  • The pin is mechanical: extract the command from each Finding.fix, check it exists in the CLI, and where a finding names a state, check the named command touches it. A finding whose fix is prose (-, "see the runbook") is out of scope.
  • TDD, mutation-verified.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions