Release Notes

What shipped, newest first. This log is append‑only: a published entry is never edited or removed, and a correction is a new entry that says what changed — the same discipline SEER's own reports are held to.

2026-09-17 build 5dac17f

The 8-K tally narrows the gate's diagnosis

Self-review of the 8-K change caught dead state. `eightks_by_filer` was tallied in `assemble_context`, carefully documented as being kept apart from the primary-registrant gate's count, and then never read by anything — the same shape as the two auditors that were built, unit-tested and never called.

The separation was right and stays: 8-Ks must not satisfy that gate, because almost every domestic registrant files them and folding them in would let a ticker whose 10-K and 10-Qs never arrived through a check built to catch exactly that.

But they are worth reading. `audit_primary_coverage` now takes the tally as an optional third argument and uses it to eliminate a cause: a primary with 8-Ks on file is being resolved to a live registrant, so the operator can stop suspecting the CIK — the loudest of the three fixes the message offers — and go look at `filing_type` instead. With no 8-Ks the message says nothing about them rather than reporting a zero, because "0 8-Ks" reads like a finding and is usually just silence.

2026-09-17 build 2ff1e71

The T1 tier now says whose filings it is made of

`source_inventory` asked whether a T1 block arrived. It never asked **whose**.

`CRITICAL["T1"]` is one entry — "primary filings (10-K/10-Q/20-F)" — and an `also_fetch` registrant's filings satisfy it completely. So a run in which the subject company contributed nothing at all reported a full T1 tier, a clean inventory, no warnings, and told synthesis its evidence base was intact.

`t1_by_filer` reads the `Ticker:` line out of each T1 block and reports the counts per registrant; `primary_t1_problem` turns "none of them are the subject company's" into a warning in the log, a field in the stored record, and an instruction in the prompt — say it in Variant Perception, say it in the Risks table, and name the filer on every T1 claim.

**This does not duplicate the primary-registrant gate.** That one stops such a run before anything is billed and is the more valuable of the two. But it is a tally incremented inside six `except` blocks, and this is read back off the context string synthesis actually receives. When they disagree, this one is right — the same reason the rest of this module derives from the context rather than counting as the fetchers run.

**It will not claim a provenance the context does not state.** A T1 block with no `Ticker:` line is counted as unattributed and alarms on nothing. "I cannot tell whose these are" and "these are somebody else's" are different statements, and asserting the second where only the first is known would put a fabricated claim about provenance into the prompt — this module's own failure mode pointed inward. A single filing of the subject company's own silences the alarm, so a ticker with a legitimate `also_fetch` does not cry wolf on every run.

2026-09-16 build fe73b26

SEER can read an investor presentation

The second thing the HOS run needed and could not do. The owner's two URLs — `ir.hornbeckoffshore.com/static-files/<uuid>` — are PDFs, `ir_fetcher` fetches an IR *page* and runs `clean_html` over it, and nothing in the repo parsed PDF text at all. Playwright was here, but only to render output PDFs, which is the opposite direction.

`fetchers/presentation_fetcher.py` reads a deck as a PDF or as a web page and emits it as a Tier 2 source. `pypdf` is now a hard dependency — pure Python and a few hundred kilobytes, where Playwright's ~400MB of Chromium stays optional — and is still imported lazily inside the extraction function, so `import main` succeeds on a machine that skipped it.

**The date is required, and nothing invents one.** A presentation is Tier 2, and T2 is not in `UNDATED_TIERS` — only T3 and T4 may be cited `undated`. So a deck without an ISO date is a grammar violation on every claim that rests on it. These URLs are opaque UUIDs carrying no date, and the fetch time is when SEER read the document, not when the issuer published it; dating a deck by when we downloaded it is what Rule 15 exists to forbid. A line without a date is refused at the operator route with a message saying where to find one, and a config hand-edited past that route is checked again at fetch time and the deck dropped with a stated reason. A test asserts the reason is stated, because deleting the check also produces an empty result and asserting only on the output would pass against a broken guard.

**Operator-supplied, with no crawler.** IR sites are JavaScript shells behind bot protection, their document lists are unstructured, and a scraper guessing which of forty links is "the Q3 deck" will eventually cite last year's. A new `presentations` field on the ticker config, a `/ui/presentations` route, and a textarea beside the additional-registrants one: `URL, YYYY-MM-DD, label`, one per line. Bad lines are reported and skipped rather than failing the save, and the field shows what is stored rather than what was typed — the same contract the registrants control follows, for the same reasons.

**An empty extraction is a failure, not an empty block.** A scanned deck with no text layer and a bot-protection interstitial both return HTTP 200 with nothing to cite, and an empty source block in the context looks exactly like a deck that had little to say. Under 200 characters raises, naming which of the two it probably was.

Content type is decided by the first five bytes, not the header: IR hosts serve the same PDF as `application/pdf`, `application/octet-stream` and occasionally `text/html`.

**Not verified:** that the owner's two URLs specifically return a text layer. This container cannot reach ir.hornbeckoffshore.com. If either is a scanned image the fetch will say so by name rather than quietly contributing nothing.

2026-09-16 build 4539ab4

SEER can fetch an 8-K

The capability the previous note made citable. `fetch_8k` reads the 8-K and 8-K/A filings of the last 180 days, keeps the ones filed under a material item, and pulls their EX-99 exhibits alongside the body.

**Why this form and not simply "more filings".** A filing can be current in days and stale in IDENTITY. `STALE_AFTER_DAYS` measures age, and by that measure the HOS context was fine: a 10-K and three 10-Qs, recent, correct, filed by the right registrant — CIK 0000866829, which was Cal Dive, then Helix, and became Hornbeck Offshore on 2026-09-01. The documents were not old. They described a company that had stopped existing in that form two weeks before the report was written. The 8-K is the only document that announces such a change.

**Item filtering, and the half of it that matters more.** An 8-K is filed for dozens of reasons and most are routine. Eight item numbers are kept — 1.01, 1.02, 2.01, 4.01, 4.02, 5.02, 8.01, 9.01 — and the rest, a Regulation FD disclosure of a conference appearance among them, are not fetched at all.

An 8-K whose item numbers EDGAR does not list is **kept**. That is deliberate and it is the more important rule: EDGAR leaves the field empty on older rows, and a filter reading "no data" as "nothing material" would drop every filing on a registrant whose rows lack it — reintroducing, inside the fix, exactly the silence the fix exists to remove.

**Exhibits are the point, not a bonus.** An 8-K/A's substance is normally an EX-99 and not the filing body, so a body-only fetch would return a near-empty stub for precisely the filing that matters. EX-99 only: the HOS 8-K of 2026-09-01 carried an indenture and a supplemental indenture running to some 56,000 words between them — legal instruments that would cost a fifth of the context window and say almost nothing a thesis turns on — while its EX-99.3 press release told the whole story in 1,241. `SEER_8K_EXHIBIT_PREFIXES` widens it deliberately.

**Item numbers are carried into the block with their meanings.** EDGAR reports them as bare numbers; `items: 2.01, 9.01` tells a reader nothing about why the filing mattered, and `2.01 completion of an acquisition or disposition` does. Items the module does not label pass through as numbers rather than being hidden, because a filing kept for one material item routinely carries others.

**8-K text does not go through `select_filing_sections`.** That selector is anchored on 10-K-shaped `Item 7.` headers. An 8-K numbers its items 1.01/2.01 under a different scheme entirely, so the selector would find nothing and fall back to truncating — discarding the filing's substance to keep its cover page. A test asserts the call is absent from the block and that the selector still has no notion of the form.

**The primary-registrant gate was not weakened to accommodate this.** 8-Ks are tallied separately from the periodic filings the gate counts. Almost every domestic registrant files 8-Ks, so folding them into that tally would let a ticker whose 10-K and 10-Qs never arrived sail through a gate built to catch exactly that.

**Cost.** This adds up to 90,000 characters of context to every run — roughly 22,000 tokens of input, on a pipeline whose runs already meter over a million. `SEER_8K_TOTAL_MAX_CHARS`, `SEER_8K_MAX_CHARS` and `SEER_8K_WINDOW_DAYS` tune it; a total budget of 0 turns the fetch off.

**What is verified and what is not.** Eleven tests against a canned EDGAR cover item filtering in both directions, the window, the amendment, the exhibit selection, the caps, the entity decoding, and that existing callers of `get_recent_filings` are byte-identical. Every assertion was negative-checked by breaking the behaviour it guards. What none of it proves is that EDGAR's live payloads match the shape assumed here — this container cannot reach data.sec.gov. `tools/diagnose_filings.py HOS` is the free way to confirm that before a run is paid for.

2026-09-16 build 91bea33

8-K was citable and unfetchable for the grammar's entire life

`citation_grammar.DOC_TYPES` is Rule 13's enum and the prompt is generated from it, never written alongside it. It has listed `8-K` under T1 since it was written. **Nothing has ever fetched one.**

So a report asked about a material event had two options, both bad: omit it, or cite a document it was never given. That is the root cause of the HOS report — it described the pre-transaction business from filings that were correct and current, while the 8-K announcing that the company had become something else sat unfetched.

The module already guards the opposite direction: a transcript event type whose doc type is missing from the T2 enum fails at import, "which is the whole reason this module exists". Nothing guarded this direction, because the question had only ever been asked one way round.

`UNSUPPLIED_DOC_TYPES` now names, with reasons, every type the grammar permits that no fetcher supplies, and a test pins its exact contents. Adding a doc type without a fetcher fails the suite until someone writes one or records why not. It currently holds exactly one entry — `Form 4`, which is genuinely useful and genuinely unbuilt.

Two types added ahead of the fetchers that will supply them, so nothing can arrive uncitable:

**`8-K/A` under T1**, as its own type rather than folded into `8-K`. An amendment is a different document filed on a different date, and it is routinely where the substance lands — the financial statements of an acquired business arrive under Item 9.01 in the amendment, not the original. A citation reading "8-K" for an amendment names a document the reader cannot find.

**`Investor Presentation` under T2**, distinct from `Investor Day` and `Conference Presentation`, which are transcripts of a spoken event. A deck is a written document with its own date, and it is where management's dated long-range targets actually live — which is precisely what the bull ladder's B2 rung admits, so this is the source that rung was built to use.

Both are correctly rejected as `undated`: T1 and T2 are not in `UNDATED_TIERS`, and a deck's date comes from the operator or the document, never from when we happened to fetch it.

2026-09-16 build b5fb5d0

The subject company must appear in its own report

`assemble_context` wraps each registrant's filing fetch in its own try/except that prints and continues. That is right when one of several registrants fails and wrong when the one that failed is the company the report is about — and nothing downstream could tell the difference, because `source_inventory` asks whether a T1 block ARRIVED, not whose. A related registrant's filings satisfy its "the report has no audited source" check completely.

So a run could assemble a report about one company out of another's filings and say nothing. It now refuses: when the primary registrant contributes zero filings, `NoPrimaryFilings` aborts the run.

**It aborts in the fetch phase, which is the whole point.** Everything `assemble_context` does — EDGAR, Yahoo, the transcript API — is free; the first billed call is the narrative lens immediately after it. Failing on that line costs nothing and saves the entire $8-12 run. A test asserts the ordering rather than trusting it, because a gate that fired after synthesis would be correct and worthless.

This is a deliberate exception to the standing preference that a paid report should print rather than be discarded, and the reasoning is that the two cases differ in kind: a thin report is worth having, and a report about a different company is worth less than nothing, because every figure in it is correctly cited to a filing that is not the subject's.

A primary contributing 10-Qs but no 10-K does **not** trip the gate — a recently listed company legitimately has no annual filing yet, and refusing to report on every new listing would be a worse bug than the one being fixed. The failure message names all three candidate causes and points at a new free diagnostic, `tools/diagnose_filings.py`, because the operator's next action differs per cause and the run cannot tell which applies.

**What this does NOT fix, stated because a fix credited with catching a bug it cannot catch is worse than no fix.** The HOS report that prompted this was not a wrong-registrant case at all. `HOS` and `HLX` are one registrant — CIK 0000866829, formerly Cal Dive International, then Helix Energy Solutions, and since the 2026-09-01 transaction named Hornbeck Offshore Services. SEER fetched that registrant's filings correctly; they were simply the pre-transaction, Helix-era ones. What it could not fetch was the 8-K announcing the company had become something else. This gate would not have fired on that run, and the 8-K fetcher that addresses it is a separate change.

2026-09-16 build 7881c09

The analogue library learns what a second-order thesis costs

`second_order_beneficiary` — the archetype calibrating 14f, the agent that traces committed spend down a supply chain — held **zero episodes**. So `prior_from_analogues` returned the uninformative `0/0` -> 50% for every 14f thesis, `closest_analogues` came back empty, and the analogue-failure veto had nothing to fire on. That veto is the rule saying a thesis whose closest analogues mostly failed must explain why this setup escapes their failure mode or not publish, and as of the evidence-ladder release it is the main brake on 14f's highest rung. It was inert. Unity's 14f printed "no analogue assessment available" and that was why.

Ten episodes now, owner-approved from `docs/ANALOGUE_SEEDING_PROPOSAL.md`: five that re-rated and five that did not.

The five winners span five sectors and, deliberately, five different waves — funded highway programmes into aggregates (Martin Marietta), funded fab capex into wafer-contact materials (Entegris), hyperscaler capex into site grading (Sterling), committed biologic launches into the elastomer closures that qualify *with* the drug (West Pharmaceutical), and funded fulfilment capex into infill logistics (Prologis). Two names from one wave would inflate apparent corroboration exactly the way three agents restating one observation do.

**The five failures are the point.** Each carries a `teaches` line naming a distinct way evidence that looks committed turns out not to be:

- Velodyne had *disclosed design wins with named OEMs* — precisely the evidence this agent admits — and they were non-binding. **A design win is not a purchase obligation.** - Chicago Bridge & Iron had record backlog off the same capex wave that paid Martin Marietta. **Backlog is not margin;** the fixed-price share was disclosed the whole time. - Photronics was second-order to the *identical* funded fab capex that carried Entegris, and never re-rated. **Upstream spend does not confer downstream pricing power** — ask who can be dual-sourced. - Nikola's order book was cancellable letters of intent. **An order book is not a backlog.** - Plug Power's revenue actually arrived, at negative gross margin. **Revenue arriving is not economics arriving** — the failure that survives a volume-only check.

The archetype's prior lands at 5/10 -> **50%**, which is the same number the empty archetype returned. That is not a null result. `0/0` gave 50% knowing nothing; 5/10 gives 50% because ten episodes say so, and what changes 14f's behaviour is everything the bare prior could not carry: a **median re-rate horizon of 20 quarters**, so a thesis promising its re-rating inside four is now arguing against its own reference class, and five named failure modes for the veto to actually fire on.

Two judgment calls are recorded in the data rather than smoothed away. West and Prologis both de-rated materially after 2021; both carry a `resolution_note` saying so and saying that a reader who scores the give-back as non-resolution would get 3/10 and 33% instead. And era matching is weaker than the headline 5-and-5 symmetry suggests — 2016 runs three-to-one for winners, 2020-21 three-to-one against — so no single era's slice is calibrated even though the whole is.

`language_signal` is **left empty on purpose**, and `_meta` now says so in the file rather than leaving a future reader to assume an oversight. 14b keys on a section-level filing diff; an episode that cannot be tied to a real diff calibrates a vaguer thing that shares the name, and a half-remembered 2-of-4 returns the same 50% while *claiming* evidence.

`analogue_library.json` is now on the release-note watch list. It is data rather than code, and it went unwatched until the day it was first seeded because that list had only ever been asked which `.py` files matter — but changing one episode's `outcome` moves the published probabilities on every future report as surely as editing the prompt does.

Cost: the library roughly doubles, adding about 2,900 tokens to each of 14f's and 14g's prompts, or roughly two cents a run.

2026-09-15 build b25b7a8

The audit checked what would prove the thesis wrong, never right

`note_audit.compare_kill_criteria` has verified, since the day kill criteria were forwarded to synthesis, that the note actually carries the dated conditions the roster validated. Milestones were forwarded in the same change and never checked at all. So the report's conditions for being WRONG were audited against the roster and its conditions for being RIGHT were taken on trust. Of all the places to find this project's bear/bull asymmetry, the audit is the worst one.

`compare_milestones` closes it, and is deliberately a WEAKER check than its mirror. A kill criterion carries an ISO date, so the date is a hard token to search for. A milestone carries a horizon in quarters — 1, 2, 4 — which appear throughout any financial note as ordinary numbers, so the same technique would match essentially any prose and report a clean pass on a note that mentioned nothing. It matches on the observable's distinctive content words instead, and needs a majority of them present.

Weaker on purpose: a milestone is a forecast, and the note is entitled to restate it in its own words or to argue with it. What this catches is a milestone DROPPED ENTIRELY — the roster said what would show the thesis working and the report said nothing about it. A milestone too generic to fingerprint is skipped rather than guessed at, in either direction, because a check that cannot run must not report a pass any more than a failure.

2026-09-15 build a060e2d

A dead agent no longer reads as a confident "no edge"

An alpha agent that FAILED TO FINISH and one that ran and found nothing produce identical-looking reports. The only thing separating them is somebody saying so, and the only place SEER said so was a line inside the collapsed evidence layer — which the reader has to expand to see.

On the QCOM run 14f truncated and never ran. The note reported no non-consensus finding, in the voice of a search that had happened, and nothing a reader would encounter said that a seventh of it had not. This is not a presentation defect. "We looked and found nothing" and "we did not finish looking" are different claims about the world, and the report was making the weaker one sound like the stronger.

Three layers now carry it, and they are separate because each fails differently. The PROMPT tells synthesis to state it in the note, which produces the best-written version and which the model can ignore. The NOTE AUDIT reports when the note did not, by keyword, so a sufficiently inventive paraphrase slips past — which is why it is an advisory observation rather than a gate, and the limit is written into the code rather than left to be discovered. The REPORT PAGE states it from stored data, with no prose involved, so that layer cannot be talked out of.

None of the three fires when the roster completed. A coverage caveat printed on every report is a caveat nobody reads, and then it is not there on the run that needed it — the same reasoning that keeps an unreachable entry from being listed as a defect. Declining is likewise not failing: an agent publishing no_thesis has done its job, and treating that as a coverage gap would report a correct considered negative as a fault on a large share of runs.

One more gap, found reviewing this change's own diff and of exactly the kind it was written to close. `run_alpha_annex` fills its exclusion list at the END, so a roster that RAISED partway through left that list empty — and an empty exclusion list is indistinguishable from a roster that finished cleanly. A total roster failure, which is the most complete coverage gap there is, was reported as no gap at all, and the report would have been written as though a full search had happened when none had. Every selected agent that published nothing before the raise is now recorded as incomplete; agents whose findings already landed are left alone, because they did complete.

The existing threading test caught the signature change immediately and was extended rather than merely repaired: it now asserts that the roster is asked for its exclusions AND that those exclusions arrive at the prompt, because those are two separate failures and the second one is silent.

2026-09-15 build 0ecbc41

The base rate could only ever make a report more cautious

The instruction carrying the analogue base rate into synthesis read: "Let this discipline the scenario probabilities — a thesis whose analogues rarely rerated does not get a high bull weight." Correct, and only half written. The other half — a thesis whose analogues MOSTLY rerated does not get a low bull weight — was never there, so the only thing a base rate could do to a report was make it more cautious. That is not calibration. It is a one-way ratchet wearing calibration's clothes, and it is one of the six places the machinery was asymmetric.

It now states both directions and points at `scenario_probability.py`, which is where that prior is actually consumed since the derivation shipped.

Five stale references corrected in the same pass, all of the kind that are individually harmless and collectively how a codebase stops describing itself:

The note has SEVEN parts. "SEER Entry Level" was added as the seventh and the count was updated nowhere, so the prompt told the model the note had six parts while specifying seven, `note_audit`'s own docstrings said six, and the operator log divided by a literal 6 — printing "7/6 parts" for a complete note, which reads as a parser fault rather than a correct report. Every count now derives from `NOTE_PARTS`, and a test asserts no literal creeps back, because changing 6 to 7 only postpones this to the eighth part.

The `[SEER]` tier's own definition pointed at Rule 17 for the rule governing it. Rule 17 is the evidence ledger; the rule that says the SEER tier is analytical context and never evidence is Rule 16.

And the roster told synthesis, twice, not to "manufacture a contrarian angle to fill the Alpha Thesis section" — a section that does not exist in this report format and has not for some time. That instruction is the one that fires on a zero-yield run, when the report is most at risk of inventing an edge, so it now names the sections that do exist: The Claim and Variant Perception.

2026-09-15 build 8f8d542

Every sector module now says how a thesis confirms, not only how it breaks

All seventeen sector modules carried a BEAR CASE TRIGGERS block — three to six dated, observable, falsifiable conditions under which the thesis is wrong. None carried a bull equivalent. Not a weaker one; none.

That is not a tone problem. It is the same structural asymmetry as the missing bull auditor: one side of the argument had a checklist the writer worked through and the other had whatever occurred to them, so the side with the checklist is the side that gets argued. A report carrying five dated conditions for being wrong and none for being right is not neutral, however even-handed its prose reads.

Each module now carries a matching BULL CASE TRIGGERS block, written to the same standard and to the same depth: observable in a filing or a disclosed metric, dated, and capable of not happening. Software asks for NRR above 120% sustained and RPO growth leading revenue; semiconductors for a dated sole-source design win and content per unit rising on a disclosed platform; financials for NCO holding below the through-cycle range through a quarter of rising unemployment. "Adoption accelerates" is not a trigger and is rejected by a test, because the way this change could do harm is by producing a hype generator, and unfalsifiable language is what that looks like on the page.

Two triggers are deliberately the SAME EVENT as a bear trigger pointing the other way, and say so. Tightening environmental regulation raises the barrier for a materials producer's higher-cost competitors. An insurtech cutting its reinsurance cession rate is either losing capacity or retaining economics from strength, and the two read identically until you check which direction the capacity moved. Both now require the report to say which and why.

The one-way rule in `semiconductors.txt` is also fixed. It read "Single-customer concentration >40% is a moat vulnerability flag regardless of margin" and applied an automatic premium discount for it. But a sole-source socket on a multi-year platform IS the concentration risk and IS the evidence of design lock-in — the same fact seen twice — and the archetypal early multi-bagger looks exactly like the archetypal fragile supplier at that point in its life. The rule now decides by the contract rather than the percentage: annually re-competed or second-sourced is the vulnerability, a qualified sole-source win running to a dated end of production is the moat, and the discount applies only to the first. As written it penalised the design-win company the module exists to find, automatically, before anyone read the contract.

2026-09-15 build 433dcb0

The analogue library was loaded from the working directory

Both loaders resolved `analogue_library.json` against the process working directory. This is the footgun this project has already paid for once — six modules resolved against the CWD and the sector-module one silently routed every ticker to `_default.txt` with no error — and here it was worse, because the read is guarded by `.exists()`. A wrong working directory did not raise. It produced a run with no historical calibration at all, and nothing in the report said so.

The consequence is specific. 14g publishes no base rates, so no thesis in the run carries one. 14f loses the analogue-failure veto — the rule that says a thesis whose closest analogues mostly failed must explain why this setup escapes their failure mode, or not publish. Under 14f's old Hard Rule that veto was a second opinion beside a rule admitting committed capital only. Under the evidence ladder shipped alongside this, it is the main brake on the highest rung. Losing it silently is the difference between a disciplined structural thesis and a press release.

The path is now anchored to the checkout, matching how `scenario_probability.LIBRARY_PATH` already resolved the same file, and a regression test runs the Futurist's preparation from a foreign working directory — which is what production does and what a developer never does.

2026-09-15 build 009242e

14f can now argue the thesis it kept finding and discarding

The Grounded Futurist's Hard Rule admitted committed capital and nothing else: signed contracts, funded capex, filed patents, regulatory applications, disclosed design wins, purchase obligations. It is a good rule, and running it as the ONLY rule had a cost that was invisible from inside it. The theses it can express are exactly the ones somebody has already pre-sold by contract, which is the smallest and latest-arriving class of future there is.

The case that made this concrete: a company that is the sole listed pure-play in a market about to be transformed has no contract saying so. That thesis could not be written down at all — not argued and found wanting, simply inexpressible. 14f returned no_thesis, and a reader could not tell "I looked and there is nothing here" from "what I found had no legal shape to go in". The agent was working exactly as designed. The design excluded the finding.

14f now works at the same four rungs the report's bull ladder prices, and must name which one its thesis rests on in the first clause of its mechanism: B1 committed, B2 management's own dated targets, B3 a forward model with every unfiled step labelled where it is used, B4 a structural change no document yet states. The old Hard Rule is B1, unchanged, and a reader who only acts on committed capital can stop reading at the first clause.

What did not move is the standard of FACT. The evidence array is still B1 and only B1 — every entry a citation copied from the record, exactly as the shared guardrail contract requires. Loosening the standard of inference is not loosening the standard of evidence, and a thesis that needs an uncited fact still does not have that fact. Vibes, TAM slides and management adjectives are no more admissible than they were.

Reaching above B1 is granted against obligations, each of which is what stops the extra room becoming a licence: label every unfiled step in place, state the chain as steps rather than as a conclusion, and carry at least one dated kill criterion that tests the INFERENCE rather than the company — an assumption with no kill date is a belief. And a B4 thesis that could be written for every company in the sector is not a thesis; the agent is asked what is true of THIS filer that is not true of its closest listed comparables, cited.

Three disciplines were deliberately NOT relaxed. The 14d boundary, which is what keeps two agents from publishing one finding. Innings honesty, including the instruction to publish "this future is real but fully priced" as a no_thesis — a prescience agent that can only say buy is a momentum chaser with a thesaurus, and a ladder that reaches higher makes that failure easier, not harder. And the analogue-failure veto, which now says in as many words that it applies most stringently at B4: the base rates are the only thing in 14f's inputs that has already watched a structural story fail, and a higher rung is exactly when one most wants to ignore them.

The prompt and the parser share one vocabulary and nothing structural keeps them agreeing, so a drift check asserts it, in the same spirit as the counter-thesis grammar's. The agent's description is updated too: left stale it would tell the operator's roster picker that 14f still admits committed capital only.

2026-09-15 build 4d390f4

Give the bull case a compiler, an auditor and four prices

Reports had become reliably bearish, and not because anyone decided a company was bad. The machinery was asymmetric in six places at once, and the audited side wins.

`counter_thesis.py` compiled the bear case's required shape into the prompt and checked the output after synthesis, and ordered SEER to construct a bear argument where none existed. Grepping this repository for the bull equivalent returned zero hits. `bull_thesis.py` is that mirror: same verdict enum, same generated-not-hand-written block, same mechanical audit, same forcing clause. Its one deliberate difference runs the other way — it requires "Confirms if" rather than "Flips if", because every sector module already carries BEAR CASE TRIGGERS and none carries a bull equivalent, and a report with two sets of conditions for being wrong and none for being right is not neutral.

Scenario probabilities were three numbers a reader could not argue with; the entire stated method was "The probabilities are your judgment; state them as such." They are now derived from two things: the analogue library's base rate, which answers whether the market rerates GIVEN the thesis is right, and the note's own dated kill criteria, which answer whether it is right. Bear = 1 - P(survive); Base = P(survive) x (1 - prior); Bull = P(survive) x prior. The derivation is printed as a table in Shown Calculations, and the weights sum to 100 by construction rather than by rounding.

Two details there matter more than the formula. The prior is Laplace-smoothed because the library holds nine hand-seeded episodes and several archetypes hold one or none — a raw 1/1 is not certainty and a raw 0/0 is not a probability. And kill criteria are grouped by driver: criteria that would be triggered by the same real-world event contribute their single worst probability instead of multiplying. Run against a real report's four criteria, all of which tested one margin question from four angles, multiplying them gave a 66% bear and grouping them gave 25% — a third of the probability mass moved onto the bear purely because the thesis had been written down carefully.

The bull number itself was capped, and by nothing anyone chose. No rule forbids a higher multiple; it is emergent. The only multiple evidence in a run's context is a 3-year price series, Rule 14 requires every computed figure to cite its inputs, and Rule 6 forbids speculation — so a multiple above that series had no citable source and was therefore illegal, and the bull arrived at roughly the recent high. The bull is now priced four times at four declared standards of evidence: committed capital only, plus management's dated targets, plus a shown forward model, plus a structural change no document yet states. Each rung runs through the same 3:1 bar against the same bear, so the bar never moves — only the evidence admitted into the bull does. A new Forward Model section carries the three-year build the upper rungs are priced off, with every rate an INFERENCE claim under the existing Rule 17 anchored to the filed figure it extrapolates from.

The asymmetry bar is untouched, because it was not the problem. An entry is live when the bull exceeds four times the mark at a zero bear, so a genuine multi-bagger cleared 3:1 comfortably; the bull simply never got near it.

Both new auditors run in the pipeline and both state their answer on the report page, beneath the counter-thesis line and styled identically to it. That last detail is deliberate: a page that announces every bear argument adjudicated and says nothing about the bull case reads as thoroughness when it is one-sidedness, and giving one side a quieter treatment would put the original asymmetry back in the stylesheet. A report written before these checks existed shows no line at all rather than a passing one — an unstated check and a check that passed are different claims. `tests/test_audit_wiring.py` asserts the seam rather than the logic, because the first draft of this change built both auditors, tested both thoroughly, and wired up neither.

All three of a ladder's structural faults reach the note audit, which is what prints problems to the operator and stores them with the report — a fault computed into a dict nobody reads is not a check. One consequence had to be designed around rather than discovered later: the note audit compares the entry line's stated value against a derived one, and now that the selected rung supplies the shared `derived` field, an operator changing `SEER_ENTRY_TIER` would otherwise have made every single report report a drifted entry line. The entry line is checked against its own arithmetic, the rung against the rung's.

Three mechanical checks keep a ladder honest, and they are the reason this is a structure rather than a licence. Rungs must be monotonic, because each admits strictly more evidence than the one above it and so cannot be worth less — a B3 below its own B2 means either a slipped calculation or a rung reaching for evidence its label forbids. Each rung's stated entry is recomputed from its own bull, and the derived figure is what gets recorded. And the rung the note marks canonical must quote the same bull as the mandated entry line: both halves can be internally perfect while disagreeing with each other, and that join is the one place where the published entry line and the public tag could have quietly stated different numbers.

For the operator: `SEER_ENTRY_TIER` (default B3) chooses which rung becomes the stored entry and the public tag. Every field `positions.json` carried before still means exactly what it meant and is still populated — `entry_tags. for_watchlist()` reads those fields to tag the whole watchlist, and a change in their meaning would silently re-tag every covered name. What the setting does change is which rung's numbers those fields carry, which is the point of storing all four: the watchlist can be re-tagged at a stricter or looser standard of evidence without paying for a re-run. The rung actually used is recorded alongside, so a tag is never unattributable.

2026-09-14 build ec99666

Never run an effort the model cannot fund

The re-run after the budget fix published, and that is the problem: it published UNVERIFIED. The roster recovered — six of seven agents published against one before — but synthesis at effort "max" hit its 128,000-token ceiling exactly. Reasoning took roughly 116,000 of it and the report got about 12,000, so 44,063 characters of prose arrived against a 24,000-token text budget. The evidence ledger is emitted last, so a report carrying 266 cited claims arrived with FOUR ledger entries, 38 claims were quarantined because every excerpt failed to tie out, and the citation layer — the product's whole proposition — was gone while the run reported success.

The cause was a silent clamp introduced by the previous fix. "max" asks for eight times a 24,000-token text budget, or 192,000; the model's hard limit is 128,000, so the budget helper quietly returned a third less headroom than the level was designed to have. Thinking is generated first and cannot be capped separately on this model, so the entire shortfall came out of the report rather than out of the reasoning.

A clamped budget is now recognised as unfunded rather than treated as a smaller working one, and an effort SEER cannot honour is stepped down BEFORE the call instead of truncated during it: synthesis requested at "max" runs at "high" with a fully funded 120,000, and says so in the log. There is no larger ceiling to escalate to, so thinking less is the only lever that exists. A report reasoned slightly less hard is worth more than one that cannot be verified.

The alpha roster's text budget also goes 8,000 to 12,000. 14f truncated a second time, now against 64,000, which means reasoning alone consumed more than 56,000 tokens before its JSON closed; 12,000 puts "max" at 96,000, still inside the model's limit and so funded rather than clamped.

2026-09-14 build cfa6def

Size the token budgets for thinking, not just for text

A QCOM run at effort "max" was billed in full and published nothing. Four of the seven alpha agents truncated mid-JSON at their 16,000-token cap, a fifth returned JSON cut off at the same ceiling, a sixth was skipped for want of candidates the others never produced, and synthesis then reasoned for about nine minutes and returned a reply whose only content block was `thinking` — no report at all. The watchlist showed ERROR and the deploy log showed nothing, because the failure branch recorded the message into the job and never printed it.

One piece of arithmetic, mis-made three times independently: `max_tokens` caps thinking PLUS text. Each of the three caps — alpha 16,000, lens 16,000, synthesis 64,000 — had been hand-tuned against whatever effort level was in use when someone last looked, and none was sized for "max". This codebase had already learned that lesson twice, raising the classifier 2,500 -> 16,000 and the lens 4,000 -> 16,000, and each fix bought headroom only for the effort in use that day. On Sonnet 5 thinking cannot be bounded separately — `budget_tokens` was removed from the API and returns a 400 — so the ceiling is the only lever, and it now derives from the text budget and the effort level in one place (`model_io.max_tokens_for`) rather than being guessed per call site. At "max" synthesis gets 128,000 and each agent 64,000; at the "medium" default, 72,000 and 24,000. The alpha and lens calls are now streamed, as synthesis already was, because a non-streaming request at those sizes risks an HTTP timeout.

Two things that made the failure expensive rather than merely annoying are fixed alongside it. A failed research job now prints the exception type and traceback instead of silently storing the message, and synthesis records its cost and stop_reason BEFORE reading the reply text — the line that raised on this run, so the one run that most needed a cost record produced none. And because 128,000 is the model's hard ceiling, with no larger cap to escalate to, a reply that carries no text at all is retried exactly once at the next lower effort: every fetch, the lens and all seven agents have already been billed by the time synthesis runs, so the alternative to one retry is discarding the whole run.

2026-09-11 build 5125b85

Operator-selected free reports in the member library

The interim release requires eligible Discord membership or existing paid reading access by default. Operators can mark individual saved reports **Free to everyone**, admitting anonymous readers to the report, available evidence and PDF. Turning that setting off restores the member gate. Hidden reports remain operator-only even when their free-reading preference is on.

The free flag defaults off and persists independently of visibility in both storage backends. Concurrent changes preserve both settings. Operator controls show an unconfirmed state when a save response is lost and require a reload to confirm the saved value. Desktop and mobile regression checks cover admission, revocation, hidden overrides, forbidden Reader edits and lost responses after the server commits. Existing research and source providers remain unchanged.

2026-09-11 build 6ff3d7b

Interim Discord Reader accounts

Eligible Discord members can connect their account to receive complimentary, read-only access without a card or automatic billing. Membership is checked every five minutes, independently of existing paid plans. Account linking uses encrypted tokens and revocable, single-use authorization state. The configurable members-only policy applies consistently to HTML, PDFs and embedded evidence; the public policy retains the current report archive's reading behavior.

Community Reader accounts cannot spend on chat, configuration or research. Community browser actions require CSRF tokens, hosted cookies require HTTPS, login returns stay on site, and application access logs omit OAuth queries. Existing reports, source providers and generation logic are unchanged. This is separate from the commercial beta; new subscription checkout remains paused. Regression coverage includes membership failures, preserved paid access, callback replay, hidden artifacts and desktop/mobile account forms.

2026-09-11 build b094e3b

Naming a second registrant, from the dashboard

Yesterday's release taught SEER to draw filings from more than one SEC registrant, for companies whose history spans two of them. It did not give anyone a way to say which. The setting lives in a ticker's configuration, and the only thing that writes that configuration is the automatic classifier, which has no way to be told about a predecessor.

The Run dialog now has a field for it. One registrant per line — its ticker, its CIK, a name, and a sentence saying why its filings belong in this report.

A malformed line costs that line and nothing else. This gets typed rarely, about a company whose filing record is already confusing, so getting one line wrong is the expected case rather than the exception: SEER saves what it understood, names what it did not, and shows you what is now stored rather than what you typed.

2026-09-10 build d01db43

Finish the backlog's launch-gate presentation

The backlog's section heading now describes launch requirements and deferrals, rather than calling already-implemented work deliberately unstarted. Paragraphs and bullet items stay intact in the existing renderer, including the checkout hold emphasis. This changes only the internal backlog's document formatting and heading; all existing backlog access and rendering regressions passed locally.

2026-09-10 build 3169322

Reconcile the operator backlog with commercialization

The operator backlog now distinguishes production fixes, implemented and tested beta work in PR #17/staging, and the evidence still required before paid launch. It retires obsolete Postgres/Auth0 migration stops and the n8n item, replaces historical cost estimates with measured-usage guidance, and records the provider, editorial and operations gates. Staging and synthetic tests are explicitly separate from actual-library recovery and customer acceptance. The existing operator-only route and public release log remain intact; all backlog and append-only release-log regressions passed locally.

2026-09-10 build 852e271

Show recorded audit findings without verification guarantees

Watchlist badges now say Audit recorded; report headers display the stored counts with their limits. Located text does not establish claim support, the legacy other-claims count is not a model-pass count, and missing measurements remain unknown. Citation drawers retain model disagreement, inferences and missing verdicts. Source-tier definitions now match the citation grammar, and the note-shape denominator comes from its seven-part definition.

Original report, PDF and evidence artifacts retain their bytes and historical wording, with a visible explanation. This display correction does not re-audit reports, approve publications or change existing authorization and metering. New purchases remain paused by default. Six focused regressions, including required Chromium at desktop and mobile widths, passed locally. The other 61 production test scripts passed; PostgreSQL checks run in CI.

2026-09-10 build 61411af

A company whose filings live under two registrants

A stock ticker is not a permanent name for a company. It moves when a company is taken private, merges, or renames itself — so a company's filing history at the SEC can sit under two different registrants, and the public map only knows who holds the symbol today.

SEER assumed otherwise: one ticker, one filer, everything else invisible. For a company like Hornbeck Offshore — public through 2020, private for five years, back on the market through a combination that moved its ticker to a different registrant — that means half the record was unreachable no matter which half SEER happened to find.

A ticker's configuration can now name additional registrants to draw filings from. Their documents are labelled with who filed them, and the report is required to name that filer in its prose wherever it uses one. A citation carries a tier, a document type and a date — not a company — so an unlabelled predecessor filing would read as the subject's own. Presenting two registrants' financials as one unbroken history is forbidden without saying where the company changes.

**Separately, and this one applies to every ticker:** SEER now says so when the newest filing it can find is more than about a year old. A company that has stopped filing still returns its last filings, correctly dated and correctly formatted, and nothing previously distinguished those from current ones. A confident report built on six-year-old financials is a worse failure than a report that says it has nothing to work with.

2026-09-09 build b72bc731828ee6cec2f33601af2bad4d2b0f50f5

New subscriptions stay paused until explicitly enabled

Configured Stripe keys and prices no longer open new purchases automatically. New Checkout sessions require `SEER_CHECKOUT_ENABLED=1`, checked both by the purchase route and by the session-creation function. With the flag absent or disabled, the pricing page explains that new subscriptions are paused and shows no purchase forms. Plans without a configured price also have no purchase form.

The pause is separate from existing subscriber access: price-to-tier mappings, signed subscription updates and cancellation handling remain active. It does not expire already-created Stripe Checkout URLs or cancel existing renewals. Keep new purchases paused while launch checks for billing, access, source rights, publication and recovery remain incomplete.

Offline regressions cover missing/nonexact flags with test and live key fixtures, anonymous and signed-in purchase attempts, a second check in the core function, the enabled path, and real Stripe signature verification through the webhook route with isolated in-memory persistence. No real payments or provider changes were made.

2026-09-09 build c6ac71003b1165c1927bfa2c166fc9bb40f28396

One admission check for report pages and PDFs

PDF downloads now apply the same visibility and existing paywall rules as the report page. A missing or orphan PDF cannot bypass the report check or consume a free read. A denied download returns a payment-required response.

Free readers can reread their three distinct monthly reports through either representation. A PostgreSQL transaction locks each reader's admission decision, recognizes rereads first, then counts and records a new report. Simultaneous requests cannot admit a fourth. An unavailable meter returns a temporary error; known unlimited tiers do not use it. This uses the existing report_reads table, with no schema migration or subscription change.

Local validation: 60/60 test scripts passed with Chromium checks required. The new real PostgreSQL concurrent-admission test uses an isolated test schema and requires the CI test database; it was explicitly skipped on the local machine. Provider integration checks remain separate from this access hotfix.

2026-09-09 build b716f2872a4a70d6f1f00e855d8a30056dc5c1a3

Sanitized rendering and private research access

Generated HTML now passes through an HTML5 sanitizer for report pages, chat, new PDF rendering and Ghost output. Scripts, active markup, unsafe links and application-UI impersonation are removed. Charts are rebuilt deterministically from cited tables, so a stored or model-supplied SVG cannot claim to be a trusted chart. Imported source artifacts are preserved; their HTML is sanitized when rendered. Financial text, citation bytes, chart coordinates and labels have regression coverage, including real Chromium execution and request checks.

Detailed research-job responses now require the existing operator sign-in. Hidden own, peer and portfolio thesis summaries are filtered before chat context is built. One canonical ticker controls report filenames and visibility; path traversal, drive/UNC paths and encoded aliases are rejected before reads. Dot/dash share-class tickers remain supported. The existing operator password, customer identity, subscription tiers and paywall flag retain their behavior.

2026-09-09 build 69cef31

VRIO is a moat assessment again

Every SEER report is supposed to assess a company's durable advantage against VRIO: is it Valuable, is it Rare, is it Inimitable, is it Organized to exploit it — four separate verdicts, each with evidence behind it.

August's restructure lost that. The framework survived as the word "VRIO" inside a parenthetical, in a section that also had to cover the business model and the balance sheet and was explicitly marked compressed. Nothing named the four dimensions and nothing asked for evidence per dimension, so a report could write a paragraph about competitive advantage and never assess any of the four. That is what recent reports did.

VRIO is its own section again, in the evidence record where length is not rationed: four verdicts, one line each, each carrying its own citation. Inimitable is judged against the sector module's moat archetype and its minimum-evidence bar — the dimension a well-funded competitor's capital actually tests.

A dimension the evidence does not support is answered "not established", with what would establish it. Four of those is a legitimate result. A moat framework that cannot report the absence of a moat is one that invents them, and inventing one is a worse outcome than the honest blank.

2026-09-04 build 7bcb097

A switch, instead of a warning

Correcting the entry below rather than editing it, which is what this log is for.

That release gave the operator the ability to withhold a report, and the enforcement was right. The presentation was not. A withheld row was dimmed and carried a red "withheld" badge, while the control that flipped it lived inside the Run dialog — the state announced twice, in the visual language of a fault, and neither announcement anywhere near the thing that changed it.

Withholding a report is a choice, not a defect, and it should look like one. The dimming and the badge are gone. Each report now carries a public/private switch under its Run button, visible only when signed in as operator, and the switch is the only thing that reports the state.

What the switch does is unchanged: private means the report's page and its PDF answer as though it does not exist, and the chat panel will not quote it — for everyone except the operator, at any subscription tier. That last point now has a test behind it rather than resting on the order two checks happen to appear in.

2026-09-02 build 794a02c

The watchlist says what SEER concluded, and you choose what is published

Two changes to the watchlist, one for readers and one for the operator.

**Entry tags.** Every report already derives a SEER Entry Level: the price at which a setup clears the asymmetry bar, computed from the note's own bear and bull figures. That number lived inside the report and nowhere else, so the watchlist could not answer the question a reader actually has — would SEER take this position. Each covered company now carries one of four tags, with the dated price it was computed from: at entry, watch, validate, no-go.

The date is part of the tag, not decoration. These are marks observed when the report ran, not a live feed, and a tag that hid its own date would read as a call on today's price.

"Validate" outranks "at entry" deliberately. Where the entry price cannot be recomputed, or the report leaves its own bear arguments unresolved, or a source tier it needed never arrived, or its citations were never tied out, the tag says so rather than publishing a conclusion the evidence does not carry. A company SEER never recorded a view on gets no tag at all — an absent record is not a verdict.

**Report visibility.** The operator can now withhold a report. A withheld report is withheld everywhere: its page and its PDF answer as though it does not exist, and the chat panel will not quote it. The company stays on the public watchlist, because coverage is a fact about what SEER is tracking and hiding a write-up should not deny the work exists.

This replaces the "Under active coverage" checkbox, which dimmed a row and did nothing else — no part of the pipeline ever read it. Reports stay public by default, so nothing that is readable today became unreadable.

2026-09-01 build e5b6059

Filings were arriving without their financial statements

A run on SandRidge Energy produced a report on an oil and gas company with no audited financial statements and no MD&A anywhere in its source context. The tier counts said the filings were there. They were: gutted.

SEER converts EDGAR filings from HTML to text before splitting them into Item sections, and that conversion never decoded HTML entities. EDGAR's inline-XBRL documents write non-breaking spaces as character references inside Item headings, inconsistently — some headings in a filing use one, others a literal space. A heading carrying one was invisible to the splitter, and an invisible heading leaves no gap: the previous section simply runs on through it. On that filing, 43% of the document ended up filed under "Market Risk", financial statements included, unreachable by any citation naming them.

Entities are now decoded. Separately, when a filing arrives without a section SEER expects, it says so and says what the absence costs — the failure above was legible only as a line that named what it found.

The same run also lost its management-narrative lens: its token budget covered the model's reasoning as well as its answer, and at high reasoning effort the reasoning consumed all of it. Budget raised, matching the fix already made for the alpha agents.

Reports generated before this are affected wherever a filing's headings used character references. Re-running a ticker regenerates it against the full document.

2026-08-28 build 2de0ba1

The staleness banner is gone from the public page

Correcting the entry above rather than editing it, which is what this log is for.

That release described a banner on /releases warning when the running build was not named by any note. It could never be cleared. SEER merges with a merge commit, so the commit that deploys is created after every note inside it was written, and the follow-up commit recording that sha becomes the newly unnamed one in turn. The banner would have fired on every correct deploy, in front of customers.

The signal was worth keeping and the audience was wrong. It now sits on the operator backlog, where an unnamed build reads as a to-do rather than a fault, and where the ordinary case — a merge commit — reads as ordinary.

Nothing depended on it. The release notes ship inside the build, so the page cannot show another version's notes, and CI still fails any branch that changes what SEER produces without writing an entry.

2026-08-28 build 9e9e41d

A release log and an operator backlog, current by construction

SEER now publishes what shipped and, behind the operator sign-in, what has not.

The release log is append-only and checked as such: every heading present in any earlier revision of the file must still be present now, verified against git history rather than trusted to a comment. Correcting a published entry means adding one that says what changed — the same discipline SEER's own reports are held to. Entries are sorted by release date descending at render time, so appending at the bottom of the file, which is the natural instinct, still produces a correctly ordered page.

The backlog page reads `SEER_HANDOFF_STATUS.md` live rather than copying it. That file opens by claiming authority over any other backlog document, and a second copy would have become exactly the stale rival it warns about. It is operator-only, and the nav link is not rendered for a visitor: a link that 401s advertises the page and refuses it in the same gesture.

Both files ship inside the build, so neither page can show content belonging to a different version of the code. The one direction that structure cannot cover — deploying without writing the note — is caught in CI, which fails a branch that changes what SEER produces without adding an entry, and stated on the page at runtime when the running build is undocumented.

2026-08-27 build e03726a

Three defects the first live run of the new structure exposed

A Block run — 76 claims, six alpha agents — was the first live test of the two-layer note. The structure held; the machinery around it had three faults.

The citation audit was flagging the report's own analysis as unsourced fact. 29 of 76 claims carried a warning and roughly two dozen were on the note's thesis sentences, which pair a sourced fact with SEER's reading of it. The writer had already declared those inferences in the evidence ledger, so the audit now honours that and reports them as analysis rather than as defects. The single genuine evidence mismatch in that run had been buried under two dozen warnings a reader could not act on.

The entry-level line rendered "Entry level: $87.50 — At or below the $83.10 mark", which is the inverse of the truth: the mark sits at or below the entry, which is why the position was live. The arithmetic was right and only the sentence was backwards, on a number a reader acts on.

The 40-character minimum for evidence excerpts is sized against a 600,000- character filing. Applied to market-data blocks a couple of hundred characters long, it reported correct excerpts as defects. It is now tier-aware.

2026-08-26 build ab15057

No transcripts is a finding, not a provider error

Adding a ticker with no earnings calls put a provider URL, an MDN documentation link and a doubled clause into the Run dialog — a diagnostic written for whoever maintains the pipeline, shown to whoever was about to authorise the spend. Failures are now classified into a consequence the operator can act on, guarded where the advisory is rendered so a new failure path inherits it.

The finding underneath it mattered more. A 404 from the transcript provider means that ticker has no earnings calls, and earnings calls are SEER's entire [T2] tier: such a run would carry no management commentary, no guidance track record and no narrative lens. That is now said before the money is spent rather than discovered in the report afterwards.

2026-08-25 build 657acea

Two-layer reports, the AKAM fixes, and asymmetric entries

The largest change to what SEER produces since the citation grammar.

Reports were too long to be worth reading — one ran to roughly eleven thousand words to deliver perhaps five load-bearing facts. A report is now a NOTE of about a thousand words that a reader actually reads, over an EVIDENCE record whose job is to be checkable rather than read. The note has seven parts: the claim, the variant perception, the load-bearing facts, kill criteria, scenarios, the SEER entry level, and the next checkpoint.

Length is steered before the writing, never enforced after it. Per-part word budgets, note-first emission order so a truncation costs evidence rather than the product, and the removal of the one prompt line most responsible for the old length. Nothing can reject a finished report over its length: the analysis is already paid for by then.

Entries became arithmetic rather than opinion. The SEER entry level is derived from the note's own bear and bull ranges at a three-to-one asymmetry bar, so a position is taken only where the upside genuinely justifies the downside — and "good business, wrong price" became something the note can say.

A real AKAM run exposed several faults now fixed: a contradiction checker firing on the ordinary words "only" and "first", two audit columns that printed "contradicted / CONFIRMED" side by side, internal diagnostics in the customer's document, three of seven alpha agents silently truncating, and a rerating section claiming a price history could not be obtained when it was one query parameter away. Price history and peer marks are now fetched.

Per-run token usage is recorded for the first time, so what a report costs can be measured instead of argued about.

2026-08-24 build dae85a2

A capped web search is a degraded answer, not a rejected one

The ticker classifier discarded a usable answer whenever its web search hit the per-call cap, along with the billed call that produced it. Hitting the cap means the model wanted one more search, not that it had nothing to work with. Search errors are now a diagnosis held in reserve: if the reply parses, the classification stands and the degradation is logged.

2026-08-21 build f9eda97

Consensus map reaches synthesis; audit table fixed

The Consensus Cartographer's output was never reaching the report writer, so on a run where it was the only agent to publish, the report said no finding had been advanced while three sat unused in an appendix. Audit table rows carrying a quoted table row no longer break out of their cell, and a blank Source cell now says which of its three meanings applies.

2026-08-21 build 2978694

Stream the classification, version the static assets

Adding a ticker had been failing at 180, 300 and 480 seconds alike. The cause was that the classification call carried eight server-side web searches without streaming, so it sent nothing until the entire turn finished. Separately, CSS and JavaScript were served with no cache-busting query, so two rounds of browser fixes never reached a browser.

2026-08-21 build 46a1902

Stop the add dialog depending on a response Railway will not deliver

The hosting edge severs any request at 300 seconds. The add dialog waited on a response that could outlive that, so a classification that succeeded still looked like a failure. The dialog now polls for the outcome instead.

2026-08-21 build e8d27f9

Harden the ticker store, and keep the reason an add failed

A truncated tickers file plus a whole-file read-modify-write could replace fifty-eight configured tickers with one, while the read-back check reported success. Writes are now atomic and refuse to overwrite a file they cannot parse.

2026-08-20 build 13d709c

Bound /config/ticker's model calls, and show its progress

Adding a ticker made two model calls with no ceiling and no visible progress. Both are now bounded, and the dialog shows which stage it is in.

2026-08-19 build 53ab1f4

Citation enforcement, Sonnet 5 / Opus 5, report and watchlist redesign

Deterministic citation enforcement: every claim's supporting sentence is located by exact text search in the document it cites, and the result is reproducible. The model migration moved every call site to a named role rather than a hardcoded model id. The report and watchlist were redesigned around reading rather than around the pipeline that produces them.