fix: the two things the re-run left as decisions - #208
Merged
vicenteliu merged 3 commits intoAug 19, 2026
Conversation
…ships ADR-0028 says playbooks opt in with `propose_actions: true`. None does, which is how this stayed hidden: the opt-in was appended to the system prompt *before* the prefetch branch, and `_do_prefetch` rebuilds the prompt from `pb.system_prompt`, so the instruction was dropped on the floor. Every playbook in the repo is `mode: "prefetch"` except `pb_vendor_doc_en`, so opting in would have changed nothing for anyone who tried. The behaviour gate has a case for this exact behaviour and it passes, because it builds the system prompt by hand from `_PROPOSE_ACTIONS_PROMPT`. It proved the prompt works. Nothing proved the prompt arrives. The opt-in now applies after the prefetch branch, so both retrieval modes carry it, and the new test asserts it against the trace — which records the system prompt actually fed to the model — parametrised over `tool` and `prefetch`. It fails on prefetch before this change. The default stays off. A proposal is a command somebody reads at 2am and the approval gate behind it is a heuristic denylist that says so in its own docstring, so shipping it on would be a posture change nobody asked for. What was wrong was discoverability: outside ROADMAP the key was named nowhere, so the two incident playbooks now carry it commented out with the reason beside it. The admin model-list editor still round-trips both files byte-identically. behaviour-gate: 6 passed — memory injection 3/3, conflict reported 3/3, distillation keeps dead ends 3/3, proposals stay read-only 3/3, memory proposal 3/3, memory proposal restraint 3/3 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`opspilot workingset status` reported "No working set open" while the web UI had one open for the same person, on the same install. A Working set is per-owner by construction (ADR-0032), and the two surfaces disagreed on who the owner was: the CLI writes `cli:<osuser>`, the loopback API writes `local-dev`. Both names are deliberate and both docstrings say why. The split between them was not. It reaches further than the Working set — a Memory entry admitted from the CLI carried a different actor than the same person's entry admitted from the UI, and `actor` is the field ADR-0030 leans on to say who decided what. The CLI now answers `local-dev` under exactly the condition `auth.deps` falls back on: no user account and no service token. A missing `users` table counts as no users, because it means auth was never set up. The check is a read-only query, so asking the question does not create the schema. The moment identity means something they diverge again, on purpose: with a user or a token configured the CLI has no auth context and must not borrow a name it cannot prove, so `cli:<osuser>` returns — advisory, not evidence. Verified against a running server: the API opens a Working set, the CLI sees it, closes it, the API confirms it closed, and a Memory entry added from the CLI comes back over REST with `actor: local-dev`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`propose_actions` and the CLI/API identity split were both filed as posture questions rather than bugs. Chasing them turned one into a defect (the opt-in never reached the model in prefetch mode) and settled the other (one unconfigured install, one operator, one name). The proposed-actions section now says the default is off *deliberately* rather than by omission, and records that until today opting in would not have worked regardless. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vicenteliu
deleted the
fix/propose-actions-wiring-and-one-local-identity
branch
August 19, 2026 13:15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#207 closed with two items filed as posture questions rather than defects.
Chasing them turned one into a bug and settled the other.
C —
propose_actionsnever reached the modelFiled as "the feature is finished, nobody opts in." The interesting half is what
turned up on the way to fixing that: opting in would not have worked.
ticket_summaryappended_PROPOSE_ACTIONS_PROMPTto the system prompt beforethe prefetch branch, and
_do_prefetchreturnspb.system_prompt + addendum—rebuilt from the playbook, so the opt-in was discarded. Every playbook in the
repo is
mode: "prefetch"exceptpb_vendor_doc_en, which is a vendor-docsummariser and would never propose a diagnostic.
The behaviour gate has a case for exactly this behaviour and it passes, because
it builds the system prompt by hand from
_PROPOSE_ACTIONS_PROMPT. It provedthe prompt works; nothing proved the prompt arrives. That is the same seam
#201–#207 kept finding, one layer up: the gate covers what the model does with
an instruction, not whether the instruction is still there by the time the model
sees it.
The opt-in now applies after the branch. The new test asserts it against the
trace — which records the system prompt actually fed to the model —
parametrised over both retrieval modes; it fails on
prefetchbefore thischange and passes after.
The default stays off. A proposal is a command somebody reads at 2am and the
approval gate behind it is a heuristic denylist that says so in its own
docstring — shipping it on is a posture change nobody asked for, and ADR-0028
says existing playbooks are unaffected. What was actually wrong is that the key
was named nowhere an operator would look, so the two incident playbooks now
carry it commented out with the reason beside it:
Checked that the admin model-list editor still round-trips both files
byte-identically — #204 fixed comment loss in that path, and this adds comments
to files it rewrites.
D — one human on one machine got two names
opspilot workingset statussaid "No working set open" while the web UI had oneopen for the same person. The CLI writes
cli:<osuser>, the loopback API writeslocal-dev, and a Working set is per-owner by construction.It reaches past the Working set: a Memory entry admitted from the CLI carried a
different actor than the same person's entry from the UI, and
actoris thefield ADR-0030 leans on to say who decided what.
Both names are deliberate and both docstrings explain themselves. The split
between them was not. The CLI now answers
local-devunder exactly thecondition
auth.depsfalls back on — no user account, no service token — with amissing
userstable counting as no users, and the check is a read-only queryso asking does not create the schema. With a user or a token configured it goes
back to
cli:<osuser>: then it genuinely has no auth context and should notborrow a name it cannot prove.
Verified against a running server:
Verification
Full suite 1357 passed — 7 new. ruff, ruff format, mypy(154) clean. The one local
failure is
test_providers_ollama.py::TestIntegration::test_chat_smoke, whichwants
gemma4:e4bfrom a local Ollama that does not have it; CI deselects it.behaviour-gate: 6 passed — memory injection 3/3, conflict reported 3/3, distillation keeps dead ends 3/3, proposals stay read-only 3/3, memory proposal 3/3, memory proposal restraint 3/3
🤖 Generated with Claude Code