Code Farm: per-plot auto-replant pause and crop-lock check; community sync and node work

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
+64
View File
@@ -83,3 +83,67 @@ def test_envelope_normalizes_origin_and_bounds_batch():
SyncBatchForm(origin="other.org", records=[{}] * (LIMITS.max_batch_records + 1))
with pytest.raises(ValidationError):
SyncBatchForm(origin="bad origin", records=[{}])
def test_new_kinds_have_models_and_a_published_schema():
from devplacepy.services.sync.records import RECORD_MODELS
from devplacepy.services.sync.spec import KINDS
assert set(RECORD_MODELS) == set(KINDS)
for model in RECORD_MODELS.values():
assert model.model_json_schema()["title"]
def test_vote_value_and_target_kind_are_closed_sets():
import pytest
from pydantic import ValidationError
from devplacepy.services.sync.records import VoteRecord
base = {"origin_uid": "v", "voter_uid": "u", "target_kind": "post", "target_uid": "p", "value": 1, "created_at": "2025-01-01T00:00:00Z"}
assert VoteRecord.model_validate(base).value == 1
for change in ({"value": 0}, {"value": 2}, {"target_kind": "gist"}, {"voter_uid": "bad id"}):
with pytest.raises(ValidationError):
VoteRecord.model_validate({**base, **change})
def test_attachment_fingerprint_ignores_the_blob_but_not_its_hash():
import base64
from devplacepy.services.sync.records import AttachmentRecord
def make(data):
return AttachmentRecord.model_validate({
"origin_uid": "a", "author_uid": "u", "target_kind": "post", "target_uid": "p",
"filename": "dir/evil\\name.png", "content_b64": base64.b64encode(data).decode(), "created_at": "2025-01-01T00:00:00Z",
})
first, second = make(b"one"), make(b"two")
assert first.filename == "name.png"
assert "content_b64" not in first.content_fields() and first.content_fields()["sha256"] != second.content_fields()["sha256"]
assert first.content_bytes() == b"one"
def test_batch_byte_cap(monkeypatch):
import pytest
from dataclasses import replace
from pydantic import ValidationError
from devplacepy.services.sync import records
from devplacepy.services.sync.spec import LIMITS
monkeypatch.setattr(records, "LIMITS", replace(LIMITS, max_batch_bytes=100))
with pytest.raises(ValidationError):
records.SyncBatchForm(origin="a.b", records=[{"x": "y" * 200}])
def test_ack_form_validates_kinds_and_ids():
import pytest
from pydantic import ValidationError
from devplacepy.services.sync.records import SyncAckForm
assert SyncAckForm(origin="A.B", items='[{"kind": "posts", "origin_uid": "p1"}]').items == [{"kind": "posts", "origin_uid": "p1"}]
for items in ([], [{"kind": "gists", "origin_uid": "x"}], [{"kind": "posts", "origin_uid": ""}], "not json"):
with pytest.raises(ValidationError):
SyncAckForm(origin="a.b", items=items)