mirror of
https://github.com/saymrwulf/BraiinsRatchet.git
synced 2026-05-14 20:37:52 +00:00
Make native app domain first
This commit is contained in:
parent
56163922c0
commit
9704ac8452
6 changed files with 192 additions and 211 deletions
27
README.md
27
README.md
|
|
@ -15,27 +15,12 @@ The first implementation is deliberately conservative:
|
|||
## Quick Start
|
||||
|
||||
```bash
|
||||
./scripts/ratchet setup
|
||||
./scripts/ratchet
|
||||
./scripts/ratchet app
|
||||
```
|
||||
|
||||
`./scripts/ratchet` is the cockpit. It tells you exactly what to do next.
|
||||
This builds and opens the native macOS control room. Use the app for normal operation; terminal commands are advanced fallback tools.
|
||||
|
||||
If the cockpit is in cooldown and you want the app to wait until the earliest next action, run:
|
||||
|
||||
```bash
|
||||
./scripts/ratchet pipeline
|
||||
```
|
||||
|
||||
It prints the exact monitor-only plan and asks `yes/no` before doing anything.
|
||||
|
||||
For the durable forever lifecycle supervisor:
|
||||
|
||||
```bash
|
||||
./scripts/ratchet supervise
|
||||
```
|
||||
|
||||
It persists lifecycle state in `data/ratchet.sqlite`. If the process crashes or the Mac reboots, start the same command again and it resumes from SQLite.
|
||||
The lifecycle state persists in `data/ratchet.sqlite`. If the app or Mac restarts, open the app again and it reads the same state.
|
||||
|
||||
When you manually place a Braiins bid, record the exposure so the supervisor blocks new experiments:
|
||||
|
||||
|
|
@ -49,7 +34,7 @@ Close it only when finished:
|
|||
./scripts/ratchet position close POSITION_ID
|
||||
```
|
||||
|
||||
For the native macOS SwiftUI shell:
|
||||
For the native macOS app:
|
||||
|
||||
```bash
|
||||
./scripts/ratchet app
|
||||
|
|
@ -57,9 +42,9 @@ For the native macOS SwiftUI shell:
|
|||
|
||||
This builds `macos/build/Braiins Ratchet.app` and opens the real app bundle. Do not use `swift run` for normal operation.
|
||||
|
||||
The app is a native visual control room: Mission Control, Research Map, Manual Exposure ledger, Reports, and a Ratchet Lecture. The design rationale is in `docs/APP_DESIGN_RESEARCH.md`.
|
||||
The app is a native visual control room: Mission Control, Research Map, Manual Exposure ledger, Advanced diagnostics, and a Ratchet Lecture. The design rationale is in `docs/APP_DESIGN_RESEARCH.md`.
|
||||
|
||||
For a 6-hour monitoring session:
|
||||
Advanced fallback for a 6-hour CLI monitoring session:
|
||||
|
||||
```bash
|
||||
./scripts/ratchet watch 6
|
||||
|
|
|
|||
175
START_HERE.md
175
START_HERE.md
|
|
@ -1,18 +1,12 @@
|
|||
# Start Here
|
||||
|
||||
This project now has one operator entry point:
|
||||
This project now has one normal operator entry point:
|
||||
|
||||
```bash
|
||||
./scripts/ratchet
|
||||
./scripts/ratchet app
|
||||
```
|
||||
|
||||
That is the same as:
|
||||
|
||||
```bash
|
||||
./scripts/ratchet next
|
||||
```
|
||||
|
||||
It prints the cockpit: current state, exact next action, interpretation, reference commands, and ratchet rule.
|
||||
That command builds and opens the native macOS app. The app is the control room. The terminal is only the launcher and fallback diagnostic path.
|
||||
|
||||
## Your Job
|
||||
|
||||
|
|
@ -20,113 +14,43 @@ Your job is not to understand every metric.
|
|||
|
||||
Your job is:
|
||||
|
||||
1. Run `./scripts/ratchet`.
|
||||
2. Do only what it says under `DO THIS NOW`.
|
||||
3. Ignore every other command unless `DO THIS NOW` tells you to run it.
|
||||
4. If it tells you to run `./scripts/ratchet watch 2`, start it, leave the terminal open, and come back after about 2 hours.
|
||||
5. If you manually place a Braiins canary, write down the order details outside this repo and wait through the maturity window before judging it.
|
||||
1. Open the app with `./scripts/ratchet app`.
|
||||
2. Stay on `Mission Control` unless you intentionally need raw diagnostics.
|
||||
3. Read `Current Decision` first.
|
||||
4. Read `Who Is In Control` second.
|
||||
5. Use `Next Passive Action` only when it is enabled.
|
||||
6. If you manually place a Braiins canary, record it in `Manual Exposure` immediately.
|
||||
|
||||
Do not start extra terminal watches while the app says a watch, cooldown, or manual exposure owns control.
|
||||
|
||||
## Who Is In Control?
|
||||
|
||||
If `watch` is running, the Python process is in control of that terminal.
|
||||
The app has one ownership model:
|
||||
|
||||
You do not need to babysit it. It will:
|
||||
1. `The app is ready`: you may start the enabled passive action.
|
||||
2. `A watch run owns control`: leave it alone until it finishes.
|
||||
3. `Cooldown owns control`: wait until the shown earliest action time.
|
||||
4. `Manual exposure owns control`: supervise the real-world Braiins/OCEAN position and do not start new experiments.
|
||||
5. `The app is busy`: a monitor-only backend operation is running right now.
|
||||
|
||||
1. Collect samples every 5 minutes.
|
||||
2. Write the run report when it finishes.
|
||||
3. Print the cockpit again.
|
||||
4. Return control to your shell prompt.
|
||||
This is the anti-babysitting rule: if the app says something else owns control, your workload is zero unless you are supervising a real manual exposure.
|
||||
|
||||
If you want the technical report, run `./scripts/ratchet report`. The normal workflow intentionally shows the cockpit first.
|
||||
## What The App Does
|
||||
|
||||
After a watch finishes, the cockpit enters a post-watch cooldown. That is deliberate.
|
||||
The app is monitor-only. It never places, modifies, or cancels Braiins orders.
|
||||
|
||||
Post-watch cooldown means:
|
||||
It can:
|
||||
|
||||
1. The current experimental stage is complete.
|
||||
2. Starting another identical watch immediately is not useful ratcheting.
|
||||
3. The run report is the evidence artifact.
|
||||
4. The next planned touch is a later fresh sample, usually `./scripts/ratchet once`.
|
||||
|
||||
During cooldown, the cockpit shows:
|
||||
|
||||
1. A progress bar.
|
||||
2. The earliest next action time.
|
||||
3. The remaining minutes.
|
||||
|
||||
## Controlled Automation
|
||||
|
||||
If you do not want to babysit the cooldown manually, run:
|
||||
|
||||
```bash
|
||||
./scripts/ratchet pipeline
|
||||
```
|
||||
|
||||
The pipeline first prints a proposal like:
|
||||
|
||||
```text
|
||||
I am going to: wait until this time, run one fresh sample, print the cockpit, then stop.
|
||||
Are you OK with this? Type yes or no.
|
||||
```
|
||||
|
||||
It only runs after you type `yes`.
|
||||
|
||||
It is still monitor-only. It never places, changes, or cancels Braiins orders.
|
||||
|
||||
## Forever Supervisor
|
||||
|
||||
For the full autoresearch lifecycle, run:
|
||||
|
||||
```bash
|
||||
./scripts/ratchet supervise
|
||||
```
|
||||
|
||||
The supervisor is the long-running engine. It:
|
||||
|
||||
1. Loads persisted state from `data/ratchet.sqlite`.
|
||||
2. Waits through cooldown if cooldown is active.
|
||||
3. Runs the next passive watch when due.
|
||||
4. Writes reports and lifecycle events.
|
||||
5. Re-enters cooldown.
|
||||
6. Repeats until you stop it.
|
||||
|
||||
If it crashes or the Mac reboots, start the same command again. It resumes from SQLite.
|
||||
|
||||
Use this to inspect persisted state without starting the loop:
|
||||
|
||||
```bash
|
||||
./scripts/ratchet supervise --status
|
||||
```
|
||||
|
||||
## Manual Braiins Exposure
|
||||
|
||||
If you manually start a Braiins bid, record it immediately:
|
||||
|
||||
```bash
|
||||
./scripts/ratchet position open --description "Braiins order abc, 0.0001 BTC, 3h canary" --maturity-hours 72
|
||||
```
|
||||
|
||||
While a manual position is active:
|
||||
|
||||
1. The cockpit says `HOLD`.
|
||||
2. The supervisor blocks new watch experiments.
|
||||
3. Restarting the app keeps the manual exposure state.
|
||||
|
||||
List positions:
|
||||
|
||||
```bash
|
||||
./scripts/ratchet position list
|
||||
```
|
||||
|
||||
When the Braiins/OCEAN exposure is truly finished:
|
||||
|
||||
```bash
|
||||
./scripts/ratchet position close POSITION_ID
|
||||
```
|
||||
1. Read persisted lifecycle state from `data/ratchet.sqlite`.
|
||||
2. Collect OCEAN and public Braiins market samples.
|
||||
3. Run passive watch-only research windows.
|
||||
4. Write run reports under `reports/`.
|
||||
5. Track manually executed Braiins exposure that you enter yourself.
|
||||
6. Resume from the same SQLite state after a crash or reboot.
|
||||
|
||||
## Native Mac App
|
||||
|
||||
The native SwiftUI shell is in:
|
||||
The native SwiftUI app is in:
|
||||
|
||||
```text
|
||||
macos/BraiinsRatchet
|
||||
|
|
@ -140,34 +64,23 @@ Build and open the real app bundle:
|
|||
|
||||
This creates `macos/build/Braiins Ratchet.app`. After that, you can open that app bundle directly from Finder or pin it in the Dock.
|
||||
|
||||
The app is a native cockpit over the same durable Python lifecycle engine.
|
||||
|
||||
The app includes controls to record and close manual exposure, but the same rule applies: it never places Braiins orders.
|
||||
|
||||
The app is organized as:
|
||||
|
||||
1. `Mission Control`: one exact next action, cooldown, metrics, and direct watch-only controls.
|
||||
1. `Mission Control`: current decision, control ownership, next passive action, progress, evidence, and plain English interpretation.
|
||||
2. `Research Map`: visual autoresearch stage model.
|
||||
3. `Manual Exposure`: record or close manually executed Braiins exposure.
|
||||
4. `Reports`: raw cockpit, report, and ledger artifacts.
|
||||
4. `Advanced`: raw cockpit, report, and ledger artifacts for diagnostics.
|
||||
5. `Ratchet Lecture`: the general observe, hypothesize, bound, mature, adapt method.
|
||||
|
||||
## Research Pathway
|
||||
|
||||
The cockpit has two different time horizons:
|
||||
The app has three 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.
|
||||
1. `Immediate`: what can happen now, usually start, wait, refresh, or hold.
|
||||
2. `Midterm`: what probably happens after the current watch, cooldown, or manual exposure matures.
|
||||
3. `Longterm`: what could happen after multiple evidence artifacts point in the same direction.
|
||||
|
||||
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.
|
||||
The pathway is allowed 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
|
||||
|
||||
|
|
@ -177,7 +90,7 @@ Expect the pathway to change after each report. That is the point of ratcheting:
|
|||
|
||||
`manual_bid` means the stricter profit-seeking guardrails cleared. The code still does not place the order. You decide manually in Braiins.
|
||||
|
||||
## Where The Reports Are
|
||||
## Where The Evidence Lives
|
||||
|
||||
The master ledger is:
|
||||
|
||||
|
|
@ -191,12 +104,24 @@ Each completed watch creates one run report:
|
|||
reports/run-*.md
|
||||
```
|
||||
|
||||
Older sessions can be embedded with:
|
||||
Use the app's `Advanced` tab when you need raw artifacts. Mission Control intentionally hides raw logs during normal operation.
|
||||
|
||||
## Advanced Fallback Commands
|
||||
|
||||
Use these only if the native app cannot be opened or you are debugging:
|
||||
|
||||
```bash
|
||||
./scripts/ratchet retro START_UTC END_UTC
|
||||
./scripts/ratchet
|
||||
./scripts/ratchet once
|
||||
./scripts/ratchet watch 2
|
||||
./scripts/ratchet supervise
|
||||
./scripts/ratchet position list
|
||||
./scripts/ratchet report
|
||||
./scripts/ratchet experiments
|
||||
```
|
||||
|
||||
The preferred workflow remains the native app.
|
||||
|
||||
## The Ratchet Rule
|
||||
|
||||
One run is not a verdict. One run is a measurement.
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ The SwiftUI app turns that into native surfaces:
|
|||
- `Mission Control`: one exact action, cooldown, direct watch-only controls, and metrics.
|
||||
- `Research Map`: the ratchet pathway as a visual stage model.
|
||||
- `Manual Exposure`: the ledger for real manually placed Braiins exposure.
|
||||
- `Reports`: raw artifacts kept available but no longer primary.
|
||||
- `Advanced`: raw artifacts kept available but no longer primary.
|
||||
- `Ratchet Lecture`: a teachable model of observe, hypothesize, bound, mature, adapt.
|
||||
|
||||
## The Ratchet UX Rule
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Braiins Ratchet Mac
|
||||
|
||||
Native SwiftUI shell for the durable Braiins Ratchet lifecycle engine.
|
||||
Native SwiftUI control room for the durable Braiins Ratchet lifecycle engine.
|
||||
|
||||
The Python supervisor remains the source of truth. This app reads the same repository-local SQLite state through `./scripts/ratchet`.
|
||||
The Python lifecycle engine remains the source of truth. This app reads the same repository-local SQLite state through structured app state, not by making Mission Control a terminal transcript.
|
||||
|
||||
## Normal Run
|
||||
|
||||
|
|
@ -19,10 +19,10 @@ This builds `macos/build/Braiins Ratchet.app` and opens the packaged app. Use th
|
|||
- Research Map with the full ratchet pathway.
|
||||
- Direct watch-only controls without an approval gate.
|
||||
- Manual exposure recording and closing controls.
|
||||
- Reports panel for raw artifacts.
|
||||
- Advanced panel for raw artifacts and backend diagnostics.
|
||||
- Ratchet Lecture for the general autoresearch method.
|
||||
- Monitor-only. It never places Braiins orders.
|
||||
|
||||
## Product Direction
|
||||
|
||||
The next production step is wiring LaunchAgent controls for the durable supervisor.
|
||||
The next production step is wiring LaunchAgent controls for the durable supervisor while keeping Mission Control domain-first.
|
||||
|
|
|
|||
|
|
@ -79,7 +79,6 @@ struct ContentView: View {
|
|||
case .mission:
|
||||
MissionControlView(
|
||||
appState: appState,
|
||||
transcript: transcript,
|
||||
isRunning: isRunning,
|
||||
glow: glow,
|
||||
refresh: { Task { await refreshAppState() } },
|
||||
|
|
@ -98,8 +97,8 @@ struct ContentView: View {
|
|||
close: closeManualExposure,
|
||||
list: { Task { await runTextCommand(label: "position list", ["position", "list"], refreshAfterwards: true) } }
|
||||
)
|
||||
case .reports:
|
||||
ReportsView(
|
||||
case .advanced:
|
||||
AdvancedView(
|
||||
transcript: transcript,
|
||||
lastCommand: lastCommand,
|
||||
isRunning: isRunning,
|
||||
|
|
@ -224,7 +223,7 @@ enum AppSection: String, CaseIterable, Identifiable {
|
|||
case mission
|
||||
case map
|
||||
case exposure
|
||||
case reports
|
||||
case advanced
|
||||
case lecture
|
||||
|
||||
var id: String { rawValue }
|
||||
|
|
@ -234,7 +233,7 @@ enum AppSection: String, CaseIterable, Identifiable {
|
|||
case .mission: "Mission Control"
|
||||
case .map: "Research Map"
|
||||
case .exposure: "Manual Exposure"
|
||||
case .reports: "Reports"
|
||||
case .advanced: "Advanced"
|
||||
case .lecture: "Ratchet Lecture"
|
||||
}
|
||||
}
|
||||
|
|
@ -244,7 +243,7 @@ enum AppSection: String, CaseIterable, Identifiable {
|
|||
case .mission: "scope"
|
||||
case .map: "point.3.connected.trianglepath.dotted"
|
||||
case .exposure: "lock.shield"
|
||||
case .reports: "doc.text.magnifyingglass"
|
||||
case .advanced: "wrench.and.screwdriver"
|
||||
case .lecture: "graduationcap"
|
||||
}
|
||||
}
|
||||
|
|
@ -252,7 +251,6 @@ enum AppSection: String, CaseIterable, Identifiable {
|
|||
|
||||
struct MissionControlView: View {
|
||||
let appState: AppStatePayload?
|
||||
let transcript: String
|
||||
let isRunning: Bool
|
||||
let glow: Bool
|
||||
let refresh: () -> Void
|
||||
|
|
@ -268,6 +266,7 @@ struct MissionControlView: View {
|
|||
VStack(spacing: 14) {
|
||||
AutoresearchOrb(phase: ResearchPhase.from(appState), glow: glow)
|
||||
.frame(height: 250)
|
||||
ControlOwnershipCard(appState: appState, isRunning: isRunning)
|
||||
PassiveRunCard(
|
||||
plan: appState?.automationPlan,
|
||||
isRunning: isRunning,
|
||||
|
|
@ -277,9 +276,9 @@ struct MissionControlView: View {
|
|||
.frame(width: 350)
|
||||
}
|
||||
|
||||
MetricsDeck(appState: appState)
|
||||
EvidenceDeck(appState: appState)
|
||||
ResearchTimeline(appState: appState, compact: false)
|
||||
PlainEnglishCard(appState: appState, transcript: transcript)
|
||||
PlainEnglishCard(appState: appState)
|
||||
}
|
||||
.padding(28)
|
||||
}
|
||||
|
|
@ -312,7 +311,7 @@ struct HeroPanel: View {
|
|||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
Text("Do This Now")
|
||||
Text("Current Decision")
|
||||
.font(.caption.weight(.heavy))
|
||||
.textCase(.uppercase)
|
||||
.foregroundStyle(.secondary)
|
||||
|
|
@ -322,17 +321,6 @@ struct HeroPanel: View {
|
|||
Text(directive.detail)
|
||||
.font(.title3.weight(.semibold))
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
if let command = directive.command {
|
||||
HStack(spacing: 10) {
|
||||
Image(systemName: "terminal")
|
||||
Text(command)
|
||||
.font(.system(.body, design: .monospaced).weight(.semibold))
|
||||
}
|
||||
.padding(12)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(.black.opacity(0.18), in: RoundedRectangle(cornerRadius: 14, style: .continuous))
|
||||
}
|
||||
}
|
||||
|
||||
if let watch = appState?.operatorState.completedWatch {
|
||||
|
|
@ -345,6 +333,53 @@ struct HeroPanel: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct ControlOwnershipCard: View {
|
||||
let appState: AppStatePayload?
|
||||
let isRunning: Bool
|
||||
|
||||
var body: some View {
|
||||
GlassPanel(padding: 16) {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
Label("Who Is In Control", systemImage: symbol)
|
||||
.font(.headline)
|
||||
Text(title)
|
||||
.font(.title3.weight(.bold))
|
||||
Text(detail)
|
||||
.font(.callout)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
|
||||
private var symbol: String {
|
||||
if isRunning { return "gearshape.2" }
|
||||
guard let state = appState?.operatorState else { return "questionmark.circle" }
|
||||
if state.activeWatch != nil { return "binoculars" }
|
||||
if !state.activeManualPositions.isEmpty { return "lock.shield" }
|
||||
if state.completedWatch != nil { return "timer" }
|
||||
return "scope"
|
||||
}
|
||||
|
||||
private var title: String {
|
||||
if isRunning { return "The app is busy" }
|
||||
guard let state = appState?.operatorState else { return "Loading state" }
|
||||
if state.activeWatch != nil { return "A watch run owns control" }
|
||||
if !state.activeManualPositions.isEmpty { return "Manual exposure owns control" }
|
||||
if state.completedWatch != nil { return "Cooldown owns control" }
|
||||
return "The app is ready"
|
||||
}
|
||||
|
||||
private var detail: String {
|
||||
if isRunning { return "A monitor-only operation is running. Do not start a competing action." }
|
||||
guard let state = appState?.operatorState else { return "Reading the lifecycle database." }
|
||||
if state.activeWatch != nil { return "Let the watch finish; duplicate watches corrupt the research trail." }
|
||||
if !state.activeManualPositions.isEmpty { return "A real-world position is active, so new experiments stay blocked." }
|
||||
if let watch = state.completedWatch { return "Wait until \(watch.earliestActionLocal) before the next useful sample." }
|
||||
return "No active watch, no manual exposure, and no cooldown block."
|
||||
}
|
||||
}
|
||||
|
||||
struct PassiveRunCard: View {
|
||||
let plan: AutomationPlanPayload?
|
||||
let isRunning: Bool
|
||||
|
|
@ -353,7 +388,7 @@ struct PassiveRunCard: View {
|
|||
var body: some View {
|
||||
GlassPanel {
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
Label("Watch-only Control", systemImage: "binoculars")
|
||||
Label("Next Passive Action", systemImage: "arrow.forward.circle")
|
||||
.font(.headline)
|
||||
Text(title)
|
||||
.font(.title3.weight(.bold))
|
||||
|
|
@ -419,7 +454,7 @@ struct PassiveRunCard: View {
|
|||
switch plan.kind {
|
||||
case "once_now": return "Refresh Now"
|
||||
case "watch_2h": return "Start Watch-only Run"
|
||||
case "wait_then_once": return plan.waitSeconds > 0 ? "Wait" : "Refresh Now"
|
||||
case "wait_then_once": return plan.waitSeconds > 0 ? "Cooldown Active" : "Refresh Now"
|
||||
case "report_only": return "Open Report"
|
||||
default: return "Refresh State"
|
||||
}
|
||||
|
|
@ -438,43 +473,50 @@ struct PassiveRunCard: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct MetricsDeck: View {
|
||||
struct EvidenceDeck: View {
|
||||
let appState: AppStatePayload?
|
||||
|
||||
var body: some View {
|
||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 14), count: 4), spacing: 14) {
|
||||
MetricTile(
|
||||
title: "Market Freshness",
|
||||
value: freshnessText,
|
||||
detail: appState?.operatorState.latestMarketTimestamp ?? "no sample",
|
||||
symbol: "clock"
|
||||
title: "Braiins Market",
|
||||
value: marketPrice,
|
||||
detail: marketDetail,
|
||||
symbol: "chart.line.uptrend.xyaxis"
|
||||
)
|
||||
MetricTile(
|
||||
title: "Strategy Action",
|
||||
value: appState?.operatorState.action ?? "none",
|
||||
title: "Model Net",
|
||||
value: proposalValue("expected_net_btc", fallback: "n/a"),
|
||||
detail: actionDetail,
|
||||
symbol: "target"
|
||||
symbol: "plus.forwardslash.minus"
|
||||
)
|
||||
MetricTile(
|
||||
title: "Manual Exposure",
|
||||
value: exposureText,
|
||||
detail: "Blocks new experiments while active",
|
||||
symbol: "shield.lefthalf.filled"
|
||||
title: "OCEAN Pool",
|
||||
value: oceanValue("pool_hashrate_eh_s", suffix: " EH/s"),
|
||||
detail: oceanValue("network_difficulty_t", prefix: "difficulty "),
|
||||
symbol: "water.waves"
|
||||
)
|
||||
MetricTile(
|
||||
title: "Latest Report",
|
||||
value: appState?.operatorState.latestReport?.lastPathComponent ?? "none",
|
||||
detail: appState?.operatorState.latestReport ?? "No artifact yet",
|
||||
symbol: "doc.text"
|
||||
title: "Evidence",
|
||||
value: evidenceValue,
|
||||
detail: evidenceDetail,
|
||||
symbol: "archivebox"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private var freshnessText: String {
|
||||
guard let state = appState?.operatorState else { return "loading" }
|
||||
if state.isFresh { return "fresh" }
|
||||
if let minutes = state.freshnessMinutes { return "stale \(minutes)m" }
|
||||
return "unknown"
|
||||
private var marketPrice: String {
|
||||
if let fillable = appState?.latest.market?["fillable_price_btc_per_eh_day"]?.description, fillable != "n/a" {
|
||||
return fillable
|
||||
}
|
||||
return appState?.latest.market?["best_ask_btc_per_eh_day"]?.description ?? "n/a"
|
||||
}
|
||||
|
||||
private var marketDetail: String {
|
||||
let freshness = appState?.operatorState.freshnessMinutes.map { "\($0)m old" } ?? "age unknown"
|
||||
let ask = appState?.latest.market?["best_ask_btc_per_eh_day"]?.description ?? "n/a"
|
||||
let last = appState?.latest.market?["last_price_btc_per_eh_day"]?.description ?? "n/a"
|
||||
return "\(freshness), ask \(ask), last \(last)"
|
||||
}
|
||||
|
||||
private var actionDetail: String {
|
||||
|
|
@ -484,9 +526,26 @@ struct MetricsDeck: View {
|
|||
return "No useful market action"
|
||||
}
|
||||
|
||||
private var exposureText: String {
|
||||
private var evidenceValue: String {
|
||||
let count = appState?.operatorState.activeManualPositions.count ?? 0
|
||||
return count == 0 ? "none" : "\(count) active"
|
||||
if count > 0 { return "\(count) active exposure" }
|
||||
return appState?.operatorState.latestReport?.lastPathComponent ?? "none"
|
||||
}
|
||||
|
||||
private var evidenceDetail: String {
|
||||
if let watch = appState?.operatorState.completedWatch {
|
||||
return "cooldown \(watch.remainingMinutes)m remaining"
|
||||
}
|
||||
return appState?.operatorState.latestReport ?? "No artifact yet"
|
||||
}
|
||||
|
||||
private func proposalValue(_ key: String, fallback: String) -> String {
|
||||
appState?.latest.proposal?[key]?.description ?? fallback
|
||||
}
|
||||
|
||||
private func oceanValue(_ key: String, prefix: String = "", suffix: String = "") -> String {
|
||||
guard let value = appState?.latest.ocean?[key]?.description else { return "n/a" }
|
||||
return "\(prefix)\(value)\(suffix)"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -617,7 +676,6 @@ struct TimelineNode: View {
|
|||
|
||||
struct PlainEnglishCard: View {
|
||||
let appState: AppStatePayload?
|
||||
let transcript: String
|
||||
|
||||
var body: some View {
|
||||
GlassPanel {
|
||||
|
|
@ -799,7 +857,7 @@ struct ActiveExposureList: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct ReportsView: View {
|
||||
struct AdvancedView: View {
|
||||
let transcript: String
|
||||
let lastCommand: String
|
||||
let isRunning: Bool
|
||||
|
|
@ -811,9 +869,9 @@ struct ReportsView: View {
|
|||
VStack(alignment: .leading, spacing: 18) {
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Reports")
|
||||
Text("Advanced")
|
||||
.font(.system(size: 36, weight: .black, design: .rounded))
|
||||
Text("Raw artifacts remain here, away from the noob cockpit.")
|
||||
Text("Raw artifacts and backend diagnostics live here, away from Mission Control.")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
Spacer()
|
||||
|
|
@ -824,7 +882,7 @@ struct ReportsView: View {
|
|||
|
||||
GlassPanel {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
Label(lastCommand, systemImage: "terminal")
|
||||
Label(lastCommand, systemImage: "wrench.and.screwdriver")
|
||||
.font(.headline)
|
||||
ScrollView {
|
||||
Text(transcript)
|
||||
|
|
@ -1095,37 +1153,35 @@ enum ResearchPhase {
|
|||
struct Directive {
|
||||
let title: String
|
||||
let detail: String
|
||||
let command: String?
|
||||
let color: Color
|
||||
|
||||
static func from(_ appState: AppStatePayload?) -> Directive {
|
||||
guard let appState else {
|
||||
return Directive(title: "LOAD STATE", detail: "The app is reading the ratchet lifecycle database.", command: nil, color: .secondary)
|
||||
return Directive(title: "LOAD STATE", detail: "The app is reading the ratchet lifecycle database.", color: .secondary)
|
||||
}
|
||||
if let watch = appState.operatorState.completedWatch {
|
||||
return Directive(
|
||||
title: "STOP",
|
||||
detail: "Wait until \(watch.earliestActionLocal). Repeating the same watch now would be loop-chasing.",
|
||||
command: "./scripts/ratchet once",
|
||||
color: .orange
|
||||
)
|
||||
}
|
||||
if appState.operatorState.activeWatch != nil {
|
||||
return Directive(title: "WAIT", detail: "A watch is already running. Do not start another one.", command: nil, color: .orange)
|
||||
return Directive(title: "WAIT", detail: "A watch is already running. Do not start another one.", color: .orange)
|
||||
}
|
||||
if !appState.operatorState.activeManualPositions.isEmpty {
|
||||
return Directive(title: "HOLD", detail: "Manual Braiins exposure is active. Supervise it; do not start new experiments.", command: "./scripts/ratchet supervise --status", color: .orange)
|
||||
return Directive(title: "HOLD", detail: "Manual Braiins exposure is active. Supervise it; do not start new experiments.", color: .orange)
|
||||
}
|
||||
if !appState.operatorState.hasOcean || !appState.operatorState.hasMarket || !appState.operatorState.isFresh {
|
||||
return Directive(title: "REFRESH", detail: "The latest market state is stale or missing. Collect exactly one fresh sample.", command: "./scripts/ratchet once", color: .green)
|
||||
return Directive(title: "REFRESH", detail: "The latest market state is stale or missing. Collect exactly one fresh sample.", color: .green)
|
||||
}
|
||||
if appState.operatorState.action == "manual_canary" {
|
||||
return Directive(title: "WATCH", detail: "Run one bounded passive watch. This buys information, not a promise of profit.", command: "./scripts/ratchet watch 2", color: .green)
|
||||
return Directive(title: "WATCH", detail: "Run one bounded passive watch. This buys information, not a promise of profit.", color: .green)
|
||||
}
|
||||
if appState.operatorState.action == "manual_bid" {
|
||||
return Directive(title: "REVIEW", detail: "Read the full report before any manual Braiins action.", command: "./scripts/ratchet report", color: .green)
|
||||
return Directive(title: "REVIEW", detail: "Read the full report before any manual Braiins action.", color: .green)
|
||||
}
|
||||
return Directive(title: "OBSERVE", detail: "No useful action window is visible right now.", command: nil, color: .secondary)
|
||||
return Directive(title: "OBSERVE", detail: "No useful action window is visible right now.", color: .secondary)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,16 @@ class MacAppPackagingTest(unittest.TestCase):
|
|||
self.assertIn("./scripts/ratchet app", text)
|
||||
self.assertNotIn("swift run BraiinsRatchetMac", text)
|
||||
|
||||
def test_start_here_is_app_first_and_not_pipeline_first(self):
|
||||
text = (ROOT / "START_HERE.md").read_text()
|
||||
|
||||
self.assertIn("This project now has one normal operator entry point", text)
|
||||
self.assertIn("./scripts/ratchet app", text)
|
||||
self.assertIn("The app is the control room", text)
|
||||
self.assertIn("Who Is In Control", text)
|
||||
self.assertNotIn("Controlled Automation", text)
|
||||
self.assertNotIn("./scripts/ratchet pipeline", text)
|
||||
|
||||
def test_swift_app_uses_native_dashboard_not_raw_terminal_as_primary_ui(self):
|
||||
source = ROOT / "macos" / "BraiinsRatchet" / "Sources" / "BraiinsRatchetMac" / "BraiinsRatchetApp.swift"
|
||||
text = source.read_text()
|
||||
|
|
@ -56,6 +66,11 @@ class MacAppPackagingTest(unittest.TestCase):
|
|||
self.assertIn("AppStatePayload", text)
|
||||
self.assertIn("loadAppState", text)
|
||||
self.assertIn("PassiveRunCard", text)
|
||||
self.assertIn("ControlOwnershipCard", text)
|
||||
self.assertIn("EvidenceDeck", text)
|
||||
self.assertIn("AdvancedView", text)
|
||||
self.assertIn("Current Decision", text)
|
||||
self.assertNotIn("Do This Now", text)
|
||||
self.assertNotIn("Automation Gate", text)
|
||||
self.assertNotIn("confirmationDialog", text)
|
||||
self.assertNotIn("showAutomationApproval", text)
|
||||
|
|
|
|||
Loading…
Reference in a new issue