Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions atris/MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,13 @@ rg "export class Memory|getMemory|getMemoryAsync" src/memory/unified.ts # Unifi

### Feature: Investing Thesis Ledger
**Purpose:** Persist canonical thesis records, sync valuation context, and append versioned thesis revisions from thesis-refresh workflows.
- **Thesis draft + snapshot builder:** `src/domain/investing/thesis.ts:320`
- **Record sync + revision store:** `src/domain/investing/thesis.ts:358`, `src/domain/investing/thesis.ts:409`
- **Confidence rule + thesis draft builder:** `src/domain/investing/thesis.ts:293`, `src/domain/investing/thesis.ts:409`
- **Record sync + revision store:** `src/domain/investing/thesis.ts:476`, `src/domain/investing/thesis.ts:528`
- **Executor thesis-refresh integration:** `src/domain/investing/executor.ts:384`, `src/domain/investing/executor.ts:563`
- **Valuation packet sync hook:** `src/domain/investing/valuation-packet.ts:280`
- **CLI surface (`zee investing thesis status`):** `packages/zee/src/cli/cmd/investing.ts:320`
- **Operator doc:** `docs/architecture/investing-thesis-ledger.md:1`
- **Confidence rule doc:** `docs/architecture/investing-thesis-confidence.md:1`

### Feature: Memory (Qdrant + Hybrid Search + Markdown Sync)
**Purpose:** Store and retrieve semantic/keyword/hybrid memories with local Qdrant.
Expand Down
68 changes: 68 additions & 0 deletions docs/architecture/investing-thesis-confidence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Investing Thesis Confidence Rules

This slice closes `#510`.

The thesis ledger now requires evidence links for every persisted thesis revision and applies a deterministic confidence rule before any conviction change is accepted.

## Rule contract

Rule ID:

- `thesis-confidence.v1`

Each revision now carries a `confidence` block with:

- `requestedConviction`
- the conviction requested by the caller or carried forward from the prior thesis state
- `appliedConviction`
- the conviction actually written after the rule runs
- `maxAllowedConviction`
- the highest conviction the linked evidence bundle is allowed to support
- `score`
- rule score derived from evidence count, tool diversity, and linked valuation context
- `evidenceCount`
- number of linked evidence references on the revision
- `uniqueTools[]`
- distinct tool paths represented by the evidence
- `reasons[]`
- operator-readable explanation for the resulting conviction

## Current rules

`thesis-confidence.v1` applies these checks:

1. A thesis revision must include at least one evidence reference.
2. Evidence count contributes to the confidence score.
3. Distinct tool-path coverage contributes to the confidence score.
4. Linked valuation context contributes to the confidence score.
5. A `balanced` valuation signal caps conviction at `medium` even when evidence is otherwise strong.
6. If the requested conviction is stronger than the rule allows, Zee downshifts it and records the reason.

The rule is intentionally simple and deterministic. It gives operators an explicit audit trail now, while leaving room for richer scoring and policy later.

## Execution integration

The `thesis-refresh` path now does two things before it writes a revision:

- converts completed research evidence into thesis evidence references
- runs `thesis-confidence.v1` and stores the result on both the revision and the current thesis record

The thesis snapshot appended to synthesis output now also shows the applied confidence rule and reasons so operators can audit the conviction before reading the persisted change log.

## Telemetry

This slice emits:

- `investing.thesis.confidence`
- one event per thesis revision confidence evaluation
- `investing.thesis.revision`
- now includes requested and applied conviction metadata

## Operator checks

Use the thesis ledger record or revision payload to inspect:

- whether the revision had evidence links
- which tools backed the change
- whether the requested conviction was downgraded
- why the rule accepted the final conviction level
8 changes: 8 additions & 0 deletions docs/architecture/investing-thesis-ledger.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Each record stores:
- next monitoring items that should trigger a refresh
- `valuation`
- latest linked valuation case, packet, run, and signal metadata
- `confidence`
- latest applied thesis confidence assessment
- `revisions[]`
- append-only change log for the thesis

Expand All @@ -57,6 +59,8 @@ Each revision stores:
- linked valuation snapshot for later diffing
- `evidence[]`
- references to persisted evidence or valuation packets
- `confidence`
- applied confidence rule, capped conviction, and operator-readable reasons
- `source`
- workflow/task/execution/artifact pointers when the revision came from automation

Expand All @@ -83,6 +87,8 @@ zee investing thesis status --json

`zee investing thesis status` reports total theses, total revisions, and current counts by status and conviction.

Confidence rules and downgrade behavior are documented in `docs/architecture/investing-thesis-confidence.md`.

## Telemetry

Flux events emitted for this slice:
Expand All @@ -91,3 +97,5 @@ Flux events emitted for this slice:
- emitted whenever a thesis record is created, updated, or advanced by a revision
- `investing.thesis.revision`
- emitted whenever a new thesis revision is appended to the change log
- `investing.thesis.confidence`
- emitted whenever Zee evaluates the confidence rule for a thesis revision
1 change: 1 addition & 0 deletions packages/zee/src/flux/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export type FluxKind =
| "investing.valuation.packet.export"
| "investing.thesis.record"
| "investing.thesis.revision"
| "investing.thesis.confidence"
| "agent.legacy_tools_alias.used"
| "gateway.fallback.invoked"
| "provider.fallback.used"
Expand Down
48 changes: 47 additions & 1 deletion packages/zee/test/investing/thesis-ledger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,22 @@ describe("investing thesis ledger", () => {
currentPrice: 100,
upsidePercent: 40,
},
evidence: [
{
kind: "research-evidence",
id: "evidence-1",
label: "[E1] Research endpoint",
link: "evidence:execution-1:E1",
toolId: "zee:invest-research",
},
{
kind: "valuation-packet",
id: "valuation-packet-1",
label: "Valuation packet for NVDA",
link: "valuation-packet:valuation-packet-1",
toolId: "zee:invest-valuation",
},
],
})

const updated = recordInvestingThesisRevision({
Expand All @@ -80,7 +96,7 @@ describe("investing thesis ledger", () => {
changeType: "refresh",
summary: "NVDA thesis moved back to a neutral stance after estimate volatility.",
thesis: "The setup is intact, but the margin of safety is narrower than the prior refresh.",
conviction: "medium",
conviction: "high",
posture: "neutral",
watchpoints: ["Monitor estimate volatility before increasing exposure."],
valuation: {
Expand All @@ -92,6 +108,15 @@ describe("investing thesis ledger", () => {
currentPrice: 105,
upsidePercent: 6.7,
},
evidence: [
{
kind: "research-evidence",
id: "evidence-2",
label: "[E1] Analyst estimates",
link: "evidence:execution-2:E1",
toolId: "zee:invest-estimates",
},
],
})

expect(updated.currentVersion).toBe(2)
Expand All @@ -110,6 +135,24 @@ describe("investing thesis ledger", () => {
expect(await Bun.file(getInvestingThesisStateFile()).exists()).toBe(true)
expect(recordSpy.mock.calls.some((call) => call[0]?.kind === "investing.thesis.record")).toBe(true)
expect(recordSpy.mock.calls.some((call) => call[0]?.kind === "investing.thesis.revision")).toBe(true)
expect(recordSpy.mock.calls.some((call) => call[0]?.kind === "investing.thesis.confidence")).toBe(true)
expect(updated.confidence?.appliedConviction).toBe("medium")
expect(updated.revisions[0]?.confidence.appliedConviction).toBe("medium")
expect(updated.revisions[0]?.confidence.reasons.some((reason) => reason.includes("downshifted"))).toBe(true)
})
})

test("rejects thesis revisions that do not carry evidence links", async () => {
await withThesisState(async () => {
expect(() =>
recordInvestingThesisRevision({
thesisKey: thesisKeyForSymbol("MSFT"),
symbol: "MSFT",
changeType: "initialize",
summary: "MSFT thesis without evidence.",
thesis: "This should fail because no evidence references were provided.",
}),
).toThrow("Thesis revisions require at least one evidence link.")
})
})

Expand Down Expand Up @@ -214,9 +257,12 @@ describe("investing thesis ledger", () => {
expect(thesis?.currentVersion).toBe(1)
expect(thesis?.summary).toContain("NVDA thesis")
expect(thesis?.valuation?.valuationCaseId).toContain("valuation_case:equity:nvda:")
expect(thesis?.confidence?.ruleVersion).toBe("thesis-confidence.v1")
expect(thesis?.revisions[0]?.source.executionId).toBe(thesisExecution.id)
expect(thesis?.revisions[0]?.source.artifactId).toBe(thesisExecution.artifactId)
expect(thesis?.revisions[0]?.evidence.some((item) => item.kind === "valuation-packet")).toBe(true)
expect(thesis?.revisions[0]?.confidence.evidenceCount).toBeGreaterThan(0)
expect(thesisExecution.synthesis).toContain("confidenceRule=thesis-confidence.v1")
})
})
})
2 changes: 1 addition & 1 deletion src/domain/investing/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ async function buildSynthesis(input: {

if (input.plan.workflow === "thesis-refresh") {
const primarySymbol = normalizeSymbol(input.plan.symbols[0]);
if (primarySymbol) {
if (primarySymbol && input.evidence.some((item) => item.status === "completed")) {
const thesisDraft = buildInvestingThesisDraft({
symbol: primarySymbol,
evidence: input.evidence,
Expand Down
Loading
Loading