From a4a19f30d8e36038a5d4c36ecc4df451bae69bd2 Mon Sep 17 00:00:00 2001 From: saymrwulf Date: Mon, 27 Apr 2026 15:41:25 +0200 Subject: [PATCH] Add ratchet pathway forecast --- START_HERE.md | 17 +++++++++ docs/RATCHET_OPERATIONS.md | 8 ++++ src/braiins_ratchet/guidance.py | 65 +++++++++++++++++++++++++++++++++ tests/test_guidance.py | 5 +++ 4 files changed, 95 insertions(+) diff --git a/START_HERE.md b/START_HERE.md index 08705fd..5e329d6 100644 --- a/START_HERE.md +++ b/START_HERE.md @@ -39,6 +39,23 @@ You do not need to babysit it. It will: If you want the technical report, run `./scripts/ratchet report`. The normal workflow intentionally shows the cockpit first. +## Research Pathway + +The cockpit has two different time horizons: + +1. `DO THIS NOW` is the only command you should run next. +2. `Ratchet Pathway Forecast` tells you what the next stages probably look like. + +The forecast is not a profit prediction. It is a workload and research-flow prediction. + +It is split into: + +1. `Immediate`: what happens now. +2. `Midterm`: what probably happens after the current run or sample. +3. `Longterm`: what could happen after multiple reports mature. + +Expect the pathway to change after each report. That is the point of ratcheting: the next stage adapts to measured evidence instead of following a rigid plan. + ## What The Actions Mean `observe` means do not bid. diff --git a/docs/RATCHET_OPERATIONS.md b/docs/RATCHET_OPERATIONS.md index 9e2f567..c8de222 100644 --- a/docs/RATCHET_OPERATIONS.md +++ b/docs/RATCHET_OPERATIONS.md @@ -10,6 +10,14 @@ Run these commands from the repository root: This prints the cockpit and tells you exactly what to do next. +The cockpit also prints `Ratchet Pathway Forecast`. That section is the stretched-out research path: + +- Immediate: the next operational step. +- Midterm: the likely next experiment stage. +- Longterm: what could happen after multiple reports mature. + +Those probabilities are workload and workflow estimates, not promises of mining profit. + If the cockpit tells you to collect one fresh sample, run: ```bash diff --git a/src/braiins_ratchet/guidance.py b/src/braiins_ratchet/guidance.py index 85fecec..f4262d5 100644 --- a/src/braiins_ratchet/guidance.py +++ b/src/braiins_ratchet/guidance.py @@ -47,6 +47,16 @@ def build_operator_cockpit(conn) -> str: action=proposal.action if proposal else None, ) ) + lines.extend(["", "Ratchet Pathway Forecast"]) + lines.extend( + _pathway_forecast( + active_watch=active_watch, + has_ocean=ocean is not None, + has_market=market is not None, + is_fresh=is_fresh, + action=proposal.action if proposal else None, + ) + ) lines.extend(["", "How To Interpret The Current Action"]) lines.extend(_action_explanation(proposal.action if proposal else None)) lines.extend(["", "Ratchet Rule"]) @@ -128,6 +138,61 @@ def _do_this_now( ] +def _pathway_forecast( + active_watch: str | None, + has_ocean: bool, + has_market: bool, + is_fresh: bool, + action: str | None, +) -> list[str]: + if active_watch: + return [ + " Planning probabilities are workflow estimates, not profit probabilities.", + " Immediate, very likely: wait for the running watch to finish; workload is zero until it ends.", + " Midterm, likely: read the final cockpit and ledger summary; workload is about 5 minutes.", + " Longterm, possible: adjust one strategy knob if the report says the run taught us something.", + ] + + if not has_ocean or not has_market: + return [ + " Planning probabilities are workflow estimates, not profit probabilities.", + " Immediate, certain: initialize local state; workload is one setup command.", + " Midterm, very likely: collect the first live sample; workload is one once command.", + " Longterm, likely: start passive watch experiments after fresh data exists.", + ] + + if not is_fresh: + return [ + " Planning probabilities are workflow estimates, not profit probabilities.", + " Immediate, certain: refresh stale data with one once command; workload is under a minute.", + " Midterm, likely: if the fresh state still says manual_canary, run a 2-hour watch.", + " Longterm, possible: compare this fresh run against prior reports before changing any knob.", + ] + + if action == "manual_bid": + return [ + " Planning probabilities are workflow estimates, not profit probabilities.", + " Immediate, certain: inspect the full report before any manual Braiins action.", + " Midterm, possible: manually place a tiny order only if the report still says manual_bid.", + " Longterm, uncertain: wait through maturity and compare realized outcome against modeled EV.", + ] + + if action == "manual_canary": + return [ + " Planning probabilities are workflow estimates, not profit probabilities.", + " Immediate, very likely: run a 2-hour passive watch; workload is start command plus waiting.", + " Midterm, likely: use the generated run report to decide one next knob to test.", + " Longterm, possible: only after repeated mature evidence, consider a manual canary spend.", + ] + + return [ + " Planning probabilities are workflow estimates, not profit probabilities.", + " Immediate, certain: do not bid.", + " Midterm, possible: run another passive watch later if you want more market coverage.", + " Longterm, possible: change one strategy knob only after multiple observe windows are logged.", + ] + + def _action_explanation(action: str | None) -> list[str]: if action == "manual_bid": return [ diff --git a/tests/test_guidance.py b/tests/test_guidance.py index 1e76354..caa5d7e 100644 --- a/tests/test_guidance.py +++ b/tests/test_guidance.py @@ -60,6 +60,10 @@ class GuidanceTests(unittest.TestCase): self.assertIn("Latest strategy action: manual_canary", text) self.assertIn("./scripts/ratchet watch 2", text) self.assertIn("not proven profit", text) + self.assertIn("Ratchet Pathway Forecast", text) + self.assertIn("Immediate, very likely", text) + self.assertIn("Midterm, likely", text) + self.assertIn("Longterm, possible", text) def test_stale_market_data_routes_operator_to_once(self) -> None: conn = sqlite3.connect(":memory:") @@ -99,6 +103,7 @@ class GuidanceTests(unittest.TestCase): self.assertIn("./scripts/ratchet once", text) self.assertIn("latest Braiins sample is stale", text) self.assertIn("DO THIS NOW", text) + self.assertIn("if the fresh state still says manual_canary", text) if __name__ == "__main__":