Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
push:
pull_request:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
Expand Down
21 changes: 12 additions & 9 deletions STATUS.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
# 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
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.
- 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,
Expand Down Expand Up @@ -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.

Expand Down
8 changes: 6 additions & 2 deletions docs/RELEASE_READINESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Expand All @@ -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
Expand Down
17 changes: 16 additions & 1 deletion src/geometry_validated_layout/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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")))

30 changes: 30 additions & 0 deletions tests/test_validation.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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)