decision-system S9b: the One Thing question at SessionStart and its verdict readback #1278
Labels
No labels
P0
P1
P2
P3
bug
category/code-change
category/cross-repo
category/eval-run
category/investigation
category/no-test-target
category/owner-ruling
category/prose-artifact
category/recurring-chore
category/structure
category/umbrella
crc-refactor
create
delete
enhancement
filed-by/agent
filed-by/user
frozen
lint-rule
needs-info
needs-triage
next
plugin/cc-architect
plugin/os
plugin/os-adr
plugin/os-aidd-lint
plugin/os-backlog
plugin/os-context
plugin/os-doc-hygiene
plugin/os-sdlc
plugin/os-vault
project/cc-os
ready-for-agent
ready-for-human
recurring
review
rule-proposal
update
waiting
wayfinder:grilling
wayfinder:map
wayfinder:map
wayfinder:research
wayfinder:task
wayfinder:task
wontfix
worklist/deviations
worklist/lint-rule
worklist/new-implement-build
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jared/cc-os#1278
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
S9a writes open lesson candidates to the Store
queue/lesson-candidates.jsonl, but nothing shows a candidate to Jared and nothing reads his answer. Review must follow the One Thing rule (D-10): at most one yes/no question per session, at a natural pause, with the answer read later from the transcript as a natural verdict.Approach
Required work:
OsMe::SessionLedgerover the Store filequeue/one-thing.jsonl:#asked?(session_id),#record(session_id:, transcript_path:, candidate:)(row{session_id, transcript_path, candidate_id, question, asked_at}),#unread(asked rows with no verdict row), and#record_verdict(session_id, verdict). check: presentclass SessionLedgerinplugins/os-me/lib/**/*.rbCandidateLog#open(rows whose latest status isopen) andCandidateLog#mark(id, status)(appends a status row; the latest row of an id wins). check: presentdef markinplugins/os-me/lib/os_me/candidate_log.rbOsMe::OneThingSelector:#pick(session_id, transcript_path)returns nil for a held-out session (Split) or a session the ledger already holds; else it takes the open candidate with the most moments (ties: the smaller id), records it in the ledger, and returns it. check: presentclass OneThingSelectorinplugins/os-me/lib/**/*.rbOsMe::OneThingPrompt:#context(payload)returns nil when the Store directory is absent or the selector picks nothing; else the SessionStart hook output hash withadditionalContextAt a natural pause, ask this one question word for word, as the last line of that reply: <question>. check: presentnatural pauseinplugins/os-me/lib/**/*.rbhooks/one_thing.rb: it reads the payload from stdin, callsOneThingPrompton~/dev/os-me-store, prints the JSON only when the result is not nil, and exits 0 also on an error (asextract_moments.rbdoes). Add it tohooks.jsonunderSessionStartwith matcherstartupand timeout 5. check: presentone_thing.rbinplugins/os-me/hooks/hooks.jsonOsMe::LessonVerdict(model-backedDecision::Model::Question.choicewith optionsconfirmed,rejected,unclear, throughLabelerClient) andOsMe::VerdictReadback: for each unread ledger row, find the firstExchangeFinder#pairspair whose AI text holds the question; classify its reply; write the verdict to the ledger; mark the candidateconfirmedorrejected(anunclearverdict leaves itopen). No pair: no model call and no verdict. check: presentclass VerdictReadbackinplugins/os-me/lib/**/*.rbos-me-eval one-thing-verdictscommand: it runsVerdictReadbackon--storewith theHeadlessClaudelabeler client and prints the number of verdicts read. check: presentdesc 'one-thing-verdicts'inplugins/os-me/lib/os_me/cli.rbNotes: O-5 AFK default: the question goes in SessionStart
additionalContext. The hook makes no model call and no embedding call; it reads two small JSONL files. The SessionStart matcher isstartup, and the ledger still guards against a second question in one session. A held-out session gets no question, so no verdict comes from a held-out session (held-out discipline, AFK rule 8). The hook prints context, so it is a separate hook fromextract_moments.rb, which prints nothing (O-10 default). All tests live underplugins/os-me/test; the hook test runs the script withHOMEset to a temporary directory, asextract_launcher_test.rbdoes. Tests use the gemFixedadapter; no test calls a model. Runbin/refresh-pluginsafter thehooks.jsonchange. Gate (not part of this ticket): the first three questions are asked in real sessions and their answers are read back as natural verdicts.Acceptance criteria
OneThingSelector#pickruns for a train session, then it returns the 7-moment candidate andSessionLedger#asked?is true for that session.OneThingSelector#pickruns for it, then it returns nil and the ledger file gains no row.Splitholds out, whenOneThingSelector#pickruns for it, then it returns nil and the ledger file gains no row.rejectedorconfirmed, whenOneThingSelector#pickruns, then it returns nil.HOMEwhosedev/os-me-storeholds one open candidate, whenhooks/one_thing.rbruns with a SessionStartstartuppayload on stdin, then it exits 0 and stdout is JSON whosehookSpecificOutput.hookEventNameisSessionStartand whoseadditionalContextholdsAt a natural pause, ask this one question word for wordand the candidate question.HOMEwith nodev/os-me-storedirectory, whenhooks/one_thing.rbruns with a SessionStart payload, then it exits 0 and stdout is empty.rejected, whenVerdictReadback#runruns, thenCandidateLog#openno longer holds the candidate andOneThingSelector#pickfor a new train session returns nil.confirmed, whenVerdictReadback#runruns, then the candidate's latest status isconfirmed.VerdictReadback#runruns, then the adapter receives no call and the candidate staysopen.VerdictReadback#runruns again, then the adapter receives no call.Owner sketch
Scope and blocking
Blocked by: #1251
Design is in
docs/decision-system/review.md(OneThingSelector,SessionLedger, where the question appears) anddocs/decision-system/learning-loops.md(demotion sequence). Related: #1240.Non-goals: writing a confirmed lesson to its destination (lint lesson,
judgment/file, or tier row); runningcandidatesandone-thing-verdictsfrom the SessionStart launcher; the calibration factor in the pick order; tuning loops (S10).Origin
DESIGN(docs/decision-system/build-plan.md).VERDICT: CREATE (user-approved; backlog search only)
Work started on branch ticket-1278 (os-sdlc implement, AFK decision-system build, S9b).
Resolution
Done: SessionStart asks at most one lesson-candidate question per train session through additionalContext; os-me-eval one-thing-verdicts reads Jared's reply as a natural verdict and marks the candidate confirmed or rejected.
Evidence: Implementation 142: 8 dispatches, 0 retries, 8 min 5 s, implementation_complete. Suite gate green. Records commit on ticket-1278.
Follow-ups: Dropped: running candidates and one-thing-verdicts from the SessionStart launcher (ticket non-goal; the S9 gate runs them by hand first). Dropped: writing a confirmed lesson to its destination (remainder of #1240, already open). Contract-auditor LENS line on the one_thing file prefix has no proposal; revisit if a third one_thing file appears.