Skip to content

feat(review): the entry-thesis question quotes what the user already said, instead of asking them to remember it (refs #636) - #653

Merged
atomchung merged 2 commits into
mainfrom
claude/issue-636-recall-entry-thesis
Jul 31, 2026
Merged

feat(review): the entry-thesis question quotes what the user already said, instead of asking them to remember it (refs #636)#653
atomchung merged 2 commits into
mainfrom
claude/issue-636-recall-entry-thesis

Conversation

@atomchung

Copy link
Copy Markdown
Owner

What the user sees

A first review asks every un-thesised holding what its entry thesis was, and offers five buckets. For a position the user had already discussed through consider, their exact words were sitting in trade_evaluations.jsonl the whole time — stored verbatim, read by nothing outside consider itself.

before  You are holding AAA at a cost basis of about $5,000.
        When you first entered this position, what was your thesis?

after   You are holding AAA at a cost basis of about $5,000.
        On 2025-12-28 you said: “…” Was that the thesis you entered on?

Same question kind, same choices, same event, same slot in the #291 density band. The question is asked better, not asked twice.

Why recall does not route through _evaluation_reconciliation

That surface looked like the obvious source and is the wrong one, twice over:

  • it is open-evaluations-only, and a resolved evaluation is still something the user said — an entry the user later marked acted is exactly the case worth recalling;
  • its schema declares it "A supply-side fact surface, not a card section or a question", so consuming it as a question source would contradict its own contract, and its item shape is additionalProperties: false.

_evaluation_recall reads the store directly and does not read decision at all.

Boundaries, each locked by a test

rule why
a statement recorded after the cycle's entry is never quoted it describes a different decision than the one this question asks about
the latest qualifying statement wins an earlier one it superseded is not the user's account of why they entered
an evaluation with no reason/why_now is dropped, not quoted empty consider runs fine with no --decision-context; a recalled blank is worse than the canned question it replaces
a cycle whose start date is unknown (TICKER#unknown) recalls nothing a statement must not attach to a cycle whose entry nobody knows

Same-day counts: contemplation and fill routinely land on one date.

This states a fact and never a cause. The stem says the user said this, on this date. It never says the position was opened because of it — the same boundary _evaluation_reconciliation documents for itself.

What this deliberately does not do

Verification

  • python3 tests/run_all.py49/49 suites pass, re-run after rebasing onto main@22417a7.
  • Two mutations, both caught, re-verified on the rebased tree: forcing _evaluation_recall to return empty → red; removing the entry-date boundary → red.
  • New tests confirmed to actually execute, not skip.

🤖 Generated with Claude Code

test and others added 2 commits July 31, 2026 10:36
…said, instead of asking them to remember it (refs #636)

A first review asked every un-thesised holding to reconstruct its entry
motive from memory, offering five buckets. For a position the user had
already discussed through `consider`, their exact `reason` was sitting in
`trade_evaluations.jsonl` the whole time -- stored verbatim by
schemas/decision-context.schema.json and read by nothing outside
`consider` itself.

The stem now quotes that statement, dated, and asks whether it was the
thesis they entered on. Same question kind, same choices, same event,
same slot in the #291 density band: the question is asked better, not
asked twice.

Recall deliberately does not route through `_evaluation_reconciliation`.
That surface is open-evaluations-only, and a resolved evaluation is still
something the user said; its schema also declares itself a supply-side
fact surface and not a question. `_evaluation_recall` reads the store
directly and ignores `decision` entirely.

Three boundaries, each locked by a test:

- a statement recorded after the cycle's entry is a different decision
  than the one this question asks about, so it is never quoted;
- the latest qualifying statement wins, because an earlier one it
  superseded is not the user's account of why they entered;
- an evaluation carrying no `reason` or `why_now` is dropped rather than
  quoted empty -- `consider` runs fine with no `--decision-context`, and
  a recalled blank is worse than the canned question it replaces.

This states a fact and never a cause. The stem says the user said this on
this date; it never says the position was opened because of it.

`initial_thesis` stays in `evals/run_episodes.py`'s `KNOWN_UNWIRED`. This
improves the question; the answer still has no consumer, and #429 is not
fixed by it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… and the field it adds is declared where readers look (refs #636)

Independent review of the first cut found two blockers and four smaller
defects. Every one is a claim the offline suite could not make false, which
is the point of the review.

**A re-entered position quoted the previous one's reason.** A cycle id
carries no lower bound, so every statement made before a ticker's *first*
entry also satisfies `created <= start` for its second. The owner's ruling
on #636 is per cycle: a ticker fully exited and re-entered is a new position
with its own reason. Bounding the window properly needs the prior cycle's
exit date, which this layer does not have, so a re-entry now recalls nothing
rather than attributing the old reason to the new position.

**The new `recalled_statement` made the emitted plan invalid.** A question
row is `additionalProperties: false` in `review-plan.schema.json`, and the
field was not declared. Nothing failed: no offline test fed a first_review
question queue through that schema. The field is declared now, and the
recall test pins every emitted row against the schema's own property set, so
the next undeclared field fails here instead of in a reader.

**`_cycle_start_date` did not enforce the contract it cited.** It split on
`#` and parsed the second segment, which accepts shapes `trade_recap` can
never produce. It is now `_cycle_entry`, matching `trade_recap.CYCLE_ID_RE`
— the single source of truth — and returning the sequence the re-entry rule
needs. The first version of the test did not catch this: `int()` and
`fromisoformat()` already reject the malformed cases it listed, so the
strictness was decorative until the test grew the shapes only the regex
rejects (a ticker with whitespace, an empty ticker, a trailing space).

Three corrections to claims that were wrong rather than incomplete:

- the docstring called an earlier statement "superseded". It is not:
  `_evaluation_id` seeds on `context`, so the same premise re-asked with a
  different `why_now` mints a *distinct* evaluation. Taking the latest is a
  choice of the statement closest to the entry, not a supersede semantic.
- a comment offered `recalled_statement` to "the receipt". `ux_receipt`'s
  `question_presented` accepts only source and digest and rejects content.
- the test fixture wrote a context with `reason` alone, a shape
  `decision-context.schema.json` forbids — it requires `why_now` too, so the
  core test was asserting against a record `consider` cannot write.

`docs/maintainer-guide.md`'s mirrored-surfaces row still said
`_evaluation_reconciliation` was the only reader of `trade_evaluations.jsonl`
outside `consider`. It now names both readers and states why their filters
differ, so a third one has to choose deliberately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant