From 72b95a45705b07d4262d219609af406d28b5ffbc Mon Sep 17 00:00:00 2001 From: David Front Date: Wed, 22 Jul 2026 14:46:08 +0300 Subject: [PATCH 1/2] Finalize publication readiness --- .github/workflows/ci.yml | 3 +++ STATUS.md | 19 +++++++++++-------- docs/RELEASE_READINESS.md | 8 ++++++-- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4dcd384..03a5aea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,9 @@ on: push: pull_request: +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest diff --git a/STATUS.md b/STATUS.md index 410e65b..921b286 100644 --- a/STATUS.md +++ b/STATUS.md @@ -1,6 +1,6 @@ # Development status -Last reviewed: 2026-07-21. +Last reviewed: 2026-07-22. This file is the canonical continuation point for unfinished and planned work. Update it whenever scope, architecture, or release readiness changes. Do not @@ -8,9 +8,10 @@ leave material plans only in chat history. ## Current state -- Repository visibility: private; the public developer-preview candidate has - been reviewed and approved for merge into `main`. Visibility remains - unchanged pending the final publication step. +- Repository visibility: private; the reviewed public developer-preview + candidate was merged into `main` through PR #1. The final privacy, + clean-clone, package, and documentation audit passed on 2026-07-22. + Visibility remains unchanged pending explicit publication authorization. - Package and CLI: `geometry-validated-layout` version `0.1.0`. - Python import: `geometry_validated_layout`. - Test baseline: 10 tests, with CI coverage for Python 3.10 through 3.14. @@ -57,10 +58,12 @@ leave material plans only in chat history. ## Deferred publication work -- Final user approval of author-email exposure and copyright-holder text. -- Change visibility only after the clean-clone and privacy checks in - `docs/RELEASE_READINESS.md` pass. -- Tagged GitHub prerelease and optional later PyPI package. +- Change visibility only after explicit repository-owner authorization. +- After the visibility change, enable the public-repository security settings + and create the tagged GitHub prerelease described in + `docs/RELEASE_READINESS.md`. +- Optional later PyPI publication; the package name was unclaimed when checked + on 2026-07-22. - Contributor-facing issue templates and broader compatibility testing. - Plugin packaging for distributing the generic and kitchen workflow skills. diff --git a/docs/RELEASE_READINESS.md b/docs/RELEASE_READINESS.md index e742d97..dc0bd32 100644 --- a/docs/RELEASE_READINESS.md +++ b/docs/RELEASE_READINESS.md @@ -33,13 +33,17 @@ privacy review. 3.14 and package installation outside the checkout. - [x] Repository owner reviewed the public-facing material and authorized the release branch merge. -- [x] Merge authorization recorded for `release/public-preview` into `main`. +- [x] Merge `release/public-preview` into `main` through reviewed PR #1. +- [x] Re-run the privacy, clean-clone, package, and documentation-link audit + against the merged `main` branch. - [ ] Change repository visibility only after all preceding items pass. ## Immediately after visibility changes - [ ] Enable GitHub private vulnerability reporting and verify the reporting language in `SECURITY.md`. +- [ ] Protect `main` against force pushes and require the CI checks supported by + the repository plan. - [ ] Create a `v0.1.0` GitHub prerelease with the developer-preview boundary and known limitations from `STATUS.md`. @@ -48,7 +52,7 @@ privacy review. Run without a sibling checkout or pre-existing virtual environment: ```bash -git clone --branch release/public-preview \ +git clone --branch main \ https://github.com/davidf9999/geometry-validated-layout.git cd geometry-validated-layout python3 -m venv .venv From d643eb2b27e6af480392d5fc03ca053f691c9e58 Mon Sep 17 00:00:00 2001 From: David Front Date: Wed, 22 Jul 2026 14:48:39 +0300 Subject: [PATCH 2/2] Reject cyclic parent relationships --- STATUS.md | 2 +- src/geometry_validated_layout/models.py | 17 +++++++++++++- tests/test_validation.py | 30 +++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/STATUS.md b/STATUS.md index 921b286..2a0ed1d 100644 --- a/STATUS.md +++ b/STATUS.md @@ -14,7 +14,7 @@ leave material plans only in chat history. Visibility remains unchanged pending explicit publication authorization. - Package and CLI: `geometry-validated-layout` version `0.1.0`. - Python import: `geometry_validated_layout`. -- Test baseline: 10 tests, with CI coverage for Python 3.10 through 3.14. +- Test baseline: 12 tests, with CI coverage for Python 3.10 through 3.14. - CI: tests, passing examples, deterministic rendering, and failing-render refusal run on every push and pull request. - Complete example: `examples/complete_kitchen/` contains synthetic YAML, diff --git a/src/geometry_validated_layout/models.py b/src/geometry_validated_layout/models.py index f67e440..25a9085 100644 --- a/src/geometry_validated_layout/models.py +++ b/src/geometry_validated_layout/models.py @@ -184,6 +184,22 @@ def object_references_are_valid(self) -> "LayoutProject": for obj in self.objects: if obj.parent is not None and obj.parent not in object_set: raise ValueError(f"{obj.id}: parent object does not exist") + + parents = {obj.id: obj.parent for obj in self.objects if obj.parent is not None} + for obj_id in object_ids: + path: list[str] = [] + positions: dict[str, int] = {} + current = obj_id + while current in parents: + if current in positions: + cycle = path[positions[current]:] + [current] + raise ValueError( + "parent relationships contain a cycle: " + " -> ".join(cycle) + ) + positions[current] = len(path) + path.append(current) + current = parents[current] + for run in self.module_runs: missing = [module_id for module_id in run.modules if module_id not in object_set] if missing: @@ -203,4 +219,3 @@ def object(self, object_id: str) -> LayoutObject: def load_project(path: Path) -> LayoutProject: return LayoutProject.model_validate(yaml.safe_load(path.read_text(encoding="utf-8"))) - diff --git a/tests/test_validation.py b/tests/test_validation.py index 18c069b..5e01704 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -1,5 +1,7 @@ from pathlib import Path +import pytest + from geometry_validated_layout.models import load_project from geometry_validated_layout.validation import validate_project @@ -94,3 +96,31 @@ def test_explicit_child_polygon_is_validated_without_recomputing_rectangle() -> report, geoms = validate_project(adjusted) assert report.hard_pass, report.errors assert list(geoms["cooktop_02"].polygon.exterior.coords)[0] == (105.0, -216.0) + + +def test_self_parent_relationship_is_rejected() -> None: + project = load_project(ROOT / "examples/irregular_kitchen/project.yaml") + data = project.model_dump(mode="json") + base = next(obj for obj in data["objects"] if obj["id"] == "base_01") + base["parent"] = "base_01" + base["parent_relationship"] = "contained_by" + + with pytest.raises(ValueError, match=r"parent relationships contain a cycle: base_01 -> base_01"): + type(project).model_validate(data) + + +def test_multi_object_parent_cycle_is_rejected() -> None: + project = load_project(ROOT / "examples/irregular_kitchen/project.yaml") + data = project.model_dump(mode="json") + base_01 = next(obj for obj in data["objects"] if obj["id"] == "base_01") + base_02 = next(obj for obj in data["objects"] if obj["id"] == "base_02") + base_01["parent"] = "base_02" + base_01["parent_relationship"] = "contained_by" + base_02["parent"] = "base_01" + base_02["parent_relationship"] = "contained_by" + + with pytest.raises( + ValueError, + match=r"parent relationships contain a cycle: base_01 -> base_02 -> base_01", + ): + type(project).model_validate(data)