Code Farm: per-plot auto-replant pause and crop-lock check; community sync and node work
DevPlace CI / test (push) Failing after 44m45s

Continuous Delivery could never be turned off once a crop was planted. Add a
one-shot per-plot "stop after harvest" flag (POST /game/plot/stop, Devii tool
game_plot_stop, game_plots.stop_after_harvest), cleared by the next plant,
autoreplant claim or Refactor. Autoreplant now also respects crop locks through
the shared _crop_lock predicate. Plan and measurements in happyplanting.md.

Also includes the pending community sync, node network and related work in the
tree; the full suite passed (4999) with all of it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-17 02:23:43 +02:00
co-authored by Claude Sonnet 5.5
parent ce899fc5e1
commit 02ab747cd0
203 changed files with 11624 additions and 301 deletions
+46 -2
View File
@@ -28,11 +28,14 @@ class CircuitBreaker:
failures: int = LIMITS.breaker_failures,
open_seconds: float = LIMITS.breaker_open_seconds,
clock: Callable[[], float] = time.monotonic,
wall: Callable[[], float] = time.time,
) -> None:
self.failures = max(1, failures)
self.open_seconds = open_seconds
self._clock = clock
self._wall = wall
self._circuits: dict[Hashable, _Circuit] = {}
self.dirty = False
def state(self, key: Hashable) -> str:
circuit = self._circuits.get(key)
@@ -55,7 +58,8 @@ class CircuitBreaker:
self._circuits[key].probing = True
def success(self, key: Hashable) -> None:
self._circuits.pop(key, None)
if self._circuits.pop(key, None) is not None:
self.dirty = True
def failure(self, key: Hashable) -> None:
circuit = self._circuits.setdefault(key, _Circuit())
@@ -63,9 +67,49 @@ class CircuitBreaker:
circuit.failures += 1
if circuit.failures >= self.failures:
circuit.opened_at = self._clock()
self.dirty = True
def forget(self, key: Hashable) -> None:
self._circuits.pop(key, None)
if self._circuits.pop(key, None) is not None:
self.dirty = True
def open_count(self) -> int:
return sum(1 for key in list(self._circuits) if self.state(key) != CLOSED)
def snapshot(self) -> list[dict]:
now_clock, now_wall = self._clock(), self._wall()
rows = []
for key, circuit in list(self._circuits.items()):
state = self.state(key)
rows.append(
{
"key": ":".join(str(part) for part in key) if isinstance(key, tuple) else str(key),
"failures": circuit.failures,
"state": state,
"opened_at": now_wall - (now_clock - circuit.opened_at) if circuit.failures >= self.failures else 0.0,
"seconds_left": max(0.0, self.open_seconds - (now_clock - circuit.opened_at))
if state == OPEN
else 0.0,
}
)
rows.sort(key=lambda item: (-item["failures"], item["key"]))
return rows
def restore(self, rows: list[dict]) -> int:
now_clock, now_wall = self._clock(), self._wall()
restored = 0
for row in rows:
key = tuple(str(row["key"]).split(":", 1))
if len(key) != 2:
continue
failures = int(row.get("failures") or 0)
opened_wall = float(row.get("opened_at") or 0.0)
if failures < self.failures or opened_wall <= 0:
continue
age = max(0.0, now_wall - opened_wall)
if age >= self.open_seconds * 2:
continue
circuit = _Circuit(failures=failures, opened_at=now_clock - age)
self._circuits[key] = circuit
restored += 1
return restored