refactor: address high-priority debt and publish to PyPI
- Fix TOCTOU race conditions by wrapping read-modify-write cycles under single-file locks in execution_state, portfolio_service, precheck_state, state_manager, and precheck_service. - Add missing test coverage (96 tests total): - test_review_service.py (15 tests) - test_check_api.py (6 tests) - test_external_gate.py main branches (+10 tests) - test_trade_execution.py new commands (+8 tests) - Unify all agent-consumed JSON messages to English. - Config-ize hardcoded values (volume filter, schema_version) via get_user_config with sensible defaults. - Add 1-hour TTL to exchange cache with force_new override. - Add ruff and mypy to dev dependencies; fix all type errors. - Add __all__ declarations to 11 service modules. - Sync README with new commands, config tuning docs, and PyPI badge. - Publish package as coinhunter==1.0.0 on PyPI with MIT license. - Deprecate coinhunter-cli==1.0.1 with runtime warning. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,17 +1,25 @@
|
||||
"""Execution state helpers (decision deduplication, executions.json)."""
|
||||
import hashlib
|
||||
|
||||
from ..runtime import get_runtime_paths
|
||||
from .file_utils import load_json_locked, save_json_locked
|
||||
from .trade_common import bj_now_iso
|
||||
__all__ = [
|
||||
"default_decision_id",
|
||||
"load_executions",
|
||||
"save_executions",
|
||||
"record_execution_state",
|
||||
"get_execution_state",
|
||||
]
|
||||
|
||||
PATHS = get_runtime_paths()
|
||||
EXECUTIONS_FILE = PATHS.executions_file
|
||||
EXECUTIONS_LOCK = PATHS.executions_lock
|
||||
from ..runtime import get_runtime_paths
|
||||
from .file_utils import load_json_locked, read_modify_write_json, save_json_locked
|
||||
|
||||
|
||||
def _paths():
|
||||
return get_runtime_paths()
|
||||
|
||||
|
||||
def default_decision_id(action: str, argv_tail: list[str]) -> str:
|
||||
from datetime import datetime
|
||||
|
||||
from .trade_common import CST
|
||||
|
||||
now = datetime.now(CST)
|
||||
@@ -22,17 +30,24 @@ def default_decision_id(action: str, argv_tail: list[str]) -> str:
|
||||
|
||||
|
||||
def load_executions() -> dict:
|
||||
return load_json_locked(EXECUTIONS_FILE, EXECUTIONS_LOCK, {"executions": {}}).get("executions", {})
|
||||
paths = _paths()
|
||||
data = load_json_locked(paths.executions_file, paths.executions_lock, {"executions": {}})
|
||||
return data.get("executions", {}) # type: ignore[no-any-return]
|
||||
|
||||
|
||||
def save_executions(executions: dict):
|
||||
save_json_locked(EXECUTIONS_FILE, EXECUTIONS_LOCK, {"executions": executions})
|
||||
paths = _paths()
|
||||
save_json_locked(paths.executions_file, paths.executions_lock, {"executions": executions})
|
||||
|
||||
|
||||
def record_execution_state(decision_id: str, payload: dict):
|
||||
executions = load_executions()
|
||||
executions[decision_id] = payload
|
||||
save_executions(executions)
|
||||
paths = _paths()
|
||||
read_modify_write_json(
|
||||
paths.executions_file,
|
||||
paths.executions_lock,
|
||||
{"executions": {}},
|
||||
lambda data: data.setdefault("executions", {}).__setitem__(decision_id, payload) or data,
|
||||
)
|
||||
|
||||
|
||||
def get_execution_state(decision_id: str):
|
||||
|
||||
Reference in New Issue
Block a user