mirror of
https://github.com/saymrwulf/BraiinsRatchet.git
synced 2026-05-14 20:37:52 +00:00
Redesign native research cockpit
This commit is contained in:
parent
427045e634
commit
3d328752c3
8 changed files with 1349 additions and 214 deletions
|
|
@ -57,6 +57,8 @@ 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`.
|
||||
|
||||
For a 6-hour monitoring session:
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -144,6 +144,14 @@ 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 automation approval.
|
||||
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.
|
||||
5. `Ratchet Lecture`: the general observe, hypothesize, bound, mature, adapt method.
|
||||
|
||||
## Research Pathway
|
||||
|
||||
The cockpit has two different time horizons:
|
||||
|
|
|
|||
64
docs/APP_DESIGN_RESEARCH.md
Normal file
64
docs/APP_DESIGN_RESEARCH.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# Native App Design Research
|
||||
|
||||
This app is not supposed to be a prettier terminal. It is supposed to be a control room for a risky, long-running research lifecycle.
|
||||
|
||||
## Design Sources
|
||||
|
||||
Apple's Liquid Glass guidance emphasizes system-native structure before visual effects:
|
||||
|
||||
- Use standard SwiftUI/AppKit structure so controls, navigation, sheets, and toolbars inherit system behavior.
|
||||
- Keep navigation and controls in a distinct functional layer above the content.
|
||||
- Avoid overusing custom glass effects; too much glass becomes noise.
|
||||
- Support arbitrary window sizes with split views.
|
||||
- Preserve accessibility when transparency or motion is reduced.
|
||||
|
||||
Source: <https://developer.apple.com/documentation/TechnologyOverviews/adopting-liquid-glass>
|
||||
|
||||
Microsoft's Human-AI Interaction Guidelines are directly relevant because this app makes recommendations under uncertainty:
|
||||
|
||||
- Make clear what the system can and cannot do.
|
||||
- Make clear how well it can do it.
|
||||
- Show contextually relevant information.
|
||||
- Explain why the system did what it did.
|
||||
- Support correction, dismissal, global controls, and cautious adaptation over time.
|
||||
|
||||
Source: <https://www.microsoft.com/en-us/research/publication/guidelines-for-human-ai-interaction/>
|
||||
|
||||
Nielsen Norman's usability heuristics matter because the operator may be tired, confused, or dealing with real money:
|
||||
|
||||
- Show system status.
|
||||
- Use real-world language.
|
||||
- Prevent errors before they happen.
|
||||
- Prefer recognition over recall.
|
||||
- Provide clear recovery paths.
|
||||
|
||||
Source: <https://media.nngroup.com/media/articles/attachments/Heuristic_Summary1-compressed.pdf>
|
||||
|
||||
## Product Decisions
|
||||
|
||||
The native app now treats the Python engine as a structured state provider, not as a terminal to embed. The new `app-state` command returns JSON with:
|
||||
|
||||
- Current operator state.
|
||||
- Automation plan.
|
||||
- Cockpit text for audit/debug.
|
||||
- Latest OCEAN, Braiins, and strategy proposal payloads.
|
||||
|
||||
The SwiftUI app turns that into native surfaces:
|
||||
|
||||
- `Mission Control`: one exact action, cooldown, approval gate, 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.
|
||||
- `Ratchet Lecture`: a teachable model of observe, hypothesize, bound, mature, adapt.
|
||||
|
||||
## The Ratchet UX Rule
|
||||
|
||||
The app must always answer these questions without forcing the user to parse logs:
|
||||
|
||||
1. Who is in control right now?
|
||||
2. What is the earliest useful next action?
|
||||
3. What evidence artifact exists?
|
||||
4. What action is blocked for safety?
|
||||
5. Which single knob, if any, is eligible for later adaptation?
|
||||
|
||||
If the app cannot answer those questions graphically and in plain language, it is failing its purpose.
|
||||
|
|
@ -14,10 +14,13 @@ This builds `macos/build/Braiins Ratchet.app` and opens the packaged app. Use th
|
|||
|
||||
## Current Scope
|
||||
|
||||
- Native macOS SwiftUI cockpit.
|
||||
- Liquid-glass-inspired material panels.
|
||||
- Buttons for cockpit, lifecycle status, automation proposal, and full report.
|
||||
- Native macOS SwiftUI control room.
|
||||
- Mission Control with one explicit next action.
|
||||
- Research Map with the full ratchet pathway.
|
||||
- Monitor-only automation approval gate.
|
||||
- Manual exposure recording and closing controls.
|
||||
- Reports panel for raw artifacts.
|
||||
- Ratchet Lecture for the general autoresearch method.
|
||||
- Monitor-only. It never places Braiins orders.
|
||||
|
||||
## Product Direction
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -18,6 +18,7 @@ Commands:
|
|||
app Build and open the native macOS app.
|
||||
position Record/list/close manually executed Braiins exposure.
|
||||
report Print the latest stored report without fetching new data.
|
||||
app-state Print structured JSON for the native macOS app.
|
||||
experiments Print the Karpathy-style experiment ledger.
|
||||
retro SINCE [UNTIL] Write a retroactive report from stored snapshots.
|
||||
raw-cycle Run one full monitor cycle and print raw JSON.
|
||||
|
|
@ -42,7 +43,7 @@ USAGE
|
|||
|
||||
ensure_venv() {
|
||||
if [[ ! -x "$PYTHON_BIN" ]]; then
|
||||
echo "Creating local virtual environment at .venv ..."
|
||||
echo "Creating local virtual environment at .venv ..." >&2
|
||||
python3 -m venv "$ROOT_DIR/.venv"
|
||||
fi
|
||||
}
|
||||
|
|
@ -113,6 +114,10 @@ cmd_report() {
|
|||
run_python -m braiins_ratchet.cli report
|
||||
}
|
||||
|
||||
cmd_app_state() {
|
||||
run_python -m braiins_ratchet.cli app-state
|
||||
}
|
||||
|
||||
cmd_pipeline() {
|
||||
run_python -m braiins_ratchet.cli pipeline "$@"
|
||||
}
|
||||
|
|
@ -177,6 +182,7 @@ main() {
|
|||
app|mac-app) cmd_app "$@" ;;
|
||||
position|positions) cmd_position "$@" ;;
|
||||
report) cmd_report "$@" ;;
|
||||
app-state) cmd_app_state "$@" ;;
|
||||
experiments) cmd_experiments "$@" ;;
|
||||
retro) cmd_retro "$@" ;;
|
||||
raw-cycle) cmd_raw_cycle "$@" ;;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from dataclasses import asdict
|
||||
from datetime import UTC, datetime, timedelta
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
|
@ -17,7 +18,7 @@ from .experiments import (
|
|||
summarize_since,
|
||||
write_retro_report,
|
||||
)
|
||||
from .guidance import build_operator_cockpit
|
||||
from .guidance import build_operator_cockpit, get_operator_state
|
||||
from .lifecycle import (
|
||||
close_manual_position,
|
||||
open_manual_position,
|
||||
|
|
@ -34,6 +35,7 @@ from .storage import (
|
|||
init_db,
|
||||
latest_market_snapshot,
|
||||
latest_ocean_snapshot,
|
||||
latest_proposal,
|
||||
save_market_snapshot,
|
||||
save_ocean_snapshot,
|
||||
save_proposal,
|
||||
|
|
@ -157,6 +159,26 @@ def cmd_next(_: argparse.Namespace) -> int:
|
|||
return 0
|
||||
|
||||
|
||||
def cmd_app_state(_: argparse.Namespace) -> int:
|
||||
with connect() as conn:
|
||||
init_db(conn)
|
||||
operator_state = get_operator_state(conn)
|
||||
automation_plan = build_automation_plan(conn)
|
||||
payload = {
|
||||
"generated_at": datetime.now(UTC).isoformat(timespec="seconds"),
|
||||
"operator_state": asdict(operator_state),
|
||||
"automation_plan": asdict(automation_plan),
|
||||
"cockpit": build_operator_cockpit(conn),
|
||||
"latest": {
|
||||
"ocean": _object_dict(latest_ocean_snapshot(conn)),
|
||||
"market": _object_dict(latest_market_snapshot(conn)),
|
||||
"proposal": _object_dict(latest_proposal(conn)),
|
||||
},
|
||||
}
|
||||
print(json.dumps(payload, default=str, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_pipeline(args: argparse.Namespace) -> int:
|
||||
config = load_config(Path(args.config) if args.config else None)
|
||||
with connect() as conn:
|
||||
|
|
@ -317,6 +339,12 @@ def _proposal_json(proposal: object) -> str:
|
|||
return json.dumps(proposal, default=default, indent=2)
|
||||
|
||||
|
||||
def _object_dict(value: object | None) -> dict[str, object] | None:
|
||||
if value is None:
|
||||
return None
|
||||
return dict(value.__dict__) if hasattr(value, "__dict__") else {"value": str(value)}
|
||||
|
||||
|
||||
def _run_one_fresh_cycle(config: object) -> None:
|
||||
with connect() as conn:
|
||||
run_cycle(conn, config)
|
||||
|
|
@ -387,6 +415,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
next_step = sub.add_parser("next", help="print exactly what the operator should do next")
|
||||
next_step.set_defaults(func=cmd_next)
|
||||
|
||||
app_state = sub.add_parser("app-state", help="print structured JSON for the native app")
|
||||
app_state.set_defaults(func=cmd_app_state)
|
||||
|
||||
pipeline = sub.add_parser("pipeline", help="propose and confirm the next automation step")
|
||||
pipeline.add_argument("--config")
|
||||
pipeline.add_argument("--yes", action="store_true", help="accept the printed plan without prompting")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
from pathlib import Path
|
||||
import unittest
|
||||
|
||||
from braiins_ratchet.cli import build_parser
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
|
@ -12,8 +14,14 @@ class MacAppPackagingTest(unittest.TestCase):
|
|||
|
||||
self.assertIn("app|mac-app", text)
|
||||
self.assertIn("cmd_app", text)
|
||||
self.assertIn("app-state", text)
|
||||
self.assertNotIn("swift run BraiinsRatchetMac", text)
|
||||
|
||||
def test_python_cli_exposes_structured_app_state(self):
|
||||
args = build_parser().parse_args(["app-state"])
|
||||
|
||||
self.assertEqual(args.func.__name__, "cmd_app_state")
|
||||
|
||||
def test_mac_app_builder_creates_bundle_contract(self):
|
||||
builder = ROOT / "scripts" / "build_mac_app"
|
||||
text = builder.read_text()
|
||||
|
|
@ -36,3 +44,14 @@ class MacAppPackagingTest(unittest.TestCase):
|
|||
text = path.read_text()
|
||||
self.assertIn("./scripts/ratchet app", text)
|
||||
self.assertNotIn("swift run BraiinsRatchetMac", 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()
|
||||
|
||||
self.assertIn("NavigationSplitView", text)
|
||||
self.assertIn("MissionControlView", text)
|
||||
self.assertIn("ResearchTimeline", text)
|
||||
self.assertIn("AutoresearchOrb", text)
|
||||
self.assertIn("AppStatePayload", text)
|
||||
self.assertIn("loadAppState", text)
|
||||
|
|
|
|||
Loading…
Reference in a new issue