diff --git a/DEPRECATED.md b/DEPRECATED.md new file mode 100644 index 0000000..80f1ae0 --- /dev/null +++ b/DEPRECATED.md @@ -0,0 +1,191 @@ +# crowdmon is deprecated + +**Decided 2026-08-07.** Development stops. The package is **frozen, not deleted**, and §4 +states the conditions under which the decision would be worth revisiting. + +--- + +## 1. Why + +The thesis was `damage = crowding x illiquidity x holder fragility`. It was tested four times, +each time against a pre-registration frozen before the statistic was computed, each time +executed in `npf` because `tests/test_boundaries.py` refuses a `crucible` import precisely so +this package cannot render a verdict on its own output. + +| test | result | +|---|---| +| §10 validation, the core claim: is `D` elevated *before* a forced-exit episode? | **`uninformative`**, and the clean episodes are **spent** | +| Index share: is index positioning stickier than swap? | **genuine null**, premise retired | +| Fragility orthogonality: do `Phi` and `I` separate the trend book's trades? | Stage 1 independent, **Stage 2 genuine null** | +| Forced-flow mechanism: when the trigger fires, does the pool move? | `supported` on the letter, **mostly artifact**, marginal residual | + +Four honest tests, no positive result. + +**The asymmetry between those failures is what decided it.** They did not all fail the same +way, and only one kind of failure is fixable. + +The mechanism test failed for a repairable reason: it measured `Delta pool_net`, which this +package's own `futures/flow.py` documents in its first paragraph as unable to tell a forced +exit from a fresh entry in the same direction. That is a bad test, not a dead idea, and +`docs/handoffs/2026-08-06-forced-flow-respecification.md` records the corrected design. + +**The §10 validation did not fail that way.** It tested the actual claim, competently, and +came back uninformative with **the evidence exhausted**. Hand-identified clean episodes are a +finite stock and they are spent. No better design recovers them; only new crises do, and those +cannot be scheduled. + +So the optimistic path is narrower than it looks. Even a clean pass on a repaired mechanism +test establishes **the mapping**, which that pre-registration's §5.6 says explicitly is not +evidence that `D` predicts anything. The claim that would justify the footprint is the §10 +claim, and it is out of ammunition. + +**Waiting does not help**, which was checked rather than assumed. The mechanism sample already +spans 2012-05-15 to 2026-07-21, **57 blocks** of 13 weeks. A further year adds about four: +roughly 7% more data and 3% tighter standard errors. §1 of that pre-registration chose the +question partly because its evidence regenerates weekly, which is true in the sense that it is +never *spent* and false in the sense of getting usefully stronger on any horizon worth waiting +for. + +### The argument that was considered and rejected + +"The mechanism test was flawed, so the idea might still work" is true here. It is also the +argument that keeps dead projects funded for another six months. It raises the ceiling on what +a better test could find; it is not evidence of anything. Recorded because it is the strongest +case against this decision and it should not have to be re-derived by whoever reads this next. + +--- + +## 2. What deprecated means here, concretely + +| | | +|---|---| +| **Frozen** | No new modules, no new engines, no new handoffs against the composite | +| **Not deleted** | Every file stays. Other repos cite these documents, and a citation has to land on real text | +| **Not retracted** | The measurements stand. A parked hypothesis is not a withdrawn one | +| **Tests are NOT all green**, and §2.1 says why | 6 of 654 fail, all of them live pins drifting against a moving store. Red here does not mean broken | + +### 2.1 The suite is red, and it is drift rather than rot + +Measured on `main` at deprecation, **6 failed, 648 passed, 5 skipped**. An earlier draft of +this file claimed the suite was left green. It is not, and the claim was corrected rather than +the tests, because what is failing is worth understanding. + +``` +tests/test_brief_live.py::test_the_two_null_causes_split_where_C20_measured_them +tests/test_brief_live.py::test_the_separating_rule_still_has_zero_exceptions +tests/test_publish_live.py::test_the_trigger_counts_reproduce_d9 +tests/test_publish_live.py::test_the_pool_column_is_supplied_so_the_agreement_flag_is_not_null +tests/test_publish_live.py::test_the_pool_check_removes_half_of_d9s_close_and_severe_cell +tests/test_publish_live.py::test_the_manifest_counts_match_the_panel_it_ships_with +``` + +**Every one is a `*_live` test comparing a recomputation against a pinned count.** The +`COTDATA_STORE` has advanced to report week **2026-08-04** while the published panel is pinned +at **2026-07-28**, so the recomputation carries one extra market-week and the pins are off by +exactly that: 6,669 against a pinned 6,668, and a date assertion reading `2026-08-04` where it +expects `2026-07-28`. + +This is the same failure mode `docs/design/amendments-2026-08-05.md` §E1 already recorded, and +it was six pins then too. + +**Three things follow, and the third is the one that matters.** + +1. **Red does not mean broken here.** Nothing regressed. A frozen package pinned against a + moving store fails by construction. +2. **It gets worse, not better.** Every week the store advances widens the gap. A reader + running the suite in six months will see the same six failures with larger deltas. +3. **It is downstream of the §3 decision, not independent of it.** If the publish job keeps + running, the panel keeps pace and these pass again; if it is switched off, they fail + permanently. **Whichever way that goes, the fix is to neutralise the pins rather than to + chase them**, because a frozen repo should not have tests that depend on data collected + after it was frozen. That work is deliberately not done here: it belongs with whoever makes + the §3 call, and doing it first would presume the answer. + +**The harvest is complete.** [`docs/HARVEST.md`](docs/HARVEST.md) classifies all 108 numbered +findings in `docs/design/amendments-*.md` as PORTED, RESOLVED or DIES, and nothing remains +unhoused: + +- **To `cotdata`**: `cross-report-comparability.md` (Legacy and TFF agree on exactly two + quantities; the `spread_contracts` null trap) and `reading-the-store.md` (universe + composition, coverage denominators, the volume parameter, code continuity, price tiers). +- **To `cotmetrics`**: `positioning-series-properties.md` (exceedances arrive in episodes, so + a count of them is not a sample size; correlating positioning levels is spurious). + +Facts were **restated, never moved**. `docs/analysis/` and the amendment series are +point-in-time records under this repo's doc lifecycle; rewriting them would destroy the record +of when each thing was learned, which is what makes "the data contradicted the brief" a +checkable claim. + +### The verdicts live in npf + +They were rendered there, and they stay there: `npf/docs/crowdmon/`, dated 2026-08-01, +2026-08-06 twice, each with a committed reproducer, a pinned store manifest, and for the +mechanism test a byte copy of the store at +`~/code/cotdata_store_snapshots/2026-08-06-forced-flow-mechanism`. + +--- + +## 3. What is still running, and what is open + +**Two launchd jobs and one consumer page**, listed rather than switched off, because turning +them off is a decision about whether the panel is still worth reading and that is not a +research question: + +- `com.mspinola.crowdmon-publish` and `com.mspinola.crowdmon-live-tests`. +- `cot-analyzer`'s `/damage` page, this package's only consumer. Nothing in `npf` or + `livebook` imports it. + +**One open work order**, +[`docs/handoffs/2026-08-06-trigger-contradicted-copy.md`](docs/handoffs/2026-08-06-trigger-contradicted-copy.md). +It is a copy and vocabulary fix, not a research task: when `pool_agrees` is `False`, both +renderers say the level "would force a book that is not there", and the measurement says flow +follows either way with the difference between groups small. **Its status depends entirely on +the page**: worth doing if the panel keeps being read, moot if it does not, and it should be +closed unstarted rather than left open in that case. + +--- + +## 4. Conditions for revisiting, stated so they are checkable + +Vague promise is how a parked project becomes an undead one. **Any one of these, on its own, +makes it worth reopening. Nothing else does.** + +1. **New clean forced-exit episodes.** §10 needs hand-identified episodes uncontaminated by + the events already used. Two or more genuinely new ones would restore the test the whole + thesis rests on. This is the only condition that addresses the core claim. +2. **A point-in-time replay becomes possible.** Vintages accumulate forward from 2026-07-31, + and §7.8 of the §10 pre-registration gates its replay at **no earlier than 2026-11-01**. + That answers a different question from §10, the decision-rule one, and it is the only + scheduled thing in this list. +3. **The respecified mechanism test is run and comes back strong on the corrected outcome + variable.** `docs/handoffs/2026-08-06-forced-flow-respecification.md` §4. This would raise + the ceiling; on its own it still would not establish that `D` predicts anything, per §5.6. + +**Not a condition**: a new module, a better weight table, another engine, or a reading of the +existing data from a fresh angle. The data has been read. What is missing is evidence that has +not happened yet. + +--- + +## 5. What was worth building anyway + +Recorded because a project that produced nothing and a project whose hypothesis did not survive +are different things, and the second one is normal. + +The measurements outlived the thesis. Several are now load-bearing in `cotdata` and +`cotmetrics`: the `propadj` / `backadj` trap that corrected cotdata's own spec, the +Legacy/TFF comparability limit, the Supplemental basis mismatch, the open-interest identity +across 48,950 market-weeks, the fact that 76% of the Disaggregated universe is power and gas +basis, and the near-unit-root property that makes level correlations on positioning +meaningless. + +The working agreement that produced them is the part most worth carrying forward: **measure, +do not assume**, and where a measurement contradicts a document, fix the document in the same +change and say so. Probing the actual files overturned a written assumption in nearly every +session here, including several of this package's own. + +The harvest also turned up something this repo never looked for: `cotmetrics` computes six +price-against-positioning **level** correlations per lookback whose null has never been +measured, which the corrected rule above says cannot be interpreted without one. It is +written up there as a check to run rather than a defect report. A project's last useful act +being to hand a neighbour a question worth asking is a reasonable way to finish. diff --git a/README.md b/README.md index 77423c6..64f389e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,23 @@ # crowdmon +> ## DEPRECATED, 2026-08-07 +> +> **Development has stopped.** The thesis below was tested four times against frozen +> pre-registrations and produced no positive result: the core claim came back +> `uninformative` with its evidence **spent**, two further tests were genuine nulls, and the +> fourth was mostly artifact. See [`DEPRECATED.md`](DEPRECATED.md) for the evidence, what +> "frozen, not deleted" means in practice, and the three conditions under which reopening +> would be worth it. +> +> **The measurements outlived the thesis and have been rehoused.** +> [`docs/HARVEST.md`](docs/HARVEST.md) maps all 108 findings; the durable ones now live in +> `cotdata` and `cotmetrics`. Nothing here was deleted, and the facts were restated rather +> than moved, so citations still land on real text. +> +> **Everything below this box describes the package as designed.** It is left unedited on +> purpose: a deprecated project rewritten to sound modest is a worse record than one that +> states what it set out to do and what happened. + A crowding and forced-exit monitor. **Damage = crowding x illiquidity x holder fragility.** Crowding is a property of the diff --git a/docs/handoffs/2026-08-06-trigger-contradicted-copy.md b/docs/handoffs/2026-08-06-trigger-contradicted-copy.md index b48448c..afd9dfb 100644 --- a/docs/handoffs/2026-08-06-trigger-contradicted-copy.md +++ b/docs/handoffs/2026-08-06-trigger-contradicted-copy.md @@ -1,6 +1,13 @@ # Handoff: a contradicted trigger row is described as an absence, and it is not one -**Status:** open +**Status:** **open, BLOCKED on one decision. Do not start it until that decision is made.** +`crowdmon` was deprecated 2026-08-07 ([`../../DEPRECATED.md`](../../DEPRECATED.md) §3). This is +a copy fix to a page whose future is undecided: **worth doing if `cot-analyzer`'s `/damage` +page keeps being read, moot if it does not**, in which case close it unstarted rather than +leaving it open. Nothing in the work below changed; only whether it is worth doing. + +The register's rule is that a handoff without a completion status gets executed by a future +session, so this marker exists to stop that happening to a page that may be on its way out. Written 2026-08-06. Released by §6 of [`2026-08-06-forced-flow-respecification.md`](2026-08-06-forced-flow-respecification.md), diff --git a/docs/handoffs/README.md b/docs/handoffs/README.md index 4842b9a..86d3f5f 100644 --- a/docs/handoffs/README.md +++ b/docs/handoffs/README.md @@ -8,6 +8,11 @@ the whole point of the convention: a session that finds an `open` handoff should work is not done. So closing one out is not bookkeeping, it is what stops the same analysis being run twice with different results. +> **This register is closed to new entries.** `crowdmon` was deprecated on 2026-08-07; see +> [`../../DEPRECATED.md`](../../DEPRECATED.md), whose §4 states the only three conditions +> under which reopening the package is worth it. Do not file a new handoff here. Every +> entry below is complete except one, which is blocked rather than available. + ``` **Status:** open **Status:** complete (PR #NN) @@ -50,7 +55,7 @@ A moved handoff leaves a **pointer row and no copy**. One document, one lineage. | [2026-08-03-swap-dealer-weight-decision.md](2026-08-03-swap-dealer-weight-decision.md) | **COMPLETE 2026-08-03 (PR #44), decided by the human as option (a): `swap: 0.4` stands.** Its §4 released exactly one piece of work and that work **corrected the handoff's own §1**: `single_weight_sweep` shipped, and re-swept over the order-preserving band it showed §1's 42.0% was the wrong BAND rather than wrong arithmetic. Honest figures **17.9% on the 13 Supplemental markets, 3.5% pooled** over `w_SD ∈ [0.2, 0.4]` (`§C10`). **Decided in parallel with `§C6-C8`, which landed as PR #43 while this was in flight and reached the same place by a different road**: §C6 closed option **(c)** by settling static weights independently, §C7 measured the cost of (a) as a **+19.6% median `Phi` inflation, worst on gold at +27.8% (2.30x cocoa) and precisely during stress weeks**, and §C8 showed most of that never reaches `D`, because the composite consumes a percentile and the median market-week moves only 5.9 of them. **§C8's split is the operating rule: on classic outrights the band is a footnote (Spearman 0.954, 18 of 20 top markets in common); on the ERCOT and PJM book it can invert a market's own history (Transco Zone 6 at -0.416), so publish the band there.** Two independent measurements, one decision, and no re-litigation. Option (c) is unscheduled rather than closed; metals remain permanently outside coverage | | [2026-08-05-forced-flow-mechanism-test.md](2026-08-05-forced-flow-mechanism-test.md) | **COMPLETE, executed 2026-08-06 in `npf` (PR #79). Verdict a MARGINAL LEAN, and §5.6's `supported` must not be quoted on its own.** When the trigger says a pool is forced out at `F*` and price reaches `F*`, does that pool move? The criteria pass on both report types, but **§5.3's own placebo carries 73.8% (Disaggregated) and 51.9% (TFF) of the effect** where §5.3 says it should be near zero, because the group label **is** the sign of `pool_net` and `pool_net` mean-reverts. Removing the label leaves **-0.001388 (p 0.047)** and **-0.002657 (p 0.0099)**, about 1.2% and 1.9% of the pool against headline figures of 4.5% and 4.0%; Disaggregated would not survive a correction across even the two report types. **The gap is the finding: §5.6 attaches no criterion to the placebo**, so the design as written cannot separate forced flow from positioning mean reversion, and a re-run should fix that first. Three more corrections in §8: §4's control is **not** readable from the panel (39 rows, one week, as §3 itself measures) and was recomputed over all 1,051 weeks, `pool_agrees` reducing to the SIGN of `pool_net` with **no lookback dependence**; §4's "the two groups share the price mechanics exactly" is **false as measured**, the contradicted group crossing more often in both types (25.60% vs 20.11%, 24.20% vs 21.80%); and §5.5's p-value **cannot be computed as written**, a self-centred bootstrap giving `p_literal` 0.465-0.526 across all six variants including those with `p_null` 0.0000. §3.1's equivalence reproduced exactly at 2.220446049250313e-16 over 96 comparisons. Count **8 variants, not 6**. Verdict: `npf/docs/crowdmon/2026-08-06-forced-flow-mechanism-verdict.md` | | [2026-08-06-forced-flow-respecification.md](2026-08-06-forced-flow-respecification.md) | **SPECIFICATION CORRECTION, NOT A WORK ORDER. Do not open this as a task; §7 says what would have to change first.** A fifth defect in the mechanism test above, larger than the four its §8 records: the outcome variable `Δ pool_net` **cannot answer the question it was asked**, and this package already says so. On the sell side an agreeing pool is long and sells at `F*` while a contradicted pool is short and **adds shorts** at the same level; both are sell flow, both give `Δnet < 0`, and only the first is forced. `futures/flow.py`'s opening paragraph states exactly this ("Net position change cannot tell them apart... that is the whole reason this module exists"), `flow.decompose` exists for the distinction, and the frozen §5 references neither. Advisory and untested: on the published `flow_state` of the following week the named pool does what the trigger says and the contradicted pool does the opposite, in **all four (report type, side) cells**, a **3x to 5x** separation the net erases (disaggregated sell `long_liquidation` **20.1% agreeing against 3.9% contradicted**, `new_shorts` 9.4% against 25.8%). **The obvious swap to `flow_state` is NOT the fix**: the labels are partly mechanical, since a net-long book has a bigger gross long leg and is likelier to be labelled `long_liquidation`, so the confound reappears one level down. §4 proposes the **fractional change in the exposed leg** instead, plus a within-group primary contrast, a falsification **with a criterion** (the gap that produced the original verdict), and a side-consistency condition (TFF's surviving residual is sell -0.0072 against buy **+0.0037**). §6 is a separate, actionable-now presentation consequence: `pool_agrees == False` currently **suppresses** the reading in `report.format_offside` and cot-analyzer's damage page, where the measurement says the level is live and only the flow's composition differs | -| [2026-08-06-trigger-contradicted-copy.md](2026-08-06-trigger-contradicted-copy.md) | **OPEN, unclaimed.** Released by §6 of the respecification above and the only part of that lineage actionable without a re-run. When `trigger_{side}_pool_agrees` is `False`, both renderers say the level "would force a book that is not there"; the verdict measures flow following a crossing **either way** (Disaggregated sell, within-group: **-0.0190** agreeing against **-0.0184** contradicted) with a crossing-specific difference of only **-0.0014 / -0.0027**. Copy and vocabulary only: **no data change, no threshold, no change to `D`, and the quadrant suppression STAYS** (its severe axis is `D`, a conditional severity for the *named* pool, so a contradicted row must not be given a cell). **The sharp edge is what may not be said**: the composition split that motivates it is a descriptive crosstab with a known confound, so "flow still follows and the difference is small" is supported and "it arrives as fresh shorts" is not. **The real work is structural, not three strings**: `pool on the other side` is authored locally in cot-analyzer's `damage.py` in two places and is **not** in `test_damage_vocabulary.py`'s `FRAGMENTS`, so changing crowdmon alone ships a producer and a page saying different things with every test green. crowdmon first (constant + `COLUMN_DEFINITIONS` + manifest), cot-analyzer second (read it, delete both copies, **extend the guard**). Do **not** bump `SCHEMA_VERSION`: `publish.py` says a bump ahead of a consumer release loses the whole `/damage` page | +| [2026-08-06-trigger-contradicted-copy.md](2026-08-06-trigger-contradicted-copy.md) | **OPEN but BLOCKED, do not start.** `crowdmon` was deprecated 2026-08-07 ([`../../DEPRECATED.md`](../../DEPRECATED.md) §3) and this is a copy fix to a page whose future is undecided: worth doing if cot-analyzer's `/damage` page keeps being read, **moot if it does not**, in which case close it unstarted. The work itself is unchanged and is described below. Released by §6 of the respecification above and the only part of that lineage actionable without a re-run. When `trigger_{side}_pool_agrees` is `False`, both renderers say the level "would force a book that is not there"; the verdict measures flow following a crossing **either way** (Disaggregated sell, within-group: **-0.0190** agreeing against **-0.0184** contradicted) with a crossing-specific difference of only **-0.0014 / -0.0027**. Copy and vocabulary only: **no data change, no threshold, no change to `D`, and the quadrant suppression STAYS** (its severe axis is `D`, a conditional severity for the *named* pool, so a contradicted row must not be given a cell). **The sharp edge is what may not be said**: the composition split that motivates it is a descriptive crosstab with a known confound, so "flow still follows and the difference is small" is supported and "it arrives as fresh shorts" is not. **The real work is structural, not three strings**: `pool on the other side` is authored locally in cot-analyzer's `damage.py` in two places and is **not** in `test_damage_vocabulary.py`'s `FRAGMENTS`, so changing crowdmon alone ships a producer and a page saying different things with every test green. crowdmon first (constant + `COLUMN_DEFINITIONS` + manifest), cot-analyzer second (read it, delete both copies, **extend the guard**). Do **not** bump `SCHEMA_VERSION`: `publish.py` says a bump ahead of a consumer release loses the whole `/damage` page | | **2026-08-05-fragility-orthogonality.md** — **MOVED to `npf/docs/handoffs/`, 2026-08-05, before execution.** Authored here and relocated the same day, which its own §7 named as conditional on `npf` growing a status register. It is **npf-owned work** (a question about npf's book) and `crowdmon` must not be read as commissioning strategy research. **No copy is kept here**, deliberately: `2026-08-03-index-share.md` records what two lineages of one file cost, and a pointer from the first day is the remedy this repo's `CLAUDE.md` states. Its cross-repo citations were qualified on the move and the change is recorded in its own §0; no threshold, statistic or criterion moved. Status is tracked **there**, not here | **MOVED** | | [2026-08-03-b-series-recovery.md](2026-08-03-b-series-recovery.md) | **COMPLETE, outcome as §7 and §8.** §1 recovered `claude/template-followups-doc-corrections-45de1d` (`11b7c81`), local-only with 2,070 insertions, by pushing it unaltered. §2 landed its content on `main` by **porting rather than merging** (the branch predates six merges and would have reverted `flow.py`, `check_skips.py`, `conftest.py` and the whole 08-03 lineage), so **that branch should be CLOSED unmerged**. `amendments-2026-08-02.md` is now B1-**B37**: it never closed at B32, it closed on a branch nobody could see. **§C4 is superseded**, having read `A_agnostic` as WEIGHT-agnostic where `§B34` defines it as DIRECTION-agnostic at a median of **3.0237**; corrected in place with both readings pinned. The cocoa split point is **resolved**: same date, 2025-10-21, differing only on which half that week falls in, and every other figure identical under both rules. §3 changed the citation convention to **path plus reproducer** and made the bare form fail loudly (`tests/test_references.py` over 368 references in 42 files; one unresolved at the time, `§C5`, recorded rather than deleted and then forced out by its own guard when #42 merged). §4 ran the band `w_SD ∈ {0.067, 0.2, 0.4, 0.7}` under a **settled static-weights decision** and filed `2026-08-03 §C6-C8`: `w_SD = 0.4` overstates fragile capital on 99.31% of market-weeks by a median +19.6%, **gold at +27.8% is 2.30x cocoa at +12.1% so the prediction holds**, and 0.067 moves the ceiling 10.0 to 14.925 so the band is not on one scale. Most of it never reaches the composite, which consumes `pct(Phi)`, except on **98 of 264 markets** where the two tables reorder a market's own weeks. **Weight table unchanged** |