S9: take the moment topic from the predictor's Toward line; leave out shown predictions without one #1299
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#1299
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
OsMe::MomentTopic(plugins/os-me/lib/os_me/moment_topic.rb) gives each ruling moment the text thatPatternClustererembeds. It reads only the shown AI message: theToward:value of a PREDICTION block in that text, else the first line that is not a heading or a stock opener, else the stored headline. When the main session rewrites a prediction as markdown before it shows it, the shown text has noToward:line. The first plain line is then layout (**Predicted action:** ...,**The predicted reply:**), not the topic.Measured live on 2026-10-23: 280 ruling memo moments in 133 transcripts; 63 transcripts contain a
Toward:line anywhere. After #1289, #1293, and #1294, the live refresh gave 2 candidates, and both were layout clusters. The predictor Agent's own result in the same turn holds theToward:line, butMomentTopicnever reads tool results or task notifications.Ruling (Jared, 2026-10-23, accepted prediction): option A, then B.
towardfield.Definitions used below:
ai_index). Entries afterai_indexare not in the turn.tool_resultblock whosetool_use_idis theidof a main-threadtool_useblock namedAgentin the same turn, or the<result>text of a main-thread task notification in the same turn (a background predictor returns through a task notification). Thetool_resultcontentis a string or an array of{type: text}items joined with newlines.strip, matches/\A[*_]*\s*(?:The\s+)?Predict|\A[*_]*\s*This is a [*_]*prediction/i. A heading line (starts with#) never matches, so## Predictionalone does not mark a shown prediction.Topic order per moment:
PredictionParser#towardvalue of the last Agent result in the turn that has one.PredictionParser#towardof the AI text.Short answer,You are right).Approach
Entrylearns to read Agent calls and tool results; a newTurnResultscollects the Agent results of one turn;ExchangeFinder#pairsgives eachPairits turn's Agent results, soMomentTopicstill reads each transcript once and adds no transcript parser.MomentTopiccallsPredictionParser#towardon each Agent result text, so the YAML path that #1292 adds totowardworks with no change here.prediction_parser.rbis not edited.MomentTopic#textsreturnsnilfor a left-out moment, andPatternClustererdrops those moments before it embeds. TheThis is a predictionstock opener is removed because the shown-prediction rule now covers it; the two #1293 disclaimer tests change to expect no embed.Required work:
EntrygetsAGENT_TOOL = 'Agent',agent_call_ids(ids of main-thread assistanttool_useblocks namedAgent), andtool_results(hash oftool_use_idto result text for main-thread usertool_resultblocks, string or text-item array content). check: presentdef agent_call_idsinplugins/os-me/lib/os_me/entry.rbEntry#tool_resultsreturns the joined result text pertool_use_id. check: presentdef tool_resultsinplugins/os-me/lib/os_me/entry.rbOsMe::TurnResultscollects, for one turn, the Agent call ids it has seen, thetool_resultstexts for those ids, and eachnotification_text, in transcript order. check: presentclass TurnResultsinplugins/os-me/lib/os_me/turn_results.rbPairgets the memberagent_results, default[], so existingPair.newcallers stay valid. check: present:agent_resultsinplugins/os-me/lib/os_me/pair.rbExchangeFinder#pairsstarts a newTurnResultsat each typed reply, snapshots its texts when it records the last AI entry, and builds eachPairwith keyword arguments that includeagent_results:.ExchangeFinder#finddoes not change. check: presentTurnResults.newinplugins/os-me/lib/os_me/exchange_finder.rbMomentTopicmapsai_indexto the wholePair, applies the topic order above, and returnsnilfor a shown prediction with no Toward. check: presentSHOWN_PREDICTIONinplugins/os-me/lib/os_me/moment_topic.rbMomentTopicreads the Agent results throughpair.agent_resultsand@parser.toward. check: presentagent_resultsinplugins/os-me/lib/os_me/moment_topic.rbThis is a predictionentry fromSTOCK_OPENERS. check: absentThis is a \**\s*predictioninplugins/os-me/lib/os_me/moment_topic.rbPatternClusterer#clusterkeeps only the rulings whose topic is notnil, returns[]when none are kept, and embeds only the kept topics. check: presentkeptinplugins/os-me/lib/os_me/pattern_clusterer.rbtool_usenamedAgentwith anid) and its result (usertool_resultwith thattool_use_id). check: presentdef agent_callinplugins/os-me/test/test_helper.rbCandidateRefresh#run. check: presentclass CandidateRefreshAgentTowardTestinplugins/os-me/test/candidate_refresh_agent_toward_test.rbclustering: Use option B for the cache.inplugins/os-me/test/candidate_refresh_stock_opener_test.rbAcceptance criteria
All tests use fixture transcripts in a temp dir, a
TranscriptCatalogover that dir, a moments log with ruling moments that carrytranscript_pathandai_index, and a fake embedder that records its input. No model, no network. Each runsCandidateRefresh#run.go, an assistantAgentcall with ida1, atool_resultfora1whose content is an array of text items holdingPREDICTIONandToward: cache the index, then the AI text**Predicted action:** accept, then typedyes, when the refresh runs, then the embedder receives exactly["clustering: cache the index"].Toward: other topic, and thetool_resultcontent is a plain string, when the refresh runs, then the embedder receives["clustering: cache the index"](the Agent result wins).<result>holdsPREDICTIONandToward: drop the workerbetween the typed message and the AI text**The predicted reply:** yes, when the refresh runs, then the embedder receives["clustering: drop the worker"].Keep two workers., when the refresh runs, then the moment whose AI text is**The predicted reply:** yesis not embedded and the embedder receives only["clustering: Keep two workers."].tool_resultfor aReadcall (not an Agent call) whose text holdsPREDICTIONandToward: template topic, and the AI textDrop the second worker., when the refresh runs, then the embedder receives["clustering: Drop the second worker."].**Predicted action:** accept,**The predicted reply:** yes,*This is a prediction, not your ruling.*, andThis is a **prediction, not a decision**., each with no Toward anywhere in its turn, when the refresh runs, then the embedder is never called and no candidate log file exists.## Prediction,**Short answer:** yes,Drop the second worker.and no Agent result, when the refresh runs, then the embedder receives["clustering: Drop the second worker."].Toward: cache the indexand an embedder that maps that text to one vector, when the refresh runs, then the candidate log holds one open row with the three moment ids.Owner sketch
Scope and blocking
Blocked by: none.
In scope:
entry.rb, newturn_results.rb,pair.rb,exchange_finder.rb,moment_topic.rb,pattern_clusterer.rb,test/test_helper.rb, the new test file, and the two disclaimer tests intest/candidate_refresh_stock_opener_test.rb.Out of scope:
prediction_parser.rb(#1292 owns it and adds the YAML path totoward; this ticket only callstoward), the Store, the candidate file, the SessionStart hook,ExchangeFinder#find, andMomentExtractor.Merge order: this ticket must merge after #1292 only if #1292 also edits one of the in-scope files above. When drafted, the #1292 worktree had not changed
moment_topic.rb.Origin
plugins/os-me/lib/os_me/moment_topic.rb).VERDICT: CREATE (user-approved; backlog search only)
Work started on branch ticket-1299 (os-sdlc implement). #1292 is merged.
Resolution
Done: MomentTopic takes the topic from the predictor agent's Toward line first, then the AI-text Toward line; a shown prediction with no Toward line is left out of clustering; Entry, TurnResults, Pair and ExchangeFinder carry the Agent result texts of each turn.
Evidence: Implementation 156 complete, all ten dispatches pass, 7 min 33 s; commits
669e1029andda487003on ticket-1299; suite gate green.Follow-ups: none